X-Git-Url: https://git.openstreetmap.org/rails.git/blobdiff_plain/32da856c00985bc2ebc7482da2ea4f76cf788832..c55d346d5f7501fd6f5f636e85c8a60ec67d88ee:/vendor/assets/iD/iD/mapillary-js/mapillary.unminified.js diff --git a/vendor/assets/iD/iD/mapillary-js/mapillary.unminified.js b/vendor/assets/iD/iD/mapillary-js/mapillary.unminified.js index 25c528dff..435894fb3 100644 --- a/vendor/assets/iD/iD/mapillary-js/mapillary.unminified.js +++ b/vendor/assets/iD/iD/mapillary-js/mapillary.unminified.js @@ -2,7 +2,7 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.mapillary = {})); -}(this, (function (exports) { 'use strict'; +})(this, (function (exports) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. @@ -23,495 +23,496 @@ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function isFunction(x) { - return typeof x === 'function'; + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var _enable_super_gross_mode_that_will_cause_bad_things = false; - var config = { - Promise: undefined, - set useDeprecatedSynchronousErrorHandling(value) { - if (value) { - var error = /*@__PURE__*/ new Error(); - /*@__PURE__*/ console.warn('DEPRECATED! RxJS was set to use deprecated synchronous error handling behavior by code at: \n' + error.stack); - } - _enable_super_gross_mode_that_will_cause_bad_things = value; - }, - get useDeprecatedSynchronousErrorHandling() { - return _enable_super_gross_mode_that_will_cause_bad_things; - }, - }; - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function hostReportError(err) { - setTimeout(function () { throw err; }, 0); + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } } - /** PURE_IMPORTS_START _config,_util_hostReportError PURE_IMPORTS_END */ - var empty$1 = { - closed: true, - next: function (value) { }, - error: function (err) { - if (config.useDeprecatedSynchronousErrorHandling) { - throw err; + function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; } - else { - hostReportError(err); + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + } + + function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); } - }, - complete: function () { } - }; + finally { if (e) throw e.error; } + } + return ar; + } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var isArray$1 = /*@__PURE__*/ (function () { return Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); })(); + function __spreadArray(to, from) { + for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) + to[j] = from[i]; + return to; + } + + function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); + } + + function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } + } + + function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } + } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function isObject$1(x) { - return x !== null && typeof x === 'object'; + function isFunction(value) { + return typeof value === 'function'; } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var UnsubscriptionErrorImpl = /*@__PURE__*/ (function () { - function UnsubscriptionErrorImpl(errors) { - Error.call(this); - this.message = errors ? - errors.length + " errors occurred during unsubscription:\n" + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') : ''; + function createErrorClass(createImpl) { + var _super = function (instance) { + Error.call(instance); + instance.stack = new Error().stack; + }; + var ctorFunc = createImpl(_super); + ctorFunc.prototype = Object.create(Error.prototype); + ctorFunc.prototype.constructor = ctorFunc; + return ctorFunc; + } + + var UnsubscriptionError = createErrorClass(function (_super) { + return function UnsubscriptionErrorImpl(errors) { + _super(this); + this.message = errors + ? errors.length + " errors occurred during unsubscription:\n" + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') + : ''; this.name = 'UnsubscriptionError'; this.errors = errors; - return this; + }; + }); + + function arrRemove(arr, item) { + if (arr) { + var index = arr.indexOf(item); + 0 <= index && arr.splice(index, 1); } - UnsubscriptionErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); - return UnsubscriptionErrorImpl; - })(); - var UnsubscriptionError = UnsubscriptionErrorImpl; + } - /** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_UnsubscriptionError PURE_IMPORTS_END */ - var Subscription = /*@__PURE__*/ (function () { - function Subscription(unsubscribe) { + var Subscription = (function () { + function Subscription(initialTeardown) { + this.initialTeardown = initialTeardown; this.closed = false; - this._parentOrParents = null; - this._subscriptions = null; - if (unsubscribe) { - this._ctorUnsubscribe = true; - this._unsubscribe = unsubscribe; - } + this._parentage = null; + this._teardowns = null; } Subscription.prototype.unsubscribe = function () { + var e_1, _a, e_2, _b; var errors; - if (this.closed) { - return; - } - var _a = this, _parentOrParents = _a._parentOrParents, _ctorUnsubscribe = _a._ctorUnsubscribe, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; - this.closed = true; - this._parentOrParents = null; - this._subscriptions = null; - if (_parentOrParents instanceof Subscription) { - _parentOrParents.remove(this); - } - else if (_parentOrParents !== null) { - for (var index = 0; index < _parentOrParents.length; ++index) { - var parent_1 = _parentOrParents[index]; - parent_1.remove(this); - } - } - if (isFunction(_unsubscribe)) { - if (_ctorUnsubscribe) { - this._unsubscribe = undefined; - } - try { - _unsubscribe.call(this); - } - catch (e) { - errors = e instanceof UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e]; - } - } - if (isArray$1(_subscriptions)) { - var index = -1; - var len = _subscriptions.length; - while (++index < len) { - var sub = _subscriptions[index]; - if (isObject$1(sub)) { + if (!this.closed) { + this.closed = true; + var _parentage = this._parentage; + if (_parentage) { + this._parentage = null; + if (Array.isArray(_parentage)) { try { - sub.unsubscribe(); - } - catch (e) { - errors = errors || []; - if (e instanceof UnsubscriptionError) { - errors = errors.concat(flattenUnsubscriptionErrors(e.errors)); + for (var _parentage_1 = __values(_parentage), _parentage_1_1 = _parentage_1.next(); !_parentage_1_1.done; _parentage_1_1 = _parentage_1.next()) { + var parent_1 = _parentage_1_1.value; + parent_1.remove(this); } - else { - errors.push(e); + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_parentage_1_1 && !_parentage_1_1.done && (_a = _parentage_1.return)) _a.call(_parentage_1); } + finally { if (e_1) throw e_1.error; } } } + else { + _parentage.remove(this); + } } - } - if (errors) { - throw new UnsubscriptionError(errors); - } - }; - Subscription.prototype.add = function (teardown) { - var subscription = teardown; - if (!teardown) { - return Subscription.EMPTY; - } - switch (typeof teardown) { - case 'function': - subscription = new Subscription(teardown); - case 'object': - if (subscription === this || subscription.closed || typeof subscription.unsubscribe !== 'function') { - return subscription; + var initialTeardown = this.initialTeardown; + if (isFunction(initialTeardown)) { + try { + initialTeardown(); } - else if (this.closed) { - subscription.unsubscribe(); - return subscription; + catch (e) { + errors = e instanceof UnsubscriptionError ? e.errors : [e]; + } + } + var _teardowns = this._teardowns; + if (_teardowns) { + this._teardowns = null; + try { + for (var _teardowns_1 = __values(_teardowns), _teardowns_1_1 = _teardowns_1.next(); !_teardowns_1_1.done; _teardowns_1_1 = _teardowns_1.next()) { + var teardown_1 = _teardowns_1_1.value; + try { + execTeardown(teardown_1); + } + catch (err) { + errors = errors !== null && errors !== void 0 ? errors : []; + if (err instanceof UnsubscriptionError) { + errors = __spreadArray(__spreadArray([], __read(errors)), __read(err.errors)); + } + else { + errors.push(err); + } + } + } } - else if (!(subscription instanceof Subscription)) { - var tmp = subscription; - subscription = new Subscription(); - subscription._subscriptions = [tmp]; + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (_teardowns_1_1 && !_teardowns_1_1.done && (_b = _teardowns_1.return)) _b.call(_teardowns_1); + } + finally { if (e_2) throw e_2.error; } } - break; - default: { - throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); } - } - var _parentOrParents = subscription._parentOrParents; - if (_parentOrParents === null) { - subscription._parentOrParents = this; - } - else if (_parentOrParents instanceof Subscription) { - if (_parentOrParents === this) { - return subscription; + if (errors) { + throw new UnsubscriptionError(errors); } - subscription._parentOrParents = [_parentOrParents, this]; } - else if (_parentOrParents.indexOf(this) === -1) { - _parentOrParents.push(this); - } - else { - return subscription; + }; + Subscription.prototype.add = function (teardown) { + var _a; + if (teardown && teardown !== this) { + if (this.closed) { + execTeardown(teardown); + } + else { + if (teardown instanceof Subscription) { + if (teardown.closed || teardown._hasParent(this)) { + return; + } + teardown._addParent(this); + } + (this._teardowns = (_a = this._teardowns) !== null && _a !== void 0 ? _a : []).push(teardown); + } } - var subscriptions = this._subscriptions; - if (subscriptions === null) { - this._subscriptions = [subscription]; + }; + Subscription.prototype._hasParent = function (parent) { + var _parentage = this._parentage; + return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent)); + }; + Subscription.prototype._addParent = function (parent) { + var _parentage = this._parentage; + this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent; + }; + Subscription.prototype._removeParent = function (parent) { + var _parentage = this._parentage; + if (_parentage === parent) { + this._parentage = null; } - else { - subscriptions.push(subscription); + else if (Array.isArray(_parentage)) { + arrRemove(_parentage, parent); } - return subscription; }; - Subscription.prototype.remove = function (subscription) { - var subscriptions = this._subscriptions; - if (subscriptions) { - var subscriptionIndex = subscriptions.indexOf(subscription); - if (subscriptionIndex !== -1) { - subscriptions.splice(subscriptionIndex, 1); - } + Subscription.prototype.remove = function (teardown) { + var _teardowns = this._teardowns; + _teardowns && arrRemove(_teardowns, teardown); + if (teardown instanceof Subscription) { + teardown._removeParent(this); } }; - Subscription.EMPTY = (function (empty) { + Subscription.EMPTY = (function () { + var empty = new Subscription(); empty.closed = true; return empty; - }(new Subscription())); + })(); return Subscription; }()); - function flattenUnsubscriptionErrors(errors) { - return errors.reduce(function (errs, err) { return errs.concat((err instanceof UnsubscriptionError) ? err.errors : err); }, []); + var EMPTY_SUBSCRIPTION = Subscription.EMPTY; + function isSubscription(value) { + return (value instanceof Subscription || + (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))); + } + function execTeardown(teardown) { + if (isFunction(teardown)) { + teardown(); + } + else { + teardown.unsubscribe(); + } } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var rxSubscriber = /*@__PURE__*/ (function () { - return typeof Symbol === 'function' - ? /*@__PURE__*/ Symbol('rxSubscriber') - : '@@rxSubscriber_' + /*@__PURE__*/ Math.random(); - })(); + var config = { + onUnhandledError: null, + onStoppedNotification: null, + Promise: undefined, + useDeprecatedSynchronousErrorHandling: false, + useDeprecatedNextContext: false, + }; + + var timeoutProvider = { + setTimeout: function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var delegate = timeoutProvider.delegate; + return ((delegate === null || delegate === void 0 ? void 0 : delegate.setTimeout) || setTimeout).apply(void 0, __spreadArray([], __read(args))); + }, + clearTimeout: function (handle) { + var delegate = timeoutProvider.delegate; + return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearTimeout) || clearTimeout)(handle); + }, + delegate: undefined, + }; + + function reportUnhandledError(err) { + timeoutProvider.setTimeout(function () { + { + throw err; + } + }); + } + + function noop() { } + + var COMPLETE_NOTIFICATION = (function () { return createNotification('C', undefined, undefined); })(); + function errorNotification(error) { + return createNotification('E', undefined, error); + } + function nextNotification(value) { + return createNotification('N', value, undefined); + } + function createNotification(kind, value, error) { + return { + kind: kind, + value: value, + error: error, + }; + } + + var context = null; + function errorContext(cb) { + if (config.useDeprecatedSynchronousErrorHandling) { + var isRoot = !context; + if (isRoot) { + context = { errorThrown: false, error: null }; + } + cb(); + if (isRoot) { + var _a = context, errorThrown = _a.errorThrown, error = _a.error; + context = null; + if (errorThrown) { + throw error; + } + } + } + else { + cb(); + } + } - /** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */ - var Subscriber = /*@__PURE__*/ (function (_super) { + var Subscriber = (function (_super) { __extends(Subscriber, _super); - function Subscriber(destinationOrNext, error, complete) { + function Subscriber(destination) { var _this = _super.call(this) || this; - _this.syncErrorValue = null; - _this.syncErrorThrown = false; - _this.syncErrorThrowable = false; _this.isStopped = false; - switch (arguments.length) { - case 0: - _this.destination = empty$1; - break; - case 1: - if (!destinationOrNext) { - _this.destination = empty$1; - break; - } - if (typeof destinationOrNext === 'object') { - if (destinationOrNext instanceof Subscriber) { - _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; - _this.destination = destinationOrNext; - destinationOrNext.add(_this); - } - else { - _this.syncErrorThrowable = true; - _this.destination = new SafeSubscriber(_this, destinationOrNext); - } - break; - } - default: - _this.syncErrorThrowable = true; - _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete); - break; + if (destination) { + _this.destination = destination; + if (isSubscription(destination)) { + destination.add(_this); + } + } + else { + _this.destination = EMPTY_OBSERVER; } return _this; } - Subscriber.prototype[rxSubscriber] = function () { return this; }; Subscriber.create = function (next, error, complete) { - var subscriber = new Subscriber(next, error, complete); - subscriber.syncErrorThrowable = false; - return subscriber; + return new SafeSubscriber(next, error, complete); }; Subscriber.prototype.next = function (value) { - if (!this.isStopped) { + if (this.isStopped) { + handleStoppedNotification(nextNotification(value), this); + } + else { this._next(value); } }; Subscriber.prototype.error = function (err) { - if (!this.isStopped) { + if (this.isStopped) { + handleStoppedNotification(errorNotification(err), this); + } + else { this.isStopped = true; this._error(err); } }; Subscriber.prototype.complete = function () { - if (!this.isStopped) { + if (this.isStopped) { + handleStoppedNotification(COMPLETE_NOTIFICATION, this); + } + else { this.isStopped = true; this._complete(); } }; Subscriber.prototype.unsubscribe = function () { - if (this.closed) { - return; + if (!this.closed) { + this.isStopped = true; + _super.prototype.unsubscribe.call(this); + this.destination = null; } - this.isStopped = true; - _super.prototype.unsubscribe.call(this); }; Subscriber.prototype._next = function (value) { this.destination.next(value); }; Subscriber.prototype._error = function (err) { - this.destination.error(err); - this.unsubscribe(); + try { + this.destination.error(err); + } + finally { + this.unsubscribe(); + } }; Subscriber.prototype._complete = function () { - this.destination.complete(); - this.unsubscribe(); - }; - Subscriber.prototype._unsubscribeAndRecycle = function () { - var _parentOrParents = this._parentOrParents; - this._parentOrParents = null; - this.unsubscribe(); - this.closed = false; - this.isStopped = false; - this._parentOrParents = _parentOrParents; - return this; + try { + this.destination.complete(); + } + finally { + this.unsubscribe(); + } }; return Subscriber; }(Subscription)); - var SafeSubscriber = /*@__PURE__*/ (function (_super) { + var SafeSubscriber = (function (_super) { __extends(SafeSubscriber, _super); - function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { + function SafeSubscriber(observerOrNext, error, complete) { var _this = _super.call(this) || this; - _this._parentSubscriber = _parentSubscriber; var next; - var context = _this; if (isFunction(observerOrNext)) { next = observerOrNext; } else if (observerOrNext) { - next = observerOrNext.next; - error = observerOrNext.error; - complete = observerOrNext.complete; - if (observerOrNext !== empty$1) { - context = Object.create(observerOrNext); - if (isFunction(context.unsubscribe)) { - _this.add(context.unsubscribe.bind(context)); - } - context.unsubscribe = _this.unsubscribe.bind(_this); - } - } - _this._context = context; - _this._next = next; - _this._error = error; - _this._complete = complete; - return _this; - } - SafeSubscriber.prototype.next = function (value) { - if (!this.isStopped && this._next) { - var _parentSubscriber = this._parentSubscriber; - if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { - this.__tryOrUnsub(this._next, value); - } - else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) { - this.unsubscribe(); - } - } - }; - SafeSubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var _parentSubscriber = this._parentSubscriber; - var useDeprecatedSynchronousErrorHandling = config.useDeprecatedSynchronousErrorHandling; - if (this._error) { - if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { - this.__tryOrUnsub(this._error, err); - this.unsubscribe(); - } - else { - this.__tryOrSetError(_parentSubscriber, this._error, err); - this.unsubscribe(); - } - } - else if (!_parentSubscriber.syncErrorThrowable) { - this.unsubscribe(); - if (useDeprecatedSynchronousErrorHandling) { - throw err; - } - hostReportError(err); - } - else { - if (useDeprecatedSynchronousErrorHandling) { - _parentSubscriber.syncErrorValue = err; - _parentSubscriber.syncErrorThrown = true; - } - else { - hostReportError(err); - } - this.unsubscribe(); - } - } - }; - SafeSubscriber.prototype.complete = function () { - var _this = this; - if (!this.isStopped) { - var _parentSubscriber = this._parentSubscriber; - if (this._complete) { - var wrappedComplete = function () { return _this._complete.call(_this._context); }; - if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { - this.__tryOrUnsub(wrappedComplete); - this.unsubscribe(); - } - else { - this.__tryOrSetError(_parentSubscriber, wrappedComplete); - this.unsubscribe(); - } - } - else { - this.unsubscribe(); - } - } - }; - SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) { - try { - fn.call(this._context, value); - } - catch (err) { - this.unsubscribe(); - if (config.useDeprecatedSynchronousErrorHandling) { - throw err; + (next = observerOrNext.next, error = observerOrNext.error, complete = observerOrNext.complete); + var context_1; + if (_this && config.useDeprecatedNextContext) { + context_1 = Object.create(observerOrNext); + context_1.unsubscribe = function () { return _this.unsubscribe(); }; } else { - hostReportError(err); + context_1 = observerOrNext; } + next = next === null || next === void 0 ? void 0 : next.bind(context_1); + error = error === null || error === void 0 ? void 0 : error.bind(context_1); + complete = complete === null || complete === void 0 ? void 0 : complete.bind(context_1); } - }; - SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) { - if (!config.useDeprecatedSynchronousErrorHandling) { - throw new Error('bad call'); + _this.destination = { + next: next ? wrapForErrorHandling(next) : noop, + error: wrapForErrorHandling(error !== null && error !== void 0 ? error : defaultErrorHandler), + complete: complete ? wrapForErrorHandling(complete) : noop, + }; + return _this; + } + return SafeSubscriber; + }(Subscriber)); + function wrapForErrorHandling(handler, instance) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; } try { - fn.call(this._context, value); + handler.apply(void 0, __spreadArray([], __read(args))); } catch (err) { - if (config.useDeprecatedSynchronousErrorHandling) { - parent.syncErrorValue = err; - parent.syncErrorThrown = true; - return true; - } - else { - hostReportError(err); - return true; + { + reportUnhandledError(err); } } - return false; }; - SafeSubscriber.prototype._unsubscribe = function () { - var _parentSubscriber = this._parentSubscriber; - this._context = null; - this._parentSubscriber = null; - _parentSubscriber.unsubscribe(); - }; - return SafeSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START _Subscriber PURE_IMPORTS_END */ - function canReportError(observer) { - while (observer) { - var _a = observer, closed_1 = _a.closed, destination = _a.destination, isStopped = _a.isStopped; - if (closed_1 || isStopped) { - return false; - } - else if (destination && destination instanceof Subscriber) { - observer = destination; - } - else { - observer = null; - } - } - return true; } - - /** PURE_IMPORTS_START _Subscriber,_symbol_rxSubscriber,_Observer PURE_IMPORTS_END */ - function toSubscriber(nextOrObserver, error, complete) { - if (nextOrObserver) { - if (nextOrObserver instanceof Subscriber) { - return nextOrObserver; - } - if (nextOrObserver[rxSubscriber]) { - return nextOrObserver[rxSubscriber](); - } - } - if (!nextOrObserver && !error && !complete) { - return new Subscriber(empty$1); - } - return new Subscriber(nextOrObserver, error, complete); + function defaultErrorHandler(err) { + throw err; } + function handleStoppedNotification(notification, subscriber) { + var onStoppedNotification = config.onStoppedNotification; + onStoppedNotification && timeoutProvider.setTimeout(function () { return onStoppedNotification(notification, subscriber); }); + } + var EMPTY_OBSERVER = { + closed: true, + next: noop, + error: defaultErrorHandler, + complete: noop, + }; - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var observable = /*@__PURE__*/ (function () { return typeof Symbol === 'function' && Symbol.observable || '@@observable'; })(); + var observable = (function () { return (typeof Symbol === 'function' && Symbol.observable) || '@@observable'; })(); - /** PURE_IMPORTS_START PURE_IMPORTS_END */ function identity(x) { return x; } - /** PURE_IMPORTS_START _identity PURE_IMPORTS_END */ - function pipe() { - var fns = []; - for (var _i = 0; _i < arguments.length; _i++) { - fns[_i] = arguments[_i]; - } - return pipeFromArray(fns); - } function pipeFromArray(fns) { if (fns.length === 0) { return identity; @@ -524,10 +525,8 @@ }; } - /** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ - var Observable = /*@__PURE__*/ (function () { + var Observable = (function () { function Observable(subscribe) { - this._isScalar = false; if (subscribe) { this._subscribe = subscribe; } @@ -539,41 +538,27 @@ return observable; }; Observable.prototype.subscribe = function (observerOrNext, error, complete) { - var operator = this.operator; - var sink = toSubscriber(observerOrNext, error, complete); - if (operator) { - sink.add(operator.call(sink, this.source)); - } - else { - sink.add(this.source || (config.useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? - this._subscribe(sink) : - this._trySubscribe(sink)); - } - if (config.useDeprecatedSynchronousErrorHandling) { - if (sink.syncErrorThrowable) { - sink.syncErrorThrowable = false; - if (sink.syncErrorThrown) { - throw sink.syncErrorValue; - } - } - } - return sink; + var _this = this; + var subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete); + errorContext(function () { + var _a = _this, operator = _a.operator, source = _a.source; + subscriber.add(operator + ? + operator.call(subscriber, source) + : source + ? + _this._subscribe(subscriber) + : + _this._trySubscribe(subscriber)); + }); + return subscriber; }; Observable.prototype._trySubscribe = function (sink) { try { return this._subscribe(sink); } catch (err) { - if (config.useDeprecatedSynchronousErrorHandling) { - sink.syncErrorThrown = true; - sink.syncErrorValue = err; - } - if (canReportError(sink)) { - sink.error(err); - } - else { - console.warn(err); - } + sink.error(err); } }; Observable.prototype.forEach = function (next, promiseCtor) { @@ -587,16 +572,14 @@ } catch (err) { reject(err); - if (subscription) { - subscription.unsubscribe(); - } + subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe(); } }, reject, resolve); }); }; Observable.prototype._subscribe = function (subscriber) { - var source = this.source; - return source && source.subscribe(subscriber); + var _a; + return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber); }; Observable.prototype[observable] = function () { return this; @@ -606,9 +589,6 @@ for (var _i = 0; _i < arguments.length; _i++) { operations[_i] = arguments[_i]; } - if (operations.length === 0) { - return this; - } return pipeFromArray(operations)(this); }; Observable.prototype.toPromise = function (promiseCtor) { @@ -616,7 +596,7 @@ promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor(function (resolve, reject) { var value; - _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); }); + _this.subscribe(function (x) { return (value = x); }, function (err) { return reject(err); }, function () { return resolve(value); }); }); }; Observable.create = function (subscribe) { @@ -625,155 +605,280 @@ return Observable; }()); function getPromiseCtor(promiseCtor) { - if (!promiseCtor) { - promiseCtor = Promise; - } - if (!promiseCtor) { - throw new Error('no Promise impl found'); - } - return promiseCtor; + var _a; + return (_a = promiseCtor !== null && promiseCtor !== void 0 ? promiseCtor : config.Promise) !== null && _a !== void 0 ? _a : Promise; + } + function isObserver(value) { + return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete); + } + function isSubscriber(value) { + return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value)); } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var ObjectUnsubscribedErrorImpl = /*@__PURE__*/ (function () { - function ObjectUnsubscribedErrorImpl() { - Error.call(this); - this.message = 'object unsubscribed'; - this.name = 'ObjectUnsubscribedError'; - return this; + function hasLift(source) { + return isFunction(source === null || source === void 0 ? void 0 : source.lift); + } + function operate(init) { + return function (source) { + if (hasLift(source)) { + return source.lift(function (liftedSource) { + try { + return init(liftedSource, this); + } + catch (err) { + this.error(err); + } + }); + } + throw new TypeError('Unable to lift unknown Observable type'); + }; + } + + var OperatorSubscriber = (function (_super) { + __extends(OperatorSubscriber, _super); + function OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize) { + var _this = _super.call(this, destination) || this; + _this.onFinalize = onFinalize; + _this._next = onNext + ? function (value) { + try { + onNext(value); + } + catch (err) { + destination.error(err); + } + } + : _super.prototype._next; + _this._error = onError + ? function (err) { + try { + onError(err); + } + catch (err) { + destination.error(err); + } + finally { + this.unsubscribe(); + } + } + : _super.prototype._error; + _this._complete = onComplete + ? function () { + try { + onComplete(); + } + catch (err) { + destination.error(err); + } + finally { + this.unsubscribe(); + } + } + : _super.prototype._complete; + return _this; } - ObjectUnsubscribedErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); - return ObjectUnsubscribedErrorImpl; - })(); - var ObjectUnsubscribedError = ObjectUnsubscribedErrorImpl; + OperatorSubscriber.prototype.unsubscribe = function () { + var _a; + var closed = this.closed; + _super.prototype.unsubscribe.call(this); + !closed && ((_a = this.onFinalize) === null || _a === void 0 ? void 0 : _a.call(this)); + }; + return OperatorSubscriber; + }(Subscriber)); + + function refCount() { + return operate(function (source, subscriber) { + var connection = null; + source._refCount++; + var refCounter = new OperatorSubscriber(subscriber, undefined, undefined, undefined, function () { + if (!source || source._refCount <= 0 || 0 < --source._refCount) { + connection = null; + return; + } + var sharedConnection = source._connection; + var conn = connection; + connection = null; + if (sharedConnection && (!conn || sharedConnection === conn)) { + sharedConnection.unsubscribe(); + } + subscriber.unsubscribe(); + }); + source.subscribe(refCounter); + if (!refCounter.closed) { + connection = source.connect(); + } + }); + } - /** PURE_IMPORTS_START tslib,_Subscription PURE_IMPORTS_END */ - var SubjectSubscription = /*@__PURE__*/ (function (_super) { - __extends(SubjectSubscription, _super); - function SubjectSubscription(subject, subscriber) { + var ConnectableObservable = (function (_super) { + __extends(ConnectableObservable, _super); + function ConnectableObservable(source, subjectFactory) { var _this = _super.call(this) || this; - _this.subject = subject; - _this.subscriber = subscriber; - _this.closed = false; + _this.source = source; + _this.subjectFactory = subjectFactory; + _this._subject = null; + _this._refCount = 0; + _this._connection = null; + if (hasLift(source)) { + _this.lift = source.lift; + } return _this; } - SubjectSubscription.prototype.unsubscribe = function () { - if (this.closed) { - return; - } - this.closed = true; - var subject = this.subject; - var observers = subject.observers; - this.subject = null; - if (!observers || observers.length === 0 || subject.isStopped || subject.closed) { - return; + ConnectableObservable.prototype._subscribe = function (subscriber) { + return this.getSubject().subscribe(subscriber); + }; + ConnectableObservable.prototype.getSubject = function () { + var subject = this._subject; + if (!subject || subject.isStopped) { + this._subject = this.subjectFactory(); } - var subscriberIndex = observers.indexOf(this.subscriber); - if (subscriberIndex !== -1) { - observers.splice(subscriberIndex, 1); + return this._subject; + }; + ConnectableObservable.prototype._teardown = function () { + this._refCount = 0; + var _connection = this._connection; + this._subject = this._connection = null; + _connection === null || _connection === void 0 ? void 0 : _connection.unsubscribe(); + }; + ConnectableObservable.prototype.connect = function () { + var _this = this; + var connection = this._connection; + if (!connection) { + connection = this._connection = new Subscription(); + var subject_1 = this.getSubject(); + connection.add(this.source.subscribe(new OperatorSubscriber(subject_1, undefined, function () { + _this._teardown(); + subject_1.complete(); + }, function (err) { + _this._teardown(); + subject_1.error(err); + }, function () { return _this._teardown(); }))); + if (connection.closed) { + this._connection = null; + connection = Subscription.EMPTY; + } } + return connection; }; - return SubjectSubscription; - }(Subscription)); + ConnectableObservable.prototype.refCount = function () { + return refCount()(this); + }; + return ConnectableObservable; + }(Observable)); - /** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */ - var SubjectSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SubjectSubscriber, _super); - function SubjectSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.destination = destination; - return _this; - } - return SubjectSubscriber; - }(Subscriber)); - var Subject = /*@__PURE__*/ (function (_super) { + var ObjectUnsubscribedError = createErrorClass(function (_super) { + return function ObjectUnsubscribedErrorImpl() { + _super(this); + this.name = 'ObjectUnsubscribedError'; + this.message = 'object unsubscribed'; + }; + }); + + var Subject = (function (_super) { __extends(Subject, _super); function Subject() { var _this = _super.call(this) || this; - _this.observers = []; _this.closed = false; + _this.observers = []; _this.isStopped = false; _this.hasError = false; _this.thrownError = null; return _this; } - Subject.prototype[rxSubscriber] = function () { - return new SubjectSubscriber(this); - }; Subject.prototype.lift = function (operator) { var subject = new AnonymousSubject(this, this); subject.operator = operator; return subject; }; - Subject.prototype.next = function (value) { + Subject.prototype._throwIfClosed = function () { if (this.closed) { throw new ObjectUnsubscribedError(); } - if (!this.isStopped) { - var observers = this.observers; - var len = observers.length; - var copy = observers.slice(); - for (var i = 0; i < len; i++) { - copy[i].next(value); + }; + Subject.prototype.next = function (value) { + var _this = this; + errorContext(function () { + var e_1, _a; + _this._throwIfClosed(); + if (!_this.isStopped) { + var copy = _this.observers.slice(); + try { + for (var copy_1 = __values(copy), copy_1_1 = copy_1.next(); !copy_1_1.done; copy_1_1 = copy_1.next()) { + var observer = copy_1_1.value; + observer.next(value); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (copy_1_1 && !copy_1_1.done && (_a = copy_1.return)) _a.call(copy_1); + } + finally { if (e_1) throw e_1.error; } + } } - } + }); }; Subject.prototype.error = function (err) { - if (this.closed) { - throw new ObjectUnsubscribedError(); - } - this.hasError = true; - this.thrownError = err; - this.isStopped = true; - var observers = this.observers; - var len = observers.length; - var copy = observers.slice(); - for (var i = 0; i < len; i++) { - copy[i].error(err); - } - this.observers.length = 0; + var _this = this; + errorContext(function () { + _this._throwIfClosed(); + if (!_this.isStopped) { + _this.hasError = _this.isStopped = true; + _this.thrownError = err; + var observers = _this.observers; + while (observers.length) { + observers.shift().error(err); + } + } + }); }; Subject.prototype.complete = function () { - if (this.closed) { - throw new ObjectUnsubscribedError(); - } - this.isStopped = true; - var observers = this.observers; - var len = observers.length; - var copy = observers.slice(); - for (var i = 0; i < len; i++) { - copy[i].complete(); - } - this.observers.length = 0; + var _this = this; + errorContext(function () { + _this._throwIfClosed(); + if (!_this.isStopped) { + _this.isStopped = true; + var observers = _this.observers; + while (observers.length) { + observers.shift().complete(); + } + } + }); }; Subject.prototype.unsubscribe = function () { - this.isStopped = true; - this.closed = true; + this.isStopped = this.closed = true; this.observers = null; }; + Object.defineProperty(Subject.prototype, "observed", { + get: function () { + var _a; + return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0; + }, + enumerable: false, + configurable: true + }); Subject.prototype._trySubscribe = function (subscriber) { - if (this.closed) { - throw new ObjectUnsubscribedError(); - } - else { - return _super.prototype._trySubscribe.call(this, subscriber); - } + this._throwIfClosed(); + return _super.prototype._trySubscribe.call(this, subscriber); }; Subject.prototype._subscribe = function (subscriber) { - if (this.closed) { - throw new ObjectUnsubscribedError(); - } - else if (this.hasError) { - subscriber.error(this.thrownError); - return Subscription.EMPTY; + this._throwIfClosed(); + this._checkFinalizedStatuses(subscriber); + return this._innerSubscribe(subscriber); + }; + Subject.prototype._innerSubscribe = function (subscriber) { + var _a = this, hasError = _a.hasError, isStopped = _a.isStopped, observers = _a.observers; + return hasError || isStopped + ? EMPTY_SUBSCRIPTION + : (observers.push(subscriber), new Subscription(function () { return arrRemove(observers, subscriber); })); + }; + Subject.prototype._checkFinalizedStatuses = function (subscriber) { + var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, isStopped = _a.isStopped; + if (hasError) { + subscriber.error(thrownError); } - else if (this.isStopped) { + else if (isStopped) { subscriber.complete(); - return Subscription.EMPTY; - } - else { - this.observers.push(subscriber); - return new SubjectSubscription(this, subscriber); } }; Subject.prototype.asObservable = function () { @@ -786,7 +891,7 @@ }; return Subject; }(Observable)); - var AnonymousSubject = /*@__PURE__*/ (function (_super) { + var AnonymousSubject = (function (_super) { __extends(AnonymousSubject, _super); function AnonymousSubject(destination, source) { var _this = _super.call(this) || this; @@ -795,179 +900,25 @@ return _this; } AnonymousSubject.prototype.next = function (value) { - var destination = this.destination; - if (destination && destination.next) { - destination.next(value); - } + var _a, _b; + (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, value); }; AnonymousSubject.prototype.error = function (err) { - var destination = this.destination; - if (destination && destination.error) { - this.destination.error(err); - } + var _a, _b; + (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err); }; AnonymousSubject.prototype.complete = function () { - var destination = this.destination; - if (destination && destination.complete) { - this.destination.complete(); - } + var _a, _b; + (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a); }; AnonymousSubject.prototype._subscribe = function (subscriber) { - var source = this.source; - if (source) { - return this.source.subscribe(subscriber); - } - else { - return Subscription.EMPTY; - } + var _a, _b; + return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : EMPTY_SUBSCRIPTION; }; return AnonymousSubject; }(Subject)); - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function refCount() { - return function refCountOperatorFunction(source) { - return source.lift(new RefCountOperator(source)); - }; - } - var RefCountOperator = /*@__PURE__*/ (function () { - function RefCountOperator(connectable) { - this.connectable = connectable; - } - RefCountOperator.prototype.call = function (subscriber, source) { - var connectable = this.connectable; - connectable._refCount++; - var refCounter = new RefCountSubscriber(subscriber, connectable); - var subscription = source.subscribe(refCounter); - if (!refCounter.closed) { - refCounter.connection = connectable.connect(); - } - return subscription; - }; - return RefCountOperator; - }()); - var RefCountSubscriber = /*@__PURE__*/ (function (_super) { - __extends(RefCountSubscriber, _super); - function RefCountSubscriber(destination, connectable) { - var _this = _super.call(this, destination) || this; - _this.connectable = connectable; - return _this; - } - RefCountSubscriber.prototype._unsubscribe = function () { - var connectable = this.connectable; - if (!connectable) { - this.connection = null; - return; - } - this.connectable = null; - var refCount = connectable._refCount; - if (refCount <= 0) { - this.connection = null; - return; - } - connectable._refCount = refCount - 1; - if (refCount > 1) { - this.connection = null; - return; - } - var connection = this.connection; - var sharedConnection = connectable._connection; - this.connection = null; - if (sharedConnection && (!connection || sharedConnection === connection)) { - sharedConnection.unsubscribe(); - } - }; - return RefCountSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_Subject,_Observable,_Subscriber,_Subscription,_operators_refCount PURE_IMPORTS_END */ - var ConnectableObservable = /*@__PURE__*/ (function (_super) { - __extends(ConnectableObservable, _super); - function ConnectableObservable(source, subjectFactory) { - var _this = _super.call(this) || this; - _this.source = source; - _this.subjectFactory = subjectFactory; - _this._refCount = 0; - _this._isComplete = false; - return _this; - } - ConnectableObservable.prototype._subscribe = function (subscriber) { - return this.getSubject().subscribe(subscriber); - }; - ConnectableObservable.prototype.getSubject = function () { - var subject = this._subject; - if (!subject || subject.isStopped) { - this._subject = this.subjectFactory(); - } - return this._subject; - }; - ConnectableObservable.prototype.connect = function () { - var connection = this._connection; - if (!connection) { - this._isComplete = false; - connection = this._connection = new Subscription(); - connection.add(this.source - .subscribe(new ConnectableSubscriber(this.getSubject(), this))); - if (connection.closed) { - this._connection = null; - connection = Subscription.EMPTY; - } - } - return connection; - }; - ConnectableObservable.prototype.refCount = function () { - return refCount()(this); - }; - return ConnectableObservable; - }(Observable)); - var connectableObservableDescriptor = /*@__PURE__*/ (function () { - var connectableProto = ConnectableObservable.prototype; - return { - operator: { value: null }, - _refCount: { value: 0, writable: true }, - _subject: { value: null, writable: true }, - _connection: { value: null, writable: true }, - _subscribe: { value: connectableProto._subscribe }, - _isComplete: { value: connectableProto._isComplete, writable: true }, - getSubject: { value: connectableProto.getSubject }, - connect: { value: connectableProto.connect }, - refCount: { value: connectableProto.refCount } - }; - })(); - var ConnectableSubscriber = /*@__PURE__*/ (function (_super) { - __extends(ConnectableSubscriber, _super); - function ConnectableSubscriber(destination, connectable) { - var _this = _super.call(this, destination) || this; - _this.connectable = connectable; - return _this; - } - ConnectableSubscriber.prototype._error = function (err) { - this._unsubscribe(); - _super.prototype._error.call(this, err); - }; - ConnectableSubscriber.prototype._complete = function () { - this.connectable._isComplete = true; - this._unsubscribe(); - _super.prototype._complete.call(this); - }; - ConnectableSubscriber.prototype._unsubscribe = function () { - var connectable = this.connectable; - if (connectable) { - this.connectable = null; - var connection = connectable._connection; - connectable._refCount = 0; - connectable._subject = null; - connectable._connection = null; - if (connection) { - connection.unsubscribe(); - } - } - }; - return ConnectableSubscriber; - }(SubjectSubscriber)); - - /** PURE_IMPORTS_START tslib,_Subject,_util_ObjectUnsubscribedError PURE_IMPORTS_END */ - var BehaviorSubject = /*@__PURE__*/ (function (_super) { + var BehaviorSubject = (function (_super) { __extends(BehaviorSubject, _super); function BehaviorSubject(_value) { var _this = _super.call(this) || this; @@ -978,47 +929,117 @@ get: function () { return this.getValue(); }, - enumerable: true, + enumerable: false, configurable: true }); BehaviorSubject.prototype._subscribe = function (subscriber) { var subscription = _super.prototype._subscribe.call(this, subscriber); - if (subscription && !subscription.closed) { - subscriber.next(this._value); - } + !subscription.closed && subscriber.next(this._value); return subscription; }; BehaviorSubject.prototype.getValue = function () { - if (this.hasError) { - throw this.thrownError; - } - else if (this.closed) { - throw new ObjectUnsubscribedError(); - } - else { - return this._value; + var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, _value = _a._value; + if (hasError) { + throw thrownError; } + this._throwIfClosed(); + return _value; }; BehaviorSubject.prototype.next = function (value) { - _super.prototype.next.call(this, this._value = value); + _super.prototype.next.call(this, (this._value = value)); }; return BehaviorSubject; }(Subject)); - /** PURE_IMPORTS_START tslib,_Subscription PURE_IMPORTS_END */ - var Action = /*@__PURE__*/ (function (_super) { - __extends(Action, _super); - function Action(scheduler, work) { - return _super.call(this) || this; + var dateTimestampProvider = { + now: function () { + return (dateTimestampProvider.delegate || Date).now(); + }, + delegate: undefined, + }; + + var ReplaySubject = (function (_super) { + __extends(ReplaySubject, _super); + function ReplaySubject(_bufferSize, _windowTime, _timestampProvider) { + if (_bufferSize === void 0) { _bufferSize = Infinity; } + if (_windowTime === void 0) { _windowTime = Infinity; } + if (_timestampProvider === void 0) { _timestampProvider = dateTimestampProvider; } + var _this = _super.call(this) || this; + _this._bufferSize = _bufferSize; + _this._windowTime = _windowTime; + _this._timestampProvider = _timestampProvider; + _this._buffer = []; + _this._infiniteTimeWindow = true; + _this._infiniteTimeWindow = _windowTime === Infinity; + _this._bufferSize = Math.max(1, _bufferSize); + _this._windowTime = Math.max(1, _windowTime); + return _this; } - Action.prototype.schedule = function (state, delay) { - return this; + ReplaySubject.prototype.next = function (value) { + var _a = this, isStopped = _a.isStopped, _buffer = _a._buffer, _infiniteTimeWindow = _a._infiniteTimeWindow, _timestampProvider = _a._timestampProvider, _windowTime = _a._windowTime; + if (!isStopped) { + _buffer.push(value); + !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime); + } + this._trimBuffer(); + _super.prototype.next.call(this, value); }; - return Action; + ReplaySubject.prototype._subscribe = function (subscriber) { + this._throwIfClosed(); + this._trimBuffer(); + var subscription = this._innerSubscribe(subscriber); + var _a = this, _infiniteTimeWindow = _a._infiniteTimeWindow, _buffer = _a._buffer; + var copy = _buffer.slice(); + for (var i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) { + subscriber.next(copy[i]); + } + this._checkFinalizedStatuses(subscriber); + return subscription; + }; + ReplaySubject.prototype._trimBuffer = function () { + var _a = this, _bufferSize = _a._bufferSize, _timestampProvider = _a._timestampProvider, _buffer = _a._buffer, _infiniteTimeWindow = _a._infiniteTimeWindow; + var adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize; + _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize); + if (!_infiniteTimeWindow) { + var now = _timestampProvider.now(); + var last = 0; + for (var i = 1; i < _buffer.length && _buffer[i] <= now; i += 2) { + last = i; + } + last && _buffer.splice(0, last + 1); + } + }; + return ReplaySubject; + }(Subject)); + + var Action = (function (_super) { + __extends(Action, _super); + function Action(scheduler, work) { + return _super.call(this) || this; + } + Action.prototype.schedule = function (state, delay) { + return this; + }; + return Action; }(Subscription)); - /** PURE_IMPORTS_START tslib,_Action PURE_IMPORTS_END */ - var AsyncAction = /*@__PURE__*/ (function (_super) { + var intervalProvider = { + setInterval: function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var delegate = intervalProvider.delegate; + return ((delegate === null || delegate === void 0 ? void 0 : delegate.setInterval) || setInterval).apply(void 0, __spreadArray([], __read(args))); + }, + clearInterval: function (handle) { + var delegate = intervalProvider.delegate; + return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearInterval) || clearInterval)(handle); + }, + delegate: undefined, + }; + + var AsyncAction = (function (_super) { __extends(AsyncAction, _super); function AsyncAction(scheduler, work) { var _this = _super.call(this, scheduler, work) || this; @@ -1028,9 +1049,7 @@ return _this; } AsyncAction.prototype.schedule = function (state, delay) { - if (delay === void 0) { - delay = 0; - } + if (delay === void 0) { delay = 0; } if (this.closed) { return this; } @@ -1045,20 +1064,16 @@ this.id = this.id || this.requestAsyncId(scheduler, this.id, delay); return this; }; - AsyncAction.prototype.requestAsyncId = function (scheduler, id, delay) { - if (delay === void 0) { - delay = 0; - } - return setInterval(scheduler.flush.bind(scheduler, this), delay); + AsyncAction.prototype.requestAsyncId = function (scheduler, _id, delay) { + if (delay === void 0) { delay = 0; } + return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay); }; - AsyncAction.prototype.recycleAsyncId = function (scheduler, id, delay) { - if (delay === void 0) { - delay = 0; - } - if (delay !== null && this.delay === delay && this.pending === false) { + AsyncAction.prototype.recycleAsyncId = function (_scheduler, id, delay) { + if (delay === void 0) { delay = 0; } + if (delay != null && this.delay === delay && this.pending === false) { return id; } - clearInterval(id); + intervalProvider.clearInterval(id); return undefined; }; AsyncAction.prototype.execute = function (state, delay) { @@ -1074,144 +1089,78 @@ this.id = this.recycleAsyncId(this.scheduler, this.id, null); } }; - AsyncAction.prototype._execute = function (state, delay) { + AsyncAction.prototype._execute = function (state, _delay) { var errored = false; - var errorValue = undefined; + var errorValue; try { this.work(state); } catch (e) { errored = true; - errorValue = !!e && e || new Error(e); + errorValue = (!!e && e) || new Error(e); } if (errored) { this.unsubscribe(); return errorValue; } }; - AsyncAction.prototype._unsubscribe = function () { - var id = this.id; - var scheduler = this.scheduler; - var actions = scheduler.actions; - var index = actions.indexOf(this); - this.work = null; - this.state = null; - this.pending = false; - this.scheduler = null; - if (index !== -1) { - actions.splice(index, 1); - } - if (id != null) { - this.id = this.recycleAsyncId(scheduler, id, null); + AsyncAction.prototype.unsubscribe = function () { + if (!this.closed) { + var _a = this, id = _a.id, scheduler = _a.scheduler; + var actions = scheduler.actions; + this.work = this.state = this.scheduler = null; + this.pending = false; + arrRemove(actions, this); + if (id != null) { + this.id = this.recycleAsyncId(scheduler, id, null); + } + this.delay = null; + _super.prototype.unsubscribe.call(this); } - this.delay = null; }; return AsyncAction; }(Action)); - /** PURE_IMPORTS_START tslib,_AsyncAction PURE_IMPORTS_END */ - var QueueAction = /*@__PURE__*/ (function (_super) { - __extends(QueueAction, _super); - function QueueAction(scheduler, work) { - var _this = _super.call(this, scheduler, work) || this; - _this.scheduler = scheduler; - _this.work = work; - return _this; - } - QueueAction.prototype.schedule = function (state, delay) { - if (delay === void 0) { - delay = 0; - } - if (delay > 0) { - return _super.prototype.schedule.call(this, state, delay); - } - this.delay = delay; - this.state = state; - this.scheduler.flush(this); - return this; - }; - QueueAction.prototype.execute = function (state, delay) { - return (delay > 0 || this.closed) ? - _super.prototype.execute.call(this, state, delay) : - this._execute(state, delay); - }; - QueueAction.prototype.requestAsyncId = function (scheduler, id, delay) { - if (delay === void 0) { - delay = 0; - } - if ((delay !== null && delay > 0) || (delay === null && this.delay > 0)) { - return _super.prototype.requestAsyncId.call(this, scheduler, id, delay); - } - return scheduler.flush(this); - }; - return QueueAction; - }(AsyncAction)); - - var Scheduler = /*@__PURE__*/ (function () { - function Scheduler(SchedulerAction, now) { - if (now === void 0) { - now = Scheduler.now; - } - this.SchedulerAction = SchedulerAction; + var Scheduler = (function () { + function Scheduler(schedulerActionCtor, now) { + if (now === void 0) { now = Scheduler.now; } + this.schedulerActionCtor = schedulerActionCtor; this.now = now; } Scheduler.prototype.schedule = function (work, delay, state) { - if (delay === void 0) { - delay = 0; - } - return new this.SchedulerAction(this, work).schedule(state, delay); + if (delay === void 0) { delay = 0; } + return new this.schedulerActionCtor(this, work).schedule(state, delay); }; - Scheduler.now = function () { return Date.now(); }; + Scheduler.now = dateTimestampProvider.now; return Scheduler; }()); - /** PURE_IMPORTS_START tslib,_Scheduler PURE_IMPORTS_END */ - var AsyncScheduler = /*@__PURE__*/ (function (_super) { + var AsyncScheduler = (function (_super) { __extends(AsyncScheduler, _super); function AsyncScheduler(SchedulerAction, now) { - if (now === void 0) { - now = Scheduler.now; - } - var _this = _super.call(this, SchedulerAction, function () { - if (AsyncScheduler.delegate && AsyncScheduler.delegate !== _this) { - return AsyncScheduler.delegate.now(); - } - else { - return now(); - } - }) || this; + if (now === void 0) { now = Scheduler.now; } + var _this = _super.call(this, SchedulerAction, now) || this; _this.actions = []; - _this.active = false; - _this.scheduled = undefined; + _this._active = false; + _this._scheduled = undefined; return _this; } - AsyncScheduler.prototype.schedule = function (work, delay, state) { - if (delay === void 0) { - delay = 0; - } - if (AsyncScheduler.delegate && AsyncScheduler.delegate !== this) { - return AsyncScheduler.delegate.schedule(work, delay, state); - } - else { - return _super.prototype.schedule.call(this, work, delay, state); - } - }; AsyncScheduler.prototype.flush = function (action) { var actions = this.actions; - if (this.active) { + if (this._active) { actions.push(action); return; } var error; - this.active = true; + this._active = true; do { - if (error = action.execute(action.state, action.delay)) { + if ((error = action.execute(action.state, action.delay))) { break; } - } while (action = actions.shift()); - this.active = false; + } while ((action = actions.shift())); + this._active = false; if (error) { - while (action = actions.shift()) { + while ((action = actions.shift())) { action.unsubscribe(); } throw error; @@ -1220,21 +1169,10 @@ return AsyncScheduler; }(Scheduler)); - /** PURE_IMPORTS_START tslib,_AsyncScheduler PURE_IMPORTS_END */ - var QueueScheduler = /*@__PURE__*/ (function (_super) { - __extends(QueueScheduler, _super); - function QueueScheduler() { - return _super !== null && _super.apply(this, arguments) || this; - } - return QueueScheduler; - }(AsyncScheduler)); - - /** PURE_IMPORTS_START _QueueAction,_QueueScheduler PURE_IMPORTS_END */ - var queueScheduler = /*@__PURE__*/ new QueueScheduler(QueueAction); - var queue = queueScheduler; + var asyncScheduler = new AsyncScheduler(AsyncAction); + var async = asyncScheduler; - /** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ - var EMPTY$1 = /*@__PURE__*/ new Observable(function (subscriber) { return subscriber.complete(); }); + var EMPTY$1 = new Observable(function (subscriber) { return subscriber.complete(); }); function empty(scheduler) { return scheduler ? emptyScheduled(scheduler) : EMPTY$1; } @@ -1242,639 +1180,29 @@ return new Observable(function (subscriber) { return scheduler.schedule(function () { return subscriber.complete(); }); }); } - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function isScheduler(value) { - return value && typeof value.schedule === 'function'; - } - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var subscribeToArray = function (array) { - return function (subscriber) { - for (var i = 0, len = array.length; i < len && !subscriber.closed; i++) { - subscriber.next(array[i]); - } - subscriber.complete(); - }; - }; - - /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */ function scheduleArray(input, scheduler) { return new Observable(function (subscriber) { - var sub = new Subscription(); var i = 0; - sub.add(scheduler.schedule(function () { + return scheduler.schedule(function () { if (i === input.length) { subscriber.complete(); - return; } - subscriber.next(input[i++]); - if (!subscriber.closed) { - sub.add(this.schedule()); + else { + subscriber.next(input[i++]); + if (!subscriber.closed) { + this.schedule(); + } } - })); - return sub; + }); }); } - /** PURE_IMPORTS_START _Observable,_util_subscribeToArray,_scheduled_scheduleArray PURE_IMPORTS_END */ - function fromArray(input, scheduler) { - if (!scheduler) { - return new Observable(subscribeToArray(input)); - } - else { - return scheduleArray(input, scheduler); - } - } - - /** PURE_IMPORTS_START _util_isScheduler,_fromArray,_scheduled_scheduleArray PURE_IMPORTS_END */ - function of() { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - var scheduler = args[args.length - 1]; - if (isScheduler(scheduler)) { - args.pop(); - return scheduleArray(args, scheduler); - } - else { - return fromArray(args); - } - } - - /** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ - function throwError(error, scheduler) { - if (!scheduler) { - return new Observable(function (subscriber) { return subscriber.error(error); }); - } - else { - return new Observable(function (subscriber) { return scheduler.schedule(dispatch$1, 0, { error: error, subscriber: subscriber }); }); - } - } - function dispatch$1(_a) { - var error = _a.error, subscriber = _a.subscriber; - subscriber.error(error); - } - - /** PURE_IMPORTS_START _observable_empty,_observable_of,_observable_throwError PURE_IMPORTS_END */ - var Notification = /*@__PURE__*/ (function () { - function Notification(kind, value, error) { - this.kind = kind; - this.value = value; - this.error = error; - this.hasValue = kind === 'N'; - } - Notification.prototype.observe = function (observer) { - switch (this.kind) { - case 'N': - return observer.next && observer.next(this.value); - case 'E': - return observer.error && observer.error(this.error); - case 'C': - return observer.complete && observer.complete(); - } - }; - Notification.prototype.do = function (next, error, complete) { - var kind = this.kind; - switch (kind) { - case 'N': - return next && next(this.value); - case 'E': - return error && error(this.error); - case 'C': - return complete && complete(); - } - }; - Notification.prototype.accept = function (nextOrObserver, error, complete) { - if (nextOrObserver && typeof nextOrObserver.next === 'function') { - return this.observe(nextOrObserver); - } - else { - return this.do(nextOrObserver, error, complete); - } - }; - Notification.prototype.toObservable = function () { - var kind = this.kind; - switch (kind) { - case 'N': - return of(this.value); - case 'E': - return throwError(this.error); - case 'C': - return empty(); - } - throw new Error('unexpected notification kind value'); - }; - Notification.createNext = function (value) { - if (typeof value !== 'undefined') { - return new Notification('N', value); - } - return Notification.undefinedValueNotification; - }; - Notification.createError = function (err) { - return new Notification('E', undefined, err); - }; - Notification.createComplete = function () { - return Notification.completeNotification; - }; - Notification.completeNotification = new Notification('C'); - Notification.undefinedValueNotification = new Notification('N', undefined); - return Notification; - }()); - - /** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ - var ObserveOnSubscriber = /*@__PURE__*/ (function (_super) { - __extends(ObserveOnSubscriber, _super); - function ObserveOnSubscriber(destination, scheduler, delay) { - if (delay === void 0) { - delay = 0; - } - var _this = _super.call(this, destination) || this; - _this.scheduler = scheduler; - _this.delay = delay; - return _this; - } - ObserveOnSubscriber.dispatch = function (arg) { - var notification = arg.notification, destination = arg.destination; - notification.observe(destination); - this.unsubscribe(); - }; - ObserveOnSubscriber.prototype.scheduleMessage = function (notification) { - var destination = this.destination; - destination.add(this.scheduler.schedule(ObserveOnSubscriber.dispatch, this.delay, new ObserveOnMessage(notification, this.destination))); - }; - ObserveOnSubscriber.prototype._next = function (value) { - this.scheduleMessage(Notification.createNext(value)); - }; - ObserveOnSubscriber.prototype._error = function (err) { - this.scheduleMessage(Notification.createError(err)); - this.unsubscribe(); - }; - ObserveOnSubscriber.prototype._complete = function () { - this.scheduleMessage(Notification.createComplete()); - this.unsubscribe(); - }; - return ObserveOnSubscriber; - }(Subscriber)); - var ObserveOnMessage = /*@__PURE__*/ (function () { - function ObserveOnMessage(notification, destination) { - this.notification = notification; - this.destination = destination; - } - return ObserveOnMessage; - }()); - - /** PURE_IMPORTS_START tslib,_Subject,_scheduler_queue,_Subscription,_operators_observeOn,_util_ObjectUnsubscribedError,_SubjectSubscription PURE_IMPORTS_END */ - var ReplaySubject = /*@__PURE__*/ (function (_super) { - __extends(ReplaySubject, _super); - function ReplaySubject(bufferSize, windowTime, scheduler) { - if (bufferSize === void 0) { - bufferSize = Number.POSITIVE_INFINITY; - } - if (windowTime === void 0) { - windowTime = Number.POSITIVE_INFINITY; - } - var _this = _super.call(this) || this; - _this.scheduler = scheduler; - _this._events = []; - _this._infiniteTimeWindow = false; - _this._bufferSize = bufferSize < 1 ? 1 : bufferSize; - _this._windowTime = windowTime < 1 ? 1 : windowTime; - if (windowTime === Number.POSITIVE_INFINITY) { - _this._infiniteTimeWindow = true; - _this.next = _this.nextInfiniteTimeWindow; - } - else { - _this.next = _this.nextTimeWindow; - } - return _this; - } - ReplaySubject.prototype.nextInfiniteTimeWindow = function (value) { - if (!this.isStopped) { - var _events = this._events; - _events.push(value); - if (_events.length > this._bufferSize) { - _events.shift(); - } - } - _super.prototype.next.call(this, value); - }; - ReplaySubject.prototype.nextTimeWindow = function (value) { - if (!this.isStopped) { - this._events.push(new ReplayEvent(this._getNow(), value)); - this._trimBufferThenGetEvents(); - } - _super.prototype.next.call(this, value); - }; - ReplaySubject.prototype._subscribe = function (subscriber) { - var _infiniteTimeWindow = this._infiniteTimeWindow; - var _events = _infiniteTimeWindow ? this._events : this._trimBufferThenGetEvents(); - var scheduler = this.scheduler; - var len = _events.length; - var subscription; - if (this.closed) { - throw new ObjectUnsubscribedError(); - } - else if (this.isStopped || this.hasError) { - subscription = Subscription.EMPTY; - } - else { - this.observers.push(subscriber); - subscription = new SubjectSubscription(this, subscriber); - } - if (scheduler) { - subscriber.add(subscriber = new ObserveOnSubscriber(subscriber, scheduler)); - } - if (_infiniteTimeWindow) { - for (var i = 0; i < len && !subscriber.closed; i++) { - subscriber.next(_events[i]); - } - } - else { - for (var i = 0; i < len && !subscriber.closed; i++) { - subscriber.next(_events[i].value); - } - } - if (this.hasError) { - subscriber.error(this.thrownError); - } - else if (this.isStopped) { - subscriber.complete(); - } - return subscription; - }; - ReplaySubject.prototype._getNow = function () { - return (this.scheduler || queue).now(); - }; - ReplaySubject.prototype._trimBufferThenGetEvents = function () { - var now = this._getNow(); - var _bufferSize = this._bufferSize; - var _windowTime = this._windowTime; - var _events = this._events; - var eventsCount = _events.length; - var spliceCount = 0; - while (spliceCount < eventsCount) { - if ((now - _events[spliceCount].time) < _windowTime) { - break; - } - spliceCount++; - } - if (eventsCount > _bufferSize) { - spliceCount = Math.max(spliceCount, eventsCount - _bufferSize); - } - if (spliceCount > 0) { - _events.splice(0, spliceCount); - } - return _events; - }; - return ReplaySubject; - }(Subject)); - var ReplayEvent = /*@__PURE__*/ (function () { - function ReplayEvent(time, value) { - this.time = time; - this.value = value; - } - return ReplayEvent; - }()); - - /** PURE_IMPORTS_START _AsyncAction,_AsyncScheduler PURE_IMPORTS_END */ - var asyncScheduler = /*@__PURE__*/ new AsyncScheduler(AsyncAction); - var async = asyncScheduler; - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function noop() { } - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var ArgumentOutOfRangeErrorImpl = /*@__PURE__*/ (function () { - function ArgumentOutOfRangeErrorImpl() { - Error.call(this); - this.message = 'argument out of range'; - this.name = 'ArgumentOutOfRangeError'; - return this; - } - ArgumentOutOfRangeErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); - return ArgumentOutOfRangeErrorImpl; - })(); - var ArgumentOutOfRangeError = ArgumentOutOfRangeErrorImpl; - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var EmptyErrorImpl = /*@__PURE__*/ (function () { - function EmptyErrorImpl() { - Error.call(this); - this.message = 'no elements in sequence'; - this.name = 'EmptyError'; - return this; - } - EmptyErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); - return EmptyErrorImpl; - })(); - var EmptyError = EmptyErrorImpl; - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - var TimeoutErrorImpl = /*@__PURE__*/ (function () { - function TimeoutErrorImpl() { - Error.call(this); - this.message = 'Timeout has occurred'; - this.name = 'TimeoutError'; - return this; - } - TimeoutErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); - return TimeoutErrorImpl; - })(); - var TimeoutError = TimeoutErrorImpl; - - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function map(project, thisArg) { - return function mapOperation(source) { - if (typeof project !== 'function') { - throw new TypeError('argument is not a function. Are you looking for `mapTo()`?'); - } - return source.lift(new MapOperator(project, thisArg)); - }; - } - var MapOperator = /*@__PURE__*/ (function () { - function MapOperator(project, thisArg) { - this.project = project; - this.thisArg = thisArg; - } - MapOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg)); - }; - return MapOperator; - }()); - var MapSubscriber = /*@__PURE__*/ (function (_super) { - __extends(MapSubscriber, _super); - function MapSubscriber(destination, project, thisArg) { - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.count = 0; - _this.thisArg = thisArg || _this; - return _this; - } - MapSubscriber.prototype._next = function (value) { - var result; - try { - result = this.project.call(this.thisArg, value, this.count++); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(result); - }; - return MapSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - var OuterSubscriber = /*@__PURE__*/ (function (_super) { - __extends(OuterSubscriber, _super); - function OuterSubscriber() { - return _super !== null && _super.apply(this, arguments) || this; - } - OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.destination.next(innerValue); - }; - OuterSubscriber.prototype.notifyError = function (error, innerSub) { - this.destination.error(error); - }; - OuterSubscriber.prototype.notifyComplete = function (innerSub) { - this.destination.complete(); - }; - return OuterSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - var InnerSubscriber = /*@__PURE__*/ (function (_super) { - __extends(InnerSubscriber, _super); - function InnerSubscriber(parent, outerValue, outerIndex) { - var _this = _super.call(this) || this; - _this.parent = parent; - _this.outerValue = outerValue; - _this.outerIndex = outerIndex; - _this.index = 0; - return _this; - } - InnerSubscriber.prototype._next = function (value) { - this.parent.notifyNext(this.outerValue, value, this.outerIndex, this.index++, this); - }; - InnerSubscriber.prototype._error = function (error) { - this.parent.notifyError(error, this); - this.unsubscribe(); - }; - InnerSubscriber.prototype._complete = function () { - this.parent.notifyComplete(this); - this.unsubscribe(); - }; - return InnerSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START _hostReportError PURE_IMPORTS_END */ - var subscribeToPromise = function (promise) { - return function (subscriber) { - promise.then(function (value) { - if (!subscriber.closed) { - subscriber.next(value); - subscriber.complete(); - } - }, function (err) { return subscriber.error(err); }) - .then(null, hostReportError); - return subscriber; - }; - }; - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function getSymbolIterator() { - if (typeof Symbol !== 'function' || !Symbol.iterator) { - return '@@iterator'; - } - return Symbol.iterator; - } - var iterator = /*@__PURE__*/ getSymbolIterator(); - - /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ - var subscribeToIterable = function (iterable) { - return function (subscriber) { - var iterator$1 = iterable[iterator](); - do { - var item = void 0; - try { - item = iterator$1.next(); - } - catch (err) { - subscriber.error(err); - return subscriber; - } - if (item.done) { - subscriber.complete(); - break; - } - subscriber.next(item.value); - if (subscriber.closed) { - break; - } - } while (true); - if (typeof iterator$1.return === 'function') { - subscriber.add(function () { - if (iterator$1.return) { - iterator$1.return(); - } - }); - } - return subscriber; - }; - }; - - /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ - var subscribeToObservable = function (obj) { - return function (subscriber) { - var obs = obj[observable](); - if (typeof obs.subscribe !== 'function') { - throw new TypeError('Provided object does not correctly implement Symbol.observable'); - } - else { - return obs.subscribe(subscriber); - } - }; - }; - - /** PURE_IMPORTS_START PURE_IMPORTS_END */ var isArrayLike = (function (x) { return x && typeof x.length === 'number' && typeof x !== 'function'; }); - /** PURE_IMPORTS_START PURE_IMPORTS_END */ function isPromise(value) { - return !!value && typeof value.subscribe !== 'function' && typeof value.then === 'function'; - } - - /** PURE_IMPORTS_START _subscribeToArray,_subscribeToPromise,_subscribeToIterable,_subscribeToObservable,_isArrayLike,_isPromise,_isObject,_symbol_iterator,_symbol_observable PURE_IMPORTS_END */ - var subscribeTo = function (result) { - if (!!result && typeof result[observable] === 'function') { - return subscribeToObservable(result); - } - else if (isArrayLike(result)) { - return subscribeToArray(result); - } - else if (isPromise(result)) { - return subscribeToPromise(result); - } - else if (!!result && typeof result[iterator] === 'function') { - return subscribeToIterable(result); - } - else { - var value = isObject$1(result) ? 'an invalid object' : "'" + result + "'"; - var msg = "You provided " + value + " where a stream was expected." - + ' You can provide an Observable, Promise, Array, or Iterable.'; - throw new TypeError(msg); - } - }; - - /** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo,_Observable PURE_IMPORTS_END */ - function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, innerSubscriber) { - if (innerSubscriber === void 0) { - innerSubscriber = new InnerSubscriber(outerSubscriber, outerValue, outerIndex); - } - if (innerSubscriber.closed) { - return undefined; - } - if (result instanceof Observable) { - return result.subscribe(innerSubscriber); - } - return subscribeTo(result)(innerSubscriber); - } - - /** PURE_IMPORTS_START tslib,_util_isScheduler,_util_isArray,_OuterSubscriber,_util_subscribeToResult,_fromArray PURE_IMPORTS_END */ - var NONE = {}; - function combineLatest() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - var resultSelector = undefined; - var scheduler = undefined; - if (isScheduler(observables[observables.length - 1])) { - scheduler = observables.pop(); - } - if (typeof observables[observables.length - 1] === 'function') { - resultSelector = observables.pop(); - } - if (observables.length === 1 && isArray$1(observables[0])) { - observables = observables[0]; - } - return fromArray(observables, scheduler).lift(new CombineLatestOperator(resultSelector)); + return isFunction(value === null || value === void 0 ? void 0 : value.then); } - var CombineLatestOperator = /*@__PURE__*/ (function () { - function CombineLatestOperator(resultSelector) { - this.resultSelector = resultSelector; - } - CombineLatestOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new CombineLatestSubscriber(subscriber, this.resultSelector)); - }; - return CombineLatestOperator; - }()); - var CombineLatestSubscriber = /*@__PURE__*/ (function (_super) { - __extends(CombineLatestSubscriber, _super); - function CombineLatestSubscriber(destination, resultSelector) { - var _this = _super.call(this, destination) || this; - _this.resultSelector = resultSelector; - _this.active = 0; - _this.values = []; - _this.observables = []; - return _this; - } - CombineLatestSubscriber.prototype._next = function (observable) { - this.values.push(NONE); - this.observables.push(observable); - }; - CombineLatestSubscriber.prototype._complete = function () { - var observables = this.observables; - var len = observables.length; - if (len === 0) { - this.destination.complete(); - } - else { - this.active = len; - this.toRespond = len; - for (var i = 0; i < len; i++) { - var observable = observables[i]; - this.add(subscribeToResult(this, observable, undefined, i)); - } - } - }; - CombineLatestSubscriber.prototype.notifyComplete = function (unused) { - if ((this.active -= 1) === 0) { - this.destination.complete(); - } - }; - CombineLatestSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { - var values = this.values; - var oldVal = values[outerIndex]; - var toRespond = !this.toRespond - ? 0 - : oldVal === NONE ? --this.toRespond : this.toRespond; - values[outerIndex] = innerValue; - if (toRespond === 0) { - if (this.resultSelector) { - this._tryResultSelector(values); - } - else { - this.destination.next(values.slice()); - } - } - }; - CombineLatestSubscriber.prototype._tryResultSelector = function (values) { - var result; - try { - result = this.resultSelector.apply(this, values); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(result); - }; - return CombineLatestSubscriber; - }(OuterSubscriber)); - /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_observable PURE_IMPORTS_END */ function scheduleObservable(input, scheduler) { return new Observable(function (subscriber) { var sub = new Subscription(); @@ -1890,54 +1218,50 @@ }); } - /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */ function schedulePromise(input, scheduler) { return new Observable(function (subscriber) { - var sub = new Subscription(); - sub.add(scheduler.schedule(function () { + return scheduler.schedule(function () { return input.then(function (value) { - sub.add(scheduler.schedule(function () { + subscriber.add(scheduler.schedule(function () { subscriber.next(value); - sub.add(scheduler.schedule(function () { return subscriber.complete(); })); + subscriber.add(scheduler.schedule(function () { return subscriber.complete(); })); })); }, function (err) { - sub.add(scheduler.schedule(function () { return subscriber.error(err); })); + subscriber.add(scheduler.schedule(function () { return subscriber.error(err); })); }); - })); - return sub; + }); }); } - /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_iterator PURE_IMPORTS_END */ - function scheduleIterable(input, scheduler) { - if (!input) { - throw new Error('Iterable cannot be null'); + function getSymbolIterator() { + if (typeof Symbol !== 'function' || !Symbol.iterator) { + return '@@iterator'; } + return Symbol.iterator; + } + var iterator = getSymbolIterator(); + + function caughtSchedule(subscriber, scheduler, execute, delay) { + if (delay === void 0) { delay = 0; } + var subscription = scheduler.schedule(function () { + try { + execute.call(this); + } + catch (err) { + subscriber.error(err); + } + }, delay); + subscriber.add(subscription); + return subscription; + } + + function scheduleIterable(input, scheduler) { return new Observable(function (subscriber) { - var sub = new Subscription(); var iterator$1; - sub.add(function () { - if (iterator$1 && typeof iterator$1.return === 'function') { - iterator$1.return(); - } - }); - sub.add(scheduler.schedule(function () { + subscriber.add(scheduler.schedule(function () { iterator$1 = input[iterator](); - sub.add(scheduler.schedule(function () { - if (subscriber.closed) { - return; - } - var value; - var done; - try { - var result = iterator$1.next(); - value = result.value; - done = result.done; - } - catch (err) { - subscriber.error(err); - return; - } + caughtSchedule(subscriber, scheduler, function () { + var _a = iterator$1.next(), value = _a.value, done = _a.done; if (done) { subscriber.complete(); } @@ -1945,2084 +1269,1402 @@ subscriber.next(value); this.schedule(); } + }); + })); + return function () { return isFunction(iterator$1 === null || iterator$1 === void 0 ? void 0 : iterator$1.return) && iterator$1.return(); }; + }); + } + + function scheduleAsyncIterable(input, scheduler) { + if (!input) { + throw new Error('Iterable cannot be null'); + } + return new Observable(function (subscriber) { + var sub = new Subscription(); + sub.add(scheduler.schedule(function () { + var iterator = input[Symbol.asyncIterator](); + sub.add(scheduler.schedule(function () { + var _this = this; + iterator.next().then(function (result) { + if (result.done) { + subscriber.complete(); + } + else { + subscriber.next(result.value); + _this.schedule(); + } + }); })); })); return sub; }); } - /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ function isInteropObservable(input) { - return input && typeof input[observable] === 'function'; + return isFunction(input[observable]); } - /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ function isIterable(input) { - return input && typeof input[iterator] === 'function'; + return isFunction(input === null || input === void 0 ? void 0 : input[iterator]); + } + + function isAsyncIterable(obj) { + return Symbol.asyncIterator && isFunction(obj === null || obj === void 0 ? void 0 : obj[Symbol.asyncIterator]); + } + + function createInvalidObservableTypeError(input) { + return new TypeError("You provided " + (input !== null && typeof input === 'object' ? 'an invalid object' : "'" + input + "'") + " where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable."); + } + + function readableStreamLikeToAsyncGenerator(readableStream) { + return __asyncGenerator(this, arguments, function readableStreamLikeToAsyncGenerator_1() { + var reader, _a, value, done; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + reader = readableStream.getReader(); + _b.label = 1; + case 1: + _b.trys.push([1, , 9, 10]); + _b.label = 2; + case 2: + return [4, __await(reader.read())]; + case 3: + _a = _b.sent(), value = _a.value, done = _a.done; + if (!done) return [3, 5]; + return [4, __await(void 0)]; + case 4: return [2, _b.sent()]; + case 5: return [4, __await(value)]; + case 6: return [4, _b.sent()]; + case 7: + _b.sent(); + return [3, 2]; + case 8: return [3, 10]; + case 9: + reader.releaseLock(); + return [7]; + case 10: return [2]; + } + }); + }); + } + function isReadableStreamLike(obj) { + return isFunction(obj === null || obj === void 0 ? void 0 : obj.getReader); + } + + function scheduleReadableStreamLike(input, scheduler) { + return scheduleAsyncIterable(readableStreamLikeToAsyncGenerator(input), scheduler); } - /** PURE_IMPORTS_START _scheduleObservable,_schedulePromise,_scheduleArray,_scheduleIterable,_util_isInteropObservable,_util_isPromise,_util_isArrayLike,_util_isIterable PURE_IMPORTS_END */ function scheduled(input, scheduler) { if (input != null) { if (isInteropObservable(input)) { return scheduleObservable(input, scheduler); } - else if (isPromise(input)) { + if (isArrayLike(input)) { + return scheduleArray(input, scheduler); + } + if (isPromise(input)) { return schedulePromise(input, scheduler); } - else if (isArrayLike(input)) { - return scheduleArray(input, scheduler); + if (isAsyncIterable(input)) { + return scheduleAsyncIterable(input, scheduler); } - else if (isIterable(input) || typeof input === 'string') { + if (isIterable(input)) { return scheduleIterable(input, scheduler); } - } - throw new TypeError((input !== null && typeof input || input) + ' is not observable'); - } - - /** PURE_IMPORTS_START _Observable,_util_subscribeTo,_scheduled_scheduled PURE_IMPORTS_END */ - function from(input, scheduler) { - if (!scheduler) { - if (input instanceof Observable) { - return input; + if (isReadableStreamLike(input)) { + return scheduleReadableStreamLike(input, scheduler); } - return new Observable(subscribeTo(input)); - } - else { - return scheduled(input, scheduler); - } - } - - /** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_util_subscribeTo PURE_IMPORTS_END */ - var SimpleInnerSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SimpleInnerSubscriber, _super); - function SimpleInnerSubscriber(parent) { - var _this = _super.call(this) || this; - _this.parent = parent; - return _this; - } - SimpleInnerSubscriber.prototype._next = function (value) { - this.parent.notifyNext(value); - }; - SimpleInnerSubscriber.prototype._error = function (error) { - this.parent.notifyError(error); - this.unsubscribe(); - }; - SimpleInnerSubscriber.prototype._complete = function () { - this.parent.notifyComplete(); - this.unsubscribe(); - }; - return SimpleInnerSubscriber; - }(Subscriber)); - var SimpleOuterSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SimpleOuterSubscriber, _super); - function SimpleOuterSubscriber() { - return _super !== null && _super.apply(this, arguments) || this; - } - SimpleOuterSubscriber.prototype.notifyNext = function (innerValue) { - this.destination.next(innerValue); - }; - SimpleOuterSubscriber.prototype.notifyError = function (err) { - this.destination.error(err); - }; - SimpleOuterSubscriber.prototype.notifyComplete = function () { - this.destination.complete(); - }; - return SimpleOuterSubscriber; - }(Subscriber)); - function innerSubscribe(result, innerSubscriber) { - if (innerSubscriber.closed) { - return undefined; - } - if (result instanceof Observable) { - return result.subscribe(innerSubscriber); } - return subscribeTo(result)(innerSubscriber); + throw createInvalidObservableTypeError(input); } - /** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ - function mergeMap(project, resultSelector, concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - if (typeof resultSelector === 'function') { - return function (source) { return source.pipe(mergeMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); }; - } - else if (typeof resultSelector === 'number') { - concurrent = resultSelector; - } - return function (source) { return source.lift(new MergeMapOperator(project, concurrent)); }; + function from(input, scheduler) { + return scheduler ? scheduled(input, scheduler) : innerFrom(input); } - var MergeMapOperator = /*@__PURE__*/ (function () { - function MergeMapOperator(project, concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - this.project = project; - this.concurrent = concurrent; - } - MergeMapOperator.prototype.call = function (observer, source) { - return source.subscribe(new MergeMapSubscriber(observer, this.project, this.concurrent)); - }; - return MergeMapOperator; - }()); - var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { - __extends(MergeMapSubscriber, _super); - function MergeMapSubscriber(destination, project, concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.concurrent = concurrent; - _this.hasCompleted = false; - _this.buffer = []; - _this.active = 0; - _this.index = 0; - return _this; + function innerFrom(input) { + if (input instanceof Observable) { + return input; } - MergeMapSubscriber.prototype._next = function (value) { - if (this.active < this.concurrent) { - this._tryNext(value); - } - else { - this.buffer.push(value); - } - }; - MergeMapSubscriber.prototype._tryNext = function (value) { - var result; - var index = this.index++; - try { - result = this.project(value, index); + if (input != null) { + if (isInteropObservable(input)) { + return fromInteropObservable(input); } - catch (err) { - this.destination.error(err); - return; + if (isArrayLike(input)) { + return fromArrayLike(input); } - this.active++; - this._innerSub(result); - }; - MergeMapSubscriber.prototype._innerSub = function (ish) { - var innerSubscriber = new SimpleInnerSubscriber(this); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = innerSubscribe(ish, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - destination.add(innerSubscription); + if (isPromise(input)) { + return fromPromise(input); } - }; - MergeMapSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (this.active === 0 && this.buffer.length === 0) { - this.destination.complete(); + if (isAsyncIterable(input)) { + return fromAsyncIterable(input); } - this.unsubscribe(); - }; - MergeMapSubscriber.prototype.notifyNext = function (innerValue) { - this.destination.next(innerValue); - }; - MergeMapSubscriber.prototype.notifyComplete = function () { - var buffer = this.buffer; - this.active--; - if (buffer.length > 0) { - this._next(buffer.shift()); + if (isIterable(input)) { + return fromIterable(input); } - else if (this.active === 0 && this.hasCompleted) { - this.destination.complete(); + if (isReadableStreamLike(input)) { + return fromReadableStreamLike(input); } - }; - return MergeMapSubscriber; - }(SimpleOuterSubscriber)); - - /** PURE_IMPORTS_START _mergeMap,_util_identity PURE_IMPORTS_END */ - function mergeAll(concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; } - return mergeMap(identity, concurrent); + throw createInvalidObservableTypeError(input); } - - /** PURE_IMPORTS_START _mergeAll PURE_IMPORTS_END */ - function concatAll() { - return mergeAll(1); + function fromInteropObservable(obj) { + return new Observable(function (subscriber) { + var obs = obj[observable](); + if (isFunction(obs.subscribe)) { + return obs.subscribe(subscriber); + } + throw new TypeError('Provided object does not correctly implement Symbol.observable'); + }); } - - /** PURE_IMPORTS_START _of,_operators_concatAll PURE_IMPORTS_END */ - function concat() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - return concatAll()(of.apply(void 0, observables)); + function fromArrayLike(array) { + return new Observable(function (subscriber) { + for (var i = 0; i < array.length && !subscriber.closed; i++) { + subscriber.next(array[i]); + } + subscriber.complete(); + }); } - - /** PURE_IMPORTS_START _Observable,_util_isArray,_util_isFunction,_operators_map PURE_IMPORTS_END */ - function fromEvent(target, eventName, options, resultSelector) { - if (isFunction(options)) { - resultSelector = options; - options = undefined; - } - if (resultSelector) { - return fromEvent(target, eventName, options).pipe(map(function (args) { return isArray$1(args) ? resultSelector.apply(void 0, args) : resultSelector(args); })); - } + function fromPromise(promise) { return new Observable(function (subscriber) { - function handler(e) { - if (arguments.length > 1) { - subscriber.next(Array.prototype.slice.call(arguments)); - } - else { - subscriber.next(e); + promise + .then(function (value) { + if (!subscriber.closed) { + subscriber.next(value); + subscriber.complete(); } - } - setupSubscription(target, eventName, handler, subscriber, options); + }, function (err) { return subscriber.error(err); }) + .then(null, reportUnhandledError); }); } - function setupSubscription(sourceObj, eventName, handler, subscriber, options) { - var unsubscribe; - if (isEventTarget(sourceObj)) { - var source_1 = sourceObj; - sourceObj.addEventListener(eventName, handler, options); - unsubscribe = function () { return source_1.removeEventListener(eventName, handler, options); }; - } - else if (isJQueryStyleEventEmitter(sourceObj)) { - var source_2 = sourceObj; - sourceObj.on(eventName, handler); - unsubscribe = function () { return source_2.off(eventName, handler); }; - } - else if (isNodeStyleEventEmitter(sourceObj)) { - var source_3 = sourceObj; - sourceObj.addListener(eventName, handler); - unsubscribe = function () { return source_3.removeListener(eventName, handler); }; - } - else if (sourceObj && sourceObj.length) { - for (var i = 0, len = sourceObj.length; i < len; i++) { - setupSubscription(sourceObj[i], eventName, handler, subscriber, options); + function fromIterable(iterable) { + return new Observable(function (subscriber) { + var e_1, _a; + try { + for (var iterable_1 = __values(iterable), iterable_1_1 = iterable_1.next(); !iterable_1_1.done; iterable_1_1 = iterable_1.next()) { + var value = iterable_1_1.value; + subscriber.next(value); + if (subscriber.closed) { + return; + } + } } - } - else { - throw new TypeError('Invalid event target'); - } - subscriber.add(unsubscribe); - } - function isNodeStyleEventEmitter(sourceObj) { - return sourceObj && typeof sourceObj.addListener === 'function' && typeof sourceObj.removeListener === 'function'; + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (iterable_1_1 && !iterable_1_1.done && (_a = iterable_1.return)) _a.call(iterable_1); + } + finally { if (e_1) throw e_1.error; } + } + subscriber.complete(); + }); } - function isJQueryStyleEventEmitter(sourceObj) { - return sourceObj && typeof sourceObj.on === 'function' && typeof sourceObj.off === 'function'; + function fromAsyncIterable(asyncIterable) { + return new Observable(function (subscriber) { + process(asyncIterable, subscriber).catch(function (err) { return subscriber.error(err); }); + }); } - function isEventTarget(sourceObj) { - return sourceObj && typeof sourceObj.addEventListener === 'function' && typeof sourceObj.removeEventListener === 'function'; + function fromReadableStreamLike(readableStream) { + return fromAsyncIterable(readableStreamLikeToAsyncGenerator(readableStream)); + } + function process(asyncIterable, subscriber) { + var asyncIterable_1, asyncIterable_1_1; + var e_2, _a; + return __awaiter(this, void 0, void 0, function () { + var value, e_2_1; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + _b.trys.push([0, 5, 6, 11]); + asyncIterable_1 = __asyncValues(asyncIterable); + _b.label = 1; + case 1: return [4, asyncIterable_1.next()]; + case 2: + if (!(asyncIterable_1_1 = _b.sent(), !asyncIterable_1_1.done)) return [3, 4]; + value = asyncIterable_1_1.value; + subscriber.next(value); + if (subscriber.closed) { + return [2]; + } + _b.label = 3; + case 3: return [3, 1]; + case 4: return [3, 11]; + case 5: + e_2_1 = _b.sent(); + e_2 = { error: e_2_1 }; + return [3, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(asyncIterable_1_1 && !asyncIterable_1_1.done && (_a = asyncIterable_1.return))) return [3, 8]; + return [4, _a.call(asyncIterable_1)]; + case 7: + _b.sent(); + _b.label = 8; + case 8: return [3, 10]; + case 9: + if (e_2) throw e_2.error; + return [7]; + case 10: return [7]; + case 11: + subscriber.complete(); + return [2]; + } + }); + }); } - /** PURE_IMPORTS_START _isArray PURE_IMPORTS_END */ - function isNumeric(val) { - return !isArray$1(val) && (val - parseFloat(val) + 1) >= 0; + function internalFromArray(input, scheduler) { + return scheduler ? scheduleArray(input, scheduler) : fromArrayLike(input); } - /** PURE_IMPORTS_START _Observable,_util_isScheduler,_operators_mergeAll,_fromArray PURE_IMPORTS_END */ - function merge() { - var observables = []; + function isScheduler(value) { + return value && isFunction(value.schedule); + } + + function last$1(arr) { + return arr[arr.length - 1]; + } + function popResultSelector(args) { + return isFunction(last$1(args)) ? args.pop() : undefined; + } + function popScheduler(args) { + return isScheduler(last$1(args)) ? args.pop() : undefined; + } + function popNumber(args, defaultValue) { + return typeof last$1(args) === 'number' ? args.pop() : defaultValue; + } + + function of() { + var args = []; for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - var concurrent = Number.POSITIVE_INFINITY; - var scheduler = null; - var last = observables[observables.length - 1]; - if (isScheduler(last)) { - scheduler = observables.pop(); - if (observables.length > 1 && typeof observables[observables.length - 1] === 'number') { - concurrent = observables.pop(); - } - } - else if (typeof last === 'number') { - concurrent = observables.pop(); - } - if (scheduler === null && observables.length === 1 && observables[0] instanceof Observable) { - return observables[0]; + args[_i] = arguments[_i]; } - return mergeAll(concurrent)(fromArray(observables, scheduler)); + var scheduler = popScheduler(args); + return scheduler ? scheduleArray(args, scheduler) : internalFromArray(args); } - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function filter(predicate, thisArg) { - return function filterOperatorFunction(source) { - return source.lift(new FilterOperator(predicate, thisArg)); - }; + function throwError(errorOrErrorFactory, scheduler) { + var errorFactory = isFunction(errorOrErrorFactory) ? errorOrErrorFactory : function () { return errorOrErrorFactory; }; + var init = function (subscriber) { return subscriber.error(errorFactory()); }; + return new Observable(scheduler ? function (subscriber) { return scheduler.schedule(init, 0, subscriber); } : init); } - var FilterOperator = /*@__PURE__*/ (function () { - function FilterOperator(predicate, thisArg) { - this.predicate = predicate; - this.thisArg = thisArg; - } - FilterOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); - }; - return FilterOperator; - }()); - var FilterSubscriber = /*@__PURE__*/ (function (_super) { - __extends(FilterSubscriber, _super); - function FilterSubscriber(destination, predicate, thisArg) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.thisArg = thisArg; - _this.count = 0; - return _this; - } - FilterSubscriber.prototype._next = function (value) { - var result; - try { - result = this.predicate.call(this.thisArg, value, this.count++); - } - catch (err) { - this.destination.error(err); - return; - } - if (result) { - this.destination.next(value); - } + + var EmptyError = createErrorClass(function (_super) { return function EmptyErrorImpl() { + _super(this); + this.name = 'EmptyError'; + this.message = 'no elements in sequence'; + }; }); + + function isValidDate(value) { + return value instanceof Date && !isNaN(value); + } + + var TimeoutError = createErrorClass(function (_super) { + return function TimeoutErrorImpl(info) { + if (info === void 0) { info = null; } + _super(this); + this.message = 'Timeout has occurred'; + this.name = 'TimeoutError'; + this.info = info; }; - return FilterSubscriber; - }(Subscriber)); + }); + function timeout(config, schedulerArg) { + var _a = (isValidDate(config) + ? { first: config } + : typeof config === 'number' + ? { each: config } + : config), first = _a.first, each = _a.each, _b = _a.with, _with = _b === void 0 ? timeoutErrorFactory : _b, _c = _a.scheduler, scheduler = _c === void 0 ? schedulerArg !== null && schedulerArg !== void 0 ? schedulerArg : asyncScheduler : _c, _d = _a.meta, meta = _d === void 0 ? null : _d; + if (first == null && each == null) { + throw new TypeError('No timeout provided.'); + } + return operate(function (source, subscriber) { + var originalSourceSubscription; + var timerSubscription; + var lastValue = null; + var seen = 0; + var startTimer = function (delay) { + timerSubscription = caughtSchedule(subscriber, scheduler, function () { + originalSourceSubscription.unsubscribe(); + innerFrom(_with({ + meta: meta, + lastValue: lastValue, + seen: seen, + })).subscribe(subscriber); + }, delay); + }; + originalSourceSubscription = source.subscribe(new OperatorSubscriber(subscriber, function (value) { + timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe(); + seen++; + subscriber.next((lastValue = value)); + each > 0 && startTimer(each); + }, undefined, undefined, function () { + if (!(timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.closed)) { + timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe(); + } + lastValue = null; + })); + startTimer(first != null ? (typeof first === 'number' ? first : +first - scheduler.now()) : each); + }); + } + function timeoutErrorFactory(info) { + throw new TimeoutError(info); + } - /** PURE_IMPORTS_START _Observable,_scheduler_async,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ - function timer(dueTime, periodOrScheduler, scheduler) { - if (dueTime === void 0) { - dueTime = 0; - } - var period = -1; - if (isNumeric(periodOrScheduler)) { - period = Number(periodOrScheduler) < 1 && 1 || Number(periodOrScheduler); - } - else if (isScheduler(periodOrScheduler)) { - scheduler = periodOrScheduler; - } - if (!isScheduler(scheduler)) { - scheduler = async; - } - return new Observable(function (subscriber) { - var due = isNumeric(dueTime) - ? dueTime - : (+dueTime - scheduler.now()); - return scheduler.schedule(dispatch, due, { - index: 0, period: period, subscriber: subscriber - }); + function map(project, thisArg) { + return operate(function (source, subscriber) { + var index = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + subscriber.next(project.call(thisArg, value, index++)); + })); }); } - function dispatch(state) { - var index = state.index, period = state.period, subscriber = state.subscriber; - subscriber.next(index); - if (subscriber.closed) { - return; - } - else if (period === -1) { - return subscriber.complete(); + + var isArray$6 = Array.isArray; + function callOrApply(fn, args) { + return isArray$6(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args); + } + function mapOneOrManyArgs(fn) { + return map(function (args) { return callOrApply(fn, args); }); + } + + var isArray$5 = Array.isArray; + var getPrototypeOf = Object.getPrototypeOf, objectProto = Object.prototype, getKeys = Object.keys; + function argsArgArrayOrObject(args) { + if (args.length === 1) { + var first_1 = args[0]; + if (isArray$5(first_1)) { + return { args: first_1, keys: null }; + } + if (isPOJO(first_1)) { + var keys = getKeys(first_1); + return { + args: keys.map(function (key) { return first_1[key]; }), + keys: keys, + }; + } } - state.index = index + 1; - this.schedule(state, period); + return { args: args, keys: null }; + } + function isPOJO(obj) { + return obj && typeof obj === 'object' && getPrototypeOf(obj) === objectProto; } - /** PURE_IMPORTS_START tslib,_fromArray,_util_isArray,_Subscriber,_.._internal_symbol_iterator,_innerSubscribe PURE_IMPORTS_END */ - function zip() { - var observables = []; + function createObject(keys, values) { + return keys.reduce(function (result, key, i) { return ((result[key] = values[i]), result); }, {}); + } + + function combineLatest() { + var args = []; for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - var resultSelector = observables[observables.length - 1]; - if (typeof resultSelector === 'function') { - observables.pop(); + args[_i] = arguments[_i]; } - return fromArray(observables, undefined).lift(new ZipOperator(resultSelector)); + var scheduler = popScheduler(args); + var resultSelector = popResultSelector(args); + var _a = argsArgArrayOrObject(args), observables = _a.args, keys = _a.keys; + if (observables.length === 0) { + return from([], scheduler); + } + var result = new Observable(combineLatestInit(observables, scheduler, keys + ? + function (values) { return createObject(keys, values); } + : + identity)); + return resultSelector ? result.pipe(mapOneOrManyArgs(resultSelector)) : result; + } + function combineLatestInit(observables, scheduler, valueTransform) { + if (valueTransform === void 0) { valueTransform = identity; } + return function (subscriber) { + maybeSchedule(scheduler, function () { + var length = observables.length; + var values = new Array(length); + var active = length; + var remainingFirstValues = length; + var _loop_1 = function (i) { + maybeSchedule(scheduler, function () { + var source = from(observables[i], scheduler); + var hasFirstValue = false; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + values[i] = value; + if (!hasFirstValue) { + hasFirstValue = true; + remainingFirstValues--; + } + if (!remainingFirstValues) { + subscriber.next(valueTransform(values.slice())); + } + }, function () { + if (!--active) { + subscriber.complete(); + } + })); + }, subscriber); + }; + for (var i = 0; i < length; i++) { + _loop_1(i); + } + }, subscriber); + }; } - var ZipOperator = /*@__PURE__*/ (function () { - function ZipOperator(resultSelector) { - this.resultSelector = resultSelector; + function maybeSchedule(scheduler, execute, subscription) { + if (scheduler) { + subscription.add(scheduler.schedule(execute)); } - ZipOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ZipSubscriber(subscriber, this.resultSelector)); - }; - return ZipOperator; - }()); - var ZipSubscriber = /*@__PURE__*/ (function (_super) { - __extends(ZipSubscriber, _super); - function ZipSubscriber(destination, resultSelector, values) { - var _this = _super.call(this, destination) || this; - _this.resultSelector = resultSelector; - _this.iterators = []; - _this.active = 0; - _this.resultSelector = (typeof resultSelector === 'function') ? resultSelector : undefined; - return _this; + else { + execute(); } - ZipSubscriber.prototype._next = function (value) { - var iterators = this.iterators; - if (isArray$1(value)) { - iterators.push(new StaticArrayIterator(value)); - } - else if (typeof value[iterator] === 'function') { - iterators.push(new StaticIterator(value[iterator]())); - } - else { - iterators.push(new ZipBufferIterator(this.destination, this, value)); + } + + function mergeInternals(source, subscriber, project, concurrent, onBeforeNext, expand, innerSubScheduler, additionalTeardown) { + var buffer = []; + var active = 0; + var index = 0; + var isComplete = false; + var checkComplete = function () { + if (isComplete && !buffer.length && !active) { + subscriber.complete(); } }; - ZipSubscriber.prototype._complete = function () { - var iterators = this.iterators; - var len = iterators.length; - this.unsubscribe(); - if (len === 0) { - this.destination.complete(); - return; - } - this.active = len; - for (var i = 0; i < len; i++) { - var iterator = iterators[i]; - if (iterator.stillUnsubscribed) { - var destination = this.destination; - destination.add(iterator.subscribe()); + var outerNext = function (value) { return (active < concurrent ? doInnerSub(value) : buffer.push(value)); }; + var doInnerSub = function (value) { + expand && subscriber.next(value); + active++; + var innerComplete = false; + innerFrom(project(value, index++)).subscribe(new OperatorSubscriber(subscriber, function (innerValue) { + onBeforeNext === null || onBeforeNext === void 0 ? void 0 : onBeforeNext(innerValue); + if (expand) { + outerNext(innerValue); } else { - this.active--; - } - } - }; - ZipSubscriber.prototype.notifyInactive = function () { - this.active--; - if (this.active === 0) { - this.destination.complete(); - } - }; - ZipSubscriber.prototype.checkIterators = function () { - var iterators = this.iterators; - var len = iterators.length; - var destination = this.destination; - for (var i = 0; i < len; i++) { - var iterator = iterators[i]; - if (typeof iterator.hasValue === 'function' && !iterator.hasValue()) { - return; - } - } - var shouldComplete = false; - var args = []; - for (var i = 0; i < len; i++) { - var iterator = iterators[i]; - var result = iterator.next(); - if (iterator.hasCompleted()) { - shouldComplete = true; + subscriber.next(innerValue); } - if (result.done) { - destination.complete(); - return; + }, function () { + innerComplete = true; + }, undefined, function () { + if (innerComplete) { + try { + active--; + var _loop_1 = function () { + var bufferedValue = buffer.shift(); + innerSubScheduler ? subscriber.add(innerSubScheduler.schedule(function () { return doInnerSub(bufferedValue); })) : doInnerSub(bufferedValue); + }; + while (buffer.length && active < concurrent) { + _loop_1(); + } + checkComplete(); + } + catch (err) { + subscriber.error(err); + } } - args.push(result.value); - } - if (this.resultSelector) { - this._tryresultSelector(args); - } - else { - destination.next(args); - } - if (shouldComplete) { - destination.complete(); - } + })); }; - ZipSubscriber.prototype._tryresultSelector = function (args) { - var result; - try { - result = this.resultSelector.apply(this, args); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(result); + source.subscribe(new OperatorSubscriber(subscriber, outerNext, function () { + isComplete = true; + checkComplete(); + })); + return function () { + additionalTeardown === null || additionalTeardown === void 0 ? void 0 : additionalTeardown(); }; - return ZipSubscriber; - }(Subscriber)); - var StaticIterator = /*@__PURE__*/ (function () { - function StaticIterator(iterator) { - this.iterator = iterator; - this.nextResult = iterator.next(); + } + + function mergeMap(project, resultSelector, concurrent) { + if (concurrent === void 0) { concurrent = Infinity; } + if (isFunction(resultSelector)) { + return mergeMap(function (a, i) { return map(function (b, ii) { return resultSelector(a, b, i, ii); })(innerFrom(project(a, i))); }, concurrent); } - StaticIterator.prototype.hasValue = function () { - return true; - }; - StaticIterator.prototype.next = function () { - var result = this.nextResult; - this.nextResult = this.iterator.next(); - return result; - }; - StaticIterator.prototype.hasCompleted = function () { - var nextResult = this.nextResult; - return Boolean(nextResult && nextResult.done); - }; - return StaticIterator; - }()); - var StaticArrayIterator = /*@__PURE__*/ (function () { - function StaticArrayIterator(array) { - this.array = array; - this.index = 0; - this.length = 0; - this.length = array.length; - } - StaticArrayIterator.prototype[iterator] = function () { - return this; - }; - StaticArrayIterator.prototype.next = function (value) { - var i = this.index++; - var array = this.array; - return i < this.length ? { value: array[i], done: false } : { value: null, done: true }; - }; - StaticArrayIterator.prototype.hasValue = function () { - return this.array.length > this.index; - }; - StaticArrayIterator.prototype.hasCompleted = function () { - return this.array.length === this.index; - }; - return StaticArrayIterator; - }()); - var ZipBufferIterator = /*@__PURE__*/ (function (_super) { - __extends(ZipBufferIterator, _super); - function ZipBufferIterator(destination, parent, observable) { - var _this = _super.call(this, destination) || this; - _this.parent = parent; - _this.observable = observable; - _this.stillUnsubscribed = true; - _this.buffer = []; - _this.isComplete = false; - return _this; + else if (typeof resultSelector === 'number') { + concurrent = resultSelector; } - ZipBufferIterator.prototype[iterator] = function () { - return this; - }; - ZipBufferIterator.prototype.next = function () { - var buffer = this.buffer; - if (buffer.length === 0 && this.isComplete) { - return { value: null, done: true }; + return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent); }); + } + + function mergeAll(concurrent) { + if (concurrent === void 0) { concurrent = Infinity; } + return mergeMap(identity, concurrent); + } + + function concatAll() { + return mergeAll(1); + } + + function concat() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return concatAll()(internalFromArray(args, popScheduler(args))); + } + + var nodeEventEmitterMethods = ['addListener', 'removeListener']; + var eventTargetMethods = ['addEventListener', 'removeEventListener']; + var jqueryMethods = ['on', 'off']; + function fromEvent(target, eventName, options, resultSelector) { + if (isFunction(options)) { + resultSelector = options; + options = undefined; + } + if (resultSelector) { + return fromEvent(target, eventName, options).pipe(mapOneOrManyArgs(resultSelector)); + } + var _a = __read(isEventTarget(target) + ? eventTargetMethods.map(function (methodName) { return function (handler) { return target[methodName](eventName, handler, options); }; }) + : + isNodeStyleEventEmitter(target) + ? nodeEventEmitterMethods.map(toCommonHandlerRegistry(target, eventName)) + : isJQueryStyleEventEmitter(target) + ? jqueryMethods.map(toCommonHandlerRegistry(target, eventName)) + : [], 2), add = _a[0], remove = _a[1]; + if (!add) { + if (isArrayLike(target)) { + return mergeMap(function (subTarget) { return fromEvent(subTarget, eventName, options); })(internalFromArray(target)); + } + } + if (!add) { + throw new TypeError('Invalid event target'); + } + return new Observable(function (subscriber) { + var handler = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return subscriber.next(1 < args.length ? args : args[0]); + }; + add(handler); + return function () { return remove(handler); }; + }); + } + function toCommonHandlerRegistry(target, eventName) { + return function (methodName) { return function (handler) { return target[methodName](eventName, handler); }; }; + } + function isNodeStyleEventEmitter(target) { + return isFunction(target.addListener) && isFunction(target.removeListener); + } + function isJQueryStyleEventEmitter(target) { + return isFunction(target.on) && isFunction(target.off); + } + function isEventTarget(target) { + return isFunction(target.addEventListener) && isFunction(target.removeEventListener); + } + + function timer(dueTime, intervalOrScheduler, scheduler) { + if (dueTime === void 0) { dueTime = 0; } + if (scheduler === void 0) { scheduler = async; } + var intervalDuration = -1; + if (intervalOrScheduler != null) { + if (isScheduler(intervalOrScheduler)) { + scheduler = intervalOrScheduler; } else { - return { value: buffer.shift(), done: false }; - } - }; - ZipBufferIterator.prototype.hasValue = function () { - return this.buffer.length > 0; - }; - ZipBufferIterator.prototype.hasCompleted = function () { - return this.buffer.length === 0 && this.isComplete; - }; - ZipBufferIterator.prototype.notifyComplete = function () { - if (this.buffer.length > 0) { - this.isComplete = true; - this.parent.notifyInactive(); + intervalDuration = intervalOrScheduler; } - else { - this.destination.complete(); + } + return new Observable(function (subscriber) { + var due = isValidDate(dueTime) ? +dueTime - scheduler.now() : dueTime; + if (due < 0) { + due = 0; } - }; - ZipBufferIterator.prototype.notifyNext = function (innerValue) { - this.buffer.push(innerValue); - this.parent.checkIterators(); - }; - ZipBufferIterator.prototype.subscribe = function () { - return innerSubscribe(this.observable, new SimpleInnerSubscriber(this)); - }; - return ZipBufferIterator; - }(SimpleOuterSubscriber)); - - /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ - function audit(durationSelector) { - return function auditOperatorFunction(source) { - return source.lift(new AuditOperator(durationSelector)); - }; + var n = 0; + return scheduler.schedule(function () { + if (!subscriber.closed) { + subscriber.next(n++); + if (0 <= intervalDuration) { + this.schedule(undefined, intervalDuration); + } + else { + subscriber.complete(); + } + } + }, due); + }); } - var AuditOperator = /*@__PURE__*/ (function () { - function AuditOperator(durationSelector) { - this.durationSelector = durationSelector; + + function merge() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; } - AuditOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new AuditSubscriber(subscriber, this.durationSelector)); - }; - return AuditOperator; - }()); - var AuditSubscriber = /*@__PURE__*/ (function (_super) { - __extends(AuditSubscriber, _super); - function AuditSubscriber(destination, durationSelector) { - var _this = _super.call(this, destination) || this; - _this.durationSelector = durationSelector; - _this.hasValue = false; - return _this; + var scheduler = popScheduler(args); + var concurrent = popNumber(args, Infinity); + var sources = args; + return !sources.length + ? + EMPTY$1 + : sources.length === 1 + ? + innerFrom(sources[0]) + : + mergeAll(concurrent)(internalFromArray(sources, scheduler)); + } + + var isArray$4 = Array.isArray; + function argsOrArgArray(args) { + return args.length === 1 && isArray$4(args[0]) ? args[0] : args; + } + + function filter(predicate, thisArg) { + return operate(function (source, subscriber) { + var index = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { return predicate.call(thisArg, value, index++) && subscriber.next(value); })); + }); + } + + function zip() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; } - AuditSubscriber.prototype._next = function (value) { - this.value = value; - this.hasValue = true; - if (!this.throttled) { - var duration = void 0; - try { - var durationSelector = this.durationSelector; - duration = durationSelector(value); - } - catch (err) { - return this.destination.error(err); - } - var innerSubscription = innerSubscribe(duration, new SimpleInnerSubscriber(this)); - if (!innerSubscription || innerSubscription.closed) { - this.clearThrottle(); + var resultSelector = popResultSelector(args); + var sources = argsOrArgArray(args); + return sources.length + ? new Observable(function (subscriber) { + var buffers = sources.map(function () { return []; }); + var completed = sources.map(function () { return false; }); + subscriber.add(function () { + buffers = completed = null; + }); + var _loop_1 = function (sourceIndex) { + innerFrom(sources[sourceIndex]).subscribe(new OperatorSubscriber(subscriber, function (value) { + buffers[sourceIndex].push(value); + if (buffers.every(function (buffer) { return buffer.length; })) { + var result = buffers.map(function (buffer) { return buffer.shift(); }); + subscriber.next(resultSelector ? resultSelector.apply(void 0, __spreadArray([], __read(result))) : result); + if (buffers.some(function (buffer, i) { return !buffer.length && completed[i]; })) { + subscriber.complete(); + } + } + }, function () { + completed[sourceIndex] = true; + !buffers[sourceIndex].length && subscriber.complete(); + })); + }; + for (var sourceIndex = 0; !subscriber.closed && sourceIndex < sources.length; sourceIndex++) { + _loop_1(sourceIndex); } - else { - this.add(this.throttled = innerSubscription); + return function () { + buffers = completed = null; + }; + }) + : EMPTY$1; + } + + function audit(durationSelector) { + return operate(function (source, subscriber) { + var hasValue = false; + var lastValue = null; + var durationSubscriber = null; + var isComplete = false; + var endDuration = function () { + durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe(); + durationSubscriber = null; + if (hasValue) { + hasValue = false; + var value = lastValue; + lastValue = null; + subscriber.next(value); } - } - }; - AuditSubscriber.prototype.clearThrottle = function () { - var _a = this, value = _a.value, hasValue = _a.hasValue, throttled = _a.throttled; - if (throttled) { - this.remove(throttled); - this.throttled = undefined; - throttled.unsubscribe(); - } - if (hasValue) { - this.value = undefined; - this.hasValue = false; - this.destination.next(value); - } - }; - AuditSubscriber.prototype.notifyNext = function () { - this.clearThrottle(); - }; - AuditSubscriber.prototype.notifyComplete = function () { - this.clearThrottle(); - }; - return AuditSubscriber; - }(SimpleOuterSubscriber)); + isComplete && subscriber.complete(); + }; + var cleanupDuration = function () { + durationSubscriber = null; + isComplete && subscriber.complete(); + }; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + hasValue = true; + lastValue = value; + if (!durationSubscriber) { + innerFrom(durationSelector(value)).subscribe((durationSubscriber = new OperatorSubscriber(subscriber, endDuration, cleanupDuration))); + } + }, function () { + isComplete = true; + (!hasValue || !durationSubscriber || durationSubscriber.closed) && subscriber.complete(); + })); + }); + } - /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ function auditTime(duration, scheduler) { - if (scheduler === void 0) { - scheduler = async; - } + if (scheduler === void 0) { scheduler = async; } return audit(function () { return timer(duration, scheduler); }); } - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ function bufferCount(bufferSize, startBufferEvery) { - if (startBufferEvery === void 0) { - startBufferEvery = null; - } - return function bufferCountOperatorFunction(source) { - return source.lift(new BufferCountOperator(bufferSize, startBufferEvery)); - }; - } - var BufferCountOperator = /*@__PURE__*/ (function () { - function BufferCountOperator(bufferSize, startBufferEvery) { - this.bufferSize = bufferSize; - this.startBufferEvery = startBufferEvery; - if (!startBufferEvery || bufferSize === startBufferEvery) { - this.subscriberClass = BufferCountSubscriber; - } - else { - this.subscriberClass = BufferSkipCountSubscriber; - } - } - BufferCountOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new this.subscriberClass(subscriber, this.bufferSize, this.startBufferEvery)); - }; - return BufferCountOperator; - }()); - var BufferCountSubscriber = /*@__PURE__*/ (function (_super) { - __extends(BufferCountSubscriber, _super); - function BufferCountSubscriber(destination, bufferSize) { - var _this = _super.call(this, destination) || this; - _this.bufferSize = bufferSize; - _this.buffer = []; - return _this; - } - BufferCountSubscriber.prototype._next = function (value) { - var buffer = this.buffer; - buffer.push(value); - if (buffer.length == this.bufferSize) { - this.destination.next(buffer); - this.buffer = []; - } - }; - BufferCountSubscriber.prototype._complete = function () { - var buffer = this.buffer; - if (buffer.length > 0) { - this.destination.next(buffer); - } - _super.prototype._complete.call(this); - }; - return BufferCountSubscriber; - }(Subscriber)); - var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { - __extends(BufferSkipCountSubscriber, _super); - function BufferSkipCountSubscriber(destination, bufferSize, startBufferEvery) { - var _this = _super.call(this, destination) || this; - _this.bufferSize = bufferSize; - _this.startBufferEvery = startBufferEvery; - _this.buffers = []; - _this.count = 0; - return _this; - } - BufferSkipCountSubscriber.prototype._next = function (value) { - var _a = this, bufferSize = _a.bufferSize, startBufferEvery = _a.startBufferEvery, buffers = _a.buffers, count = _a.count; - this.count++; - if (count % startBufferEvery === 0) { - buffers.push([]); - } - for (var i = buffers.length; i--;) { - var buffer = buffers[i]; - buffer.push(value); - if (buffer.length === bufferSize) { - buffers.splice(i, 1); - this.destination.next(buffer); + if (startBufferEvery === void 0) { startBufferEvery = null; } + startBufferEvery = startBufferEvery !== null && startBufferEvery !== void 0 ? startBufferEvery : bufferSize; + return operate(function (source, subscriber) { + var buffers = []; + var count = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + var e_1, _a, e_2, _b; + var toEmit = null; + if (count++ % startBufferEvery === 0) { + buffers.push([]); } - } - }; - BufferSkipCountSubscriber.prototype._complete = function () { - var _a = this, buffers = _a.buffers, destination = _a.destination; - while (buffers.length > 0) { - var buffer = buffers.shift(); - if (buffer.length > 0) { - destination.next(buffer); + try { + for (var buffers_1 = __values(buffers), buffers_1_1 = buffers_1.next(); !buffers_1_1.done; buffers_1_1 = buffers_1.next()) { + var buffer = buffers_1_1.value; + buffer.push(value); + if (bufferSize <= buffer.length) { + toEmit = toEmit !== null && toEmit !== void 0 ? toEmit : []; + toEmit.push(buffer); + } + } } - } - _super.prototype._complete.call(this); - }; - return BufferSkipCountSubscriber; - }(Subscriber)); + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (buffers_1_1 && !buffers_1_1.done && (_a = buffers_1.return)) _a.call(buffers_1); + } + finally { if (e_1) throw e_1.error; } + } + if (toEmit) { + try { + for (var toEmit_1 = __values(toEmit), toEmit_1_1 = toEmit_1.next(); !toEmit_1_1.done; toEmit_1_1 = toEmit_1.next()) { + var buffer = toEmit_1_1.value; + arrRemove(buffers, buffer); + subscriber.next(buffer); + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (toEmit_1_1 && !toEmit_1_1.done && (_b = toEmit_1.return)) _b.call(toEmit_1); + } + finally { if (e_2) throw e_2.error; } + } + } + }, function () { + var e_3, _a; + try { + for (var buffers_2 = __values(buffers), buffers_2_1 = buffers_2.next(); !buffers_2_1.done; buffers_2_1 = buffers_2.next()) { + var buffer = buffers_2_1.value; + subscriber.next(buffer); + } + } + catch (e_3_1) { e_3 = { error: e_3_1 }; } + finally { + try { + if (buffers_2_1 && !buffers_2_1.done && (_a = buffers_2.return)) _a.call(buffers_2); + } + finally { if (e_3) throw e_3.error; } + } + subscriber.complete(); + }, undefined, function () { + buffers = null; + })); + }); + } - /** PURE_IMPORTS_START tslib,_Subscription,_innerSubscribe PURE_IMPORTS_END */ function bufferWhen(closingSelector) { - return function (source) { - return source.lift(new BufferWhenOperator(closingSelector)); - }; + return operate(function (source, subscriber) { + var buffer = null; + var closingSubscriber = null; + var openBuffer = function () { + closingSubscriber === null || closingSubscriber === void 0 ? void 0 : closingSubscriber.unsubscribe(); + var b = buffer; + buffer = []; + b && subscriber.next(b); + innerFrom(closingSelector()).subscribe((closingSubscriber = new OperatorSubscriber(subscriber, openBuffer, noop))); + }; + openBuffer(); + source.subscribe(new OperatorSubscriber(subscriber, function (value) { return buffer === null || buffer === void 0 ? void 0 : buffer.push(value); }, function () { + buffer && subscriber.next(buffer); + subscriber.complete(); + }, undefined, function () { return (buffer = closingSubscriber = null); })); + }); } - var BufferWhenOperator = /*@__PURE__*/ (function () { - function BufferWhenOperator(closingSelector) { - this.closingSelector = closingSelector; - } - BufferWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new BufferWhenSubscriber(subscriber, this.closingSelector)); - }; - return BufferWhenOperator; - }()); - var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { - __extends(BufferWhenSubscriber, _super); - function BufferWhenSubscriber(destination, closingSelector) { - var _this = _super.call(this, destination) || this; - _this.closingSelector = closingSelector; - _this.subscribing = false; - _this.openBuffer(); - return _this; - } - BufferWhenSubscriber.prototype._next = function (value) { - this.buffer.push(value); - }; - BufferWhenSubscriber.prototype._complete = function () { - var buffer = this.buffer; - if (buffer) { - this.destination.next(buffer); - } - _super.prototype._complete.call(this); - }; - BufferWhenSubscriber.prototype._unsubscribe = function () { - this.buffer = undefined; - this.subscribing = false; - }; - BufferWhenSubscriber.prototype.notifyNext = function () { - this.openBuffer(); - }; - BufferWhenSubscriber.prototype.notifyComplete = function () { - if (this.subscribing) { - this.complete(); - } - else { - this.openBuffer(); - } - }; - BufferWhenSubscriber.prototype.openBuffer = function () { - var closingSubscription = this.closingSubscription; - if (closingSubscription) { - this.remove(closingSubscription); - closingSubscription.unsubscribe(); - } - var buffer = this.buffer; - if (this.buffer) { - this.destination.next(buffer); - } - this.buffer = []; - var closingNotifier; - try { - var closingSelector = this.closingSelector; - closingNotifier = closingSelector(); - } - catch (err) { - return this.error(err); - } - closingSubscription = new Subscription(); - this.closingSubscription = closingSubscription; - this.add(closingSubscription); - this.subscribing = true; - closingSubscription.add(innerSubscribe(closingNotifier, new SimpleInnerSubscriber(this))); - this.subscribing = false; - }; - return BufferWhenSubscriber; - }(SimpleOuterSubscriber)); - /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ function catchError(selector) { - return function catchErrorOperatorFunction(source) { - var operator = new CatchOperator(selector); - var caught = source.lift(operator); - return (operator.caught = caught); - }; - } - var CatchOperator = /*@__PURE__*/ (function () { - function CatchOperator(selector) { - this.selector = selector; - } - CatchOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new CatchSubscriber(subscriber, this.selector, this.caught)); - }; - return CatchOperator; - }()); - var CatchSubscriber = /*@__PURE__*/ (function (_super) { - __extends(CatchSubscriber, _super); - function CatchSubscriber(destination, selector, caught) { - var _this = _super.call(this, destination) || this; - _this.selector = selector; - _this.caught = caught; - return _this; - } - CatchSubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var result = void 0; - try { - result = this.selector(err, this.caught); + return operate(function (source, subscriber) { + var innerSub = null; + var syncUnsub = false; + var handledResult; + innerSub = source.subscribe(new OperatorSubscriber(subscriber, undefined, undefined, function (err) { + handledResult = innerFrom(selector(err, catchError(selector)(source))); + if (innerSub) { + innerSub.unsubscribe(); + innerSub = null; + handledResult.subscribe(subscriber); } - catch (err2) { - _super.prototype.error.call(this, err2); - return; - } - this._unsubscribeAndRecycle(); - var innerSubscriber = new SimpleInnerSubscriber(this); - this.add(innerSubscriber); - var innerSubscription = innerSubscribe(result, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - this.add(innerSubscription); + else { + syncUnsub = true; } + })); + if (syncUnsub) { + innerSub.unsubscribe(); + innerSub = null; + handledResult.subscribe(subscriber); } + }); + } + + function scanInternals(accumulator, seed, hasSeed, emitOnNext, emitBeforeComplete) { + return function (source, subscriber) { + var hasState = hasSeed; + var state = seed; + var index = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + var i = index++; + state = hasState + ? + accumulator(state, value, i) + : + ((hasState = true), value); + emitOnNext && subscriber.next(state); + }, emitBeforeComplete && + (function () { + hasState && subscriber.next(state); + subscriber.complete(); + }))); }; - return CatchSubscriber; - }(SimpleOuterSubscriber)); + } + + function reduce(accumulator, seed) { + return operate(scanInternals(accumulator, seed, arguments.length >= 2, false, true)); + } - /** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ function concatMap(project, resultSelector) { - return mergeMap(project, resultSelector, 1); + return isFunction(resultSelector) ? mergeMap(project, resultSelector, 1) : mergeMap(project, 1); } - /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ - function debounceTime(dueTime, scheduler) { - if (scheduler === void 0) { - scheduler = async; - } - return function (source) { return source.lift(new DebounceTimeOperator(dueTime, scheduler)); }; + function fromSubscribable(subscribable) { + return new Observable(function (subscriber) { return subscribable.subscribe(subscriber); }); } - var DebounceTimeOperator = /*@__PURE__*/ (function () { - function DebounceTimeOperator(dueTime, scheduler) { - this.dueTime = dueTime; - this.scheduler = scheduler; - } - DebounceTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DebounceTimeSubscriber(subscriber, this.dueTime, this.scheduler)); - }; - return DebounceTimeOperator; - }()); - var DebounceTimeSubscriber = /*@__PURE__*/ (function (_super) { - __extends(DebounceTimeSubscriber, _super); - function DebounceTimeSubscriber(destination, dueTime, scheduler) { - var _this = _super.call(this, destination) || this; - _this.dueTime = dueTime; - _this.scheduler = scheduler; - _this.debouncedSubscription = null; - _this.lastValue = null; - _this.hasValue = false; - return _this; - } - DebounceTimeSubscriber.prototype._next = function (value) { - this.clearDebounce(); - this.lastValue = value; - this.hasValue = true; - this.add(this.debouncedSubscription = this.scheduler.schedule(dispatchNext, this.dueTime, this)); - }; - DebounceTimeSubscriber.prototype._complete = function () { - this.debouncedNext(); - this.destination.complete(); - }; - DebounceTimeSubscriber.prototype.debouncedNext = function () { - this.clearDebounce(); - if (this.hasValue) { - var lastValue = this.lastValue; - this.lastValue = null; - this.hasValue = false; - this.destination.next(lastValue); - } - }; - DebounceTimeSubscriber.prototype.clearDebounce = function () { - var debouncedSubscription = this.debouncedSubscription; - if (debouncedSubscription !== null) { - this.remove(debouncedSubscription); - debouncedSubscription.unsubscribe(); - this.debouncedSubscription = null; + + var DEFAULT_CONFIG = { + connector: function () { return new Subject(); }, + }; + function connect(selector, config) { + if (config === void 0) { config = DEFAULT_CONFIG; } + var connector = config.connector; + return operate(function (source, subscriber) { + var subject = connector(); + from(selector(fromSubscribable(subject))).subscribe(subscriber); + subscriber.add(source.subscribe(subject)); + }); + } + + function debounceTime(dueTime, scheduler) { + if (scheduler === void 0) { scheduler = asyncScheduler; } + return operate(function (source, subscriber) { + var activeTask = null; + var lastValue = null; + var lastTime = null; + var emit = function () { + if (activeTask) { + activeTask.unsubscribe(); + activeTask = null; + var value = lastValue; + lastValue = null; + subscriber.next(value); + } + }; + function emitWhenIdle() { + var targetTime = lastTime + dueTime; + var now = scheduler.now(); + if (now < targetTime) { + activeTask = this.schedule(undefined, targetTime - now); + subscriber.add(activeTask); + return; + } + emit(); } - }; - return DebounceTimeSubscriber; - }(Subscriber)); - function dispatchNext(subscriber) { - subscriber.debouncedNext(); + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + lastValue = value; + lastTime = scheduler.now(); + if (!activeTask) { + activeTask = scheduler.schedule(emitWhenIdle, dueTime); + subscriber.add(activeTask); + } + }, function () { + emit(); + subscriber.complete(); + }, undefined, function () { + lastValue = activeTask = null; + })); + }); } - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ function defaultIfEmpty(defaultValue) { - if (defaultValue === void 0) { - defaultValue = null; - } - return function (source) { return source.lift(new DefaultIfEmptyOperator(defaultValue)); }; + return operate(function (source, subscriber) { + var hasValue = false; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + hasValue = true; + subscriber.next(value); + }, function () { + if (!hasValue) { + subscriber.next(defaultValue); + } + subscriber.complete(); + })); + }); } - var DefaultIfEmptyOperator = /*@__PURE__*/ (function () { - function DefaultIfEmptyOperator(defaultValue) { - this.defaultValue = defaultValue; - } - DefaultIfEmptyOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DefaultIfEmptySubscriber(subscriber, this.defaultValue)); - }; - return DefaultIfEmptyOperator; - }()); - var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { - __extends(DefaultIfEmptySubscriber, _super); - function DefaultIfEmptySubscriber(destination, defaultValue) { - var _this = _super.call(this, destination) || this; - _this.defaultValue = defaultValue; - _this.isEmpty = true; - return _this; - } - DefaultIfEmptySubscriber.prototype._next = function (value) { - this.isEmpty = false; - this.destination.next(value); - }; - DefaultIfEmptySubscriber.prototype._complete = function () { - if (this.isEmpty) { - this.destination.next(this.defaultValue); - } - this.destination.complete(); - }; - return DefaultIfEmptySubscriber; - }(Subscriber)); - /** PURE_IMPORTS_START PURE_IMPORTS_END */ - function isDate(value) { - return value instanceof Date && !isNaN(+value); + function take(count) { + return count <= 0 + ? + function () { return EMPTY$1; } + : operate(function (source, subscriber) { + var seen = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + if (++seen <= count) { + subscriber.next(value); + if (count <= seen) { + subscriber.complete(); + } + } + })); + }); } - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function distinctUntilChanged(compare, keySelector) { - return function (source) { return source.lift(new DistinctUntilChangedOperator(compare, keySelector)); }; + function distinctUntilChanged(comparator, keySelector) { + if (keySelector === void 0) { keySelector = identity; } + comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare$3; + return operate(function (source, subscriber) { + var previousKey; + var first = true; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + var currentKey = keySelector(value); + if (first || !comparator(previousKey, currentKey)) { + first = false; + previousKey = currentKey; + subscriber.next(value); + } + })); + }); } - var DistinctUntilChangedOperator = /*@__PURE__*/ (function () { - function DistinctUntilChangedOperator(compare, keySelector) { - this.compare = compare; - this.keySelector = keySelector; - } - DistinctUntilChangedOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DistinctUntilChangedSubscriber(subscriber, this.compare, this.keySelector)); - }; - return DistinctUntilChangedOperator; - }()); - var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { - __extends(DistinctUntilChangedSubscriber, _super); - function DistinctUntilChangedSubscriber(destination, compare, keySelector) { - var _this = _super.call(this, destination) || this; - _this.keySelector = keySelector; - _this.hasKey = false; - if (typeof compare === 'function') { - _this.compare = compare; - } - return _this; - } - DistinctUntilChangedSubscriber.prototype.compare = function (x, y) { - return x === y; - }; - DistinctUntilChangedSubscriber.prototype._next = function (value) { - var key; + function defaultCompare$3(a, b) { + return a === b; + } + + function throwIfEmpty(errorFactory) { + if (errorFactory === void 0) { errorFactory = defaultErrorFactory; } + return operate(function (source, subscriber) { + var hasValue = false; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + hasValue = true; + subscriber.next(value); + }, function () { return (hasValue ? subscriber.complete() : subscriber.error(errorFactory())); })); + }); + } + function defaultErrorFactory() { + return new EmptyError(); + } + + function expand(project, concurrent, scheduler) { + if (concurrent === void 0) { concurrent = Infinity; } + concurrent = (concurrent || 0) < 1 ? Infinity : concurrent; + return operate(function (source, subscriber) { + return mergeInternals(source, subscriber, project, concurrent, undefined, true, scheduler); + }); + } + + function finalize(callback) { + return operate(function (source, subscriber) { try { - var keySelector = this.keySelector; - key = keySelector ? keySelector(value) : value; - } - catch (err) { - return this.destination.error(err); - } - var result = false; - if (this.hasKey) { - try { - var compare = this.compare; - result = compare(this.key, key); - } - catch (err) { - return this.destination.error(err); - } - } - else { - this.hasKey = true; + source.subscribe(subscriber); } - if (!result) { - this.key = key; - this.destination.next(value); + finally { + subscriber.add(callback); } + }); + } + + function first(predicate, defaultValue) { + var hasDefaultValue = arguments.length >= 2; + return function (source) { + return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, take(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; - return DistinctUntilChangedSubscriber; - }(Subscriber)); + } - /** PURE_IMPORTS_START tslib,_util_EmptyError,_Subscriber PURE_IMPORTS_END */ - function throwIfEmpty(errorFactory) { - if (errorFactory === void 0) { - errorFactory = defaultErrorFactory; - } + function takeLast(count) { + return count <= 0 + ? function () { return EMPTY$1; } + : operate(function (source, subscriber) { + var buffer = []; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + buffer.push(value); + count < buffer.length && buffer.shift(); + }, function () { + var e_1, _a; + try { + for (var buffer_1 = __values(buffer), buffer_1_1 = buffer_1.next(); !buffer_1_1.done; buffer_1_1 = buffer_1.next()) { + var value = buffer_1_1.value; + subscriber.next(value); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (buffer_1_1 && !buffer_1_1.done && (_a = buffer_1.return)) _a.call(buffer_1); + } + finally { if (e_1) throw e_1.error; } + } + subscriber.complete(); + }, undefined, function () { + buffer = null; + })); + }); + } + + function last(predicate, defaultValue) { + var hasDefaultValue = arguments.length >= 2; return function (source) { - return source.lift(new ThrowIfEmptyOperator(errorFactory)); + return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, takeLast(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; } - var ThrowIfEmptyOperator = /*@__PURE__*/ (function () { - function ThrowIfEmptyOperator(errorFactory) { - this.errorFactory = errorFactory; + + function multicast(subjectOrSubjectFactory, selector) { + var subjectFactory = isFunction(subjectOrSubjectFactory) ? subjectOrSubjectFactory : function () { return subjectOrSubjectFactory; }; + if (isFunction(selector)) { + return connect(selector, { + connector: subjectFactory, + }); } - ThrowIfEmptyOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ThrowIfEmptySubscriber(subscriber, this.errorFactory)); - }; - return ThrowIfEmptyOperator; - }()); - var ThrowIfEmptySubscriber = /*@__PURE__*/ (function (_super) { - __extends(ThrowIfEmptySubscriber, _super); - function ThrowIfEmptySubscriber(destination, errorFactory) { - var _this = _super.call(this, destination) || this; - _this.errorFactory = errorFactory; - _this.hasValue = false; - return _this; + return function (source) { return new ConnectableObservable(source, subjectFactory); }; + } + + function pairwise() { + return operate(function (source, subscriber) { + var prev; + var hasPrev = false; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + var p = prev; + prev = value; + hasPrev && subscriber.next([p, value]); + hasPrev = true; + })); + }); + } + + function pluck() { + var properties = []; + for (var _i = 0; _i < arguments.length; _i++) { + properties[_i] = arguments[_i]; } - ThrowIfEmptySubscriber.prototype._next = function (value) { - this.hasValue = true; - this.destination.next(value); - }; - ThrowIfEmptySubscriber.prototype._complete = function () { - if (!this.hasValue) { - var err = void 0; - try { - err = this.errorFactory(); + var length = properties.length; + if (length === 0) { + throw new Error('list of properties cannot be empty.'); + } + return map(function (x) { + var currentProp = x; + for (var i = 0; i < length; i++) { + var p = currentProp === null || currentProp === void 0 ? void 0 : currentProp[properties[i]]; + if (typeof p !== 'undefined') { + currentProp = p; } - catch (e) { - err = e; + else { + return undefined; } - this.destination.error(err); } - else { - return this.destination.complete(); - } - }; - return ThrowIfEmptySubscriber; - }(Subscriber)); - function defaultErrorFactory() { - return new EmptyError(); + return currentProp; + }); } - /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ - function take(count) { - return function (source) { - if (count === 0) { - return empty(); - } - else { - return source.lift(new TakeOperator(count)); - } - }; + function publish(selector) { + return selector ? function (source) { return connect(selector)(source); } : function (source) { return multicast(new Subject())(source); }; } - var TakeOperator = /*@__PURE__*/ (function () { - function TakeOperator(total) { - this.total = total; - if (this.total < 0) { - throw new ArgumentOutOfRangeError; - } + + function publishReplay(bufferSize, windowTime, selectorOrScheduler, timestampProvider) { + if (selectorOrScheduler && !isFunction(selectorOrScheduler)) { + timestampProvider = selectorOrScheduler; } - TakeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeSubscriber(subscriber, this.total)); - }; - return TakeOperator; - }()); - var TakeSubscriber = /*@__PURE__*/ (function (_super) { - __extends(TakeSubscriber, _super); - function TakeSubscriber(destination, total) { - var _this = _super.call(this, destination) || this; - _this.total = total; - _this.count = 0; - return _this; - } - TakeSubscriber.prototype._next = function (value) { - var total = this.total; - var count = ++this.count; - if (count <= total) { - this.destination.next(value); - if (count === total) { - this.destination.complete(); - this.unsubscribe(); - } - } - }; - return TakeSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ - function expand(project, concurrent, scheduler) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - concurrent = (concurrent || 0) < 1 ? Number.POSITIVE_INFINITY : concurrent; - return function (source) { return source.lift(new ExpandOperator(project, concurrent, scheduler)); }; - } - var ExpandOperator = /*@__PURE__*/ (function () { - function ExpandOperator(project, concurrent, scheduler) { - this.project = project; - this.concurrent = concurrent; - this.scheduler = scheduler; - } - ExpandOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ExpandSubscriber(subscriber, this.project, this.concurrent, this.scheduler)); - }; - return ExpandOperator; - }()); - var ExpandSubscriber = /*@__PURE__*/ (function (_super) { - __extends(ExpandSubscriber, _super); - function ExpandSubscriber(destination, project, concurrent, scheduler) { - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.concurrent = concurrent; - _this.scheduler = scheduler; - _this.index = 0; - _this.active = 0; - _this.hasCompleted = false; - if (concurrent < Number.POSITIVE_INFINITY) { - _this.buffer = []; - } - return _this; - } - ExpandSubscriber.dispatch = function (arg) { - var subscriber = arg.subscriber, result = arg.result, value = arg.value, index = arg.index; - subscriber.subscribeToProjection(result, value, index); - }; - ExpandSubscriber.prototype._next = function (value) { - var destination = this.destination; - if (destination.closed) { - this._complete(); - return; - } - var index = this.index++; - if (this.active < this.concurrent) { - destination.next(value); - try { - var project = this.project; - var result = project(value, index); - if (!this.scheduler) { - this.subscribeToProjection(result, value, index); - } - else { - var state = { subscriber: this, result: result, value: value, index: index }; - var destination_1 = this.destination; - destination_1.add(this.scheduler.schedule(ExpandSubscriber.dispatch, 0, state)); - } - } - catch (e) { - destination.error(e); - } - } - else { - this.buffer.push(value); - } - }; - ExpandSubscriber.prototype.subscribeToProjection = function (result, value, index) { - this.active++; - var destination = this.destination; - destination.add(innerSubscribe(result, new SimpleInnerSubscriber(this))); - }; - ExpandSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (this.hasCompleted && this.active === 0) { - this.destination.complete(); - } - this.unsubscribe(); - }; - ExpandSubscriber.prototype.notifyNext = function (innerValue) { - this._next(innerValue); - }; - ExpandSubscriber.prototype.notifyComplete = function () { - var buffer = this.buffer; - this.active--; - if (buffer && buffer.length > 0) { - this._next(buffer.shift()); - } - if (this.hasCompleted && this.active === 0) { - this.destination.complete(); - } - }; - return ExpandSubscriber; - }(SimpleOuterSubscriber)); - - /** PURE_IMPORTS_START tslib,_Subscriber,_Subscription PURE_IMPORTS_END */ - function finalize(callback) { - return function (source) { return source.lift(new FinallyOperator(callback)); }; - } - var FinallyOperator = /*@__PURE__*/ (function () { - function FinallyOperator(callback) { - this.callback = callback; - } - FinallyOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new FinallySubscriber(subscriber, this.callback)); - }; - return FinallyOperator; - }()); - var FinallySubscriber = /*@__PURE__*/ (function (_super) { - __extends(FinallySubscriber, _super); - function FinallySubscriber(destination, callback) { - var _this = _super.call(this, destination) || this; - _this.add(new Subscription(callback)); - return _this; - } - return FinallySubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ - function first(predicate, defaultValue) { - var hasDefaultValue = arguments.length >= 2; - return function (source) { return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, take(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; - } - - /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ - function takeLast(count) { - return function takeLastOperatorFunction(source) { - if (count === 0) { - return empty(); - } - else { - return source.lift(new TakeLastOperator(count)); - } - }; + var selector = isFunction(selectorOrScheduler) ? selectorOrScheduler : undefined; + return function (source) { return multicast(new ReplaySubject(bufferSize, windowTime, timestampProvider), selector)(source); }; } - var TakeLastOperator = /*@__PURE__*/ (function () { - function TakeLastOperator(total) { - this.total = total; - if (this.total < 0) { - throw new ArgumentOutOfRangeError; - } - } - TakeLastOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeLastSubscriber(subscriber, this.total)); - }; - return TakeLastOperator; - }()); - var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { - __extends(TakeLastSubscriber, _super); - function TakeLastSubscriber(destination, total) { - var _this = _super.call(this, destination) || this; - _this.total = total; - _this.ring = new Array(); - _this.count = 0; - return _this; - } - TakeLastSubscriber.prototype._next = function (value) { - var ring = this.ring; - var total = this.total; - var count = this.count++; - if (ring.length < total) { - ring.push(value); - } - else { - var index = count % total; - ring[index] = value; - } - }; - TakeLastSubscriber.prototype._complete = function () { - var destination = this.destination; - var count = this.count; - if (count > 0) { - var total = this.count >= this.total ? this.total : this.count; - var ring = this.ring; - for (var i = 0; i < total; i++) { - var idx = (count++) % total; - destination.next(ring[idx]); - } - } - destination.complete(); - }; - return TakeLastSubscriber; - }(Subscriber)); - /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ - function last(predicate, defaultValue) { - var hasDefaultValue = arguments.length >= 2; - return function (source) { return source.pipe(predicate ? filter(function (v, i) { return predicate(v, i, source); }) : identity, takeLast(1), hasDefaultValue ? defaultIfEmpty(defaultValue) : throwIfEmpty(function () { return new EmptyError(); })); }; - } - - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function scan(accumulator, seed) { - var hasSeed = false; - if (arguments.length >= 2) { - hasSeed = true; - } - return function scanOperatorFunction(source) { - return source.lift(new ScanOperator(accumulator, seed, hasSeed)); - }; - } - var ScanOperator = /*@__PURE__*/ (function () { - function ScanOperator(accumulator, seed, hasSeed) { - if (hasSeed === void 0) { - hasSeed = false; - } - this.accumulator = accumulator; - this.seed = seed; - this.hasSeed = hasSeed; + function retry(configOrCount) { + if (configOrCount === void 0) { configOrCount = Infinity; } + var config; + if (configOrCount && typeof configOrCount === 'object') { + config = configOrCount; } - ScanOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ScanSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); - }; - return ScanOperator; - }()); - var ScanSubscriber = /*@__PURE__*/ (function (_super) { - __extends(ScanSubscriber, _super); - function ScanSubscriber(destination, accumulator, _seed, hasSeed) { - var _this = _super.call(this, destination) || this; - _this.accumulator = accumulator; - _this._seed = _seed; - _this.hasSeed = hasSeed; - _this.index = 0; - return _this; - } - Object.defineProperty(ScanSubscriber.prototype, "seed", { - get: function () { - return this._seed; - }, - set: function (value) { - this.hasSeed = true; - this._seed = value; - }, - enumerable: true, - configurable: true - }); - ScanSubscriber.prototype._next = function (value) { - if (!this.hasSeed) { - this.seed = value; - this.destination.next(value); - } - else { - return this._tryNext(value); - } - }; - ScanSubscriber.prototype._tryNext = function (value) { - var index = this.index++; - var result; - try { - result = this.accumulator(this.seed, value, index); - } - catch (err) { - this.destination.error(err); - } - this.seed = result; - this.destination.next(result); - }; - return ScanSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ - function reduce(accumulator, seed) { - if (arguments.length >= 2) { - return function reduceOperatorFunctionWithSeed(source) { - return pipe(scan(accumulator, seed), takeLast(1), defaultIfEmpty(seed))(source); + else { + config = { + count: configOrCount, }; } - return function reduceOperatorFunction(source) { - return pipe(scan(function (acc, value, index) { return accumulator(acc, value, index + 1); }), takeLast(1))(source); - }; - } - - /** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ - function multicast(subjectOrSubjectFactory, selector) { - return function multicastOperatorFunction(source) { - var subjectFactory; - if (typeof subjectOrSubjectFactory === 'function') { - subjectFactory = subjectOrSubjectFactory; - } - else { - subjectFactory = function subjectFactory() { - return subjectOrSubjectFactory; + var _a = config.count, count = _a === void 0 ? Infinity : _a, delay = config.delay, _b = config.resetOnSuccess, resetOnSuccess = _b === void 0 ? false : _b; + return count <= 0 + ? identity + : operate(function (source, subscriber) { + var soFar = 0; + var innerSub; + var subscribeForRetry = function () { + var syncUnsub = false; + innerSub = source.subscribe(new OperatorSubscriber(subscriber, function (value) { + if (resetOnSuccess) { + soFar = 0; + } + subscriber.next(value); + }, undefined, function (err) { + if (soFar++ < count) { + var resub_1 = function () { + if (innerSub) { + innerSub.unsubscribe(); + innerSub = null; + subscribeForRetry(); + } + else { + syncUnsub = true; + } + }; + if (delay != null) { + var notifier = typeof delay === 'number' ? timer(delay) : innerFrom(delay(err, soFar)); + var notifierSubscriber_1 = new OperatorSubscriber(subscriber, function () { + notifierSubscriber_1.unsubscribe(); + resub_1(); + }, function () { + subscriber.complete(); + }); + notifier.subscribe(notifierSubscriber_1); + } + else { + resub_1(); + } + } + else { + subscriber.error(err); + } + })); + if (syncUnsub) { + innerSub.unsubscribe(); + innerSub = null; + subscribeForRetry(); + } }; - } - if (typeof selector === 'function') { - return source.lift(new MulticastOperator(subjectFactory, selector)); - } - var connectable = Object.create(source, connectableObservableDescriptor); - connectable.source = source; - connectable.subjectFactory = subjectFactory; - return connectable; - }; + subscribeForRetry(); + }); } - var MulticastOperator = /*@__PURE__*/ (function () { - function MulticastOperator(subjectFactory, selector) { - this.subjectFactory = subjectFactory; - this.selector = selector; - } - MulticastOperator.prototype.call = function (subscriber, source) { - var selector = this.selector; - var subject = this.subjectFactory(); - var subscription = selector(subject).subscribe(subscriber); - subscription.add(source.subscribe(subject)); - return subscription; - }; - return MulticastOperator; - }()); - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function pairwise() { - return function (source) { return source.lift(new PairwiseOperator()); }; - } - var PairwiseOperator = /*@__PURE__*/ (function () { - function PairwiseOperator() { - } - PairwiseOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new PairwiseSubscriber(subscriber)); - }; - return PairwiseOperator; - }()); - var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { - __extends(PairwiseSubscriber, _super); - function PairwiseSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.hasPrev = false; - return _this; - } - PairwiseSubscriber.prototype._next = function (value) { - var pair; - if (this.hasPrev) { - pair = [this.prev, value]; - } - else { - this.hasPrev = true; - } - this.prev = value; - if (pair) { - this.destination.next(pair); - } - }; - return PairwiseSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START _map PURE_IMPORTS_END */ - function pluck() { - var properties = []; - for (var _i = 0; _i < arguments.length; _i++) { - properties[_i] = arguments[_i]; - } - var length = properties.length; - if (length === 0) { - throw new Error('list of properties cannot be empty.'); - } - return function (source) { return map(plucker(properties, length))(source); }; - } - function plucker(props, length) { - var mapper = function (x) { - var currentProp = x; - for (var i = 0; i < length; i++) { - var p = currentProp != null ? currentProp[props[i]] : undefined; - if (p !== void 0) { - currentProp = p; - } - else { - return undefined; + function sample(notifier) { + return operate(function (source, subscriber) { + var hasValue = false; + var lastValue = null; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + hasValue = true; + lastValue = value; + })); + var emit = function () { + if (hasValue) { + hasValue = false; + var value = lastValue; + lastValue = null; + subscriber.next(value); } - } - return currentProp; - }; - return mapper; - } - - /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ - function publish(selector) { - return selector ? - multicast(function () { return new Subject(); }, selector) : - multicast(new Subject()); - } - - /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ - function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { - if (selectorOrScheduler && typeof selectorOrScheduler !== 'function') { - scheduler = selectorOrScheduler; - } - var selector = typeof selectorOrScheduler === 'function' ? selectorOrScheduler : undefined; - var subject = new ReplaySubject(bufferSize, windowTime, scheduler); - return function (source) { return multicast(function () { return subject; }, selector)(source); }; + }; + notifier.subscribe(new OperatorSubscriber(subscriber, emit, noop)); + }); } - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - function retry(count) { - if (count === void 0) { - count = -1; - } - return function (source) { return source.lift(new RetryOperator(count, source)); }; - } - var RetryOperator = /*@__PURE__*/ (function () { - function RetryOperator(count, source) { - this.count = count; - this.source = source; - } - RetryOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RetrySubscriber(subscriber, this.count, this.source)); - }; - return RetryOperator; - }()); - var RetrySubscriber = /*@__PURE__*/ (function (_super) { - __extends(RetrySubscriber, _super); - function RetrySubscriber(destination, count, source) { - var _this = _super.call(this, destination) || this; - _this.count = count; - _this.source = source; - return _this; - } - RetrySubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var _a = this, source = _a.source, count = _a.count; - if (count === 0) { - return _super.prototype.error.call(this, err); + function scan(accumulator, seed) { + return operate(scanInternals(accumulator, seed, arguments.length >= 2, true)); + } + + function share(options) { + if (options === void 0) { options = {}; } + var _a = options.connector, connector = _a === void 0 ? function () { return new Subject(); } : _a, _b = options.resetOnError, resetOnError = _b === void 0 ? true : _b, _c = options.resetOnComplete, resetOnComplete = _c === void 0 ? true : _c, _d = options.resetOnRefCountZero, resetOnRefCountZero = _d === void 0 ? true : _d; + return function (wrapperSource) { + var connection = null; + var resetConnection = null; + var subject = null; + var refCount = 0; + var hasCompleted = false; + var hasErrored = false; + var cancelReset = function () { + resetConnection === null || resetConnection === void 0 ? void 0 : resetConnection.unsubscribe(); + resetConnection = null; + }; + var reset = function () { + cancelReset(); + connection = subject = null; + hasCompleted = hasErrored = false; + }; + var resetAndUnsubscribe = function () { + var conn = connection; + reset(); + conn === null || conn === void 0 ? void 0 : conn.unsubscribe(); + }; + return operate(function (source, subscriber) { + refCount++; + if (!hasErrored && !hasCompleted) { + cancelReset(); } - else if (count > -1) { - this.count = count - 1; + var dest = (subject = subject !== null && subject !== void 0 ? subject : connector()); + subscriber.add(function () { + refCount--; + if (refCount === 0 && !hasErrored && !hasCompleted) { + resetConnection = handleReset(resetAndUnsubscribe, resetOnRefCountZero); + } + }); + dest.subscribe(subscriber); + if (!connection) { + connection = new SafeSubscriber({ + next: function (value) { return dest.next(value); }, + error: function (err) { + hasErrored = true; + cancelReset(); + resetConnection = handleReset(reset, resetOnError, err); + dest.error(err); + }, + complete: function () { + hasCompleted = true; + cancelReset(); + resetConnection = handleReset(reset, resetOnComplete); + dest.complete(); + }, + }); + from(source).subscribe(connection); } - source.subscribe(this._unsubscribeAndRecycle()); - } + })(wrapperSource); }; - return RetrySubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ - function sample(notifier) { - return function (source) { return source.lift(new SampleOperator(notifier)); }; } - var SampleOperator = /*@__PURE__*/ (function () { - function SampleOperator(notifier) { - this.notifier = notifier; + function handleReset(reset, on) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; } - SampleOperator.prototype.call = function (subscriber, source) { - var sampleSubscriber = new SampleSubscriber(subscriber); - var subscription = source.subscribe(sampleSubscriber); - subscription.add(innerSubscribe(this.notifier, new SimpleInnerSubscriber(sampleSubscriber))); - return subscription; - }; - return SampleOperator; - }()); - var SampleSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SampleSubscriber, _super); - function SampleSubscriber() { - var _this = _super !== null && _super.apply(this, arguments) || this; - _this.hasValue = false; - return _this; + if (on === true) { + reset(); + return null; } - SampleSubscriber.prototype._next = function (value) { - this.value = value; - this.hasValue = true; - }; - SampleSubscriber.prototype.notifyNext = function () { - this.emitValue(); - }; - SampleSubscriber.prototype.notifyComplete = function () { - this.emitValue(); - }; - SampleSubscriber.prototype.emitValue = function () { - if (this.hasValue) { - this.hasValue = false; - this.destination.next(this.value); - } - }; - return SampleSubscriber; - }(SimpleOuterSubscriber)); - - /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ - function shareSubjectFactory() { - return new Subject(); - } - function share() { - return function (source) { return refCount()(multicast(shareSubjectFactory)(source)); }; + if (on === false) { + return null; + } + return on.apply(void 0, __spreadArray([], __read(args))).pipe(take(1)) + .subscribe(function () { return reset(); }); } - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ function skip(count) { - return function (source) { return source.lift(new SkipOperator(count)); }; + return filter(function (_, index) { return count <= index; }); } - var SkipOperator = /*@__PURE__*/ (function () { - function SkipOperator(total) { - this.total = total; - } - SkipOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SkipSubscriber(subscriber, this.total)); - }; - return SkipOperator; - }()); - var SkipSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SkipSubscriber, _super); - function SkipSubscriber(destination, total) { - var _this = _super.call(this, destination) || this; - _this.total = total; - _this.count = 0; - return _this; - } - SkipSubscriber.prototype._next = function (x) { - if (++this.count > this.total) { - this.destination.next(x); - } - }; - return SkipSubscriber; - }(Subscriber)); - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ function skipWhile(predicate) { - return function (source) { return source.lift(new SkipWhileOperator(predicate)); }; + return operate(function (source, subscriber) { + var taking = false; + var index = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { return (taking || (taking = !predicate(value, index++))) && subscriber.next(value); })); + }); } - var SkipWhileOperator = /*@__PURE__*/ (function () { - function SkipWhileOperator(predicate) { - this.predicate = predicate; - } - SkipWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SkipWhileSubscriber(subscriber, this.predicate)); - }; - return SkipWhileOperator; - }()); - var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SkipWhileSubscriber, _super); - function SkipWhileSubscriber(destination, predicate) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.skipping = true; - _this.index = 0; - return _this; - } - SkipWhileSubscriber.prototype._next = function (value) { - var destination = this.destination; - if (this.skipping) { - this.tryCallPredicate(value); - } - if (!this.skipping) { - destination.next(value); - } - }; - SkipWhileSubscriber.prototype.tryCallPredicate = function (value) { - try { - var result = this.predicate(value, this.index++); - this.skipping = Boolean(result); - } - catch (err) { - this.destination.error(err); - } - }; - return SkipWhileSubscriber; - }(Subscriber)); - /** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ function startWith() { - var array = []; + var values = []; for (var _i = 0; _i < arguments.length; _i++) { - array[_i] = arguments[_i]; - } - var scheduler = array[array.length - 1]; - if (isScheduler(scheduler)) { - array.pop(); - return function (source) { return concat(array, source, scheduler); }; - } - else { - return function (source) { return concat(array, source); }; + values[_i] = arguments[_i]; } + var scheduler = popScheduler(values); + return operate(function (source, subscriber) { + (scheduler ? concat(values, source, scheduler) : concat(values, source)).subscribe(subscriber); + }); } - /** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ function switchMap(project, resultSelector) { - if (typeof resultSelector === 'function') { - return function (source) { return source.pipe(switchMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; - } - return function (source) { return source.lift(new SwitchMapOperator(project)); }; + return operate(function (source, subscriber) { + var innerSubscriber = null; + var index = 0; + var isComplete = false; + var checkComplete = function () { return isComplete && !innerSubscriber && subscriber.complete(); }; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + innerSubscriber === null || innerSubscriber === void 0 ? void 0 : innerSubscriber.unsubscribe(); + var innerIndex = 0; + var outerIndex = index++; + innerFrom(project(value, outerIndex)).subscribe((innerSubscriber = new OperatorSubscriber(subscriber, function (innerValue) { return subscriber.next(resultSelector ? resultSelector(value, innerValue, outerIndex, innerIndex++) : innerValue); }, function () { + innerSubscriber = null; + checkComplete(); + }))); + }, function () { + isComplete = true; + checkComplete(); + })); + }); } - var SwitchMapOperator = /*@__PURE__*/ (function () { - function SwitchMapOperator(project) { - this.project = project; - } - SwitchMapOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SwitchMapSubscriber(subscriber, this.project)); - }; - return SwitchMapOperator; - }()); - var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { - __extends(SwitchMapSubscriber, _super); - function SwitchMapSubscriber(destination, project) { - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.index = 0; - return _this; - } - SwitchMapSubscriber.prototype._next = function (value) { - var result; - var index = this.index++; - try { - result = this.project(value, index); - } - catch (error) { - this.destination.error(error); - return; - } - this._innerSub(result); - }; - SwitchMapSubscriber.prototype._innerSub = function (result) { - var innerSubscription = this.innerSubscription; - if (innerSubscription) { - innerSubscription.unsubscribe(); - } - var innerSubscriber = new SimpleInnerSubscriber(this); - var destination = this.destination; - destination.add(innerSubscriber); - this.innerSubscription = innerSubscribe(result, innerSubscriber); - if (this.innerSubscription !== innerSubscriber) { - destination.add(this.innerSubscription); - } - }; - SwitchMapSubscriber.prototype._complete = function () { - var innerSubscription = this.innerSubscription; - if (!innerSubscription || innerSubscription.closed) { - _super.prototype._complete.call(this); - } - this.unsubscribe(); - }; - SwitchMapSubscriber.prototype._unsubscribe = function () { - this.innerSubscription = undefined; - }; - SwitchMapSubscriber.prototype.notifyComplete = function () { - this.innerSubscription = undefined; - if (this.isStopped) { - _super.prototype._complete.call(this); - } - }; - SwitchMapSubscriber.prototype.notifyNext = function (innerValue) { - this.destination.next(innerValue); - }; - return SwitchMapSubscriber; - }(SimpleOuterSubscriber)); - /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ function takeUntil(notifier) { - return function (source) { return source.lift(new TakeUntilOperator(notifier)); }; + return operate(function (source, subscriber) { + innerFrom(notifier).subscribe(new OperatorSubscriber(subscriber, function () { return subscriber.complete(); }, noop)); + !subscriber.closed && source.subscribe(subscriber); + }); } - var TakeUntilOperator = /*@__PURE__*/ (function () { - function TakeUntilOperator(notifier) { - this.notifier = notifier; - } - TakeUntilOperator.prototype.call = function (subscriber, source) { - var takeUntilSubscriber = new TakeUntilSubscriber(subscriber); - var notifierSubscription = innerSubscribe(this.notifier, new SimpleInnerSubscriber(takeUntilSubscriber)); - if (notifierSubscription && !takeUntilSubscriber.seenValue) { - takeUntilSubscriber.add(notifierSubscription); - return source.subscribe(takeUntilSubscriber); - } - return takeUntilSubscriber; - }; - return TakeUntilOperator; - }()); - var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { - __extends(TakeUntilSubscriber, _super); - function TakeUntilSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.seenValue = false; - return _this; - } - TakeUntilSubscriber.prototype.notifyNext = function () { - this.seenValue = true; - this.complete(); - }; - TakeUntilSubscriber.prototype.notifyComplete = function () { - }; - return TakeUntilSubscriber; - }(SimpleOuterSubscriber)); - /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ function takeWhile(predicate, inclusive) { - if (inclusive === void 0) { - inclusive = false; - } - return function (source) { - return source.lift(new TakeWhileOperator(predicate, inclusive)); - }; - } - var TakeWhileOperator = /*@__PURE__*/ (function () { - function TakeWhileOperator(predicate, inclusive) { - this.predicate = predicate; - this.inclusive = inclusive; - } - TakeWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate, this.inclusive)); - }; - return TakeWhileOperator; - }()); - var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { - __extends(TakeWhileSubscriber, _super); - function TakeWhileSubscriber(destination, predicate, inclusive) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.inclusive = inclusive; - _this.index = 0; - return _this; - } - TakeWhileSubscriber.prototype._next = function (value) { - var destination = this.destination; - var result; - try { - result = this.predicate(value, this.index++); - } - catch (err) { - destination.error(err); - return; - } - this.nextOrComplete(value, result); - }; - TakeWhileSubscriber.prototype.nextOrComplete = function (value, predicateResult) { - var destination = this.destination; - if (Boolean(predicateResult)) { - destination.next(value); - } - else { - if (this.inclusive) { - destination.next(value); - } - destination.complete(); - } - }; - return TakeWhileSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ - function tap(nextOrObserver, error, complete) { - return function tapOperatorFunction(source) { - return source.lift(new DoOperator(nextOrObserver, error, complete)); - }; - } - var DoOperator = /*@__PURE__*/ (function () { - function DoOperator(nextOrObserver, error, complete) { - this.nextOrObserver = nextOrObserver; - this.error = error; - this.complete = complete; - } - DoOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); - }; - return DoOperator; - }()); - var TapSubscriber = /*@__PURE__*/ (function (_super) { - __extends(TapSubscriber, _super); - function TapSubscriber(destination, observerOrNext, error, complete) { - var _this = _super.call(this, destination) || this; - _this._tapNext = noop; - _this._tapError = noop; - _this._tapComplete = noop; - _this._tapError = error || noop; - _this._tapComplete = complete || noop; - if (isFunction(observerOrNext)) { - _this._context = _this; - _this._tapNext = observerOrNext; - } - else if (observerOrNext) { - _this._context = observerOrNext; - _this._tapNext = observerOrNext.next || noop; - _this._tapError = observerOrNext.error || noop; - _this._tapComplete = observerOrNext.complete || noop; - } - return _this; - } - TapSubscriber.prototype._next = function (value) { - try { - this._tapNext.call(this._context, value); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(value); - }; - TapSubscriber.prototype._error = function (err) { - try { - this._tapError.call(this._context, err); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.error(err); - }; - TapSubscriber.prototype._complete = function () { - try { - this._tapComplete.call(this._context); - } - catch (err) { - this.destination.error(err); - return; - } - return this.destination.complete(); - }; - return TapSubscriber; - }(Subscriber)); - - /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ - function timeoutWith(due, withObservable, scheduler) { - if (scheduler === void 0) { - scheduler = async; - } - return function (source) { - var absoluteTimeout = isDate(due); - var waitFor = absoluteTimeout ? (+due - scheduler.now()) : Math.abs(due); - return source.lift(new TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler)); - }; + if (inclusive === void 0) { inclusive = false; } + return operate(function (source, subscriber) { + var index = 0; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + var result = predicate(value, index++); + (result || inclusive) && subscriber.next(value); + !result && subscriber.complete(); + })); + }); } - var TimeoutWithOperator = /*@__PURE__*/ (function () { - function TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler) { - this.waitFor = waitFor; - this.absoluteTimeout = absoluteTimeout; - this.withObservable = withObservable; - this.scheduler = scheduler; - } - TimeoutWithOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TimeoutWithSubscriber(subscriber, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)); - }; - return TimeoutWithOperator; - }()); - var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { - __extends(TimeoutWithSubscriber, _super); - function TimeoutWithSubscriber(destination, absoluteTimeout, waitFor, withObservable, scheduler) { - var _this = _super.call(this, destination) || this; - _this.absoluteTimeout = absoluteTimeout; - _this.waitFor = waitFor; - _this.withObservable = withObservable; - _this.scheduler = scheduler; - _this.scheduleTimeout(); - return _this; - } - TimeoutWithSubscriber.dispatchTimeout = function (subscriber) { - var withObservable = subscriber.withObservable; - subscriber._unsubscribeAndRecycle(); - subscriber.add(innerSubscribe(withObservable, new SimpleInnerSubscriber(subscriber))); - }; - TimeoutWithSubscriber.prototype.scheduleTimeout = function () { - var action = this.action; - if (action) { - this.action = action.schedule(this, this.waitFor); - } - else { - this.add(this.action = this.scheduler.schedule(TimeoutWithSubscriber.dispatchTimeout, this.waitFor, this)); - } - }; - TimeoutWithSubscriber.prototype._next = function (value) { - if (!this.absoluteTimeout) { - this.scheduleTimeout(); - } - _super.prototype._next.call(this, value); - }; - TimeoutWithSubscriber.prototype._unsubscribe = function () { - this.action = undefined; - this.scheduler = null; - this.withObservable = null; - }; - return TimeoutWithSubscriber; - }(SimpleOuterSubscriber)); - /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ - function timeout(due, scheduler) { - if (scheduler === void 0) { - scheduler = async; - } - return timeoutWith(due, throwError(new TimeoutError()), scheduler); + function tap(observerOrNext, error, complete) { + var tapObserver = isFunction(observerOrNext) || error || complete + ? + { next: observerOrNext, error: error, complete: complete } + : observerOrNext; + return tapObserver + ? operate(function (source, subscriber) { + var _a; + (_a = tapObserver.subscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); + var isUnsub = true; + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + var _a; + (_a = tapObserver.next) === null || _a === void 0 ? void 0 : _a.call(tapObserver, value); + subscriber.next(value); + }, function () { + var _a; + isUnsub = false; + (_a = tapObserver.complete) === null || _a === void 0 ? void 0 : _a.call(tapObserver); + subscriber.complete(); + }, function (err) { + var _a; + isUnsub = false; + (_a = tapObserver.error) === null || _a === void 0 ? void 0 : _a.call(tapObserver, err); + subscriber.error(err); + }, function () { + var _a, _b; + if (isUnsub) { + (_a = tapObserver.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); + } + (_b = tapObserver.finalize) === null || _b === void 0 ? void 0 : _b.call(tapObserver); + })); + }) + : + identity; } - /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ function withLatestFrom() { - var args = []; + var inputs = []; for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return function (source) { - var project; - if (typeof args[args.length - 1] === 'function') { - project = args.pop(); - } - var observables = args; - return source.lift(new WithLatestFromOperator(observables, project)); - }; - } - var WithLatestFromOperator = /*@__PURE__*/ (function () { - function WithLatestFromOperator(observables, project) { - this.observables = observables; - this.project = project; - } - WithLatestFromOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WithLatestFromSubscriber(subscriber, this.observables, this.project)); - }; - return WithLatestFromOperator; - }()); - var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { - __extends(WithLatestFromSubscriber, _super); - function WithLatestFromSubscriber(destination, observables, project) { - var _this = _super.call(this, destination) || this; - _this.observables = observables; - _this.project = project; - _this.toRespond = []; - var len = observables.length; - _this.values = new Array(len); - for (var i = 0; i < len; i++) { - _this.toRespond.push(i); - } + inputs[_i] = arguments[_i]; + } + var project = popResultSelector(inputs); + return operate(function (source, subscriber) { + var len = inputs.length; + var otherValues = new Array(len); + var hasValue = inputs.map(function () { return false; }); + var ready = false; + var _loop_1 = function (i) { + innerFrom(inputs[i]).subscribe(new OperatorSubscriber(subscriber, function (value) { + otherValues[i] = value; + if (!ready && !hasValue[i]) { + hasValue[i] = true; + (ready = hasValue.every(identity)) && (hasValue = null); + } + }, noop)); + }; for (var i = 0; i < len; i++) { - var observable = observables[i]; - _this.add(subscribeToResult(_this, observable, undefined, i)); + _loop_1(i); } - return _this; - } - WithLatestFromSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { - this.values[outerIndex] = innerValue; - var toRespond = this.toRespond; - if (toRespond.length > 0) { - var found = toRespond.indexOf(outerIndex); - if (found !== -1) { - toRespond.splice(found, 1); - } - } - }; - WithLatestFromSubscriber.prototype.notifyComplete = function () { - }; - WithLatestFromSubscriber.prototype._next = function (value) { - if (this.toRespond.length === 0) { - var args = [value].concat(this.values); - if (this.project) { - this._tryProject(args); - } - else { - this.destination.next(args); + source.subscribe(new OperatorSubscriber(subscriber, function (value) { + if (ready) { + var values = __spreadArray([value], __read(otherValues)); + subscriber.next(project ? project.apply(void 0, __spreadArray([], __read(values))) : values); } - } - }; - WithLatestFromSubscriber.prototype._tryProject = function (args) { - var result; - try { - result = this.project.apply(this, args); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(result); - }; - return WithLatestFromSubscriber; - }(OuterSubscriber)); + })); + }); + } /** * @class Filter @@ -4104,8 +2746,12 @@ } } - // threejs.org/license - const REVISION = '125'; + /** + * @license + * Copyright 2010-2021 Three.js Authors + * SPDX-License-Identifier: MIT + */ + const REVISION = '134'; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; @@ -4189,6 +2835,7 @@ const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; + const RGBEFormat = RGBAFormat; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; @@ -4273,11 +2920,9 @@ * https://github.com/mrdoob/eventdispatcher.js/ */ - function EventDispatcher() {} - - Object.assign( EventDispatcher.prototype, { + class EventDispatcher { - addEventListener: function ( type, listener ) { + addEventListener( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; @@ -4295,9 +2940,9 @@ } - }, + } - hasEventListener: function ( type, listener ) { + hasEventListener( type, listener ) { if ( this._listeners === undefined ) return false; @@ -4305,9 +2950,9 @@ return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - }, + } - removeEventListener: function ( type, listener ) { + removeEventListener( type, listener ) { if ( this._listeners === undefined ) return; @@ -4326,9 +2971,9 @@ } - }, + } - dispatchEvent: function ( event ) { + dispatchEvent( event ) { if ( this._listeners === undefined ) return; @@ -4348,11 +2993,20 @@ } + event.target = null; + } } - } ); + } + + let _seed = 1234567; + + const DEG2RAD$1 = Math.PI / 180; + const RAD2DEG$1 = 180 / Math.PI; + + // const _lut = []; @@ -4362,234 +3016,263 @@ } - let _seed = 1234567; + const hasRandomUUID = typeof crypto !== 'undefined' && 'randomUUID' in crypto; - const MathUtils = { + function generateUUID() { - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, + if ( hasRandomUUID ) { - generateUUID: function () { + return crypto.randomUUID().toUpperCase(); - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 + } - const d0 = Math.random() * 0xffffffff | 0; - const d1 = Math.random() * 0xffffffff | 0; - const d2 = Math.random() * 0xffffffff | 0; - const d3 = Math.random() * 0xffffffff | 0; - const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + + // TODO Remove this code when crypto.randomUUID() is available everywhere + // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 + + const d0 = Math.random() * 0xffffffff | 0; + const d1 = Math.random() * 0xffffffff | 0; + const d2 = Math.random() * 0xffffffff | 0; + const d3 = Math.random() * 0xffffffff | 0; + const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); - - }, - - clamp: function ( value, min, max ) { - - return Math.max( min, Math.min( max, value ) ); - - }, + // .toUpperCase() here flattens concatenated strings to save heap memory space. + return uuid.toUpperCase(); - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation + } - euclideanModulo: function ( n, m ) { + function clamp$1( value, min, max ) { - return ( ( n % m ) + m ) % m; + return Math.max( min, Math.min( max, value ) ); - }, + } - // Linear mapping from range to range + // compute euclidian modulo of m % n + // https://en.wikipedia.org/wiki/Modulo_operation + function euclideanModulo( n, m ) { - mapLinear: function ( x, a1, a2, b1, b2 ) { + return ( ( n % m ) + m ) % m; - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + } - }, + // Linear mapping from range to range + function mapLinear( x, a1, a2, b1, b2 ) { - // https://en.wikipedia.org/wiki/Linear_interpolation + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - lerp: function ( x, y, t ) { + } - return ( 1 - t ) * x + t * y; + // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ + function inverseLerp( x, y, value ) { - }, + if ( x !== y ) { - // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ + return ( value - x ) / ( y - x ); - damp: function ( x, y, lambda, dt ) { + } else { - return MathUtils.lerp( x, y, 1 - Math.exp( - lambda * dt ) ); + return 0; - }, + } - // https://www.desmos.com/calculator/vcsjnyz7x4 + } - pingpong: function ( x, length = 1 ) { + // https://en.wikipedia.org/wiki/Linear_interpolation + function lerp( x, y, t ) { - return length - Math.abs( MathUtils.euclideanModulo( x, length * 2 ) - length ); + return ( 1 - t ) * x + t * y; - }, + } - // http://en.wikipedia.org/wiki/Smoothstep + // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ + function damp( x, y, lambda, dt ) { - smoothstep: function ( x, min, max ) { + return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); - if ( x <= min ) return 0; - if ( x >= max ) return 1; + } - x = ( x - min ) / ( max - min ); + // https://www.desmos.com/calculator/vcsjnyz7x4 + function pingpong( x, length = 1 ) { - return x * x * ( 3 - 2 * x ); + return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); - }, + } - smootherstep: function ( x, min, max ) { + // http://en.wikipedia.org/wiki/Smoothstep + function smoothstep( x, min, max ) { - if ( x <= min ) return 0; - if ( x >= max ) return 1; + if ( x <= min ) return 0; + if ( x >= max ) return 1; - x = ( x - min ) / ( max - min ); + x = ( x - min ) / ( max - min ); - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + return x * x * ( 3 - 2 * x ); - }, + } - // Random integer from interval + function smootherstep( x, min, max ) { - randInt: function ( low, high ) { + if ( x <= min ) return 0; + if ( x >= max ) return 1; - return low + Math.floor( Math.random() * ( high - low + 1 ) ); + x = ( x - min ) / ( max - min ); - }, + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - // Random float from interval + } - randFloat: function ( low, high ) { + // Random integer from interval + function randInt( low, high ) { - return low + Math.random() * ( high - low ); + return low + Math.floor( Math.random() * ( high - low + 1 ) ); - }, + } - // Random float from <-range/2, range/2> interval + // Random float from interval + function randFloat( low, high ) { - randFloatSpread: function ( range ) { + return low + Math.random() * ( high - low ); - return range * ( 0.5 - Math.random() ); + } - }, + // Random float from <-range/2, range/2> interval + function randFloatSpread( range ) { - // Deterministic pseudo-random float in the interval [ 0, 1 ] + return range * ( 0.5 - Math.random() ); - seededRandom: function ( s ) { + } - if ( s !== undefined ) _seed = s % 2147483647; + // Deterministic pseudo-random float in the interval [ 0, 1 ] + function seededRandom( s ) { - // Park-Miller algorithm + if ( s !== undefined ) _seed = s % 2147483647; - _seed = _seed * 16807 % 2147483647; + // Park-Miller algorithm - return ( _seed - 1 ) / 2147483646; + _seed = _seed * 16807 % 2147483647; - }, + return ( _seed - 1 ) / 2147483646; - degToRad: function ( degrees ) { + } - return degrees * MathUtils.DEG2RAD; + function degToRad( degrees ) { - }, + return degrees * DEG2RAD$1; - radToDeg: function ( radians ) { + } - return radians * MathUtils.RAD2DEG; + function radToDeg( radians ) { - }, + return radians * RAD2DEG$1; - isPowerOfTwo: function ( value ) { + } - return ( value & ( value - 1 ) ) === 0 && value !== 0; + function isPowerOfTwo( value ) { - }, + return ( value & ( value - 1 ) ) === 0 && value !== 0; - ceilPowerOfTwo: function ( value ) { + } - return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); + function ceilPowerOfTwo( value ) { - }, + return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); - floorPowerOfTwo: function ( value ) { + } - return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); + function floorPowerOfTwo( value ) { - }, + return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); - setQuaternionFromProperEuler: function ( q, a, b, c, order ) { + } - // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles + function setQuaternionFromProperEuler( q, a, b, c, order ) { - // rotations are applied to the axes in the order specified by 'order' - // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' - // angles are in radians + // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles - const cos = Math.cos; - const sin = Math.sin; + // rotations are applied to the axes in the order specified by 'order' + // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' + // angles are in radians - const c2 = cos( b / 2 ); - const s2 = sin( b / 2 ); + const cos = Math.cos; + const sin = Math.sin; - const c13 = cos( ( a + c ) / 2 ); - const s13 = sin( ( a + c ) / 2 ); + const c2 = cos( b / 2 ); + const s2 = sin( b / 2 ); - const c1_3 = cos( ( a - c ) / 2 ); - const s1_3 = sin( ( a - c ) / 2 ); + const c13 = cos( ( a + c ) / 2 ); + const s13 = sin( ( a + c ) / 2 ); - const c3_1 = cos( ( c - a ) / 2 ); - const s3_1 = sin( ( c - a ) / 2 ); + const c1_3 = cos( ( a - c ) / 2 ); + const s1_3 = sin( ( a - c ) / 2 ); - switch ( order ) { + const c3_1 = cos( ( c - a ) / 2 ); + const s3_1 = sin( ( c - a ) / 2 ); - case 'XYX': - q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); - break; + switch ( order ) { - case 'YZY': - q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); - break; + case 'XYX': + q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); + break; - case 'ZXZ': - q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); - break; + case 'YZY': + q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); + break; - case 'XZX': - q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); - break; + case 'ZXZ': + q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); + break; - case 'YXY': - q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); - break; + case 'XZX': + q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); + break; - case 'ZYZ': - q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); - break; + case 'YXY': + q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); + break; - default: - console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); + case 'ZYZ': + q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); + break; - } + default: + console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); } - }; + } + + var MathUtils = /*#__PURE__*/Object.freeze({ + __proto__: null, + DEG2RAD: DEG2RAD$1, + RAD2DEG: RAD2DEG$1, + generateUUID: generateUUID, + clamp: clamp$1, + euclideanModulo: euclideanModulo, + mapLinear: mapLinear, + inverseLerp: inverseLerp, + lerp: lerp, + damp: damp, + pingpong: pingpong, + smoothstep: smoothstep, + smootherstep: smootherstep, + randInt: randInt, + randFloat: randFloat, + randFloatSpread: randFloatSpread, + seededRandom: seededRandom, + degToRad: degToRad, + radToDeg: radToDeg, + isPowerOfTwo: isPowerOfTwo, + ceilPowerOfTwo: ceilPowerOfTwo, + floorPowerOfTwo: floorPowerOfTwo, + setQuaternionFromProperEuler: setQuaternionFromProperEuler + }); class Vector2 { constructor( x = 0, y = 0 ) { - Object.defineProperty( this, 'isVector2', { value: true } ); - this.x = x; this.y = y; @@ -5058,14 +3741,21 @@ } + *[ Symbol.iterator ]() { + + yield this.x; + yield this.y; + + } + } + Vector2.prototype.isVector2 = true; + class Matrix3 { constructor() { - Object.defineProperty( this, 'isMatrix3', { value: true } ); - this.elements = [ 1, 0, 0, @@ -5108,12 +3798,6 @@ } - clone() { - - return new this.constructor().fromArray( this.elements ); - - } - copy( m ) { const te = this.elements; @@ -5268,7 +3952,7 @@ getNormalMatrix( matrix4 ) { - return this.setFromMatrix4( matrix4 ).copy( this ).invert().transpose(); + return this.setFromMatrix4( matrix4 ).invert().transpose(); } @@ -5396,13 +4080,79 @@ } + clone() { + + return new this.constructor().fromArray( this.elements ); + + } + + } + + Matrix3.prototype.isMatrix3 = true; + + function arrayMax( array ) { + + if ( array.length === 0 ) return - Infinity; + + let max = array[ 0 ]; + + for ( let i = 1, l = array.length; i < l; ++ i ) { + + if ( array[ i ] > max ) max = array[ i ]; + + } + + return max; + + } + + function createElementNS( name ) { + + return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); + + } + + /** + * cyrb53 hash for string from: https://stackoverflow.com/a/52171480 + * + * Public Domain, @bryc - https://stackoverflow.com/users/815680/bryc + * + * It is roughly similar to the well-known MurmurHash/xxHash algorithms. It uses a combination + * of multiplication and Xorshift to generate the hash, but not as thorough. As a result it's + * faster than either would be in JavaScript and significantly simpler to implement. Keep in + * mind this is not a secure algorithm, if privacy/security is a concern, this is not for you. + * + * @param {string} str + * @param {number} seed, default 0 + * @returns number + */ + function hashString( str, seed = 0 ) { + + let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; + + for ( let i = 0, ch; i < str.length; i ++ ) { + + ch = str.charCodeAt( i ); + + h1 = Math.imul( h1 ^ ch, 2654435761 ); + + h2 = Math.imul( h2 ^ ch, 1597334677 ); + + } + + h1 = Math.imul( h1 ^ ( h1 >>> 16 ), 2246822507 ) ^ Math.imul( h2 ^ ( h2 >>> 13 ), 3266489909 ); + + h2 = Math.imul( h2 ^ ( h2 >>> 16 ), 2246822507 ) ^ Math.imul( h1 ^ ( h1 >>> 13 ), 3266489909 ); + + return 4294967296 * ( 2097151 & h2 ) + ( h1 >>> 0 ); + } let _canvas; - const ImageUtils = { + class ImageUtils { - getDataURL: function ( image ) { + static getDataURL( image ) { if ( /^data:/i.test( image.src ) ) { @@ -5424,7 +4174,7 @@ } else { - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' ); _canvas.width = image.width; _canvas.height = image.height; @@ -5447,6 +4197,8 @@ if ( canvas.width > 2048 || canvas.height > 2048 ) { + console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); + return canvas.toDataURL( 'image/jpeg', 0.6 ); } else { @@ -5457,81 +4209,80 @@ } - }; + } let textureId = 0; - function Texture( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { + class Texture extends EventDispatcher { - Object.defineProperty( this, 'id', { value: textureId ++ } ); + constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { - this.uuid = MathUtils.generateUUID(); + super(); - this.name = ''; + Object.defineProperty( this, 'id', { value: textureId ++ } ); - this.image = image; - this.mipmaps = []; + this.uuid = generateUUID(); - this.mapping = mapping; + this.name = ''; - this.wrapS = wrapS; - this.wrapT = wrapT; + this.image = image; + this.mipmaps = []; - this.magFilter = magFilter; - this.minFilter = minFilter; + this.mapping = mapping; - this.anisotropy = anisotropy; + this.wrapS = wrapS; + this.wrapT = wrapT; - this.format = format; - this.internalFormat = null; - this.type = type; + this.magFilter = magFilter; + this.minFilter = minFilter; - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; + this.anisotropy = anisotropy; - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); + this.format = format; + this.internalFormat = null; + this.type = type; - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + this.offset = new Vector2( 0, 0 ); + this.repeat = new Vector2( 1, 1 ); + this.center = new Vector2( 0, 0 ); + this.rotation = 0; - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding; + this.matrixAutoUpdate = true; + this.matrix = new Matrix3(); - this.version = 0; - this.onUpdate = null; + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - } + // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. + // + // Also changing the encoding after already used by a Material will not automatically make the Material + // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. + this.encoding = encoding; - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; + this.userData = {}; - Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + this.version = 0; + this.onUpdate = null; - constructor: Texture, + this.isRenderTargetTexture = false; - isTexture: true, + } - updateMatrix: function () { + updateMatrix() { this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); - }, + } - clone: function () { + clone() { return new this.constructor().copy( this ); - }, + } - copy: function ( source ) { + copy( source ) { this.name = source.name; @@ -5566,11 +4317,13 @@ this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + return this; - }, + } - toJSON: function ( meta ) { + toJSON( meta ) { const isRootObject = ( meta === undefined || typeof meta === 'string' ); @@ -5623,7 +4376,7 @@ if ( image.uuid === undefined ) { - image.uuid = MathUtils.generateUUID(); // UGH + image.uuid = generateUUID(); // UGH } @@ -5672,6 +4425,8 @@ } + if ( JSON.stringify( this.userData ) !== '{}' ) output.userData = this.userData; + if ( ! isRootObject ) { meta.textures[ this.uuid ] = output; @@ -5680,15 +4435,15 @@ return output; - }, + } - dispose: function () { + dispose() { this.dispatchEvent( { type: 'dispose' } ); - }, + } - transformUv: function ( uv ) { + transformUv( uv ) { if ( this.mapping !== UVMapping ) return uv; @@ -5768,17 +4523,18 @@ } - } ); - - Object.defineProperty( Texture.prototype, 'needsUpdate', { - - set: function ( value ) { + set needsUpdate( value ) { if ( value === true ) this.version ++; } - } ); + } + + Texture.DEFAULT_IMAGE = undefined; + Texture.DEFAULT_MAPPING = UVMapping; + + Texture.prototype.isTexture = true; function serializeImage( image ) { @@ -5818,8 +4574,6 @@ constructor( x = 0, y = 0, z = 0, w = 1 ) { - Object.defineProperty( this, 'isVector4', { value: true } ); - this.x = x; this.y = y; this.z = z; @@ -6466,8 +5220,19 @@ } + *[ Symbol.iterator ]() { + + yield this.x; + yield this.y; + yield this.z; + yield this.w; + + } + } + Vector4.prototype.isVector4 = true; + /* In options, we can specify: * Texture parameters for an auto-generated target texture @@ -6475,29 +5240,26 @@ */ class WebGLRenderTarget extends EventDispatcher { - constructor( width, height, options ) { + constructor( width, height, options = {} ) { super(); - Object.defineProperty( this, 'isWebGLRenderTarget', { value: true } ); - this.width = width; this.height = height; + this.depth = 1; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); - options = options || {}; - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + this.texture.isRenderTargetTexture = true; - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; + this.texture.image = { width: width, height: height, depth: 1 }; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; @@ -6506,15 +5268,29 @@ } - setSize( width, height ) { + setTexture( texture ) { - if ( this.width !== width || this.height !== height ) { + texture.image = { + width: this.width, + height: this.height, + depth: this.depth + }; + + this.texture = texture; + + } + + setSize( width, height, depth = 1 ) { + + if ( this.width !== width || this.height !== height || this.depth !== depth ) { this.width = width; this.height = height; + this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; + this.texture.image.depth = depth; this.dispose(); @@ -6535,10 +5311,12 @@ this.width = source.width; this.height = source.height; + this.depth = source.depth; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); + this.texture.image = { ...this.texture.image }; // See #20328. this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; @@ -6556,12 +5334,112 @@ } + WebGLRenderTarget.prototype.isWebGLRenderTarget = true; + + class WebGLMultipleRenderTargets extends WebGLRenderTarget { + + constructor( width, height, count ) { + + super( width, height ); + + const texture = this.texture; + + this.texture = []; + + for ( let i = 0; i < count; i ++ ) { + + this.texture[ i ] = texture.clone(); + + } + + } + + setSize( width, height, depth = 1 ) { + + if ( this.width !== width || this.height !== height || this.depth !== depth ) { + + this.width = width; + this.height = height; + this.depth = depth; + + for ( let i = 0, il = this.texture.length; i < il; i ++ ) { + + this.texture[ i ].image.width = width; + this.texture[ i ].image.height = height; + this.texture[ i ].image.depth = depth; + + } + + this.dispose(); + + } + + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); + + return this; + + } + + copy( source ) { + + this.dispose(); + + this.width = source.width; + this.height = source.height; + this.depth = source.depth; + + this.viewport.set( 0, 0, this.width, this.height ); + this.scissor.set( 0, 0, this.width, this.height ); + + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + this.depthTexture = source.depthTexture; + + this.texture.length = 0; + + for ( let i = 0, il = source.texture.length; i < il; i ++ ) { + + this.texture[ i ] = source.texture[ i ].clone(); + + } + + return this; + + } + + } + + WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; + + class WebGLMultisampleRenderTarget extends WebGLRenderTarget { + + constructor( width, height, options ) { + + super( width, height, options ); + + this.samples = 4; + + } + + copy( source ) { + + super.copy.call( this, source ); + + this.samples = source.samples; + + return this; + + } + + } + + WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; + class Quaternion { constructor( x = 0, y = 0, z = 0, w = 1 ) { - Object.defineProperty( this, 'isQuaternion', { value: true } ); - this._x = x; this._y = y; this._z = z; @@ -6571,7 +5449,8 @@ static slerp( qa, qb, qm, t ) { - return qm.copy( qa ).slerp( qb, t ); + console.warn( 'THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead.' ); + return qm.slerpQuaternions( qa, qb, t ); } @@ -6589,6 +5468,26 @@ z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; + if ( t === 0 ) { + + dst[ dstOffset + 0 ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; + return; + + } + + if ( t === 1 ) { + + dst[ dstOffset + 0 ] = x1; + dst[ dstOffset + 1 ] = y1; + dst[ dstOffset + 2 ] = z1; + dst[ dstOffset + 3 ] = w1; + return; + + } + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { let s = 1 - t; @@ -6901,11 +5800,11 @@ // assumes direction vectors vFrom and vTo are normalized - const EPS = 0.000001; - let r = vFrom.dot( vTo ) + 1; - if ( r < EPS ) { + if ( r < Number.EPSILON ) { + + // vFrom and vTo point in opposite directions r = 0; @@ -6942,7 +5841,7 @@ angleTo( q ) { - return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) ); + return 2 * Math.acos( Math.abs( clamp$1( this.dot( q ), - 1, 1 ) ) ); } @@ -7139,6 +6038,35 @@ } + slerpQuaternions( qa, qb, t ) { + + this.copy( qa ).slerp( qb, t ); + + } + + random() { + + // Derived from http://planning.cs.uiuc.edu/node198.html + // Note, this source uses w, x, y, z ordering, + // so we swap the order below. + + const u1 = Math.random(); + const sqrt1u1 = Math.sqrt( 1 - u1 ); + const sqrtu1 = Math.sqrt( u1 ); + + const u2 = 2 * Math.PI * Math.random(); + + const u3 = 2 * Math.PI * Math.random(); + + return this.set( + sqrt1u1 * Math.cos( u2 ), + sqrtu1 * Math.sin( u3 ), + sqrtu1 * Math.cos( u3 ), + sqrt1u1 * Math.sin( u2 ), + ); + + } + equals( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); @@ -7192,12 +6120,12 @@ } + Quaternion.prototype.isQuaternion = true; + class Vector3 { constructor( x = 0, y = 0, z = 0 ) { - Object.defineProperty( this, 'isVector3', { value: true } ); - this.x = x; this.y = y; this.z = z; @@ -7423,13 +6351,13 @@ } - return this.applyQuaternion( _quaternion.setFromEuler( euler ) ); + return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } applyAxisAngle( axis, angle ) { - return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) ); + return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); } @@ -7731,9 +6659,9 @@ projectOnPlane( planeNormal ) { - _vector.copy( this ).projectOnVector( planeNormal ); + _vector$c.copy( this ).projectOnVector( planeNormal ); - return this.sub( _vector ); + return this.sub( _vector$c ); } @@ -7742,7 +6670,7 @@ // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length - return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); } @@ -7756,7 +6684,7 @@ // clamp, to handle numerical problems - return Math.acos( MathUtils.clamp( theta, - 1, 1 ) ); + return Math.acos( clamp$1( theta, - 1, 1 ) ); } @@ -7904,19 +6832,43 @@ } + randomDirection() { + + // Derived from https://mathworld.wolfram.com/SpherePointPicking.html + + const u = ( Math.random() - 0.5 ) * 2; + const t = Math.random() * Math.PI * 2; + const f = Math.sqrt( 1 - u ** 2 ); + + this.x = f * Math.cos( t ); + this.y = f * Math.sin( t ); + this.z = u; + + return this; + + } + + *[ Symbol.iterator ]() { + + yield this.x; + yield this.y; + yield this.z; + + } + } - const _vector = /*@__PURE__*/ new Vector3(); - const _quaternion = /*@__PURE__*/ new Quaternion(); + Vector3.prototype.isVector3 = true; - class Box3 { + const _vector$c = /*@__PURE__*/ new Vector3(); + const _quaternion$4 = /*@__PURE__*/ new Quaternion(); - constructor( min, max ) { + class Box3 { - Object.defineProperty( this, 'isBox3', { value: true } ); + constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); + this.min = min; + this.max = max; } @@ -8011,7 +6963,7 @@ setFromCenterAndSize( center, size ) { - const halfSize = _vector$1.copy( size ).multiplyScalar( 0.5 ); + const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); @@ -8062,26 +7014,12 @@ getCenter( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); - - } - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } getSize( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); - - } - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); } @@ -8130,10 +7068,10 @@ } - _box.copy( geometry.boundingBox ); - _box.applyMatrix4( object.matrixWorld ); + _box$3.copy( geometry.boundingBox ); + _box$3.applyMatrix4( object.matrixWorld ); - this.union( _box ); + this.union( _box$3 ); } @@ -8170,13 +7108,6 @@ // This can potentially have a divide by zero if the box // has a size dimension of 0. - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); - - } - return target.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), @@ -8197,10 +7128,10 @@ intersectsSphere( sphere ) { // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$1 ); + this.clampPoint( sphere.center, _vector$b ); // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$1.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); + return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); } @@ -8264,14 +7195,14 @@ _extents.subVectors( this.max, _center ); // translate triangle to aabb origin - _v0.subVectors( triangle.a, _center ); - _v1.subVectors( triangle.b, _center ); - _v2.subVectors( triangle.c, _center ); + _v0$2.subVectors( triangle.a, _center ); + _v1$7.subVectors( triangle.b, _center ); + _v2$3.subVectors( triangle.c, _center ); // compute edge vectors for triangle - _f0.subVectors( _v1, _v0 ); - _f1.subVectors( _v2, _v1 ); - _f2.subVectors( _v0, _v2 ); + _f0.subVectors( _v1$7, _v0$2 ); + _f1.subVectors( _v2$3, _v1$7 ); + _f2.subVectors( _v0$2, _v2$3 ); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation @@ -8281,7 +7212,7 @@ _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 ]; - if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) { + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { return false; @@ -8289,7 +7220,7 @@ // test 3 face normals from the aabb axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) { + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { return false; @@ -8300,26 +7231,19 @@ _triangleNormal.crossVectors( _f0, _f1 ); axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - return satForAxes( axes, _v0, _v1, _v2, _extents ); + return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); } clampPoint( point, target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); - - } - return target.copy( point ).clamp( this.min, this.max ); } distanceToPoint( point ) { - const clampedPoint = _vector$1.copy( point ).clamp( this.min, this.max ); + const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); @@ -8327,16 +7251,9 @@ getBoundingSphere( target ) { - if ( target === undefined ) { - - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency - - } - this.getCenter( target.center ); - target.radius = this.getSize( _vector$1 ).length() * 0.5; + target.radius = this.getSize( _vector$b ).length() * 0.5; return target; @@ -8401,31 +7318,7 @@ } - function satForAxes( axes, v0, v1, v2, extents ) { - - for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - const p0 = v0.dot( _testAxis ); - const p1 = v1.dot( _testAxis ); - const p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; - - } - - } - - return true; - - } + Box3.prototype.isBox3 = true; const _points = [ /*@__PURE__*/ new Vector3(), @@ -8438,15 +7331,15 @@ /*@__PURE__*/ new Vector3() ]; - const _vector$1 = /*@__PURE__*/ new Vector3(); + const _vector$b = /*@__PURE__*/ new Vector3(); - const _box = /*@__PURE__*/ new Box3(); + const _box$3 = /*@__PURE__*/ new Box3(); // triangle centered vertices - const _v0 = /*@__PURE__*/ new Vector3(); - const _v1 = /*@__PURE__*/ new Vector3(); - const _v2 = /*@__PURE__*/ new Vector3(); + const _v0$2 = /*@__PURE__*/ new Vector3(); + const _v1$7 = /*@__PURE__*/ new Vector3(); + const _v2$3 = /*@__PURE__*/ new Vector3(); // triangle edge vectors @@ -8459,14 +7352,43 @@ const _triangleNormal = /*@__PURE__*/ new Vector3(); const _testAxis = /*@__PURE__*/ new Vector3(); - const _box$1 = /*@__PURE__*/ new Box3(); + function satForAxes( axes, v0, v1, v2, extents ) { + + for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { + + _testAxis.fromArray( axes, i ); + // project the aabb onto the seperating axis + const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); + // project all 3 vertices of the triangle onto the seperating axis + const p0 = v0.dot( _testAxis ); + const p1 = v1.dot( _testAxis ); + const p2 = v2.dot( _testAxis ); + // actual test, basically see if either of the most extreme of the triangle points intersects r + if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { + + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is seperating and we can exit + return false; + + } + + } + + return true; + + } + + const _box$2 = /*@__PURE__*/ new Box3(); + const _v1$6 = /*@__PURE__*/ new Vector3(); + const _toFarthestPoint = /*@__PURE__*/ new Vector3(); + const _toPoint = /*@__PURE__*/ new Vector3(); class Sphere { - constructor( center, radius ) { + constructor( center = new Vector3(), radius = - 1 ) { - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : - 1; + this.center = center; + this.radius = radius; } @@ -8489,7 +7411,7 @@ } else { - _box$1.setFromPoints( points ).getCenter( center ); + _box$2.setFromPoints( points ).getCenter( center ); } @@ -8507,12 +7429,6 @@ } - clone() { - - return new this.constructor().copy( this ); - - } - copy( sphere ) { this.center.copy( sphere.center ); @@ -8573,13 +7489,6 @@ const deltaLengthSq = this.center.distanceToSquared( point ); - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); - - } - target.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { @@ -8595,13 +7504,6 @@ getBoundingBox( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); - - } - if ( this.isEmpty() ) { // Empty sphere produces empty bounding box @@ -8634,29 +7536,78 @@ } + expandByPoint( point ) { + + // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 + + _toPoint.subVectors( point, this.center ); + + const lengthSq = _toPoint.lengthSq(); + + if ( lengthSq > ( this.radius * this.radius ) ) { + + const length = Math.sqrt( lengthSq ); + const missingRadiusHalf = ( length - this.radius ) * 0.5; + + // Nudge this sphere towards the target point. Add half the missing distance to radius, + // and the other half to position. This gives a tighter enclosure, instead of if + // the whole missing distance were just added to radius. + + this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); + this.radius += missingRadiusHalf; + + } + + return this; + + } + + union( sphere ) { + + // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 + + // To enclose another sphere into this sphere, we only need to enclose two points: + // 1) Enclose the farthest point on the other sphere into this sphere. + // 2) Enclose the opposite point of the farthest point into this sphere. + + _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); + + this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); + this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); + + return this; + + } + equals( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } + clone() { + + return new this.constructor().copy( this ); + + } + } - const _vector$2 = /*@__PURE__*/ new Vector3(); + const _vector$a = /*@__PURE__*/ new Vector3(); const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); const _edge1 = /*@__PURE__*/ new Vector3(); const _edge2 = /*@__PURE__*/ new Vector3(); - const _normal = /*@__PURE__*/ new Vector3(); + const _normal$1 = /*@__PURE__*/ new Vector3(); class Ray { - constructor( origin, direction ) { + constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 ); + this.origin = origin; + this.direction = direction; } @@ -8669,12 +7620,6 @@ } - clone() { - - return new this.constructor().copy( this ); - - } - copy( ray ) { this.origin.copy( ray.origin ); @@ -8686,13 +7631,6 @@ at( t, target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); - - } - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); } @@ -8707,7 +7645,7 @@ recast( t ) { - this.origin.copy( this.at( t, _vector$2 ) ); + this.origin.copy( this.at( t, _vector$a ) ); return this; @@ -8715,13 +7653,6 @@ closestPointToPoint( point, target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - target.subVectors( point, this.origin ); const directionDistance = target.dot( this.direction ); @@ -8744,7 +7675,7 @@ distanceSqToPoint( point ) { - const directionDistance = _vector$2.subVectors( point, this.origin ).dot( this.direction ); + const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray @@ -8754,9 +7685,9 @@ } - _vector$2.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - return _vector$2.distanceToSquared( point ); + return _vector$a.distanceToSquared( point ); } @@ -8881,9 +7812,9 @@ intersectSphere( sphere, target ) { - _vector$2.subVectors( sphere.center, this.origin ); - const tca = _vector$2.dot( this.direction ); - const d2 = _vector$2.dot( _vector$2 ) - tca * tca; + _vector$a.subVectors( sphere.center, this.origin ); + const tca = _vector$a.dot( this.direction ); + const d2 = _vector$a.dot( _vector$a ) - tca * tca; const radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; @@ -9053,7 +7984,7 @@ intersectsBox( box ) { - return this.intersectBox( box, _vector$2 ) !== null; + return this.intersectBox( box, _vector$a ) !== null; } @@ -9065,14 +7996,14 @@ _edge1.subVectors( b, a ); _edge2.subVectors( c, a ); - _normal.crossVectors( _edge1, _edge2 ); + _normal$1.crossVectors( _edge1, _edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal ); + let DdN = this.direction.dot( _normal$1 ); let sign; if ( DdN > 0 ) { @@ -9118,7 +8049,7 @@ } // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal ); + const QdN = - sign * _diff.dot( _normal$1 ); // t < 0, no intersection if ( QdN < 0 ) { @@ -9147,14 +8078,18 @@ } + clone() { + + return new this.constructor().copy( this ); + + } + } class Matrix4 { constructor() { - Object.defineProperty( this, 'isMatrix4', { value: true } ); - this.elements = [ 1, 0, 0, 0, @@ -9279,9 +8214,9 @@ const te = this.elements; const me = m.elements; - const scaleX = 1 / _v1$1.setFromMatrixColumn( m, 0 ).length(); - const scaleY = 1 / _v1$1.setFromMatrixColumn( m, 1 ).length(); - const scaleZ = 1 / _v1$1.setFromMatrixColumn( m, 2 ).length(); + const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); + const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); + const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; @@ -9824,13 +8759,13 @@ } - makeShear( x, y, z ) { + makeShear( xy, xz, yx, yz, zx, zy ) { this.set( - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, + 1, yx, zx, 0, + xy, 1, zy, 0, + xz, yz, 1, 0, 0, 0, 0, 1 ); @@ -9879,9 +8814,9 @@ const te = this.elements; - let sx = _v1$1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - const sy = _v1$1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - const sz = _v1$1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); @@ -9892,25 +8827,25 @@ position.z = te[ 14 ]; // scale the rotation part - _m1.copy( this ); + _m1$2.copy( this ); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; - _m1.elements[ 0 ] *= invSX; - _m1.elements[ 1 ] *= invSX; - _m1.elements[ 2 ] *= invSX; + _m1$2.elements[ 0 ] *= invSX; + _m1$2.elements[ 1 ] *= invSX; + _m1$2.elements[ 2 ] *= invSX; - _m1.elements[ 4 ] *= invSY; - _m1.elements[ 5 ] *= invSY; - _m1.elements[ 6 ] *= invSY; + _m1$2.elements[ 4 ] *= invSY; + _m1$2.elements[ 5 ] *= invSY; + _m1$2.elements[ 6 ] *= invSY; - _m1.elements[ 8 ] *= invSZ; - _m1.elements[ 9 ] *= invSZ; - _m1.elements[ 10 ] *= invSZ; + _m1$2.elements[ 8 ] *= invSZ; + _m1$2.elements[ 9 ] *= invSZ; + _m1$2.elements[ 10 ] *= invSZ; - quaternion.setFromRotationMatrix( _m1 ); + quaternion.setFromRotationMatrix( _m1$2 ); scale.x = sx; scale.y = sy; @@ -10023,20 +8958,23 @@ } - const _v1$1 = /*@__PURE__*/ new Vector3(); - const _m1 = /*@__PURE__*/ new Matrix4(); + Matrix4.prototype.isMatrix4 = true; + + const _v1$5 = /*@__PURE__*/ new Vector3(); + const _m1$2 = /*@__PURE__*/ new Matrix4(); const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); const _x = /*@__PURE__*/ new Vector3(); const _y = /*@__PURE__*/ new Vector3(); const _z = /*@__PURE__*/ new Vector3(); + const _matrix$1 = /*@__PURE__*/ new Matrix4(); + const _quaternion$3 = /*@__PURE__*/ new Quaternion(); + class Euler { constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { - Object.defineProperty( this, 'isEuler', { value: true } ); - this._x = x; this._y = y; this._z = z; @@ -10096,12 +9034,12 @@ } - set( x, y, z, order ) { + set( x, y, z, order = this._order ) { this._x = x; this._y = y; this._z = z; - this._order = order || this._order; + this._order = order; this._onChangeCallback(); @@ -10128,9 +9066,7 @@ } - setFromRotationMatrix( m, order, update ) { - - const clamp = MathUtils.clamp; + setFromRotationMatrix( m, order = this._order, update = true ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) @@ -10139,13 +9075,11 @@ const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - order = order || this._order; - switch ( order ) { case 'XYZ': - this._y = Math.asin( clamp( m13, - 1, 1 ) ); + this._y = Math.asin( clamp$1( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.9999999 ) { @@ -10163,7 +9097,7 @@ case 'YXZ': - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + this._x = Math.asin( - clamp$1( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.9999999 ) { @@ -10181,7 +9115,7 @@ case 'ZXY': - this._x = Math.asin( clamp( m32, - 1, 1 ) ); + this._x = Math.asin( clamp$1( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.9999999 ) { @@ -10199,7 +9133,7 @@ case 'ZYX': - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + this._y = Math.asin( - clamp$1( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.9999999 ) { @@ -10217,7 +9151,7 @@ case 'YZX': - this._z = Math.asin( clamp( m21, - 1, 1 ) ); + this._z = Math.asin( clamp$1( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.9999999 ) { @@ -10235,7 +9169,7 @@ case 'XZY': - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + this._z = Math.asin( - clamp$1( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.9999999 ) { @@ -10259,7 +9193,7 @@ this._order = order; - if ( update !== false ) this._onChangeCallback(); + if ( update === true ) this._onChangeCallback(); return this; @@ -10267,15 +9201,15 @@ setFromQuaternion( q, order, update ) { - _matrix.makeRotationFromQuaternion( q ); + _matrix$1.makeRotationFromQuaternion( q ); - return this.setFromRotationMatrix( _matrix, order, update ); + return this.setFromRotationMatrix( _matrix$1, order, update ); } - setFromVector3( v, order ) { + setFromVector3( v, order = this._order ) { - return this.set( v.x, v.y, v.z, order || this._order ); + return this.set( v.x, v.y, v.z, order ); } @@ -10283,9 +9217,9 @@ // WARNING: this discards revolution information -bhouston - _quaternion$1.setFromEuler( this ); + _quaternion$3.setFromEuler( this ); - return this.setFromQuaternion( _quaternion$1, newOrder ); + return this.setFromQuaternion( _quaternion$3, newOrder ); } @@ -10345,12 +9279,11 @@ } + Euler.prototype.isEuler = true; + Euler.DefaultOrder = 'XYZ'; Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - const _matrix = /*@__PURE__*/ new Matrix4(); - const _quaternion$1 = /*@__PURE__*/ new Quaternion(); - class Layers { constructor() { @@ -10405,119 +9338,115 @@ let _object3DId = 0; - const _v1$2 = new Vector3(); - const _q1 = new Quaternion(); - const _m1$1 = new Matrix4(); - const _target = new Vector3(); + const _v1$4 = /*@__PURE__*/ new Vector3(); + const _q1 = /*@__PURE__*/ new Quaternion(); + const _m1$1 = /*@__PURE__*/ new Matrix4(); + const _target = /*@__PURE__*/ new Vector3(); - const _position = new Vector3(); - const _scale = new Vector3(); - const _quaternion$2 = new Quaternion(); + const _position$3 = /*@__PURE__*/ new Vector3(); + const _scale$2 = /*@__PURE__*/ new Vector3(); + const _quaternion$2 = /*@__PURE__*/ new Quaternion(); - const _xAxis = new Vector3( 1, 0, 0 ); - const _yAxis = new Vector3( 0, 1, 0 ); - const _zAxis = new Vector3( 0, 0, 1 ); + const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); + const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); + const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); const _addedEvent = { type: 'added' }; const _removedEvent = { type: 'removed' }; - function Object3D() { + class Object3D extends EventDispatcher { - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); + constructor() { - this.uuid = MathUtils.generateUUID(); + super(); - this.name = ''; - this.type = 'Object3D'; + Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - this.parent = null; - this.children = []; + this.uuid = generateUUID(); - this.up = Object3D.DefaultUp.clone(); + this.name = ''; + this.type = 'Object3D'; - const position = new Vector3(); - const rotation = new Euler(); - const quaternion = new Quaternion(); - const scale = new Vector3( 1, 1, 1 ); + this.parent = null; + this.children = []; - function onRotationChange() { + this.up = Object3D.DefaultUp.clone(); - quaternion.setFromEuler( rotation, false ); + const position = new Vector3(); + const rotation = new Euler(); + const quaternion = new Quaternion(); + const scale = new Vector3( 1, 1, 1 ); - } + function onRotationChange() { - function onQuaternionChange() { + quaternion.setFromEuler( rotation, false ); - rotation.setFromQuaternion( quaternion, undefined, false ); + } - } + function onQuaternionChange() { - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); + rotation.setFromQuaternion( quaternion, undefined, false ); - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() } - } ); - - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; + rotation._onChange( onRotationChange ); + quaternion._onChange( onQuaternionChange ); - this.layers = new Layers(); - this.visible = true; + Object.defineProperties( this, { + position: { + configurable: true, + enumerable: true, + value: position + }, + rotation: { + configurable: true, + enumerable: true, + value: rotation + }, + quaternion: { + configurable: true, + enumerable: true, + value: quaternion + }, + scale: { + configurable: true, + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new Matrix4() + }, + normalMatrix: { + value: new Matrix3() + } + } ); - this.castShadow = false; - this.receiveShadow = false; + this.matrix = new Matrix4(); + this.matrixWorld = new Matrix4(); - this.frustumCulled = true; - this.renderOrder = 0; + this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; + this.matrixWorldNeedsUpdate = false; - this.animations = []; + this.layers = new Layers(); + this.visible = true; - this.userData = {}; + this.castShadow = false; + this.receiveShadow = false; - } + this.frustumCulled = true; + this.renderOrder = 0; - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; + this.animations = []; - Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + this.userData = {}; - constructor: Object3D, + } - isObject3D: true, + onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} - onBeforeRender: function () {}, - onAfterRender: function () {}, + onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} - applyMatrix4: function ( matrix ) { + applyMatrix4( matrix ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); @@ -10525,47 +9454,47 @@ this.matrix.decompose( this.position, this.quaternion, this.scale ); - }, + } - applyQuaternion: function ( q ) { + applyQuaternion( q ) { this.quaternion.premultiply( q ); return this; - }, + } - setRotationFromAxisAngle: function ( axis, angle ) { + setRotationFromAxisAngle( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); - }, + } - setRotationFromEuler: function ( euler ) { + setRotationFromEuler( euler ) { this.quaternion.setFromEuler( euler, true ); - }, + } - setRotationFromMatrix: function ( m ) { + setRotationFromMatrix( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); - }, + } - setRotationFromQuaternion: function ( q ) { + setRotationFromQuaternion( q ) { // assumes q is normalized this.quaternion.copy( q ); - }, + } - rotateOnAxis: function ( axis, angle ) { + rotateOnAxis( axis, angle ) { // rotate object on axis in object space // axis is assumed to be normalized @@ -10576,9 +9505,9 @@ return this; - }, + } - rotateOnWorldAxis: function ( axis, angle ) { + rotateOnWorldAxis( axis, angle ) { // rotate object on axis in world space // axis is assumed to be normalized @@ -10590,70 +9519,70 @@ return this; - }, + } - rotateX: function ( angle ) { + rotateX( angle ) { return this.rotateOnAxis( _xAxis, angle ); - }, + } - rotateY: function ( angle ) { + rotateY( angle ) { return this.rotateOnAxis( _yAxis, angle ); - }, + } - rotateZ: function ( angle ) { + rotateZ( angle ) { return this.rotateOnAxis( _zAxis, angle ); - }, + } - translateOnAxis: function ( axis, distance ) { + translateOnAxis( axis, distance ) { // translate object by distance along axis in object space // axis is assumed to be normalized - _v1$2.copy( axis ).applyQuaternion( this.quaternion ); + _v1$4.copy( axis ).applyQuaternion( this.quaternion ); - this.position.add( _v1$2.multiplyScalar( distance ) ); + this.position.add( _v1$4.multiplyScalar( distance ) ); return this; - }, + } - translateX: function ( distance ) { + translateX( distance ) { return this.translateOnAxis( _xAxis, distance ); - }, + } - translateY: function ( distance ) { + translateY( distance ) { return this.translateOnAxis( _yAxis, distance ); - }, + } - translateZ: function ( distance ) { + translateZ( distance ) { return this.translateOnAxis( _zAxis, distance ); - }, + } - localToWorld: function ( vector ) { + localToWorld( vector ) { return vector.applyMatrix4( this.matrixWorld ); - }, + } - worldToLocal: function ( vector ) { + worldToLocal( vector ) { return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); - }, + } - lookAt: function ( x, y, z ) { + lookAt( x, y, z ) { // This method does not support objects having non-uniformly-scaled parent(s) @@ -10671,15 +9600,15 @@ this.updateWorldMatrix( true, false ); - _position.setFromMatrixPosition( this.matrixWorld ); + _position$3.setFromMatrixPosition( this.matrixWorld ); if ( this.isCamera || this.isLight ) { - _m1$1.lookAt( _position, _target, this.up ); + _m1$1.lookAt( _position$3, _target, this.up ); } else { - _m1$1.lookAt( _target, _position, this.up ); + _m1$1.lookAt( _target, _position$3, this.up ); } @@ -10693,9 +9622,9 @@ } - }, + } - add: function ( object ) { + add( object ) { if ( arguments.length > 1 ) { @@ -10737,9 +9666,9 @@ return this; - }, + } - remove: function ( object ) { + remove( object ) { if ( arguments.length > 1 ) { @@ -10766,9 +9695,23 @@ return this; - }, + } - clear: function () { + removeFromParent() { + + const parent = this.parent; + + if ( parent !== null ) { + + parent.remove( this ); + + } + + return this; + + } + + clear() { for ( let i = 0; i < this.children.length; i ++ ) { @@ -10785,9 +9728,9 @@ return this; - }, + } - attach: function ( object ) { + attach( object ) { // adds object as a child of this, while maintaining the object's world transform @@ -10805,27 +9748,27 @@ object.applyMatrix4( _m1$1 ); - object.updateWorldMatrix( false, false ); - this.add( object ); + object.updateWorldMatrix( false, true ); + return this; - }, + } - getObjectById: function ( id ) { + getObjectById( id ) { return this.getObjectByProperty( 'id', id ); - }, + } - getObjectByName: function ( name ) { + getObjectByName( name ) { return this.getObjectByProperty( 'name', name ); - }, + } - getObjectByProperty: function ( name, value ) { + getObjectByProperty( name, value ) { if ( this[ name ] === value ) return this; @@ -10844,65 +9787,37 @@ return undefined; - }, - - getWorldPosition: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); + } - } + getWorldPosition( target ) { this.updateWorldMatrix( true, false ); return target.setFromMatrixPosition( this.matrixWorld ); - }, - - getWorldQuaternion: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); + } - } + getWorldQuaternion( target ) { this.updateWorldMatrix( true, false ); - this.matrixWorld.decompose( _position, target, _scale ); + this.matrixWorld.decompose( _position$3, target, _scale$2 ); return target; - }, - - getWorldScale: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); + } - } + getWorldScale( target ) { this.updateWorldMatrix( true, false ); - this.matrixWorld.decompose( _position, _quaternion$2, target ); + this.matrixWorld.decompose( _position$3, _quaternion$2, target ); return target; - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); + } - } + getWorldDirection( target ) { this.updateWorldMatrix( true, false ); @@ -10910,11 +9825,11 @@ return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - }, + } - raycast: function () {}, + raycast() {} - traverse: function ( callback ) { + traverse( callback ) { callback( this ); @@ -10926,9 +9841,9 @@ } - }, + } - traverseVisible: function ( callback ) { + traverseVisible( callback ) { if ( this.visible === false ) return; @@ -10942,9 +9857,9 @@ } - }, + } - traverseAncestors: function ( callback ) { + traverseAncestors( callback ) { const parent = this.parent; @@ -10956,17 +9871,17 @@ } - }, + } - updateMatrix: function () { + updateMatrix() { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; - }, + } - updateMatrixWorld: function ( force ) { + updateMatrixWorld( force ) { if ( this.matrixAutoUpdate ) this.updateMatrix(); @@ -10998,9 +9913,9 @@ } - }, + } - updateWorldMatrix: function ( updateParents, updateChildren ) { + updateWorldMatrix( updateParents, updateChildren ) { const parent = this.parent; @@ -11036,9 +9951,9 @@ } - }, + } - toJSON: function ( meta ) { + toJSON( meta ) { // meta is a string when called from JSON.stringify const isRootObject = ( meta === undefined || typeof meta === 'string' ); @@ -11096,6 +10011,7 @@ object.type = 'InstancedMesh'; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); + if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); } @@ -11113,7 +10029,29 @@ } - if ( this.isMesh || this.isLine || this.isPoints ) { + if ( this.isScene ) { + + if ( this.background ) { + + if ( this.background.isColor ) { + + object.background = this.background.toJSON(); + + } else if ( this.background.isTexture ) { + + object.background = this.background.toJSON( meta ).uuid; + + } + + } + + if ( this.environment && this.environment.isTexture ) { + + object.environment = this.environment.toJSON( meta ).uuid; + + } + + } else if ( this.isMesh || this.isLine || this.isPoints ) { object.geometry = serialize( meta.geometries, this.geometry ); @@ -11252,15 +10190,15 @@ } - }, + } - clone: function ( recursive ) { + clone( recursive ) { return new this.constructor().copy( this, recursive ); - }, + } - copy: function ( source, recursive = true ) { + copy( source, recursive = true ) { this.name = source.name; @@ -11303,234 +10241,17 @@ } - } ); - - const _vector1 = /*@__PURE__*/ new Vector3(); - const _vector2 = /*@__PURE__*/ new Vector3(); - const _normalMatrix = /*@__PURE__*/ new Matrix3(); - - class Plane { - - constructor( normal, constant ) { - - Object.defineProperty( this, 'isPlane', { value: true } ); - - // normal is assumed to be normalized - - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; - - } - - set( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - } - - setComponents( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - } - - setFromNormalAndCoplanarPoint( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); - - return this; - - } - - setFromCoplanarPoints( a, b, c ) { - - const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - } - - normalize() { - - // Note: will lead to a divide by zero if the plane is invalid. - - const inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - } - - negate() { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - } - - distanceToPoint( point ) { - - return this.normal.dot( point ) + this.constant; - - } - - distanceToSphere( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - } - - projectPoint( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); - - } - - intersectLine( line, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); - - } - - const direction = line.delta( _vector1 ); - - const denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return target.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return undefined; - - } - - const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return undefined; - - } - - return target.copy( direction ).multiplyScalar( t ).add( line.start ); - - } - - intersectsLine( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - const startSign = this.distanceToPoint( line.start ); - const endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - } - - intersectsBox( box ) { - - return box.intersectsPlane( this ); - - } - - intersectsSphere( sphere ) { - - return sphere.intersectsPlane( this ); - - } - - coplanarPoint( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.constant ); - - } - - applyMatrix4( matrix, optionalNormalMatrix ) { - - const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); - - const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); - - const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - - this.constant = - referencePoint.dot( normal ); - - return this; - - } - - translate( offset ) { - - this.constant -= offset.dot( this.normal ); - - return this; - - } - - equals( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + } - } + Object3D.DefaultUp = new Vector3( 0, 1, 0 ); + Object3D.DefaultMatrixAutoUpdate = true; - } + Object3D.prototype.isObject3D = true; const _v0$1 = /*@__PURE__*/ new Vector3(); const _v1$3 = /*@__PURE__*/ new Vector3(); - const _v2$1 = /*@__PURE__*/ new Vector3(); - const _v3 = /*@__PURE__*/ new Vector3(); + const _v2$2 = /*@__PURE__*/ new Vector3(); + const _v3$1 = /*@__PURE__*/ new Vector3(); const _vab = /*@__PURE__*/ new Vector3(); const _vac = /*@__PURE__*/ new Vector3(); @@ -11541,23 +10262,16 @@ class Triangle { - constructor( a, b, c ) { + constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); + this.a = a; + this.b = b; + this.c = c; } static getNormal( a, b, c, target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); - - } - target.subVectors( c, b ); _v0$1.subVectors( a, b ); target.cross( _v0$1 ); @@ -11579,23 +10293,16 @@ _v0$1.subVectors( c, a ); _v1$3.subVectors( b, a ); - _v2$1.subVectors( point, a ); + _v2$2.subVectors( point, a ); const dot00 = _v0$1.dot( _v0$1 ); const dot01 = _v0$1.dot( _v1$3 ); - const dot02 = _v0$1.dot( _v2$1 ); + const dot02 = _v0$1.dot( _v2$2 ); const dot11 = _v1$3.dot( _v1$3 ); - const dot12 = _v1$3.dot( _v2$1 ); + const dot12 = _v1$3.dot( _v2$2 ); const denom = ( dot00 * dot11 - dot01 * dot01 ); - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); - - } - // collinear or singular triangle if ( denom === 0 ) { @@ -11616,20 +10323,20 @@ static containsPoint( point, a, b, c ) { - this.getBarycoord( point, a, b, c, _v3 ); + this.getBarycoord( point, a, b, c, _v3$1 ); - return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 ); + return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); } static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - this.getBarycoord( point, p1, p2, p3, _v3 ); + this.getBarycoord( point, p1, p2, p3, _v3$1 ); target.set( 0, 0 ); - target.addScaledVector( uv1, _v3.x ); - target.addScaledVector( uv2, _v3.y ); - target.addScaledVector( uv3, _v3.z ); + target.addScaledVector( uv1, _v3$1.x ); + target.addScaledVector( uv2, _v3$1.y ); + target.addScaledVector( uv3, _v3$1.z ); return target; @@ -11665,6 +10372,16 @@ } + setFromAttributeAndIndices( attribute, i0, i1, i2 ) { + + this.a.fromBufferAttribute( attribute, i0 ); + this.b.fromBufferAttribute( attribute, i1 ); + this.c.fromBufferAttribute( attribute, i2 ); + + return this; + + } + clone() { return new this.constructor().copy( this ); @@ -11692,13 +10409,6 @@ getMidpoint( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); - - } - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); } @@ -11711,13 +10421,6 @@ getPlane( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Plane(); - - } - return target.setFromCoplanarPoints( this.a, this.b, this.c ); } @@ -11754,13 +10457,6 @@ closestPointToPoint( p, target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - const a = this.a, b = this.b, c = this.c; let v, w; @@ -11848,6 +10544,492 @@ } + let materialId = 0; + + class Material extends EventDispatcher { + + constructor() { + + super(); + + Object.defineProperty( this, 'id', { value: materialId ++ } ); + + this.uuid = generateUUID(); + + this.name = ''; + this.type = 'Material'; + + this.fog = true; + + this.blending = NormalBlending; + this.side = FrontSide; + this.vertexColors = false; + + this.opacity = 1; + this.format = RGBAFormat; + this.transparent = false; + + this.blendSrc = SrcAlphaFactor; + this.blendDst = OneMinusSrcAlphaFactor; + this.blendEquation = AddEquation; + this.blendSrcAlpha = null; + this.blendDstAlpha = null; + this.blendEquationAlpha = null; + + this.depthFunc = LessEqualDepth; + this.depthTest = true; + this.depthWrite = true; + + this.stencilWriteMask = 0xff; + this.stencilFunc = AlwaysStencilFunc; + this.stencilRef = 0; + this.stencilFuncMask = 0xff; + this.stencilFail = KeepStencilOp; + this.stencilZFail = KeepStencilOp; + this.stencilZPass = KeepStencilOp; + this.stencilWrite = false; + + this.clippingPlanes = null; + this.clipIntersection = false; + this.clipShadows = false; + + this.shadowSide = null; + + this.colorWrite = true; + + this.precision = null; // override the renderer's default precision for this material + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + + this.dithering = false; + + this.alphaToCoverage = false; + this.premultipliedAlpha = false; + + this.visible = true; + + this.toneMapped = true; + + this.userData = {}; + + this.version = 0; + + this._alphaTest = 0; + + } + + get alphaTest() { + + return this._alphaTest; + + } + + set alphaTest( value ) { + + if ( this._alphaTest > 0 !== value > 0 ) { + + this.version ++; + + } + + this._alphaTest = value; + + } + + onBuild( /* shaderobject, renderer */ ) {} + + onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} + + onBeforeCompile( /* shaderobject, renderer */ ) {} + + customProgramCacheKey() { + + return this.onBeforeCompile.toString(); + + } + + setValues( values ) { + + if ( values === undefined ) return; + + for ( const key in values ) { + + const newValue = values[ key ]; + + if ( newValue === undefined ) { + + console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); + continue; + + } + + // for backward compatability if shading is set in the constructor + if ( key === 'shading' ) { + + console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + this.flatShading = ( newValue === FlatShading ) ? true : false; + continue; + + } + + const currentValue = this[ key ]; + + if ( currentValue === undefined ) { + + console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); + continue; + + } + + if ( currentValue && currentValue.isColor ) { + + currentValue.set( newValue ); + + } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { + + currentValue.copy( newValue ); + + } else { + + this[ key ] = newValue; + + } + + } + + } + + toJSON( meta ) { + + const isRoot = ( meta === undefined || typeof meta === 'string' ); + + if ( isRoot ) { + + meta = { + textures: {}, + images: {} + }; + + } + + const data = { + metadata: { + version: 4.5, + type: 'Material', + generator: 'Material.toJSON' + } + }; + + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; + + if ( this.name !== '' ) data.name = this.name; + + if ( this.color && this.color.isColor ) data.color = this.color.getHex(); + + if ( this.roughness !== undefined ) data.roughness = this.roughness; + if ( this.metalness !== undefined ) data.metalness = this.metalness; + + if ( this.sheen !== undefined ) data.sheen = this.sheen; + if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); + if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; + if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); + if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; + + if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); + if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; + if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); + if ( this.shininess !== undefined ) data.shininess = this.shininess; + if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; + if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; + + if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { + + data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; + + } + + if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { + + data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; + + } + + if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { + + data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; + data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); + + } + + if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; + if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + + if ( this.lightMap && this.lightMap.isTexture ) { + + data.lightMap = this.lightMap.toJSON( meta ).uuid; + data.lightMapIntensity = this.lightMapIntensity; + + } + + if ( this.aoMap && this.aoMap.isTexture ) { + + data.aoMap = this.aoMap.toJSON( meta ).uuid; + data.aoMapIntensity = this.aoMapIntensity; + + } + + if ( this.bumpMap && this.bumpMap.isTexture ) { + + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; + + } + + if ( this.normalMap && this.normalMap.isTexture ) { + + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalMapType = this.normalMapType; + data.normalScale = this.normalScale.toArray(); + + } + + if ( this.displacementMap && this.displacementMap.isTexture ) { + + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; + + } + + if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; + if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; + + if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; + if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; + if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; + + if ( this.envMap && this.envMap.isTexture ) { + + data.envMap = this.envMap.toJSON( meta ).uuid; + + if ( this.combine !== undefined ) data.combine = this.combine; + + } + + if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; + if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; + if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; + + if ( this.gradientMap && this.gradientMap.isTexture ) { + + data.gradientMap = this.gradientMap.toJSON( meta ).uuid; + + } + + if ( this.transmission !== undefined ) data.transmission = this.transmission; + if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; + if ( this.thickness !== undefined ) data.thickness = this.thickness; + if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; + if ( this.attenuationDistance !== undefined ) data.attenuationDistance = this.attenuationDistance; + if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); + + if ( this.size !== undefined ) data.size = this.size; + if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + + if ( this.blending !== NormalBlending ) data.blending = this.blending; + if ( this.side !== FrontSide ) data.side = this.side; + if ( this.vertexColors ) data.vertexColors = true; + + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.format !== RGBAFormat ) data.format = this.format; + if ( this.transparent === true ) data.transparent = this.transparent; + + data.depthFunc = this.depthFunc; + data.depthTest = this.depthTest; + data.depthWrite = this.depthWrite; + data.colorWrite = this.colorWrite; + + data.stencilWrite = this.stencilWrite; + data.stencilWriteMask = this.stencilWriteMask; + data.stencilFunc = this.stencilFunc; + data.stencilRef = this.stencilRef; + data.stencilFuncMask = this.stencilFuncMask; + data.stencilFail = this.stencilFail; + data.stencilZFail = this.stencilZFail; + data.stencilZPass = this.stencilZPass; + + // rotation (SpriteMaterial) + if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; + + if ( this.polygonOffset === true ) data.polygonOffset = true; + if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; + if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; + + if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth; + if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; + if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; + if ( this.scale !== undefined ) data.scale = this.scale; + + if ( this.dithering === true ) data.dithering = true; + + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; + if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; + + if ( this.wireframe === true ) data.wireframe = this.wireframe; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; + if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; + if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; + + if ( this.flatShading === true ) data.flatShading = this.flatShading; + + if ( this.visible === false ) data.visible = false; + + if ( this.toneMapped === false ) data.toneMapped = false; + + if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; + + // TODO: Copied from Object3D.toJSON + + function extractFromCache( cache ) { + + const values = []; + + for ( const key in cache ) { + + const data = cache[ key ]; + delete data.metadata; + values.push( data ); + + } + + return values; + + } + + if ( isRoot ) { + + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); + + if ( textures.length > 0 ) data.textures = textures; + if ( images.length > 0 ) data.images = images; + + } + + return data; + + } + + clone() { + + return new this.constructor().copy( this ); + + } + + copy( source ) { + + this.name = source.name; + + this.fog = source.fog; + + this.blending = source.blending; + this.side = source.side; + this.vertexColors = source.vertexColors; + + this.opacity = source.opacity; + this.format = source.format; + this.transparent = source.transparent; + + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; + + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; + + this.stencilWriteMask = source.stencilWriteMask; + this.stencilFunc = source.stencilFunc; + this.stencilRef = source.stencilRef; + this.stencilFuncMask = source.stencilFuncMask; + this.stencilFail = source.stencilFail; + this.stencilZFail = source.stencilZFail; + this.stencilZPass = source.stencilZPass; + this.stencilWrite = source.stencilWrite; + + const srcPlanes = source.clippingPlanes; + let dstPlanes = null; + + if ( srcPlanes !== null ) { + + const n = srcPlanes.length; + dstPlanes = new Array( n ); + + for ( let i = 0; i !== n; ++ i ) { + + dstPlanes[ i ] = srcPlanes[ i ].clone(); + + } + + } + + this.clippingPlanes = dstPlanes; + this.clipIntersection = source.clipIntersection; + this.clipShadows = source.clipShadows; + + this.shadowSide = source.shadowSide; + + this.colorWrite = source.colorWrite; + + this.precision = source.precision; + + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; + + this.dithering = source.dithering; + + this.alphaTest = source.alphaTest; + this.alphaToCoverage = source.alphaToCoverage; + this.premultipliedAlpha = source.premultipliedAlpha; + + this.visible = source.visible; + + this.toneMapped = source.toneMapped; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + return this; + + } + + dispose() { + + this.dispatchEvent( { type: 'dispose' } ); + + } + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + } + + } + + Material.prototype.isMaterial = true; + const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, @@ -11903,8 +11085,6 @@ constructor( r, g, b ) { - Object.defineProperty( this, 'isColor', { value: true } ); - if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string @@ -11971,9 +11151,9 @@ setHSL( h, s, l ) { // h,s,l ranges are in 0.0 - 1.0 - h = MathUtils.euclideanModulo( h, 1 ); - s = MathUtils.clamp( s, 0, 1 ); - l = MathUtils.clamp( l, 0, 1 ); + h = euclideanModulo( h, 1 ); + s = clamp$1( s, 0, 1 ); + l = clamp$1( l, 0, 1 ); if ( s === 0 ) { @@ -12114,7 +11294,7 @@ setColorName( style ) { // color keywords - const hex = _colorKeywords[ style ]; + const hex = _colorKeywords[ style.toLowerCase() ]; if ( hex !== undefined ) { @@ -12238,13 +11418,6 @@ // h,s,l ranges are in 0.0 - 1.0 - if ( target === undefined ) { - - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; - - } - const r = this.r, g = this.g, b = this.b; const max = Math.max( r, g, b ); @@ -12387,9 +11560,9 @@ this.getHSL( _hslA ); color.getHSL( _hslB ); - const h = MathUtils.lerp( _hslA.h, _hslB.h, alpha ); - const s = MathUtils.lerp( _hslA.s, _hslB.s, alpha ); - const l = MathUtils.lerp( _hslA.l, _hslB.l, alpha ); + const h = lerp( _hslA.h, _hslB.h, alpha ); + const s = lerp( _hslA.s, _hslB.s, alpha ); + const l = lerp( _hslA.l, _hslB.l, alpha ); this.setHSL( h, s, l ); @@ -12452,56 +11625,104 @@ } Color.NAMES = _colorKeywords; + + Color.prototype.isColor = true; Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; - class Face3 { + /** + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * } + */ + + class MeshBasicMaterial extends Material { - constructor( a, b, c, normal, color, materialIndex = 0 ) { + constructor( parameters ) { - this.a = a; - this.b = b; - this.c = c; + super(); - this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; + this.type = 'MeshBasicMaterial'; - this.color = ( color && color.isColor ) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; + this.color = new Color( 0xffffff ); // emissive - this.materialIndex = materialIndex; + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - clone() { + this.aoMap = null; + this.aoMapIntensity = 1.0; - return new this.constructor().copy( this ); + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.setValues( parameters ); } copy( source ) { - this.a = source.a; - this.b = source.b; - this.c = source.c; + super.copy( source ); - this.normal.copy( source.normal ); this.color.copy( source.color ); - this.materialIndex = source.materialIndex; + this.map = source.map; - for ( let i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - } + this.specularMap = source.specularMap; - for ( let i = 0, il = source.vertexColors.length; i < il; i ++ ) { + this.alphaMap = source.alphaMap; - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; return this; @@ -12509,646 +11730,89 @@ } - let materialId = 0; + MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - function Material() { + const _vector$9 = /*@__PURE__*/ new Vector3(); + const _vector2$1 = /*@__PURE__*/ new Vector2(); - Object.defineProperty( this, 'id', { value: materialId ++ } ); + class BufferAttribute { - this.uuid = MathUtils.generateUUID(); + constructor( array, itemSize, normalized ) { - this.name = ''; - this.type = 'Material'; + if ( Array.isArray( array ) ) { - this.fog = true; + throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - this.blending = NormalBlending; - this.side = FrontSide; - this.flatShading = false; - this.vertexColors = false; + } - this.opacity = 1; - this.transparent = false; + this.name = ''; - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; + this.array = array; + this.itemSize = itemSize; + this.count = array !== undefined ? array.length / itemSize : 0; + this.normalized = normalized === true; - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; + this.version = 0; - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; + } - this.shadowSide = null; + onUploadCallback() {} - this.colorWrite = true; + set needsUpdate( value ) { - this.precision = null; // override the renderer's default precision for this material + if ( value === true ) this.version ++; - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; + } - this.dithering = false; + setUsage( value ) { - this.alphaTest = 0; - this.premultipliedAlpha = false; + this.usage = value; - this.visible = true; + return this; - this.toneMapped = true; + } - this.userData = {}; + copy( source ) { - this.version = 0; + this.name = source.name; + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + this.count = source.count; + this.normalized = source.normalized; - } + this.usage = source.usage; - Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + return this; - constructor: Material, + } - isMaterial: true, + copyAt( index1, attribute, index2 ) { - onBeforeCompile: function ( /* shaderobject, renderer */ ) {}, + index1 *= this.itemSize; + index2 *= attribute.itemSize; - customProgramCacheKey: function () { + for ( let i = 0, l = this.itemSize; i < l; i ++ ) { - return this.onBeforeCompile.toString(); + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - }, + } - setValues: function ( values ) { + return this; - if ( values === undefined ) return; + } - for ( const key in values ) { + copyArray( array ) { - const newValue = values[ key ]; + this.array.set( array ); - if ( newValue === undefined ) { + return this; - console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); - continue; + } - } - - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; - - } - - const currentValue = this[ key ]; - - if ( currentValue === undefined ) { - - console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); - continue; - - } - - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); - - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - - currentValue.copy( newValue ); - - } else { - - this[ key ] = newValue; - - } - - } - - }, - - toJSON: function ( meta ) { - - const isRoot = ( meta === undefined || typeof meta === 'string' ); - - if ( isRoot ) { - - meta = { - textures: {}, - images: {} - }; - - } - - const data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; - - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - - if ( this.name !== '' ) data.name = this.name; - - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; - - if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex(); - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - - if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; - if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; - if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; - - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - - } - - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; - - if ( this.aoMap && this.aoMap.isTexture ) { - - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; - - } - - if ( this.bumpMap && this.bumpMap.isTexture ) { - - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; - - } - - if ( this.normalMap && this.normalMap.isTexture ) { - - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); - - } - - if ( this.displacementMap && this.displacementMap.isTexture ) { - - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; - - } - - if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; - if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - - if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; - if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - - if ( this.envMap && this.envMap.isTexture ) { - - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap - data.refractionRatio = this.refractionRatio; - - if ( this.combine !== undefined ) data.combine = this.combine; - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; - - } - - if ( this.gradientMap && this.gradientMap.isTexture ) { - - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - - } - - if ( this.size !== undefined ) data.size = this.size; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.flatShading === true ) data.flatShading = this.flatShading; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors ) data.vertexColors = true; - - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; - - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; - - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; - - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; - - if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth; - if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; - if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; - if ( this.scale !== undefined ) data.scale = this.scale; - - if ( this.dithering === true ) data.dithering = true; - - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; - - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; - if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; - - if ( this.morphTargets === true ) data.morphTargets = true; - if ( this.morphNormals === true ) data.morphNormals = true; - if ( this.skinning === true ) data.skinning = true; - - if ( this.visible === false ) data.visible = false; - - if ( this.toneMapped === false ) data.toneMapped = false; - - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; - - // TODO: Copied from Object3D.toJSON - - function extractFromCache( cache ) { - - const values = []; - - for ( const key in cache ) { - - const data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - if ( isRoot ) { - - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; - - } - - return data; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.fog = source.fog; - - this.blending = source.blending; - this.side = source.side; - this.flatShading = source.flatShading; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.transparent = source.transparent; - - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; - - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; - - this.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; - - const srcPlanes = source.clippingPlanes; - let dstPlanes = null; - - if ( srcPlanes !== null ) { - - const n = srcPlanes.length; - dstPlanes = new Array( n ); - - for ( let i = 0; i !== n; ++ i ) { - - dstPlanes[ i ] = srcPlanes[ i ].clone(); - - } - - } - - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; - - this.shadowSide = source.shadowSide; - - this.colorWrite = source.colorWrite; - - this.precision = source.precision; - - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; - - this.dithering = source.dithering; - - this.alphaTest = source.alphaTest; - this.premultipliedAlpha = source.premultipliedAlpha; - - this.visible = source.visible; - - this.toneMapped = source.toneMapped; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - Object.defineProperty( Material.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; - - const _vector$3 = new Vector3(); - const _vector2$1 = new Vector2(); - - function BufferAttribute( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.name = ''; - - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( BufferAttribute.prototype, { - - isBufferAttribute: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; - - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; - - for ( let i = 0, l = this.itemSize; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - copyArray: function ( array ) { - - this.array.set( array ); - - return this; - - }, - - copyColorsArray: function ( colors ) { + copyColorsArray( colors ) { const array = this.array; let offset = 0; @@ -13172,9 +11836,9 @@ return this; - }, + } - copyVector2sArray: function ( vectors ) { + copyVector2sArray( vectors ) { const array = this.array; let offset = 0; @@ -13197,9 +11861,9 @@ return this; - }, + } - copyVector3sArray: function ( vectors ) { + copyVector3sArray( vectors ) { const array = this.array; let offset = 0; @@ -13223,9 +11887,9 @@ return this; - }, + } - copyVector4sArray: function ( vectors ) { + copyVector4sArray( vectors ) { const array = this.array; let offset = 0; @@ -13250,9 +11914,9 @@ return this; - }, + } - applyMatrix3: function ( m ) { + applyMatrix3( m ) { if ( this.itemSize === 2 ) { @@ -13269,10 +11933,10 @@ for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$3.fromBufferAttribute( this, i ); - _vector$3.applyMatrix3( m ); + _vector$9.fromBufferAttribute( this, i ); + _vector$9.applyMatrix3( m ); - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } @@ -13280,127 +11944,127 @@ return this; - }, + } - applyMatrix4: function ( m ) { + applyMatrix4( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); + _vector$9.x = this.getX( i ); + _vector$9.y = this.getY( i ); + _vector$9.z = this.getZ( i ); - _vector$3.applyMatrix4( m ); + _vector$9.applyMatrix4( m ); - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } return this; - }, + } - applyNormalMatrix: function ( m ) { + applyNormalMatrix( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); + _vector$9.x = this.getX( i ); + _vector$9.y = this.getY( i ); + _vector$9.z = this.getZ( i ); - _vector$3.applyNormalMatrix( m ); + _vector$9.applyNormalMatrix( m ); - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } return this; - }, + } - transformDirection: function ( m ) { + transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); + _vector$9.x = this.getX( i ); + _vector$9.y = this.getY( i ); + _vector$9.z = this.getZ( i ); - _vector$3.transformDirection( m ); + _vector$9.transformDirection( m ); - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); } return this; - }, + } - set: function ( value, offset = 0 ) { + set( value, offset = 0 ) { this.array.set( value, offset ); return this; - }, + } - getX: function ( index ) { + getX( index ) { return this.array[ index * this.itemSize ]; - }, + } - setX: function ( index, x ) { + setX( index, x ) { this.array[ index * this.itemSize ] = x; return this; - }, + } - getY: function ( index ) { + getY( index ) { return this.array[ index * this.itemSize + 1 ]; - }, + } - setY: function ( index, y ) { + setY( index, y ) { this.array[ index * this.itemSize + 1 ] = y; return this; - }, + } - getZ: function ( index ) { + getZ( index ) { return this.array[ index * this.itemSize + 2 ]; - }, + } - setZ: function ( index, z ) { + setZ( index, z ) { this.array[ index * this.itemSize + 2 ] = z; return this; - }, + } - getW: function ( index ) { + getW( index ) { return this.array[ index * this.itemSize + 3 ]; - }, + } - setW: function ( index, w ) { + setW( index, w ) { this.array[ index * this.itemSize + 3 ] = w; return this; - }, + } - setXY: function ( index, x, y ) { + setXY( index, x, y ) { index *= this.itemSize; @@ -13409,9 +12073,9 @@ return this; - }, + } - setXYZ: function ( index, x, y, z ) { + setXYZ( index, x, y, z ) { index *= this.itemSize; @@ -13421,9 +12085,9 @@ return this; - }, + } - setXYZW: function ( index, x, y, z, w ) { + setXYZW( index, x, y, z, w ) { index *= this.itemSize; @@ -13434,218 +12098,131 @@ return this; - }, + } - onUpload: function ( callback ) { + onUpload( callback ) { this.onUploadCallback = callback; return this; - }, + } - clone: function () { + clone() { return new this.constructor( this.array, this.itemSize ).copy( this ); - }, + } - toJSON: function () { + toJSON() { - return { + const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.prototype.slice.call( this.array ), normalized: this.normalized }; - } - - } ); - - // - - function Int8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); - - } - - Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; - - - function Uint8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); - - } - - Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; - + if ( this.name !== '' ) data.name = this.name; + if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; + if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; - function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { + return data; - BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); + } } - Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; - - - function Int16BufferAttribute( array, itemSize, normalized ) { + BufferAttribute.prototype.isBufferAttribute = true; - BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); - - } + class Uint16BufferAttribute extends BufferAttribute { - Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; + constructor( array, itemSize, normalized ) { + super( new Uint16Array( array ), itemSize, normalized ); - function Uint16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); + } } - Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; + class Uint32BufferAttribute extends BufferAttribute { + constructor( array, itemSize, normalized ) { - function Int32BufferAttribute( array, itemSize, normalized ) { + super( new Uint32Array( array ), itemSize, normalized ); - BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); + } } - Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; - + class Float16BufferAttribute extends BufferAttribute { - function Uint32BufferAttribute( array, itemSize, normalized ) { + constructor( array, itemSize, normalized ) { - BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); + super( new Uint16Array( array ), itemSize, normalized ); - } - - Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; - - function Float16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); + } } - Float16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float16BufferAttribute.prototype.constructor = Float16BufferAttribute; Float16BufferAttribute.prototype.isFloat16BufferAttribute = true; - function Float32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); - - } - - Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; + class Float32BufferAttribute extends BufferAttribute { + constructor( array, itemSize, normalized ) { - function Float64BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); - - } - - Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; - - function arrayMax( array ) { - - if ( array.length === 0 ) return - Infinity; - - let max = array[ 0 ]; - - for ( let i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) max = array[ i ]; + super( new Float32Array( array ), itemSize, normalized ); } - return max; - - } - - const TYPED_ARRAYS = { - Int8Array: Int8Array, - Uint8Array: Uint8Array, - // Workaround for IE11 pre KB2929437. See #11440 - Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array, - Int16Array: Int16Array, - Uint16Array: Uint16Array, - Int32Array: Int32Array, - Uint32Array: Uint32Array, - Float32Array: Float32Array, - Float64Array: Float64Array - }; - - function getTypedArray( type, buffer ) { - - return new TYPED_ARRAYS[ type ]( buffer ); - } let _id = 0; - const _m1$2 = new Matrix4(); - const _obj = new Object3D(); - const _offset = new Vector3(); - const _box$2 = new Box3(); - const _boxMorphTargets = new Box3(); - const _vector$4 = new Vector3(); - - function BufferGeometry() { + const _m1 = /*@__PURE__*/ new Matrix4(); + const _obj = /*@__PURE__*/ new Object3D(); + const _offset = /*@__PURE__*/ new Vector3(); + const _box$1 = /*@__PURE__*/ new Box3(); + const _boxMorphTargets = /*@__PURE__*/ new Box3(); + const _vector$8 = /*@__PURE__*/ new Vector3(); - Object.defineProperty( this, 'id', { value: _id ++ } ); + class BufferGeometry extends EventDispatcher { - this.uuid = MathUtils.generateUUID(); + constructor() { - this.name = ''; - this.type = 'BufferGeometry'; + super(); - this.index = null; - this.attributes = {}; + Object.defineProperty( this, 'id', { value: _id ++ } ); - this.morphAttributes = {}; - this.morphTargetsRelative = false; + this.uuid = generateUUID(); - this.groups = []; + this.name = ''; + this.type = 'BufferGeometry'; - this.boundingBox = null; - this.boundingSphere = null; + this.index = null; + this.attributes = {}; - this.drawRange = { start: 0, count: Infinity }; + this.morphAttributes = {}; + this.morphTargetsRelative = false; - this.userData = {}; + this.groups = []; - } + this.boundingBox = null; + this.boundingSphere = null; - BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + this.drawRange = { start: 0, count: Infinity }; - constructor: BufferGeometry, + this.userData = {}; - isBufferGeometry: true, + } - getIndex: function () { + getIndex() { return this.index; - }, + } - setIndex: function ( index ) { + setIndex( index ) { if ( Array.isArray( index ) ) { @@ -13659,37 +12236,37 @@ return this; - }, + } - getAttribute: function ( name ) { + getAttribute( name ) { return this.attributes[ name ]; - }, + } - setAttribute: function ( name, attribute ) { + setAttribute( name, attribute ) { this.attributes[ name ] = attribute; return this; - }, + } - deleteAttribute: function ( name ) { + deleteAttribute( name ) { delete this.attributes[ name ]; return this; - }, + } - hasAttribute: function ( name ) { + hasAttribute( name ) { return this.attributes[ name ] !== undefined; - }, + } - addGroup: function ( start, count, materialIndex = 0 ) { + addGroup( start, count, materialIndex = 0 ) { this.groups.push( { @@ -13699,22 +12276,22 @@ } ); - }, + } - clearGroups: function () { + clearGroups() { this.groups = []; - }, + } - setDrawRange: function ( start, count ) { + setDrawRange( start, count ) { this.drawRange.start = start; this.drawRange.count = count; - }, + } - applyMatrix4: function ( matrix ) { + applyMatrix4( matrix ) { const position = this.attributes.position; @@ -13762,69 +12339,79 @@ return this; - }, + } - rotateX: function ( angle ) { + applyQuaternion( q ) { + + _m1.makeRotationFromQuaternion( q ); + + this.applyMatrix4( _m1 ); + + return this; + + } + + rotateX( angle ) { // rotate geometry around world x-axis - _m1$2.makeRotationX( angle ); + _m1.makeRotationX( angle ); - this.applyMatrix4( _m1$2 ); + this.applyMatrix4( _m1 ); return this; - }, + } - rotateY: function ( angle ) { + rotateY( angle ) { // rotate geometry around world y-axis - _m1$2.makeRotationY( angle ); + _m1.makeRotationY( angle ); - this.applyMatrix4( _m1$2 ); + this.applyMatrix4( _m1 ); return this; - }, + } - rotateZ: function ( angle ) { + rotateZ( angle ) { // rotate geometry around world z-axis - _m1$2.makeRotationZ( angle ); + _m1.makeRotationZ( angle ); - this.applyMatrix4( _m1$2 ); + this.applyMatrix4( _m1 ); return this; - }, + } - translate: function ( x, y, z ) { + translate( x, y, z ) { // translate geometry - _m1$2.makeTranslation( x, y, z ); + _m1.makeTranslation( x, y, z ); - this.applyMatrix4( _m1$2 ); + this.applyMatrix4( _m1 ); return this; - }, + } - scale: function ( x, y, z ) { + scale( x, y, z ) { // scale geometry - _m1$2.makeScale( x, y, z ); + _m1.makeScale( x, y, z ); - this.applyMatrix4( _m1$2 ); + this.applyMatrix4( _m1 ); return this; - }, + } - lookAt: function ( vector ) { + lookAt( vector ) { _obj.lookAt( vector ); @@ -13834,9 +12421,9 @@ return this; - }, + } - center: function () { + center() { this.computeBoundingBox(); @@ -13846,9 +12433,9 @@ return this; - }, + } - setFromPoints: function ( points ) { + setFromPoints( points ) { const position = []; @@ -13863,9 +12450,9 @@ return this; - }, + } - computeBoundingBox: function () { + computeBoundingBox() { if ( this.boundingBox === null ) { @@ -13900,20 +12487,20 @@ for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { const morphAttribute = morphAttributesPosition[ i ]; - _box$2.setFromBufferAttribute( morphAttribute ); + _box$1.setFromBufferAttribute( morphAttribute ); if ( this.morphTargetsRelative ) { - _vector$4.addVectors( this.boundingBox.min, _box$2.min ); - this.boundingBox.expandByPoint( _vector$4 ); + _vector$8.addVectors( this.boundingBox.min, _box$1.min ); + this.boundingBox.expandByPoint( _vector$8 ); - _vector$4.addVectors( this.boundingBox.max, _box$2.max ); - this.boundingBox.expandByPoint( _vector$4 ); + _vector$8.addVectors( this.boundingBox.max, _box$1.max ); + this.boundingBox.expandByPoint( _vector$8 ); } else { - this.boundingBox.expandByPoint( _box$2.min ); - this.boundingBox.expandByPoint( _box$2.max ); + this.boundingBox.expandByPoint( _box$1.min ); + this.boundingBox.expandByPoint( _box$1.max ); } @@ -13933,9 +12520,9 @@ } - }, + } - computeBoundingSphere: function () { + computeBoundingSphere() { if ( this.boundingSphere === null ) { @@ -13962,7 +12549,7 @@ const center = this.boundingSphere.center; - _box$2.setFromBufferAttribute( position ); + _box$1.setFromBufferAttribute( position ); // process morph attributes if present @@ -13975,16 +12562,16 @@ if ( this.morphTargetsRelative ) { - _vector$4.addVectors( _box$2.min, _boxMorphTargets.min ); - _box$2.expandByPoint( _vector$4 ); + _vector$8.addVectors( _box$1.min, _boxMorphTargets.min ); + _box$1.expandByPoint( _vector$8 ); - _vector$4.addVectors( _box$2.max, _boxMorphTargets.max ); - _box$2.expandByPoint( _vector$4 ); + _vector$8.addVectors( _box$1.max, _boxMorphTargets.max ); + _box$1.expandByPoint( _vector$8 ); } else { - _box$2.expandByPoint( _boxMorphTargets.min ); - _box$2.expandByPoint( _boxMorphTargets.max ); + _box$1.expandByPoint( _boxMorphTargets.min ); + _box$1.expandByPoint( _boxMorphTargets.max ); } @@ -13992,7 +12579,7 @@ } - _box$2.getCenter( center ); + _box$1.getCenter( center ); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case @@ -14001,9 +12588,9 @@ for ( let i = 0, il = position.count; i < il; i ++ ) { - _vector$4.fromBufferAttribute( position, i ); + _vector$8.fromBufferAttribute( position, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) ); } @@ -14018,16 +12605,16 @@ for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { - _vector$4.fromBufferAttribute( morphAttribute, j ); + _vector$8.fromBufferAttribute( morphAttribute, j ); if ( morphTargetsRelative ) { _offset.fromBufferAttribute( position, j ); - _vector$4.add( _offset ); + _vector$8.add( _offset ); } - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$8 ) ); } @@ -14045,15 +12632,9 @@ } - }, - - computeFaceNormals: function () { - - // backwards compatibility - - }, + } - computeTangents: function () { + computeTangents() { const index = this.index; const attributes = this.attributes; @@ -14216,9 +12797,9 @@ } - }, + } - computeVertexNormals: function () { + computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute( 'position' ); @@ -14308,9 +12889,9 @@ } - }, + } - merge: function ( geometry, offset ) { + merge( geometry, offset ) { if ( ! ( geometry && geometry.isBufferGeometry ) ) { @@ -14355,25 +12936,25 @@ return this; - }, + } - normalizeNormals: function () { + normalizeNormals() { const normals = this.attributes.normal; for ( let i = 0, il = normals.count; i < il; i ++ ) { - _vector$4.fromBufferAttribute( normals, i ); + _vector$8.fromBufferAttribute( normals, i ); - _vector$4.normalize(); + _vector$8.normalize(); - normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z ); + normals.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } - }, + } - toNonIndexed: function () { + toNonIndexed() { function convertBufferAttribute( attribute, indices ) { @@ -14387,7 +12968,15 @@ for ( let i = 0, l = indices.length; i < l; i ++ ) { - index = indices[ i ] * itemSize; + if ( attribute.isInterleavedBufferAttribute ) { + + index = indices[ i ] * attribute.data.stride + attribute.offset; + + } else { + + index = indices[ i ] * itemSize; + + } for ( let j = 0; j < itemSize; j ++ ) { @@ -14465,9 +13054,9 @@ return geometry2; - }, + } - toJSON: function () { + toJSON() { const data = { metadata: { @@ -14498,6 +13087,8 @@ } + // for simplicity the code assumes attributes are not shared across geometries, see #15811 + data.data = { attributes: {} }; const index = this.index; @@ -14517,11 +13108,7 @@ const attribute = attributes[ key ]; - const attributeData = attribute.toJSON( data.data ); - - if ( attribute.name !== '' ) attributeData.name = attribute.name; - - data.data.attributes[ key ] = attributeData; + data.data.attributes[ key ] = attribute.toJSON( data.data ); } @@ -14538,11 +13125,7 @@ const attribute = attributeArray[ i ]; - const attributeData = attribute.toJSON( data.data ); - - if ( attribute.name !== '' ) attributeData.name = attribute.name; - - array.push( attributeData ); + array.push( attribute.toJSON( data.data ) ); } @@ -14584,39 +13167,15 @@ return data; - }, - - clone: function () { - - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; + } - } + clone() { return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); - }, + } - copy: function ( source ) { + copy( source ) { // reset @@ -14717,63 +13276,65 @@ this.userData = source.userData; + // geometry generator parameters + + if ( source.parameters !== undefined ) this.parameters = Object.assign( {}, source.parameters ); + return this; - }, + } - dispose: function () { + dispose() { this.dispatchEvent( { type: 'dispose' } ); } - } ); - - const _inverseMatrix = new Matrix4(); - const _ray = new Ray(); - const _sphere = new Sphere(); + } - const _vA = new Vector3(); - const _vB = new Vector3(); - const _vC = new Vector3(); + BufferGeometry.prototype.isBufferGeometry = true; - const _tempA = new Vector3(); - const _tempB = new Vector3(); - const _tempC = new Vector3(); + const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); + const _ray$2 = /*@__PURE__*/ new Ray(); + const _sphere$3 = /*@__PURE__*/ new Sphere(); - const _morphA = new Vector3(); - const _morphB = new Vector3(); - const _morphC = new Vector3(); + const _vA$1 = /*@__PURE__*/ new Vector3(); + const _vB$1 = /*@__PURE__*/ new Vector3(); + const _vC$1 = /*@__PURE__*/ new Vector3(); - const _uvA = new Vector2(); - const _uvB = new Vector2(); - const _uvC = new Vector2(); + const _tempA = /*@__PURE__*/ new Vector3(); + const _tempB = /*@__PURE__*/ new Vector3(); + const _tempC = /*@__PURE__*/ new Vector3(); - const _intersectionPoint = new Vector3(); - const _intersectionPointWorld = new Vector3(); + const _morphA = /*@__PURE__*/ new Vector3(); + const _morphB = /*@__PURE__*/ new Vector3(); + const _morphC = /*@__PURE__*/ new Vector3(); - function Mesh( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { + const _uvA$1 = /*@__PURE__*/ new Vector2(); + const _uvB$1 = /*@__PURE__*/ new Vector2(); + const _uvC$1 = /*@__PURE__*/ new Vector2(); - Object3D.call( this ); + const _intersectionPoint = /*@__PURE__*/ new Vector3(); + const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); - this.type = 'Mesh'; + class Mesh extends Object3D { - this.geometry = geometry; - this.material = material; + constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { - this.updateMorphTargets(); + super(); - } + this.type = 'Mesh'; - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { + this.geometry = geometry; + this.material = material; - constructor: Mesh, + this.updateMorphTargets(); - isMesh: true, + } - copy: function ( source ) { + copy( source ) { - Object3D.prototype.copy.call( this, source ); + super.copy( source ); if ( source.morphTargetInfluences !== undefined ) { @@ -14792,9 +13353,9 @@ return this; - }, + } - updateMorphTargets: function () { + updateMorphTargets() { const geometry = this.geometry; @@ -14837,9 +13398,9 @@ } - }, + } - raycast: function ( raycaster, intersects ) { + raycast( raycaster, intersects ) { const geometry = this.geometry; const material = this.material; @@ -14851,21 +13412,21 @@ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); + _sphere$3.copy( geometry.boundingSphere ); + _sphere$3.applyMatrix4( matrixWorld ); - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; + if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; // - _inverseMatrix.copy( matrixWorld ).invert(); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); + _inverseMatrix$2.copy( matrixWorld ).invert(); + _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { - if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return; + if ( _ray$2.intersectsBox( geometry.boundingBox ) === false ) return; } @@ -14894,7 +13455,7 @@ const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { @@ -14902,7 +13463,7 @@ const b = index.getX( j + 1 ); const c = index.getX( j + 2 ); - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); + intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { @@ -14927,7 +13488,7 @@ const b = index.getX( i + 1 ); const c = index.getX( i + 2 ); - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); + intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { @@ -14952,7 +13513,7 @@ const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { @@ -14960,7 +13521,7 @@ const b = j + 1; const c = j + 2; - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); + intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { @@ -14985,7 +13546,7 @@ const b = i + 1; const c = i + 2; - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); + intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray$2, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); if ( intersection ) { @@ -15008,7 +13569,9 @@ } - } ); + } + + Mesh.prototype.isMesh = true; function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { @@ -15043,13 +13606,13 @@ function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) { - _vA.fromBufferAttribute( position, a ); - _vB.fromBufferAttribute( position, b ); - _vC.fromBufferAttribute( position, c ); + _vA$1.fromBufferAttribute( position, a ); + _vB$1.fromBufferAttribute( position, b ); + _vC$1.fromBufferAttribute( position, c ); const morphInfluences = object.morphTargetInfluences; - if ( material.morphTargets && morphPosition && morphInfluences ) { + if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); _morphB.set( 0, 0, 0 ); @@ -15074,54 +13637,61 @@ } else { - _morphA.addScaledVector( _tempA.sub( _vA ), influence ); - _morphB.addScaledVector( _tempB.sub( _vB ), influence ); - _morphC.addScaledVector( _tempC.sub( _vC ), influence ); + _morphA.addScaledVector( _tempA.sub( _vA$1 ), influence ); + _morphB.addScaledVector( _tempB.sub( _vB$1 ), influence ); + _morphC.addScaledVector( _tempC.sub( _vC$1 ), influence ); } } - _vA.add( _morphA ); - _vB.add( _morphB ); - _vC.add( _morphC ); + _vA$1.add( _morphA ); + _vB$1.add( _morphB ); + _vC$1.add( _morphC ); } if ( object.isSkinnedMesh ) { - object.boneTransform( a, _vA ); - object.boneTransform( b, _vB ); - object.boneTransform( c, _vC ); + object.boneTransform( a, _vA$1 ); + object.boneTransform( b, _vB$1 ); + object.boneTransform( c, _vC$1 ); } - const intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint ); + const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); if ( intersection ) { if ( uv ) { - _uvA.fromBufferAttribute( uv, a ); - _uvB.fromBufferAttribute( uv, b ); - _uvC.fromBufferAttribute( uv, c ); + _uvA$1.fromBufferAttribute( uv, a ); + _uvB$1.fromBufferAttribute( uv, b ); + _uvC$1.fromBufferAttribute( uv, c ); - intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); + intersection.uv = Triangle.getUV( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } if ( uv2 ) { - _uvA.fromBufferAttribute( uv2, a ); - _uvB.fromBufferAttribute( uv2, b ); - _uvC.fromBufferAttribute( uv2, c ); + _uvA$1.fromBufferAttribute( uv2, a ); + _uvB$1.fromBufferAttribute( uv2, b ); + _uvC$1.fromBufferAttribute( uv2, c ); - intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); + intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); } - const face = new Face3( a, b, c ); - Triangle.getNormal( _vA, _vB, _vC, face.normal ); + const face = { + a: a, + b: b, + c: c, + normal: new Vector3(), + materialIndex: 0 + }; + + Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); intersection.face = face; @@ -15288,6 +13858,12 @@ } + static fromJSON( data ) { + + return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); + + } + } /** @@ -15309,7 +13885,7 @@ if ( property && ( property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || - property.isTexture ) ) { + property.isTexture || property.isQuaternion ) ) { dst[ u ][ p ] = property.clone(); @@ -15370,222 +13946,206 @@ * wireframe: , * wireframeLinewidth: , * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: + * lights: * } */ - function ShaderMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShaderMaterial'; + class ShaderMaterial extends Material { - this.defines = {}; - this.uniforms = {}; + constructor( parameters ) { - this.vertexShader = default_vertex; - this.fragmentShader = default_fragment; + super(); - this.linewidth = 1; + this.type = 'ShaderMaterial'; - this.wireframe = false; - this.wireframeLinewidth = 1; + this.defines = {}; + this.uniforms = {}; - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes + this.vertexShader = default_vertex; + this.fragmentShader = default_fragment; - this.skinning = false; // set to use skinning attribute streams - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals + this.linewidth = 1; - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; + this.wireframe = false; + this.wireframeLinewidth = 1; - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; + this.fog = false; // set to use scene fog + this.lights = false; // set to use scene lights + this.clipping = false; // set to use user-defined clipping planes - this.index0AttributeName = undefined; - this.uniformsNeedUpdate = false; - - this.glslVersion = null; + this.extensions = { + derivatives: false, // set to use derivatives + fragDepth: false, // set to use fragment depth values + drawBuffers: false, // set to use draw buffers + shaderTextureLOD: false // set to use shader texture LOD + }; - if ( parameters !== undefined ) { + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; - if ( parameters.attributes !== undefined ) { + this.index0AttributeName = undefined; + this.uniformsNeedUpdate = false; - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); + this.glslVersion = null; - } + if ( parameters !== undefined ) { - this.setValues( parameters ); + if ( parameters.attributes !== undefined ) { - } + console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - } + } - ShaderMaterial.prototype = Object.create( Material.prototype ); - ShaderMaterial.prototype.constructor = ShaderMaterial; + this.setValues( parameters ); - ShaderMaterial.prototype.isShaderMaterial = true; + } - ShaderMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + copy( source ) { - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; + super.copy( source ); - this.uniforms = cloneUniforms( source.uniforms ); + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; - this.defines = Object.assign( {}, source.defines ); + this.uniforms = cloneUniforms( source.uniforms ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + this.defines = Object.assign( {}, source.defines ); - this.lights = source.lights; - this.clipping = source.clipping; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - this.skinning = source.skinning; + this.lights = source.lights; + this.clipping = source.clipping; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + this.extensions = Object.assign( {}, source.extensions ); - this.extensions = Object.assign( {}, source.extensions ); + this.glslVersion = source.glslVersion; - this.glslVersion = source.glslVersion; + return this; - return this; + } - }; + toJSON( meta ) { - ShaderMaterial.prototype.toJSON = function ( meta ) { + const data = super.toJSON( meta ); - const data = Material.prototype.toJSON.call( this, meta ); + data.glslVersion = this.glslVersion; + data.uniforms = {}; - data.glslVersion = this.glslVersion; - data.uniforms = {}; + for ( const name in this.uniforms ) { - for ( const name in this.uniforms ) { + const uniform = this.uniforms[ name ]; + const value = uniform.value; - const uniform = this.uniforms[ name ]; - const value = uniform.value; + if ( value && value.isTexture ) { - if ( value && value.isTexture ) { + data.uniforms[ name ] = { + type: 't', + value: value.toJSON( meta ).uuid + }; - data.uniforms[ name ] = { - type: 't', - value: value.toJSON( meta ).uuid - }; + } else if ( value && value.isColor ) { - } else if ( value && value.isColor ) { + data.uniforms[ name ] = { + type: 'c', + value: value.getHex() + }; - data.uniforms[ name ] = { - type: 'c', - value: value.getHex() - }; + } else if ( value && value.isVector2 ) { - } else if ( value && value.isVector2 ) { + data.uniforms[ name ] = { + type: 'v2', + value: value.toArray() + }; - data.uniforms[ name ] = { - type: 'v2', - value: value.toArray() - }; + } else if ( value && value.isVector3 ) { - } else if ( value && value.isVector3 ) { + data.uniforms[ name ] = { + type: 'v3', + value: value.toArray() + }; - data.uniforms[ name ] = { - type: 'v3', - value: value.toArray() - }; + } else if ( value && value.isVector4 ) { - } else if ( value && value.isVector4 ) { + data.uniforms[ name ] = { + type: 'v4', + value: value.toArray() + }; - data.uniforms[ name ] = { - type: 'v4', - value: value.toArray() - }; + } else if ( value && value.isMatrix3 ) { - } else if ( value && value.isMatrix3 ) { + data.uniforms[ name ] = { + type: 'm3', + value: value.toArray() + }; - data.uniforms[ name ] = { - type: 'm3', - value: value.toArray() - }; + } else if ( value && value.isMatrix4 ) { - } else if ( value && value.isMatrix4 ) { + data.uniforms[ name ] = { + type: 'm4', + value: value.toArray() + }; - data.uniforms[ name ] = { - type: 'm4', - value: value.toArray() - }; + } else { - } else { + data.uniforms[ name ] = { + value: value + }; - data.uniforms[ name ] = { - value: value - }; + // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far - // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far + } } - } + if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; - if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; + const extensions = {}; - const extensions = {}; + for ( const key in this.extensions ) { - for ( const key in this.extensions ) { + if ( this.extensions[ key ] === true ) extensions[ key ] = true; - if ( this.extensions[ key ] === true ) extensions[ key ] = true; - - } + } - if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; + if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; - return data; + return data; - }; + } - function Camera$1() { + } - Object3D.call( this ); + ShaderMaterial.prototype.isShaderMaterial = true; - this.type = 'Camera'; + class Camera$1 extends Object3D { - this.matrixWorldInverse = new Matrix4(); + constructor() { - this.projectionMatrix = new Matrix4(); - this.projectionMatrixInverse = new Matrix4(); + super(); - } + this.type = 'Camera'; - Camera$1.prototype = Object.assign( Object.create( Object3D.prototype ), { + this.matrixWorldInverse = new Matrix4(); - constructor: Camera$1, + this.projectionMatrix = new Matrix4(); + this.projectionMatrixInverse = new Matrix4(); - isCamera: true, + } - copy: function ( source, recursive ) { + copy( source, recursive ) { - Object3D.prototype.copy.call( this, source, recursive ); + super.copy( source, recursive ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); @@ -15594,16 +14154,9 @@ return this; - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); + } - } + getWorldDirection( target ) { this.updateWorldMatrix( true, false ); @@ -15611,64 +14164,62 @@ return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); - }, + } - updateMatrixWorld: function ( force ) { + updateMatrixWorld( force ) { - Object3D.prototype.updateMatrixWorld.call( this, force ); + super.updateMatrixWorld( force ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); - }, + } - updateWorldMatrix: function ( updateParents, updateChildren ) { + updateWorldMatrix( updateParents, updateChildren ) { - Object3D.prototype.updateWorldMatrix.call( this, updateParents, updateChildren ); + super.updateWorldMatrix( updateParents, updateChildren ); this.matrixWorldInverse.copy( this.matrixWorld ).invert(); - }, + } - clone: function () { + clone() { return new this.constructor().copy( this ); } - } ); - - function PerspectiveCamera( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { + } - Camera$1.call( this ); + Camera$1.prototype.isCamera = true; - this.type = 'PerspectiveCamera'; + class PerspectiveCamera extends Camera$1 { - this.fov = fov; - this.zoom = 1; + constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { - this.near = near; - this.far = far; - this.focus = 10; + super(); - this.aspect = aspect; - this.view = null; + this.type = 'PerspectiveCamera'; - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) + this.fov = fov; + this.zoom = 1; - this.updateProjectionMatrix(); + this.near = near; + this.far = far; + this.focus = 10; - } + this.aspect = aspect; + this.view = null; - PerspectiveCamera.prototype = Object.assign( Object.create( Camera$1.prototype ), { + this.filmGauge = 35; // width of the film (default in millimeters) + this.filmOffset = 0; // horizontal film offset (same unit as gauge) - constructor: PerspectiveCamera, + this.updateProjectionMatrix(); - isPerspectiveCamera: true, + } - copy: function ( source, recursive ) { + copy( source, recursive ) { - Camera$1.prototype.copy.call( this, source, recursive ); + super.copy( source, recursive ); this.fov = source.fov; this.zoom = source.zoom; @@ -15685,7 +14236,7 @@ return this; - }, + } /** * Sets the FOV by focal length in respect to the current .filmGauge. @@ -15695,47 +14246,47 @@ * * Values for focal length and film gauge must have the same unit. */ - setFocalLength: function ( focalLength ) { + setFocalLength( focalLength ) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - this.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope ); + this.fov = RAD2DEG$1 * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); - }, + } /** * Calculates the focal length from the current .fov and .filmGauge. */ - getFocalLength: function () { + getFocalLength() { - const vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ); + const vExtentSlope = Math.tan( DEG2RAD$1 * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; - }, + } - getEffectiveFOV: function () { + getEffectiveFOV() { - return MathUtils.RAD2DEG * 2 * Math.atan( - Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom ); + return RAD2DEG$1 * 2 * Math.atan( + Math.tan( DEG2RAD$1 * 0.5 * this.fov ) / this.zoom ); - }, + } - getFilmWidth: function () { + getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); - }, + } - getFilmHeight: function () { + getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); - }, + } /** * Sets an offset in a larger frustum. This is useful for multi-window or @@ -15772,7 +14323,7 @@ * * Note there is no reason monitors have to be the same size or in a grid. */ - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; @@ -15800,9 +14351,9 @@ this.updateProjectionMatrix(); - }, + } - clearViewOffset: function () { + clearViewOffset() { if ( this.view !== null ) { @@ -15812,12 +14363,12 @@ this.updateProjectionMatrix(); - }, + } - updateProjectionMatrix: function () { + updateProjectionMatrix() { const near = this.near; - let top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom; + let top = near * Math.tan( DEG2RAD$1 * 0.5 * this.fov ) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = - 0.5 * width; @@ -15842,11 +14393,11 @@ this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - }, + } - toJSON: function ( meta ) { + toJSON( meta ) { - const data = Object3D.prototype.toJSON.call( this, meta ); + const data = super.toJSON( meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; @@ -15866,65 +14417,75 @@ } - } ); + } + + PerspectiveCamera.prototype.isPerspectiveCamera = true; const fov = 90, aspect = 1; - function CubeCamera( near, far, renderTarget ) { + class CubeCamera extends Object3D { - Object3D.call( this ); + constructor( near, far, renderTarget ) { - this.type = 'CubeCamera'; + super(); - if ( renderTarget.isWebGLCubeRenderTarget !== true ) { + this.type = 'CubeCamera'; - console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' ); - return; + if ( renderTarget.isWebGLCubeRenderTarget !== true ) { - } + console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' ); + return; + + } - this.renderTarget = renderTarget; + this.renderTarget = renderTarget; - const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.layers = this.layers; - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); + const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); + cameraPX.layers = this.layers; + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); - const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.layers = this.layers; - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); + const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); + cameraNX.layers = this.layers; + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); - const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.layers = this.layers; - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); + const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); + cameraPY.layers = this.layers; + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); - const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.layers = this.layers; - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); + const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); + cameraNY.layers = this.layers; + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); - const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.layers = this.layers; - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); + const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.layers = this.layers; + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); - const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.layers = this.layers; - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); + const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.layers = this.layers; + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); - this.update = function ( renderer, scene ) { + } + + update( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); + const renderTarget = this.renderTarget; + + const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; + const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); @@ -15958,57 +14519,38 @@ renderer.xr.enabled = currentXrEnabled; - }; + } } - CubeCamera.prototype = Object.create( Object3D.prototype ); - CubeCamera.prototype.constructor = CubeCamera; - - function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; - - Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + class CubeTexture extends Texture { - this.flipY = false; + constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - // Why CubeTexture._needsFlipEnvMap is necessary: - // - // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) - // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, - // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - - // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped - // and the flag _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false) - // when using WebGLCubeRenderTarget.texture as a cube texture. - - this._needsFlipEnvMap = true; - - } + images = images !== undefined ? images : []; + mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - CubeTexture.prototype = Object.create( Texture.prototype ); - CubeTexture.prototype.constructor = CubeTexture; + super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - CubeTexture.prototype.isCubeTexture = true; + this.flipY = false; - Object.defineProperty( CubeTexture.prototype, 'images', { + } - get: function () { + get images() { return this.image; - }, + } - set: function ( value ) { + set images( value ) { this.image = value; } - } ); + } + + CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { @@ -16024,11 +14566,21 @@ super( size, size, options ); - Object.defineProperty( this, 'isWebGLCubeRenderTarget', { value: true } ); - options = options || {}; + // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) + // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, + // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. + + // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped + // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture + // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). + this.texture = new CubeTexture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + this.texture.isRenderTargetTexture = true; + + this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.texture._needsFlipEnvMap = false; @@ -16143,45 +14695,217 @@ } - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; + + const _vector1 = /*@__PURE__*/ new Vector3(); + const _vector2 = /*@__PURE__*/ new Vector3(); + const _normalMatrix = /*@__PURE__*/ new Matrix3(); + + class Plane { - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { - this.image = { data: data || null, width: width || 1, height: height || 1 }; + // normal is assumed to be normalized - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + this.normal = normal; + this.constant = constant; - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + } - this.needsUpdate = true; + set( normal, constant ) { - } + this.normal.copy( normal ); + this.constant = constant; - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; + return this; - DataTexture.prototype.isDataTexture = true; + } - const _sphere$1 = /*@__PURE__*/ new Sphere(); - const _vector$5 = /*@__PURE__*/ new Vector3(); + setComponents( x, y, z, w ) { - class Frustum { + this.normal.set( x, y, z ); + this.constant = w; - constructor( p0, p1, p2, p3, p4, p5 ) { + return this; - this.planes = [ + } - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() + setFromNormalAndCoplanarPoint( normal, point ) { - ]; + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); + + return this; + + } + + setFromCoplanarPoints( a, b, c ) { + + const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + } + + copy( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + } + + normalize() { + + // Note: will lead to a divide by zero if the plane is invalid. + + const inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + } + + negate() { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + } + + distanceToPoint( point ) { + + return this.normal.dot( point ) + this.constant; + + } + + distanceToSphere( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + } + + projectPoint( point, target ) { + + return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); + + } + + intersectLine( line, target ) { + + const direction = line.delta( _vector1 ); + + const denominator = this.normal.dot( direction ); + + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { + + return target.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return null; + + } + + const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return null; + + } + + return target.copy( direction ).multiplyScalar( t ).add( line.start ); + + } + + intersectsLine( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + const startSign = this.distanceToPoint( line.start ); + const endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + } + + intersectsBox( box ) { + + return box.intersectsPlane( this ); + + } + + intersectsSphere( sphere ) { + + return sphere.intersectsPlane( this ); + + } + + coplanarPoint( target ) { + + return target.copy( this.normal ).multiplyScalar( - this.constant ); + + } + + applyMatrix4( matrix, optionalNormalMatrix ) { + + const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); + + const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); + + const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + + this.constant = - referencePoint.dot( normal ); + + return this; + + } + + translate( offset ) { + + this.constant -= offset.dot( this.normal ); + + return this; + + } + + equals( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + + } + + clone() { + + return new this.constructor().copy( this ); + + } + + } + + Plane.prototype.isPlane = true; + + const _sphere$2 = /*@__PURE__*/ new Sphere(); + const _vector$7 = /*@__PURE__*/ new Vector3(); + + class Frustum { + + constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { + + this.planes = [ p0, p1, p2, p3, p4, p5 ]; } @@ -16200,12 +14924,6 @@ } - clone() { - - return new this.constructor().copy( this ); - - } - copy( frustum ) { const planes = this.planes; @@ -16246,19 +14964,19 @@ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); + _sphere$2.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); - return this.intersectsSphere( _sphere$1 ); + return this.intersectsSphere( _sphere$2 ); } intersectsSprite( sprite ) { - _sphere$1.center.set( 0, 0, 0 ); - _sphere$1.radius = 0.7071067811865476; - _sphere$1.applyMatrix4( sprite.matrixWorld ); + _sphere$2.center.set( 0, 0, 0 ); + _sphere$2.radius = 0.7071067811865476; + _sphere$2.applyMatrix4( sprite.matrixWorld ); - return this.intersectsSphere( _sphere$1 ); + return this.intersectsSphere( _sphere$2 ); } @@ -16294,11 +15012,11 @@ // corner at max distance - _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x; - _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y; - _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z; + _vector$7.x = plane.normal.x > 0 ? box.max.x : box.min.x; + _vector$7.y = plane.normal.y > 0 ? box.max.y : box.min.y; + _vector$7.z = plane.normal.z > 0 ? box.max.z : box.min.z; - if ( plane.distanceToPoint( _vector$5 ) < 0 ) { + if ( plane.distanceToPoint( _vector$7 ) < 0 ) { return false; @@ -16328,6 +15046,12 @@ } + clone() { + + return new this.constructor().copy( this ); + + } + } function WebGLAnimation() { @@ -16450,6 +15174,10 @@ type = 5121; + } else if ( array instanceof Uint8ClampedArray ) { + + type = 5121; + } return { @@ -16644,15 +15372,23 @@ } + static fromJSON( data ) { + + return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); + + } + } var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; + var alphatest_fragment = "#ifdef USE_ALPHATEST\n\tif ( diffuseColor.a < alphaTest ) discard;\n#endif"; + + var alphatest_pars_fragment = "#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif"; - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; + var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\t#endif\n#endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; @@ -16660,9 +15396,9 @@ var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; + var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( V * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif"; - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; + var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; @@ -16672,15 +15408,15 @@ var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; - var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; + var color_fragment = "#if defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#elif defined( USE_COLOR )\n\tdiffuseColor.rgb *= vColor;\n#endif"; - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; + var color_pars_fragment = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR )\n\tvarying vec3 vColor;\n#endif"; - var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; + var color_pars_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; - var color_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; + var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; - var common$1 = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; + var common$1 = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract( sin( sn ) * c );\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; @@ -16698,7 +15434,7 @@ var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn 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.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn 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.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; + var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; @@ -16708,41 +15444,41 @@ var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif"; + var fog_vertex = "#ifdef USE_FOG\n\tvFogDepth = - mvPosition.z;\n#endif"; - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; + var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float vFogDepth;\n#endif"; - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; + var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; + var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; + var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tlightMapIrradiance *= PI;\n\t#endif\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n#endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; + var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointLightInfo( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( - dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotLightInfo( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( - dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalLightInfo( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( - dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; - var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; + var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tif ( cutoffDistance > 0.0 ) {\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t}\n\t\treturn distanceFalloff;\n\t#else\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t\t}\n\t\treturn 1.0;\n\t#endif\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) {\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; + var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 reflectVec;\n\t\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\t\treflectVec = reflect( - viewDir, normal );\n\t\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\t#else\n\t\t\t\treflectVec = refract( - viewDir, normal, refractionRatio );\n\t\t\t#endif\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n#endif"; var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; - var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; + var lights_toon_pars_fragment = "varying vec3 vViewPosition;\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; + var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; + var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\t#ifdef SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULARINTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a;\n\t\t#endif\n\t\t#ifdef USE_SPECULARCOLORMAP\n\t\t\tspecularColorFactor *= specularColorMapTexelToLinear( texture2D( specularColorMap, vUv ) ).rgb;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEENCOLORMAP\n\t\tmaterial.sheenColor *= sheenColorMapTexelToLinear( texture2D( sheenColorMap, vUv ) ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\t#ifdef USE_SHEENROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a;\n\t#endif\n#endif"; - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; + var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n};\nvec3 clearcoatSpecular = vec3( 0.0 );\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\tvec3 FssEss = specularColor * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness );\n\t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tcomputeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; + var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef USE_CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry.normal );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; + var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometry.normal );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; @@ -16766,25 +15502,33 @@ var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; + var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] > 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1, 2 ) * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\t\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\t\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\t\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n\t#endif\n#endif"; - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; + var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\n\t\tuniform sampler2DArray morphTargetsTexture;\n\t\tuniform vec2 morphTargetsTextureSize;\n\t\tvec3 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset, const in int stride ) {\n\t\t\tfloat texelIndex = float( vertexIndex * stride + offset );\n\t\t\tfloat y = floor( texelIndex / morphTargetsTextureSize.x );\n\t\t\tfloat x = texelIndex - y * morphTargetsTextureSize.x;\n\t\t\tvec3 morphUV = vec3( ( x + 0.5 ) / morphTargetsTextureSize.x, y / morphTargetsTextureSize.y, morphTargetIndex );\n\t\t\treturn texture( morphTargetsTexture, morphUV ).xyz;\n\t\t}\n\t#else\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\tuniform float morphTargetInfluences[ 8 ];\n\t\t#else\n\t\t\tuniform float morphTargetInfluences[ 4 ];\n\t\t#endif\n\t#endif\n#endif"; - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; + var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\t#ifndef USE_MORPHNORMALS\n\t\t\t\tif ( morphTargetInfluences[ i ] > 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 1 ) * morphTargetInfluences[ i ];\n\t\t\t#else\n\t\t\t\tif ( morphTargetInfluences[ i ] > 0.0 ) transformed += getMorph( gl_VertexID, i, 0, 2 ) * morphTargetInfluences[ i ];\n\t\t\t#endif\n\t\t}\n\t#else\n\t\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\t\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\t\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\t\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t\t#endif\n\t#endif\n#endif"; - var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; + var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif"; + var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif"; + var normal_pars_fragment = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; - var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; + var normal_pars_vertex = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; - var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN );\n\t#endif\n#endif"; + var normal_vertex = "#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif"; + + var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif"; + + var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; + + var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; + var output_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= transmissionAlpha + 0.1;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; + + var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; @@ -16820,11 +15564,11 @@ var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; + var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; - var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif"; + var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tfloat transmissionAlpha = 1.0;\n\tfloat transmissionFactor = transmission;\n\tfloat thicknessFactor = thickness;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\ttransmissionFactor *= texture2D( transmissionMap, vUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tthicknessFactor *= texture2D( thicknessMap, vUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmission = getIBLVolumeRefraction(\n\t\tn, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor,\n\t\tattenuationColor, attenuationDistance );\n\ttotalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor );\n\ttransmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor );\n#endif"; - var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif"; + var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tvec3 getVolumeTransmissionRay( vec3 n, vec3 v, float thickness, float ior, mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( float roughness, float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( vec2 fragCoord, float roughness, float ior ) {\n\t\tfloat framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\treturn texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#else\n\t\t\treturn texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#endif\n\t}\n\tvec3 applyVolumeAttenuation( vec3 radiance, float transmissionDistance, vec3 attenuationColor, float attenuationDistance ) {\n\t\tif ( attenuationDistance == 0.0 ) {\n\t\t\treturn radiance;\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance * radiance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( vec3 n, vec3 v, float roughness, vec3 diffuseColor, vec3 specularColor, float specularF90,\n\t\tvec3 position, mat4 modelMatrix, mat4 viewMatrix, mat4 projMatrix, float ior, float thickness,\n\t\tvec3 attenuationColor, float attenuationDistance ) {\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\tvec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a );\n\t}\n#endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; @@ -16838,76 +15582,77 @@ var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; + var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + const vertex$g = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; + const fragment$g = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; + const vertex$f = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; + const fragment$f = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; + const vertex$e = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; + const fragment$e = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; + const vertex$d = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; + const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + const vertex$c = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; + const fragment$c = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$b = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$b = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$a = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$a = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$9 = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$9 = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$8 = "#define MATCAP\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; + const fragment$8 = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$7 = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$6 = "#define PHONG\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$5 = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULARINTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n\t#ifdef USE_SPECULARCOLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEENCOLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEENROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - clearcoat * Fcc ) + clearcoatSpecular * clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; + const vertex$4 = "#define TOON\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; + const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$3 = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$3 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; + const vertex$2 = "#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$2 = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; + const vertex$1 = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; + const fragment$1 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, + alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, @@ -16970,10 +15715,14 @@ morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, + normal_pars_fragment: normal_pars_fragment, + normal_pars_vertex: normal_pars_vertex, + normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, + output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, @@ -16993,8 +15742,8 @@ specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, - transmissionmap_fragment: transmissionmap_fragment, - transmissionmap_pars_fragment: transmissionmap_pars_fragment, + transmission_fragment: transmission_fragment, + transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, @@ -17003,38 +15752,38 @@ uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, - background_frag: background_frag, - background_vert: background_vert, - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - normal_frag: normal_frag, - normal_vert: normal_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert + background_vert: vertex$g, + background_frag: fragment$g, + cube_vert: vertex$f, + cube_frag: fragment$f, + depth_vert: vertex$e, + depth_frag: fragment$e, + distanceRGBA_vert: vertex$d, + distanceRGBA_frag: fragment$d, + equirect_vert: vertex$c, + equirect_frag: fragment$c, + linedashed_vert: vertex$b, + linedashed_frag: fragment$b, + meshbasic_vert: vertex$a, + meshbasic_frag: fragment$a, + meshlambert_vert: vertex$9, + meshlambert_frag: fragment$9, + meshmatcap_vert: vertex$8, + meshmatcap_frag: fragment$8, + meshnormal_vert: vertex$7, + meshnormal_frag: fragment$7, + meshphong_vert: vertex$6, + meshphong_frag: fragment$6, + meshphysical_vert: vertex$5, + meshphysical_frag: fragment$5, + meshtoon_vert: vertex$4, + meshtoon_frag: fragment$4, + points_vert: vertex$3, + points_frag: fragment$3, + shadow_vert: vertex$2, + shadow_frag: fragment$2, + sprite_vert: vertex$1, + sprite_frag: fragment$1 }; /** @@ -17045,7 +15794,7 @@ common: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, @@ -17053,6 +15802,7 @@ uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, + alphaTest: { value: 0 } }, @@ -17066,7 +15816,8 @@ envMap: { value: null }, flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, + reflectivity: { value: 1.0 }, // basic, lambert, phong + ior: { value: 1.5 }, // standard, physical refractionRatio: { value: 0.98 }, maxMipLevel: { value: 0 } @@ -17222,24 +15973,26 @@ points: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, + alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, + alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } @@ -17433,8 +16186,8 @@ } ] ), - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag + vertexShader: ShaderChunk.meshnormal_vert, + fragmentShader: ShaderChunk.meshnormal_frag }, @@ -17536,9 +16289,23 @@ clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, clearcoatNormalMap: { value: null }, - sheen: { value: new Color( 0x000000 ) }, + sheen: { value: 0 }, + sheenColor: { value: new Color( 0x000000 ) }, + sheenColorMap: { value: null }, + sheenRoughness: { value: 0 }, + sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, + transmissionSamplerSize: { value: new Vector2() }, + transmissionSamplerMap: { value: null }, + thickness: { value: 0 }, + thicknessMap: { value: null }, + attenuationDistance: { value: 0 }, + attenuationColor: { value: new Color( 0x000000 ) }, + specularIntensity: { value: 0 }, + specularIntensityMap: { value: null }, + specularColor: { value: new Color( 1, 1, 1 ) }, + specularColorMap: { value: null }, } ] ), @@ -17559,8 +16326,9 @@ let currentBackgroundVersion = 0; let currentTonemapping = null; - function render( renderList, scene, camera, forceClear ) { + function render( renderList, scene ) { + let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { @@ -17598,7 +16366,7 @@ } - if ( background && ( background.isCubeTexture || background.isWebGLCubeRenderTarget || background.mapping === CubeUVReflectionMapping ) ) { + if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { if ( boxMesh === undefined ) { @@ -17640,16 +16408,8 @@ } - if ( background.isWebGLCubeRenderTarget ) { - - // TODO Deprecate - - background = background.texture; - - } - boxMesh.material.uniforms.envMap.value = background; - boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background._needsFlipEnvMap ) ? - 1 : 1; + boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; if ( currentBackground !== background || currentBackgroundVersion !== background.version || @@ -18092,9 +16852,16 @@ const programAttribute = programAttributes[ name ]; - if ( programAttribute >= 0 ) { + if ( programAttribute.location >= 0 ) { + + let geometryAttribute = geometryAttributes[ name ]; + + if ( geometryAttribute === undefined ) { + + if ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; + if ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor; - const geometryAttribute = geometryAttributes[ name ]; + } if ( geometryAttribute !== undefined ) { @@ -18119,85 +16886,85 @@ if ( data && data.isInstancedInterleavedBuffer ) { - enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - geometry._maxInstanceCount = data.meshPerAttribute * data.count; + enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } - } else { - - enableAttribute( programAttribute ); + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - } + geometry._maxInstanceCount = data.meshPerAttribute * data.count; - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); + } - } else { + } else { - if ( geometryAttribute.isInstancedBufferAttribute ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); + enableAttribute( programAttribute.location + i ); - if ( geometry._maxInstanceCount === undefined ) { + } - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + } - } + gl.bindBuffer( 34962, buffer ); - } else { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttribute( programAttribute ); + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + stride * bytesPerElement, + ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement + ); } - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); + } else { - } + if ( geometryAttribute.isInstancedBufferAttribute ) { - } else if ( name === 'instanceMatrix' ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - const attribute = attributes.get( object.instanceMatrix ); + enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - const buffer = attribute.buffer; - const type = attribute.type; + geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - enableAttributeAndDivisor( programAttribute + 0, 1 ); - enableAttributeAndDivisor( programAttribute + 1, 1 ); - enableAttributeAndDivisor( programAttribute + 2, 1 ); - enableAttributeAndDivisor( programAttribute + 3, 1 ); + } - gl.bindBuffer( 34962, buffer ); + } else { - gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); - gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); - gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); - gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - } else if ( name === 'instanceColor' ) { + enableAttribute( programAttribute.location + i ); - const attribute = attributes.get( object.instanceColor ); + } - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + gl.bindBuffer( 34962, buffer ); - const buffer = attribute.buffer; - const type = attribute.type; + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttributeAndDivisor( programAttribute, 1 ); + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + size * bytesPerElement, + ( size / programAttribute.locationSize ) * i * bytesPerElement + ); - gl.bindBuffer( 34962, buffer ); + } - gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 ); + } } else if ( materialDefaultAttributeValues !== undefined ) { @@ -18208,19 +16975,19 @@ switch ( value.length ) { case 2: - gl.vertexAttrib2fv( programAttribute, value ); + gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: - gl.vertexAttrib3fv( programAttribute, value ); + gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: - gl.vertexAttrib4fv( programAttribute, value ); + gl.vertexAttrib4fv( programAttribute.location, value ); break; default: - gl.vertexAttrib1fv( programAttribute, value ); + gl.vertexAttrib1fv( programAttribute.location, value ); } @@ -18421,9 +17188,9 @@ if ( maxAnisotropy !== undefined ) return maxAnisotropy; - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - if ( extension !== null ) { + const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); @@ -18482,6 +17249,8 @@ } + const drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ); + const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( 34930 ); @@ -18495,7 +17264,7 @@ const maxFragmentUniforms = gl.getParameter( 36349 ); const vertexTextures = maxVertexTextures > 0; - const floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); + const floatFragmentTextures = isWebGL2 || extensions.has( 'OES_texture_float' ); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; @@ -18504,6 +17273,8 @@ isWebGL2: isWebGL2, + drawBuffers: drawBuffers, + getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, @@ -18714,7 +17485,7 @@ function get( texture ) { - if ( texture && texture.isTexture ) { + if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; @@ -18731,7 +17502,6 @@ if ( image && image.height > 0 ) { - const currentRenderList = renderer.getRenderList(); const currentRenderTarget = renderer.getRenderTarget(); const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); @@ -18739,7 +17509,6 @@ cubemaps.set( texture, renderTarget ); renderer.setRenderTarget( currentRenderTarget ); - renderer.setRenderList( currentRenderList ); texture.addEventListener( 'dispose', onTextureDispose ); @@ -18793,12548 +17562,12871 @@ } - function WebGLExtensions( gl ) { - - const extensions = {}; - - function getExtension( name ) { + class OrthographicCamera extends Camera$1 { - if ( extensions[ name ] !== undefined ) { + constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { - return extensions[ name ]; + super(); - } + this.type = 'OrthographicCamera'; - let extension; + this.zoom = 1; + this.view = null; - switch ( name ) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; + this.near = near; + this.far = far; - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; + this.updateProjectionMatrix(); - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; + } - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; + copy( source, recursive ) { - default: - extension = gl.getExtension( name ); + super.copy( source, recursive ); - } + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; - extensions[ name ] = extension; + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign( {}, source.view ); - return extension; + return this; } - return { + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { - has: function ( name ) { + if ( this.view === null ) { - return getExtension( name ) !== null; + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; - }, + } - init: function ( capabilities ) { + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; - if ( capabilities.isWebGL2 ) { + this.updateProjectionMatrix(); - getExtension( 'EXT_color_buffer_float' ); + } - } else { + clearViewOffset() { - getExtension( 'WEBGL_depth_texture' ); - getExtension( 'OES_texture_float' ); - getExtension( 'OES_texture_half_float' ); - getExtension( 'OES_texture_half_float_linear' ); - getExtension( 'OES_standard_derivatives' ); - getExtension( 'OES_element_index_uint' ); - getExtension( 'OES_vertex_array_object' ); - getExtension( 'ANGLE_instanced_arrays' ); + if ( this.view !== null ) { - } + this.view.enabled = false; - getExtension( 'OES_texture_float_linear' ); - getExtension( 'EXT_color_buffer_half_float' ); + } - }, + this.updateProjectionMatrix(); - get: function ( name ) { + } - const extension = getExtension( name ); + updateProjectionMatrix() { - if ( extension === null ) { + const dx = ( this.right - this.left ) / ( 2 * this.zoom ); + const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + const cx = ( this.right + this.left ) / 2; + const cy = ( this.top + this.bottom ) / 2; - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + let left = cx - dx; + let right = cx + dx; + let top = cy + dy; + let bottom = cy - dy; - } + if ( this.view !== null && this.view.enabled ) { - return extension; + const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; + const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; + + left += scaleW * this.view.offsetX; + right = left + scaleW * this.view.width; + top -= scaleH * this.view.offsetY; + bottom = top - scaleH * this.view.height; } - }; + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - } + this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - function WebGLGeometries( gl, attributes, info, bindingStates ) { + } - const geometries = {}; - const wireframeAttributes = new WeakMap(); + toJSON( meta ) { - function onGeometryDispose( event ) { + const data = super.toJSON( meta ); - const geometry = event.target; + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; - if ( geometry.index !== null ) { + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - attributes.remove( geometry.index ); + return data; - } + } - for ( const name in geometry.attributes ) { + } - attributes.remove( geometry.attributes[ name ] ); + OrthographicCamera.prototype.isOrthographicCamera = true; - } + class RawShaderMaterial extends ShaderMaterial { - geometry.removeEventListener( 'dispose', onGeometryDispose ); + constructor( parameters ) { - delete geometries[ geometry.id ]; + super( parameters ); - const attribute = wireframeAttributes.get( geometry ); + this.type = 'RawShaderMaterial'; - if ( attribute ) { + } - attributes.remove( attribute ); - wireframeAttributes.delete( geometry ); + } - } + RawShaderMaterial.prototype.isRawShaderMaterial = true; - bindingStates.releaseStatesOfGeometry( geometry ); + const LOD_MIN = 4; + const LOD_MAX = 8; + const SIZE_MAX = Math.pow( 2, LOD_MAX ); + + // The standard deviations (radians) associated with the extra mips. These are + // chosen to approximate a Trowbridge-Reitz distribution function times the + // geometric shadowing function. These sigma values squared must match the + // variance #defines in cube_uv_reflection_fragment.glsl.js. + const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; + + const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; + + // The maximum length of the blur for loop. Smaller sigmas will use fewer + // samples and exit early, but not recompile the shader. + const MAX_SAMPLES = 20; + + const ENCODINGS = { + [ LinearEncoding ]: 0, + [ sRGBEncoding ]: 1, + [ RGBEEncoding ]: 2, + [ RGBM7Encoding ]: 3, + [ RGBM16Encoding ]: 4, + [ RGBDEncoding ]: 5, + [ GammaEncoding ]: 6 + }; - if ( geometry.isInstancedBufferGeometry === true ) { + const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); + const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/ _createPlanes(); + const _clearColor = /*@__PURE__*/ new Color(); + let _oldTarget = null; + + // Golden Ratio + const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; + const INV_PHI = 1 / PHI; + + // Vertices of a dodecahedron (except the opposites, which represent the + // same axis), used as axis directions evenly spread on a sphere. + const _axisDirections = [ + /*@__PURE__*/ new Vector3( 1, 1, 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, 1 ), + /*@__PURE__*/ new Vector3( 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), + /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), + /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), + /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; - delete geometry._maxInstanceCount; + /** + * This class generates a Prefiltered, Mipmapped Radiance Environment Map + * (PMREM) from a cubeMap environment texture. This allows different levels of + * blur to be quickly accessed based on material roughness. It is packed into a + * special CubeUV format that allows us to perform custom interpolation so that + * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap + * chain, it only goes down to the LOD_MIN level (above), and then creates extra + * even more filtered 'mips' at the same LOD_MIN resolution, associated with + * higher roughness levels. In this way we maintain resolution to smoothly + * interpolate diffuse lighting while limiting sampling computation. + * + * Paper: Fast, Accurate Image-Based Lighting + * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view + */ - } + class PMREMGenerator { - // + constructor( renderer ) { - info.memory.geometries --; + this._renderer = renderer; + this._pingPongRenderTarget = null; + + this._blurMaterial = _getBlurShader( MAX_SAMPLES ); + this._equirectShader = null; + this._cubemapShader = null; + + this._compileMaterial( this._blurMaterial ); } - function get( object, geometry ) { + /** + * Generates a PMREM from a supplied Scene, which can be faster than using an + * image if networking bandwidth is low. Optional sigma specifies a blur radius + * in radians to be applied to the scene before PMREM generation. Optional near + * and far planes ensure the scene is rendered in its entirety (the cubeCamera + * is placed at the origin). + */ + fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { - if ( geometries[ geometry.id ] === true ) return geometry; + _oldTarget = this._renderer.getRenderTarget(); + const cubeUVRenderTarget = this._allocateTargets(); - geometry.addEventListener( 'dispose', onGeometryDispose ); + this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); + if ( sigma > 0 ) { - geometries[ geometry.id ] = true; + this._blur( cubeUVRenderTarget, 0, 0, sigma ); - info.memory.geometries ++; + } - return geometry; + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); + + return cubeUVRenderTarget; } - function update( geometry ) { + /** + * Generates a PMREM from an equirectangular texture, which can be either LDR + * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512), + * as this matches best with the 256 x 256 cubemap output. + */ + fromEquirectangular( equirectangular ) { - const geometryAttributes = geometry.attributes; + return this._fromTexture( equirectangular ); - // Updating index buffer in VAO now. See WebGLBindingStates. + } - for ( const name in geometryAttributes ) { + /** + * Generates a PMREM from an cubemap texture, which can be either LDR + * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256, + * as this matches best with the 256 x 256 cubemap output. + */ + fromCubemap( cubemap ) { - attributes.update( geometryAttributes[ name ], 34962 ); + return this._fromTexture( cubemap ); - } + } - // morph targets + /** + * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + compileCubemapShader() { - const morphAttributes = geometry.morphAttributes; + if ( this._cubemapShader === null ) { - for ( const name in morphAttributes ) { + this._cubemapShader = _getCubemapShader(); + this._compileMaterial( this._cubemapShader ); - const array = morphAttributes[ name ]; + } - for ( let i = 0, l = array.length; i < l; i ++ ) { + } - attributes.update( array[ i ], 34962 ); + /** + * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + compileEquirectangularShader() { - } + if ( this._equirectShader === null ) { + + this._equirectShader = _getEquirectShader(); + this._compileMaterial( this._equirectShader ); } } - function updateWireframeAttribute( geometry ) { - - const indices = []; - - const geometryIndex = geometry.index; - const geometryPosition = geometry.attributes.position; - let version = 0; + /** + * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, + * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on + * one of them will cause any others to also become unusable. + */ + dispose() { - if ( geometryIndex !== null ) { + this._blurMaterial.dispose(); - const array = geometryIndex.array; - version = geometryIndex.version; + if ( this._cubemapShader !== null ) this._cubemapShader.dispose(); + if ( this._equirectShader !== null ) this._equirectShader.dispose(); - for ( let i = 0, l = array.length; i < l; i += 3 ) { + for ( let i = 0; i < _lodPlanes.length; i ++ ) { - const a = array[ i + 0 ]; - const b = array[ i + 1 ]; - const c = array[ i + 2 ]; + _lodPlanes[ i ].dispose(); - indices.push( a, b, b, c, c, a ); + } - } + } - } else { + // private interface - const array = geometryPosition.array; - version = geometryPosition.version; + _cleanup( outputTarget ) { - for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + this._pingPongRenderTarget.dispose(); + this._renderer.setRenderTarget( _oldTarget ); + outputTarget.scissorTest = false; + _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); - const a = i + 0; - const b = i + 1; - const c = i + 2; + } - indices.push( a, b, b, c, c, a ); + _fromTexture( texture ) { - } + _oldTarget = this._renderer.getRenderTarget(); + const cubeUVRenderTarget = this._allocateTargets( texture ); + this._textureToCubeUV( texture, cubeUVRenderTarget ); + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - } + return cubeUVRenderTarget; - const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; + } - // Updating index buffer in VAO now. See WebGLBindingStates + _allocateTargets( texture ) { // warning: null texture is valid - // + const params = { + magFilter: NearestFilter, + minFilter: NearestFilter, + generateMipmaps: false, + type: UnsignedByteType, + format: RGBEFormat, + encoding: _isLDR( texture ) ? texture.encoding : RGBEEncoding, + depthBuffer: false + }; - const previousAttribute = wireframeAttributes.get( geometry ); + const cubeUVRenderTarget = _createRenderTarget( params ); + cubeUVRenderTarget.depthBuffer = texture ? false : true; + this._pingPongRenderTarget = _createRenderTarget( params ); + return cubeUVRenderTarget; - if ( previousAttribute ) attributes.remove( previousAttribute ); + } - // + _compileMaterial( material ) { - wireframeAttributes.set( geometry, attribute ); + const tmpMesh = new Mesh( _lodPlanes[ 0 ], material ); + this._renderer.compile( tmpMesh, _flatCamera ); } - function getWireframeAttribute( geometry ) { + _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { - const currentAttribute = wireframeAttributes.get( geometry ); + const fov = 90; + const aspect = 1; + const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); + const upSign = [ 1, - 1, 1, 1, 1, 1 ]; + const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; + const renderer = this._renderer; - if ( currentAttribute ) { + const originalAutoClear = renderer.autoClear; + const outputEncoding = renderer.outputEncoding; + const toneMapping = renderer.toneMapping; + renderer.getClearColor( _clearColor ); - const geometryIndex = geometry.index; + renderer.toneMapping = NoToneMapping; + renderer.outputEncoding = LinearEncoding; + renderer.autoClear = false; - if ( geometryIndex !== null ) { + const backgroundMaterial = new MeshBasicMaterial( { + name: 'PMREM.Background', + side: BackSide, + depthWrite: false, + depthTest: false, + } ); - // if the attribute is obsolete, create a new one + const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); - if ( currentAttribute.version < geometryIndex.version ) { + let useSolidColor = false; + const background = scene.background; - updateWireframeAttribute( geometry ); + if ( background ) { - } + if ( background.isColor ) { + + backgroundMaterial.color.copy( background ); + scene.background = null; + useSolidColor = true; } } else { - updateWireframeAttribute( geometry ); + backgroundMaterial.color.copy( _clearColor ); + useSolidColor = true; } - return wireframeAttributes.get( geometry ); + for ( let i = 0; i < 6; i ++ ) { - } + const col = i % 3; + if ( col == 0 ) { - return { + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); - get: get, - update: update, + } else if ( col == 1 ) { - getWireframeAttribute: getWireframeAttribute + cubeCamera.up.set( 0, 0, upSign[ i ] ); + cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); - }; + } else { - } + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { + } - const isWebGL2 = capabilities.isWebGL2; + _setViewport( cubeUVRenderTarget, + col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX ); + renderer.setRenderTarget( cubeUVRenderTarget ); - let mode; + if ( useSolidColor ) { - function setMode( value ) { + renderer.render( backgroundBox, cubeCamera ); - mode = value; + } - } + renderer.render( scene, cubeCamera ); - let type, bytesPerElement; + } - function setIndex( value ) { + backgroundBox.geometry.dispose(); + backgroundBox.material.dispose(); - type = value.type; - bytesPerElement = value.bytesPerElement; + renderer.toneMapping = toneMapping; + renderer.outputEncoding = outputEncoding; + renderer.autoClear = originalAutoClear; + scene.background = background; } - function render( start, count ) { + _setEncoding( uniform, texture ) { - gl.drawElements( mode, count, type, start * bytesPerElement ); + if ( this._renderer.capabilities.isWebGL2 === true && texture.format === RGBAFormat && texture.type === UnsignedByteType && texture.encoding === sRGBEncoding ) { - info.update( count, mode, 1 ); + uniform.value = ENCODINGS[ LinearEncoding ]; - } + } else { - function renderInstances( start, count, primcount ) { + uniform.value = ENCODINGS[ texture.encoding ]; - if ( primcount === 0 ) return; + } - let extension, methodName; + } - if ( isWebGL2 ) { + _textureToCubeUV( texture, cubeUVRenderTarget ) { - extension = gl; - methodName = 'drawElementsInstanced'; + const renderer = this._renderer; - } else { + const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; + if ( isCubeTexture ) { - if ( extension === null ) { + if ( this._cubemapShader == null ) { - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + this._cubemapShader = _getCubemapShader(); } - } + } else { - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); + if ( this._equirectShader == null ) { - info.update( count, mode, primcount ); + this._equirectShader = _getEquirectShader(); - } + } - // + } - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; + const material = isCubeTexture ? this._cubemapShader : this._equirectShader; + const mesh = new Mesh( _lodPlanes[ 0 ], material ); - } + const uniforms = material.uniforms; - function WebGLInfo( gl ) { + uniforms[ 'envMap' ].value = texture; - const memory = { - geometries: 0, - textures: 0 - }; + if ( ! isCubeTexture ) { - const render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; + uniforms[ 'texelSize' ].value.set( 1.0 / texture.image.width, 1.0 / texture.image.height ); - function update( count, mode, instanceCount ) { + } - render.calls ++; + this._setEncoding( uniforms[ 'inputEncoding' ], texture ); + this._setEncoding( uniforms[ 'outputEncoding' ], cubeUVRenderTarget.texture ); - switch ( mode ) { + _setViewport( cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX ); - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; + renderer.setRenderTarget( cubeUVRenderTarget ); + renderer.render( mesh, _flatCamera ); - case 1: - render.lines += instanceCount * ( count / 2 ); - break; + } - case 3: - render.lines += instanceCount * ( count - 1 ); - break; + _applyPMREM( cubeUVRenderTarget ) { - case 2: - render.lines += instanceCount * count; - break; + const renderer = this._renderer; + const autoClear = renderer.autoClear; + renderer.autoClear = false; - case 0: - render.points += instanceCount * count; - break; + for ( let i = 1; i < TOTAL_LODS; i ++ ) { - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; + const sigma = Math.sqrt( _sigmas[ i ] * _sigmas[ i ] - _sigmas[ i - 1 ] * _sigmas[ i - 1 ] ); - } + const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; - } + this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); - function reset() { + } - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; + renderer.autoClear = autoClear; } - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; - - } + /** + * This is a two-pass Gaussian blur for a cubemap. Normally this is done + * vertically and horizontally, but this breaks down on a cube. Here we apply + * the blur latitudinally (around the poles), and then longitudinally (towards + * the poles) to approximate the orthogonally-separable blur. It is least + * accurate at the poles, but still does a decent job. + */ + _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { - function numericalSort( a, b ) { + const pingPongRenderTarget = this._pingPongRenderTarget; - return a[ 0 ] - b[ 0 ]; + this._halfBlur( + cubeUVRenderTarget, + pingPongRenderTarget, + lodIn, + lodOut, + sigma, + 'latitudinal', + poleAxis ); - } + this._halfBlur( + pingPongRenderTarget, + cubeUVRenderTarget, + lodOut, + lodOut, + sigma, + 'longitudinal', + poleAxis ); - function absNumericalSort( a, b ) { + } - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); + _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { - } + const renderer = this._renderer; + const blurMaterial = this._blurMaterial; - function WebGLMorphtargets( gl ) { + if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - const influencesList = {}; - const morphInfluences = new Float32Array( 8 ); + console.error( + 'blur direction must be either latitudinal or longitudinal!' ); - const workInfluences = []; + } - for ( let i = 0; i < 8; i ++ ) { + // Number of standard deviations at which to cut off the discrete approximation. + const STANDARD_DEVIATIONS = 3; - workInfluences[ i ] = [ i, 0 ]; + const blurMesh = new Mesh( _lodPlanes[ lodOut ], blurMaterial ); + const blurUniforms = blurMaterial.uniforms; - } + const pixels = _sizeLods[ lodIn ] - 1; + const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); + const sigmaPixels = sigmaRadians / radiansPerPixel; + const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; - function update( object, geometry, material, program ) { + if ( samples > MAX_SAMPLES ) { - const objectInfluences = object.morphTargetInfluences; + console.warn( `sigmaRadians, ${ + sigmaRadians}, is too large and will clip, as it requested ${ + samples} samples when the maximum is set to ${MAX_SAMPLES}` ); - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences + } - const length = objectInfluences === undefined ? 0 : objectInfluences.length; + const weights = []; + let sum = 0; - let influences = influencesList[ geometry.id ]; + for ( let i = 0; i < MAX_SAMPLES; ++ i ) { - if ( influences === undefined ) { + const x = i / sigmaPixels; + const weight = Math.exp( - x * x / 2 ); + weights.push( weight ); - // initialise list + if ( i == 0 ) { - influences = []; + sum += weight; - for ( let i = 0; i < length; i ++ ) { + } else if ( i < samples ) { - influences[ i ] = [ i, 0 ]; + sum += 2 * weight; } - influencesList[ geometry.id ] = influences; - } - // Collect influences + for ( let i = 0; i < weights.length; i ++ ) { - for ( let i = 0; i < length; i ++ ) { + weights[ i ] = weights[ i ] / sum; + + } + + blurUniforms[ 'envMap' ].value = targetIn.texture; + blurUniforms[ 'samples' ].value = samples; + blurUniforms[ 'weights' ].value = weights; + blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal'; - const influence = influences[ i ]; + if ( poleAxis ) { - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; + blurUniforms[ 'poleAxis' ].value = poleAxis; } - influences.sort( absNumericalSort ); + blurUniforms[ 'dTheta' ].value = radiansPerPixel; + blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn; - for ( let i = 0; i < 8; i ++ ) { + this._setEncoding( blurUniforms[ 'inputEncoding' ], targetIn.texture ); + this._setEncoding( blurUniforms[ 'outputEncoding' ], targetIn.texture ); - if ( i < length && influences[ i ][ 1 ] ) { + const outputSize = _sizeLods[ lodOut ]; + const x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize ); + const y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) + 2 * outputSize * ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 ); - workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; - workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; + _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); + renderer.setRenderTarget( targetOut ); + renderer.render( blurMesh, _flatCamera ); - } else { + } - workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; - workInfluences[ i ][ 1 ] = 0; + } - } + function _isLDR( texture ) { - } + if ( texture === undefined || texture.type !== UnsignedByteType ) return false; - workInfluences.sort( numericalSort ); + return texture.encoding === LinearEncoding || texture.encoding === sRGBEncoding || texture.encoding === GammaEncoding; - const morphTargets = material.morphTargets && geometry.morphAttributes.position; - const morphNormals = material.morphNormals && geometry.morphAttributes.normal; + } - let morphInfluencesSum = 0; + function _createPlanes() { - for ( let i = 0; i < 8; i ++ ) { + const _lodPlanes = []; + const _sizeLods = []; + const _sigmas = []; - const influence = workInfluences[ i ]; - const index = influence[ 0 ]; - const value = influence[ 1 ]; + let lod = LOD_MAX; - if ( index !== Number.MAX_SAFE_INTEGER && value ) { + for ( let i = 0; i < TOTAL_LODS; i ++ ) { - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { + const sizeLod = Math.pow( 2, lod ); + _sizeLods.push( sizeLod ); + let sigma = 1.0 / sizeLod; - geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); + if ( i > LOD_MAX - LOD_MIN ) { - } + sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ]; - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { + } else if ( i == 0 ) { - geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); + sigma = 0; - } + } - morphInfluences[ i ] = value; - morphInfluencesSum += value; + _sigmas.push( sigma ); - } else { + const texelSize = 1.0 / ( sizeLod - 1 ); + const min = - texelSize / 2; + const max = 1 + texelSize / 2; + const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { + const cubeFaces = 6; + const vertices = 6; + const positionSize = 3; + const uvSize = 2; + const faceIndexSize = 1; - geometry.deleteAttribute( 'morphTarget' + i ); + const position = new Float32Array( positionSize * vertices * cubeFaces ); + const uv = new Float32Array( uvSize * vertices * cubeFaces ); + const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); - } + for ( let face = 0; face < cubeFaces; face ++ ) { - if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { + const x = ( face % 3 ) * 2 / 3 - 1; + const y = face > 2 ? 0 : - 1; + const coordinates = [ + x, y, 0, + x + 2 / 3, y, 0, + x + 2 / 3, y + 1, 0, + x, y, 0, + x + 2 / 3, y + 1, 0, + x, y + 1, 0 + ]; + position.set( coordinates, positionSize * vertices * face ); + uv.set( uv1, uvSize * vertices * face ); + const fill = [ face, face, face, face, face, face ]; + faceIndex.set( fill, faceIndexSize * vertices * face ); - geometry.deleteAttribute( 'morphNormal' + i ); + } - } + const planes = new BufferGeometry(); + planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); + planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); + planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); + _lodPlanes.push( planes ); - morphInfluences[ i ] = 0; + if ( lod > LOD_MIN ) { - } + lod --; } - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; + } + + return { _lodPlanes, _sizeLods, _sigmas }; - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); + } - } + function _createRenderTarget( params ) { - return { + const cubeUVRenderTarget = new WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params ); + cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; + cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; + cubeUVRenderTarget.scissorTest = true; + return cubeUVRenderTarget; - update: update + } - }; + function _setViewport( target, x, y, width, height ) { + + target.viewport.set( x, y, width, height ); + target.scissor.set( x, y, width, height ); } - function WebGLObjects( gl, geometries, attributes, info ) { + function _getBlurShader( maxSamples ) { - let updateMap = new WeakMap(); + const weights = new Float32Array( maxSamples ); + const poleAxis = new Vector3( 0, 1, 0 ); + const shaderMaterial = new RawShaderMaterial( { - function update( object ) { + name: 'SphericalGaussianBlur', - const frame = info.render.frame; + defines: { 'n': maxSamples }, - const geometry = object.geometry; - const buffergeometry = geometries.get( object, geometry ); + uniforms: { + 'envMap': { value: null }, + 'samples': { value: 1 }, + 'weights': { value: weights }, + 'latitudinal': { value: false }, + 'dTheta': { value: 0 }, + 'mipInt': { value: 0 }, + 'poleAxis': { value: poleAxis }, + 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, + 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } + }, - // Update once per frame + vertexShader: _getCommonVertexShader(), - if ( updateMap.get( buffergeometry ) !== frame ) { + fragmentShader: /* glsl */` - geometries.update( buffergeometry ); + precision mediump float; + precision mediump int; - updateMap.set( buffergeometry, frame ); + varying vec3 vOutputDirection; - } + uniform sampler2D envMap; + uniform int samples; + uniform float weights[ n ]; + uniform bool latitudinal; + uniform float dTheta; + uniform float mipInt; + uniform vec3 poleAxis; - if ( object.isInstancedMesh ) { + ${ _getEncodings() } - if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { + #define ENVMAP_TYPE_CUBE_UV + #include - object.addEventListener( 'dispose', onInstancedMeshDispose ); + vec3 getSample( float theta, vec3 axis ) { - } + float cosTheta = cos( theta ); + // Rodrigues' axis-angle rotation + vec3 sampleDirection = vOutputDirection * cosTheta + + cross( axis, vOutputDirection ) * sin( theta ) + + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - attributes.update( object.instanceMatrix, 34962 ); + return bilinearCubeUV( envMap, sampleDirection, mipInt ); - if ( object.instanceColor !== null ) { + } - attributes.update( object.instanceColor, 34962 ); + void main() { - } + vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - } + if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - return buffergeometry; + axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - } + } - function dispose() { + axis = normalize( axis ); - updateMap = new WeakMap(); + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - } + for ( int i = 1; i < n; i++ ) { - function onInstancedMeshDispose( event ) { + if ( i >= samples ) { - const instancedMesh = event.target; + break; - instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); + } - attributes.remove( instancedMesh.instanceMatrix ); + float theta = dTheta * float( i ); + gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); + gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); - if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); + } - } + gl_FragColor = linearToOutputTexel( gl_FragColor ); - return { + } + `, - update: update, - dispose: dispose + blending: NoBlending, + depthTest: false, + depthWrite: false - }; + } ); + + return shaderMaterial; } - function DataTexture2DArray( data = null, width = 1, height = 1, depth = 1 ) { + function _getEquirectShader() { - Texture.call( this, null ); + const texelSize = new Vector2( 1, 1 ); + const shaderMaterial = new RawShaderMaterial( { - this.image = { data, width, height, depth }; + name: 'EquirectangularToCubeUV', - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + uniforms: { + 'envMap': { value: null }, + 'texelSize': { value: texelSize }, + 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, + 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } + }, - this.wrapR = ClampToEdgeWrapping; + vertexShader: _getCommonVertexShader(), - this.generateMipmaps = false; - this.flipY = false; + fragmentShader: /* glsl */` - this.needsUpdate = true; + precision mediump float; + precision mediump int; - } + varying vec3 vOutputDirection; - DataTexture2DArray.prototype = Object.create( Texture.prototype ); - DataTexture2DArray.prototype.constructor = DataTexture2DArray; - DataTexture2DArray.prototype.isDataTexture2DArray = true; + uniform sampler2D envMap; + uniform vec2 texelSize; - function DataTexture3D( data = null, width = 1, height = 1, depth = 1 ) { + ${ _getEncodings() } - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // const texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 + #include - Texture.call( this, null ); + void main() { - this.image = { data, width, height, depth }; + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + vec3 outputDirection = normalize( vOutputDirection ); + vec2 uv = equirectUv( outputDirection ); - this.wrapR = ClampToEdgeWrapping; + vec2 f = fract( uv / texelSize - 0.5 ); + uv -= f * texelSize; + vec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + uv.x += texelSize.x; + vec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + uv.y += texelSize.y; + vec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + uv.x -= texelSize.x; + vec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; - this.generateMipmaps = false; - this.flipY = false; + vec3 tm = mix( tl, tr, f.x ); + vec3 bm = mix( bl, br, f.x ); + gl_FragColor.rgb = mix( tm, bm, f.y ); - this.needsUpdate = true; + gl_FragColor = linearToOutputTexel( gl_FragColor ); + } + `, - } + blending: NoBlending, + depthTest: false, + depthWrite: false - DataTexture3D.prototype = Object.create( Texture.prototype ); - DataTexture3D.prototype.constructor = DataTexture3D; - DataTexture3D.prototype.isDataTexture3D = true; + } ); - /** - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ + return shaderMaterial; - const emptyTexture = new Texture(); - const emptyTexture2dArray = new DataTexture2DArray(); - const emptyTexture3d = new DataTexture3D(); - const emptyCubeTexture = new CubeTexture(); + } - // --- Utilities --- + function _getCubemapShader() { - // Array Caches (provide typed arrays for temporary by size) + const shaderMaterial = new RawShaderMaterial( { - const arrayCacheF32 = []; - const arrayCacheI32 = []; + name: 'CubemapToCubeUV', - // Float32Array caches used for uploading Matrix uniforms + uniforms: { + 'envMap': { value: null }, + 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, + 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } + }, - const mat4array = new Float32Array( 16 ); - const mat3array = new Float32Array( 9 ); - const mat2array = new Float32Array( 4 ); + vertexShader: _getCommonVertexShader(), - // Flattening for arrays of vectors and matrices + fragmentShader: /* glsl */` - function flatten( array, nBlocks, blockSize ) { + precision mediump float; + precision mediump int; - const firstElem = array[ 0 ]; + varying vec3 vOutputDirection; - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 + uniform samplerCube envMap; - const n = nBlocks * blockSize; - let r = arrayCacheF32[ n ]; + ${ _getEncodings() } - if ( r === undefined ) { + void main() { - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb; + gl_FragColor = linearToOutputTexel( gl_FragColor ); - } + } + `, - if ( nBlocks !== 0 ) { + blending: NoBlending, + depthTest: false, + depthWrite: false - firstElem.toArray( r, 0 ); + } ); - for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { + return shaderMaterial; - offset += blockSize; - array[ i ].toArray( r, offset ); + } - } + function _getCommonVertexShader() { - } + return /* glsl */` - return r; + precision mediump float; + precision mediump int; - } + attribute vec3 position; + attribute vec2 uv; + attribute float faceIndex; - function arraysEqual( a, b ) { + varying vec3 vOutputDirection; - if ( a.length !== b.length ) return false; + // RH coordinate system; PMREM face-indexing convention + vec3 getDirection( vec2 uv, float face ) { - for ( let i = 0, l = a.length; i < l; i ++ ) { + uv = 2.0 * uv - 1.0; - if ( a[ i ] !== b[ i ] ) return false; + vec3 direction = vec3( uv, 1.0 ); - } + if ( face == 0.0 ) { - return true; + direction = direction.zyx; // ( 1, v, u ) pos x - } + } else if ( face == 1.0 ) { - function copyArray( a, b ) { + direction = direction.xzy; + direction.xz *= -1.0; // ( -u, 1, -v ) pos y - for ( let i = 0, l = b.length; i < l; i ++ ) { + } else if ( face == 2.0 ) { - a[ i ] = b[ i ]; + direction.x *= -1.0; // ( -u, v, 1 ) pos z - } + } else if ( face == 3.0 ) { - } + direction = direction.zyx; + direction.xz *= -1.0; // ( -1, v, -u ) neg x - // Texture unit allocation + } else if ( face == 4.0 ) { - function allocTexUnits( textures, n ) { + direction = direction.xzy; + direction.xy *= -1.0; // ( -u, -1, v ) neg y - let r = arrayCacheI32[ n ]; + } else if ( face == 5.0 ) { - if ( r === undefined ) { + direction.z *= -1.0; // ( u, v, -1 ) neg z - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; + } - } + return direction; - for ( let i = 0; i !== n; ++ i ) { + } - r[ i ] = textures.allocateTextureUnit(); + void main() { - } + vOutputDirection = getDirection( uv, faceIndex ); + gl_Position = vec4( position, 1.0 ); - return r; + } + `; } - // --- Setters --- - - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. + function _getEncodings() { - // Single scalar + return /* glsl */` - function setValueV1f( gl, v ) { + uniform int inputEncoding; + uniform int outputEncoding; - const cache = this.cache; + #include - if ( cache[ 0 ] === v ) return; + vec4 inputTexelToLinear( vec4 value ) { - gl.uniform1f( this.addr, v ); + if ( inputEncoding == 0 ) { - cache[ 0 ] = v; + return value; - } + } else if ( inputEncoding == 1 ) { - // Single float vector (from flat array or THREE.VectorN) + return sRGBToLinear( value ); - function setValueV2f( gl, v ) { + } else if ( inputEncoding == 2 ) { - const cache = this.cache; + return RGBEToLinear( value ); - if ( v.x !== undefined ) { + } else if ( inputEncoding == 3 ) { - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { + return RGBMToLinear( value, 7.0 ); - gl.uniform2f( this.addr, v.x, v.y ); + } else if ( inputEncoding == 4 ) { - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; + return RGBMToLinear( value, 16.0 ); - } + } else if ( inputEncoding == 5 ) { - } else { + return RGBDToLinear( value, 256.0 ); - if ( arraysEqual( cache, v ) ) return; + } else { - gl.uniform2fv( this.addr, v ); + return GammaToLinear( value, 2.2 ); - copyArray( cache, v ); + } - } + } - } + vec4 linearToOutputTexel( vec4 value ) { - function setValueV3f( gl, v ) { + if ( outputEncoding == 0 ) { - const cache = this.cache; + return value; - if ( v.x !== undefined ) { + } else if ( outputEncoding == 1 ) { - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { + return LinearTosRGB( value ); - gl.uniform3f( this.addr, v.x, v.y, v.z ); + } else if ( outputEncoding == 2 ) { - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; + return LinearToRGBE( value ); - } + } else if ( outputEncoding == 3 ) { - } else if ( v.r !== undefined ) { + return LinearToRGBM( value, 7.0 ); - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { + } else if ( outputEncoding == 4 ) { - gl.uniform3f( this.addr, v.r, v.g, v.b ); + return LinearToRGBM( value, 16.0 ); - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; + } else if ( outputEncoding == 5 ) { - } + return LinearToRGBD( value, 256.0 ); - } else { + } else { - if ( arraysEqual( cache, v ) ) return; + return LinearToGamma( value, 2.2 ); - gl.uniform3fv( this.addr, v ); + } - copyArray( cache, v ); + } - } + vec4 envMapTexelToLinear( vec4 color ) { - } + return inputTexelToLinear( color ); - function setValueV4f( gl, v ) { + } + `; - const cache = this.cache; + } - if ( v.x !== undefined ) { + function WebGLCubeUVMaps( renderer ) { - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { + let cubeUVmaps = new WeakMap(); - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); + let pmremGenerator = null; - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; + function get( texture ) { - } + if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { - } else { + const mapping = texture.mapping; - if ( arraysEqual( cache, v ) ) return; + const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); + const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); - gl.uniform4fv( this.addr, v ); + if ( isEquirectMap || isCubeMap ) { - copyArray( cache, v ); + // equirect/cube map to cubeUV conversion - } + if ( cubeUVmaps.has( texture ) ) { - } + return cubeUVmaps.get( texture ).texture; - // Single matrix (from flat array or MatrixN) + } else { - function setValueM2( gl, v ) { + const image = texture.image; - const cache = this.cache; - const elements = v.elements; + if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { - if ( elements === undefined ) { + const currentRenderTarget = renderer.getRenderTarget(); - if ( arraysEqual( cache, v ) ) return; + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - gl.uniformMatrix2fv( this.addr, false, v ); + const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); + cubeUVmaps.set( texture, renderTarget ); - copyArray( cache, v ); + renderer.setRenderTarget( currentRenderTarget ); - } else { + texture.addEventListener( 'dispose', onTextureDispose ); - if ( arraysEqual( cache, elements ) ) return; + return renderTarget.texture; - mat2array.set( elements ); + } else { - gl.uniformMatrix2fv( this.addr, false, mat2array ); + // image not yet ready. try the conversion next frame - copyArray( cache, elements ); + return null; - } + } - } + } - function setValueM3( gl, v ) { + } - const cache = this.cache; - const elements = v.elements; + } - if ( elements === undefined ) { + return texture; - if ( arraysEqual( cache, v ) ) return; + } - gl.uniformMatrix3fv( this.addr, false, v ); + function isCubeTextureComplete( image ) { - copyArray( cache, v ); + let count = 0; + const length = 6; - } else { + for ( let i = 0; i < length; i ++ ) { - if ( arraysEqual( cache, elements ) ) return; + if ( image[ i ] !== undefined ) count ++; - mat3array.set( elements ); + } - gl.uniformMatrix3fv( this.addr, false, mat3array ); + return count === length; - copyArray( cache, elements ); } - } - - function setValueM4( gl, v ) { + function onTextureDispose( event ) { - const cache = this.cache; - const elements = v.elements; + const texture = event.target; - if ( elements === undefined ) { + texture.removeEventListener( 'dispose', onTextureDispose ); - if ( arraysEqual( cache, v ) ) return; + const cubemapUV = cubeUVmaps.get( texture ); - gl.uniformMatrix4fv( this.addr, false, v ); + if ( cubemapUV !== undefined ) { - copyArray( cache, v ); + cubeUVmaps.delete( texture ); + cubemapUV.dispose(); - } else { + } - if ( arraysEqual( cache, elements ) ) return; + } - mat4array.set( elements ); + function dispose() { - gl.uniformMatrix4fv( this.addr, false, mat4array ); + cubeUVmaps = new WeakMap(); - copyArray( cache, elements ); + if ( pmremGenerator !== null ) { - } + pmremGenerator.dispose(); + pmremGenerator = null; - } + } - // Single texture (2D / Cube) + } - function setValueT1( gl, v, textures ) { + return { + get: get, + dispose: dispose + }; - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + } - if ( cache[ 0 ] !== unit ) { + function WebGLExtensions( gl ) { - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + const extensions = {}; - } + function getExtension( name ) { - textures.safeSetTexture2D( v || emptyTexture, unit ); + if ( extensions[ name ] !== undefined ) { - } + return extensions[ name ]; - function setValueT2DArray1( gl, v, textures ) { + } - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + let extension; - if ( cache[ 0 ] !== unit ) { + switch ( name ) { - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + case 'WEBGL_depth_texture': + extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); + break; - } + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; - } + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - function setValueT3D1( gl, v, textures ) { + default: + extension = gl.getExtension( name ); - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + } - if ( cache[ 0 ] !== unit ) { + extensions[ name ] = extension; - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + return extension; } - textures.setTexture3D( v || emptyTexture3d, unit ); - - } + return { - function setValueT6( gl, v, textures ) { + has: function ( name ) { - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + return getExtension( name ) !== null; - if ( cache[ 0 ] !== unit ) { + }, - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + init: function ( capabilities ) { - } + if ( capabilities.isWebGL2 ) { - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); + getExtension( 'EXT_color_buffer_float' ); - } + } else { - // Integer / Boolean vectors or arrays thereof (always flat arrays) + getExtension( 'WEBGL_depth_texture' ); + getExtension( 'OES_texture_float' ); + getExtension( 'OES_texture_half_float' ); + getExtension( 'OES_texture_half_float_linear' ); + getExtension( 'OES_standard_derivatives' ); + getExtension( 'OES_element_index_uint' ); + getExtension( 'OES_vertex_array_object' ); + getExtension( 'ANGLE_instanced_arrays' ); - function setValueV1i( gl, v ) { + } - const cache = this.cache; + getExtension( 'OES_texture_float_linear' ); + getExtension( 'EXT_color_buffer_half_float' ); - if ( cache[ 0 ] === v ) return; + }, - gl.uniform1i( this.addr, v ); + get: function ( name ) { - cache[ 0 ] = v; + const extension = getExtension( name ); - } + if ( extension === null ) { - function setValueV2i( gl, v ) { + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - const cache = this.cache; + } - if ( arraysEqual( cache, v ) ) return; + return extension; - gl.uniform2iv( this.addr, v ); + } - copyArray( cache, v ); + }; } - function setValueV3i( gl, v ) { - - const cache = this.cache; + function WebGLGeometries( gl, attributes, info, bindingStates ) { - if ( arraysEqual( cache, v ) ) return; + const geometries = {}; + const wireframeAttributes = new WeakMap(); - gl.uniform3iv( this.addr, v ); + function onGeometryDispose( event ) { - copyArray( cache, v ); + const geometry = event.target; - } + if ( geometry.index !== null ) { - function setValueV4i( gl, v ) { + attributes.remove( geometry.index ); - const cache = this.cache; + } - if ( arraysEqual( cache, v ) ) return; + for ( const name in geometry.attributes ) { - gl.uniform4iv( this.addr, v ); + attributes.remove( geometry.attributes[ name ] ); - copyArray( cache, v ); + } - } + geometry.removeEventListener( 'dispose', onGeometryDispose ); - // uint + delete geometries[ geometry.id ]; - function setValueV1ui( gl, v ) { + const attribute = wireframeAttributes.get( geometry ); - const cache = this.cache; + if ( attribute ) { - if ( cache[ 0 ] === v ) return; + attributes.remove( attribute ); + wireframeAttributes.delete( geometry ); - gl.uniform1ui( this.addr, v ); + } - cache[ 0 ] = v; + bindingStates.releaseStatesOfGeometry( geometry ); - } + if ( geometry.isInstancedBufferGeometry === true ) { - // Helper to pick the right setter for the singular case + delete geometry._maxInstanceCount; - function getSingularSetter( type ) { + } - switch ( type ) { + // - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 + info.memory.geometries --; - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 + } - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 + function get( object, geometry ) { - case 0x1405: return setValueV1ui; // UINT + if ( geometries[ geometry.id ] === true ) return geometry; - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; + geometry.addEventListener( 'dispose', onGeometryDispose ); - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; + geometries[ geometry.id ] = true; - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; + info.memory.geometries ++; - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; + return geometry; } - } + function update( geometry ) { - // Array of scalars - function setValueV1fArray( gl, v ) { + const geometryAttributes = geometry.attributes; - gl.uniform1fv( this.addr, v ); + // Updating index buffer in VAO now. See WebGLBindingStates. - } + for ( const name in geometryAttributes ) { - // Integer / Boolean vectors or arrays thereof (always flat arrays) - function setValueV1iArray( gl, v ) { + attributes.update( geometryAttributes[ name ], 34962 ); - gl.uniform1iv( this.addr, v ); + } - } + // morph targets - function setValueV2iArray( gl, v ) { + const morphAttributes = geometry.morphAttributes; - gl.uniform2iv( this.addr, v ); + for ( const name in morphAttributes ) { - } + const array = morphAttributes[ name ]; - function setValueV3iArray( gl, v ) { + for ( let i = 0, l = array.length; i < l; i ++ ) { - gl.uniform3iv( this.addr, v ); + attributes.update( array[ i ], 34962 ); - } + } - function setValueV4iArray( gl, v ) { + } - gl.uniform4iv( this.addr, v ); + } - } + function updateWireframeAttribute( geometry ) { + const indices = []; - // Array of vectors (flat or from THREE classes) + const geometryIndex = geometry.index; + const geometryPosition = geometry.attributes.position; + let version = 0; - function setValueV2fArray( gl, v ) { + if ( geometryIndex !== null ) { - const data = flatten( v, this.size, 2 ); + const array = geometryIndex.array; + version = geometryIndex.version; - gl.uniform2fv( this.addr, data ); + for ( let i = 0, l = array.length; i < l; i += 3 ) { - } + const a = array[ i + 0 ]; + const b = array[ i + 1 ]; + const c = array[ i + 2 ]; - function setValueV3fArray( gl, v ) { + indices.push( a, b, b, c, c, a ); - const data = flatten( v, this.size, 3 ); + } - gl.uniform3fv( this.addr, data ); + } else { - } + const array = geometryPosition.array; + version = geometryPosition.version; - function setValueV4fArray( gl, v ) { + for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - const data = flatten( v, this.size, 4 ); + const a = i + 0; + const b = i + 1; + const c = i + 2; - gl.uniform4fv( this.addr, data ); + indices.push( a, b, b, c, c, a ); - } + } - // Array of matrices (flat or from THREE clases) + } - function setValueM2Array( gl, v ) { + const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); + attribute.version = version; - const data = flatten( v, this.size, 4 ); + // Updating index buffer in VAO now. See WebGLBindingStates - gl.uniformMatrix2fv( this.addr, false, data ); + // - } + const previousAttribute = wireframeAttributes.get( geometry ); - function setValueM3Array( gl, v ) { + if ( previousAttribute ) attributes.remove( previousAttribute ); - const data = flatten( v, this.size, 9 ); + // - gl.uniformMatrix3fv( this.addr, false, data ); + wireframeAttributes.set( geometry, attribute ); - } - - function setValueM4Array( gl, v ) { + } - const data = flatten( v, this.size, 16 ); + function getWireframeAttribute( geometry ) { - gl.uniformMatrix4fv( this.addr, false, data ); + const currentAttribute = wireframeAttributes.get( geometry ); - } + if ( currentAttribute ) { - // Array of textures (2D / Cube) + const geometryIndex = geometry.index; - function setValueT1Array( gl, v, textures ) { + if ( geometryIndex !== null ) { - const n = v.length; + // if the attribute is obsolete, create a new one - const units = allocTexUnits( textures, n ); + if ( currentAttribute.version < geometryIndex.version ) { - gl.uniform1iv( this.addr, units ); + updateWireframeAttribute( geometry ); - for ( let i = 0; i !== n; ++ i ) { + } - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); + } - } + } else { - } + updateWireframeAttribute( geometry ); - function setValueT6Array( gl, v, textures ) { + } - const n = v.length; + return wireframeAttributes.get( geometry ); - const units = allocTexUnits( textures, n ); + } - gl.uniform1iv( this.addr, units ); + return { - for ( let i = 0; i !== n; ++ i ) { + get: get, + update: update, - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); + getWireframeAttribute: getWireframeAttribute - } + }; } - // Helper to pick the right setter for a pure (bottom-level) array + function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - function getPureArraySetter( type ) { + const isWebGL2 = capabilities.isWebGL2; - switch ( type ) { + let mode; - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 + function setMode( value ) { - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 + mode = value; - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 + } - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; + let type, bytesPerElement; - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; + function setIndex( value ) { + + type = value.type; + bytesPerElement = value.bytesPerElement; } - } + function render( start, count ) { - // --- Uniform Classes --- + gl.drawElements( mode, count, type, start * bytesPerElement ); - function SingleUniform( id, activeInfo, addr ) { + info.update( count, mode, 1 ); - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); + } - // this.path = activeInfo.name; // DEBUG + function renderInstances( start, count, primcount ) { - } + if ( primcount === 0 ) return; - function PureArrayUniform( id, activeInfo, addr ) { + let extension, methodName; - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); + if ( isWebGL2 ) { - // this.path = activeInfo.name; // DEBUG + extension = gl; + methodName = 'drawElementsInstanced'; - } + } else { - PureArrayUniform.prototype.updateCache = function ( data ) { + extension = extensions.get( 'ANGLE_instanced_arrays' ); + methodName = 'drawElementsInstancedANGLE'; - const cache = this.cache; + if ( extension === null ) { - if ( data instanceof Float32Array && cache.length !== data.length ) { + console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - this.cache = new Float32Array( data.length ); + } - } + } - copyArray( cache, data ); + extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - }; + info.update( count, mode, primcount ); - function StructuredUniform( id ) { + } - this.id = id; + // - this.seq = []; - this.map = {}; + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; } - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - - const seq = this.seq; + function WebGLInfo( gl ) { - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + const memory = { + geometries: 0, + textures: 0 + }; - const u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); + const render = { + frame: 0, + calls: 0, + triangles: 0, + points: 0, + lines: 0 + }; - } + function update( count, mode, instanceCount ) { - }; + render.calls ++; - // --- Top-level --- + switch ( mode ) { - // Parser - builds up the property tree from the path strings + case 4: + render.triangles += instanceCount * ( count / 3 ); + break; - const RePathPart = /(\w+)(\])?(\[|\.)?/g; + case 1: + render.lines += instanceCount * ( count / 2 ); + break; - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. + case 3: + render.lines += instanceCount * ( count - 1 ); + break; - function addUniform( container, uniformObject ) { + case 2: + render.lines += instanceCount * count; + break; - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; + case 0: + render.points += instanceCount * count; + break; - } + default: + console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); + break; - function parseUniform( activeInfo, addr, container ) { + } - const path = activeInfo.name, - pathLength = path.length; + } - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; + function reset() { - while ( true ) { + render.frame ++; + render.calls = 0; + render.triangles = 0; + render.points = 0; + render.lines = 0; - const match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex; + } - let id = match[ 1 ]; - const idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; + return { + memory: memory, + render: render, + programs: null, + autoReset: true, + reset: reset, + update: update + }; - if ( idIsIndex ) id = id | 0; // convert to integer + } - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { + class DataTexture2DArray extends Texture { - // bare name or "pure" bottom-level array "[0]" suffix + constructor( data = null, width = 1, height = 1, depth = 1 ) { - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); + super( null ); - break; + this.image = { data, width, height, depth }; - } else { + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - // step into inner node / create it in case it doesn't exist + this.wrapR = ClampToEdgeWrapping; - const map = container.map; - let next = map[ id ]; + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - if ( next === undefined ) { + this.needsUpdate = true; - next = new StructuredUniform( id ); - addUniform( container, next ); + } - } + } - container = next; + DataTexture2DArray.prototype.isDataTexture2DArray = true; - } + function numericalSort( a, b ) { - } + return a[ 0 ] - b[ 0 ]; } - // Root Container - - function WebGLUniforms( gl, program ) { + function absNumericalSort( a, b ) { - this.seq = []; - this.map = {}; + return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); - const n = gl.getProgramParameter( program, 35718 ); + } - for ( let i = 0; i < n; ++ i ) { + function denormalize( morph, attribute ) { - const info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); + let denominator = 1; + const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; - parseUniform( info, addr, this ); + if ( array instanceof Int8Array ) denominator = 127; + else if ( array instanceof Int16Array ) denominator = 32767; + else if ( array instanceof Int32Array ) denominator = 2147483647; + else console.error( 'THREE.WebGLMorphtargets: Unsupported morph attribute data type: ', array ); - } + morph.divideScalar( denominator ); } - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { + function WebGLMorphtargets( gl, capabilities, textures ) { - const u = this.map[ name ]; + const influencesList = {}; + const morphInfluences = new Float32Array( 8 ); + const morphTextures = new WeakMap(); + const morph = new Vector3(); - if ( u !== undefined ) u.setValue( gl, value, textures ); + const workInfluences = []; - }; + for ( let i = 0; i < 8; i ++ ) { - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { + workInfluences[ i ] = [ i, 0 ]; - const v = object[ name ]; + } - if ( v !== undefined ) this.setValue( gl, name, v ); + function update( object, geometry, material, program ) { - }; + const objectInfluences = object.morphTargetInfluences; + if ( capabilities.isWebGL2 === true ) { - // Static interface + // instead of using attributes, the WebGL 2 code path encodes morph targets + // into an array of data textures. Each layer represents a single morph target. - WebGLUniforms.upload = function ( gl, seq, values, textures ) { + const numberOfMorphTargets = geometry.morphAttributes.position.length; - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + let entry = morphTextures.get( geometry ); - const u = seq[ i ], - v = values[ u.id ]; + if ( entry === undefined || entry.count !== numberOfMorphTargets ) { - if ( v.needsUpdate !== false ) { + if ( entry !== undefined ) entry.texture.dispose(); - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); + const hasMorphNormals = geometry.morphAttributes.normal !== undefined; - } + const morphTargets = geometry.morphAttributes.position; + const morphNormals = geometry.morphAttributes.normal || []; - } + const numberOfVertices = geometry.attributes.position.count; + const numberOfVertexData = ( hasMorphNormals === true ) ? 2 : 1; // (v,n) vs. (v) - }; + let width = numberOfVertices * numberOfVertexData; + let height = 1; - WebGLUniforms.seqWithValue = function ( seq, values ) { + if ( width > capabilities.maxTextureSize ) { - const r = []; + height = Math.ceil( width / capabilities.maxTextureSize ); + width = capabilities.maxTextureSize; - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + } - const u = seq[ i ]; - if ( u.id in values ) r.push( u ); + const buffer = new Float32Array( width * height * 4 * numberOfMorphTargets ); - } + const texture = new DataTexture2DArray( buffer, width, height, numberOfMorphTargets ); + texture.format = RGBAFormat; // using RGBA since RGB might be emulated (and is thus slower) + texture.type = FloatType; - return r; + // fill buffer - }; + const vertexDataStride = numberOfVertexData * 4; - function WebGLShader( gl, type, string ) { + for ( let i = 0; i < numberOfMorphTargets; i ++ ) { - const shader = gl.createShader( type ); + const morphTarget = morphTargets[ i ]; + const morphNormal = morphNormals[ i ]; - gl.shaderSource( shader, string ); - gl.compileShader( shader ); + const offset = width * height * 4 * i; - return shader; + for ( let j = 0; j < morphTarget.count; j ++ ) { - } + morph.fromBufferAttribute( morphTarget, j ); - let programIdCount = 0; + if ( morphTarget.normalized === true ) denormalize( morph, morphTarget ); - function addLineNumbers( string ) { + const stride = j * vertexDataStride; - const lines = string.split( '\n' ); + buffer[ offset + stride + 0 ] = morph.x; + buffer[ offset + stride + 1 ] = morph.y; + buffer[ offset + stride + 2 ] = morph.z; + buffer[ offset + stride + 3 ] = 0; - for ( let i = 0; i < lines.length; i ++ ) { + if ( hasMorphNormals === true ) { - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + morph.fromBufferAttribute( morphNormal, j ); - } + if ( morphNormal.normalized === true ) denormalize( morph, morphNormal ); - return lines.join( '\n' ); + buffer[ offset + stride + 4 ] = morph.x; + buffer[ offset + stride + 5 ] = morph.y; + buffer[ offset + stride + 6 ] = morph.z; + buffer[ offset + stride + 7 ] = 0; - } + } - function getEncodingComponents( encoding ) { + } - switch ( encoding ) { + } - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); - return [ 'Linear', '( value )' ]; + entry = { + count: numberOfMorphTargets, + texture: texture, + size: new Vector2( width, height ) + }; - } + morphTextures.set( geometry, entry ); - } + } - function getShaderErrors( gl, shader, type ) { + // - const status = gl.getShaderParameter( shader, 35713 ); - const log = gl.getShaderInfoLog( shader ).trim(); + let morphInfluencesSum = 0; - if ( status && log === '' ) return ''; + for ( let i = 0; i < objectInfluences.length; i ++ ) { - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + morphInfluencesSum += objectInfluences[ i ]; - const source = gl.getShaderSource( shader ); + } - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - } + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences ); - function getTexelDecodingFunction( functionName, encoding ) { + program.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures ); + program.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size ); - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - } + } else { - function getTexelEncodingFunction( functionName, encoding ) { + // When object doesn't have morph target influences defined, we treat it as a 0-length array + // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; + const length = objectInfluences === undefined ? 0 : objectInfluences.length; - } + let influences = influencesList[ geometry.id ]; - function getToneMappingFunction( functionName, toneMapping ) { + if ( influences === undefined || influences.length !== length ) { - let toneMappingName; + // initialise list - switch ( toneMapping ) { + influences = []; - case LinearToneMapping: - toneMappingName = 'Linear'; - break; + for ( let i = 0; i < length; i ++ ) { - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; + influences[ i ] = [ i, 0 ]; - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; + } - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; + influencesList[ geometry.id ] = influences; - case CustomToneMapping: - toneMappingName = 'Custom'; - break; + } - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; + // Collect influences - } + for ( let i = 0; i < length; i ++ ) { - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + const influence = influences[ i ]; - } + influence[ 0 ] = i; + influence[ 1 ] = objectInfluences[ i ]; - function generateExtensions( parameters ) { + } - const chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; + influences.sort( absNumericalSort ); - return chunks.filter( filterEmptyLine ).join( '\n' ); + for ( let i = 0; i < 8; i ++ ) { - } + if ( i < length && influences[ i ][ 1 ] ) { - function generateDefines( defines ) { + workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; + workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; - const chunks = []; + } else { - for ( const name in defines ) { + workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; + workInfluences[ i ][ 1 ] = 0; - const value = defines[ name ]; + } - if ( value === false ) continue; + } - chunks.push( '#define ' + name + ' ' + value ); + workInfluences.sort( numericalSort ); - } + const morphTargets = geometry.morphAttributes.position; + const morphNormals = geometry.morphAttributes.normal; - return chunks.join( '\n' ); + let morphInfluencesSum = 0; - } + for ( let i = 0; i < 8; i ++ ) { - function fetchAttributeLocations( gl, program ) { + const influence = workInfluences[ i ]; + const index = influence[ 0 ]; + const value = influence[ 1 ]; - const attributes = {}; + if ( index !== Number.MAX_SAFE_INTEGER && value ) { - const n = gl.getProgramParameter( program, 35721 ); + if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - for ( let i = 0; i < n; i ++ ) { + geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); - const info = gl.getActiveAttrib( program, i ); - const name = info.name; + } - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); + if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - attributes[ name ] = gl.getAttribLocation( program, name ); + geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); - } + } - return attributes; + morphInfluences[ i ] = value; + morphInfluencesSum += value; - } + } else { - function filterEmptyLine( string ) { + if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { - return string !== ''; + geometry.deleteAttribute( 'morphTarget' + i ); - } + } - function replaceLightNums( string, parameters ) { + if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); + geometry.deleteAttribute( 'morphNormal' + i ); - } + } - function replaceClippingPlaneNums( string, parameters ) { + morphInfluences[ i ] = 0; - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); + } - } + } - // Resolve Includes + // GLSL shader uses formula baseinfluence * base + sum(target * influence) + // This allows us to switch between absolute morphs and relative morphs without changing shader code + // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - function resolveIncludes( string ) { + } - return string.replace( includePattern, includeReplacer ); + } - } + return { - function includeReplacer( match, include ) { + update: update - const string = ShaderChunk[ include ]; + }; - if ( string === undefined ) { + } - throw new Error( 'Can not resolve #include <' + include + '>' ); + function WebGLObjects( gl, geometries, attributes, info ) { - } + let updateMap = new WeakMap(); - return resolveIncludes( string ); + function update( object ) { - } + const frame = info.render.frame; - // Unroll Loops + const geometry = object.geometry; + const buffergeometry = geometries.get( object, geometry ); - const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; + // Update once per frame - function unrollLoops( string ) { + if ( updateMap.get( buffergeometry ) !== frame ) { - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); + geometries.update( buffergeometry ); - } + updateMap.set( buffergeometry, frame ); - function deprecatedLoopReplacer( match, start, end, snippet ) { + } - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); + if ( object.isInstancedMesh ) { - } + if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { - function loopReplacer( match, start, end, snippet ) { + object.addEventListener( 'dispose', onInstancedMeshDispose ); - let string = ''; + } - for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { + attributes.update( object.instanceMatrix, 34962 ); - string += snippet - .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); + if ( object.instanceColor !== null ) { - } + attributes.update( object.instanceColor, 34962 ); - return string; + } - } + } - // + return buffergeometry; - function generatePrecision( parameters ) { + } - let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; + function dispose() { - if ( parameters.precision === 'highp' ) { + updateMap = new WeakMap(); - precisionstring += '\n#define HIGH_PRECISION'; + } - } else if ( parameters.precision === 'mediump' ) { + function onInstancedMeshDispose( event ) { - precisionstring += '\n#define MEDIUM_PRECISION'; + const instancedMesh = event.target; - } else if ( parameters.precision === 'lowp' ) { + instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); - precisionstring += '\n#define LOW_PRECISION'; + attributes.remove( instancedMesh.instanceMatrix ); - } + if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); - return precisionstring; + } + + return { + + update: update, + dispose: dispose + + }; } - function generateShadowMapTypeDefine( parameters ) { + class DataTexture3D extends Texture { - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + constructor( data = null, width = 1, height = 1, depth = 1 ) { - if ( parameters.shadowMapType === PCFShadowMap ) { + // We're going to add .setXXX() methods for setting properties later. + // Users can still set in DataTexture3D directly. + // + // const texture = new THREE.DataTexture3D( data, width, height, depth ); + // texture.anisotropy = 16; + // + // See #14839 - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + super( null ); - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { + this.image = { data, width, height, depth }; - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - } else if ( parameters.shadowMapType === VSMShadowMap ) { + this.wrapR = ClampToEdgeWrapping; - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - } + this.needsUpdate = true; - return shadowMapTypeDefine; + } } - function generateEnvMapTypeDefine( parameters ) { + DataTexture3D.prototype.isDataTexture3D = true; - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + /** + * Uniforms of a program. + * Those form a tree structure with a special top-level container for the root, + * which you get by calling 'new WebGLUniforms( gl, program )'. + * + * + * Properties of inner nodes including the top-level container: + * + * .seq - array of nested uniforms + * .map - nested uniforms by name + * + * + * Methods of all nodes except the top-level container: + * + * .setValue( gl, value, [textures] ) + * + * uploads a uniform value(s) + * the 'textures' parameter is needed for sampler uniforms + * + * + * Static methods of the top-level container (textures factorizations): + * + * .upload( gl, seq, values, textures ) + * + * sets uniforms in 'seq' to 'values[id].value' + * + * .seqWithValue( seq, values ) : filteredSeq + * + * filters 'seq' entries with corresponding entry in values + * + * + * Methods of the top-level container (textures factorizations): + * + * .setValue( gl, name, value, textures ) + * + * sets uniform with name 'name' to 'value' + * + * .setOptional( gl, obj, prop ) + * + * like .set for an optional property of the object + * + */ - if ( parameters.envMap ) { + const emptyTexture = new Texture(); + const emptyTexture2dArray = new DataTexture2DArray(); + const emptyTexture3d = new DataTexture3D(); + const emptyCubeTexture = new CubeTexture(); - switch ( parameters.envMapMode ) { + // --- Utilities --- - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; + // Array Caches (provide typed arrays for temporary by size) - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; + const arrayCacheF32 = []; + const arrayCacheI32 = []; - } + // Float32Array caches used for uploading Matrix uniforms - } + const mat4array = new Float32Array( 16 ); + const mat3array = new Float32Array( 9 ); + const mat2array = new Float32Array( 4 ); - return envMapTypeDefine; + // Flattening for arrays of vectors and matrices - } + function flatten( array, nBlocks, blockSize ) { - function generateEnvMapModeDefine( parameters ) { + const firstElem = array[ 0 ]; - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + if ( firstElem <= 0 || firstElem > 0 ) return array; + // unoptimized: ! isNaN( firstElem ) + // see http://jacksondunstan.com/articles/983 - if ( parameters.envMap ) { + const n = nBlocks * blockSize; + let r = arrayCacheF32[ n ]; - switch ( parameters.envMapMode ) { + if ( r === undefined ) { - case CubeRefractionMapping: - case CubeUVRefractionMapping: + r = new Float32Array( n ); + arrayCacheF32[ n ] = r; - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; + } - } + if ( nBlocks !== 0 ) { - } + firstElem.toArray( r, 0 ); - return envMapModeDefine; + for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { - } + offset += blockSize; + array[ i ].toArray( r, offset ); - function generateEnvMapBlendingDefine( parameters ) { + } - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; + } - if ( parameters.envMap ) { + return r; - switch ( parameters.combine ) { + } - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; + function arraysEqual( a, b ) { - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; + if ( a.length !== b.length ) return false; - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; + for ( let i = 0, l = a.length; i < l; i ++ ) { - } + if ( a[ i ] !== b[ i ] ) return false; } - return envMapBlendingDefine; + return true; } - function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { + function copyArray( a, b ) { - const gl = renderer.getContext(); + for ( let i = 0, l = b.length; i < l; i ++ ) { - const defines = parameters.defines; + a[ i ] = b[ i ]; - let vertexShader = parameters.vertexShader; - let fragmentShader = parameters.fragmentShader; + } - const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - const envMapModeDefine = generateEnvMapModeDefine( parameters ); - const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); + } + // Texture unit allocation - const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + function allocTexUnits( textures, n ) { - const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); + let r = arrayCacheI32[ n ]; - const customDefines = generateDefines( defines ); + if ( r === undefined ) { - const program = gl.createProgram(); + r = new Int32Array( n ); + arrayCacheI32[ n ] = r; - let prefixVertex, prefixFragment; - let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; + } - if ( parameters.isRawShaderMaterial ) { + for ( let i = 0; i !== n; ++ i ) { - prefixVertex = [ + r[ i ] = textures.allocateTextureUnit(); - customDefines + } - ].filter( filterEmptyLine ).join( '\n' ); + return r; - if ( prefixVertex.length > 0 ) { + } - prefixVertex += '\n'; + // --- Setters --- - } + // Note: Defining these methods externally, because they come in a bunch + // and this way their names minify. - prefixFragment = [ + // Single scalar - customExtensions, - customDefines + function setValueV1f( gl, v ) { - ].filter( filterEmptyLine ).join( '\n' ); + const cache = this.cache; - if ( prefixFragment.length > 0 ) { + if ( cache[ 0 ] === v ) return; - prefixFragment += '\n'; + gl.uniform1f( this.addr, v ); - } + cache[ 0 ] = v; - } else { + } - prefixVertex = [ + // Single float vector (from flat array or THREE.VectorN) - generatePrecision( parameters ), + function setValueV2f( gl, v ) { - '#define SHADER_NAME ' + parameters.shaderName, + const cache = this.cache; - customDefines, + if ( v.x !== undefined ) { - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + gl.uniform2f( this.addr, v.x, v.y ); - '#define GAMMA_FACTOR ' + gammaFactorDefine, + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', + } - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', + } else { - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + if ( arraysEqual( cache, v ) ) return; - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', + gl.uniform2fv( this.addr, v ); - parameters.flatShading ? '#define FLAT_SHADED' : '', + copyArray( cache, v ); - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + } - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + } - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + function setValueV3f( gl, v ) { - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', + const cache = this.cache; - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + if ( v.x !== undefined ) { - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - '#ifdef USE_INSTANCING', + gl.uniform3f( this.addr, v.x, v.y, v.z ); - ' attribute mat4 instanceMatrix;', + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; - '#endif', + } - '#ifdef USE_INSTANCING_COLOR', + } else if ( v.r !== undefined ) { - ' attribute vec3 instanceColor;', + if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - '#endif', + gl.uniform3f( this.addr, v.r, v.g, v.b ); - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', + cache[ 0 ] = v.r; + cache[ 1 ] = v.g; + cache[ 2 ] = v.b; - '#ifdef USE_TANGENT', + } - ' attribute vec4 tangent;', + } else { - '#endif', + if ( arraysEqual( cache, v ) ) return; - '#ifdef USE_COLOR', + gl.uniform3fv( this.addr, v ); - ' attribute vec3 color;', + copyArray( cache, v ); - '#endif', + } - '#ifdef USE_MORPHTARGETS', + } - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', + function setValueV4f( gl, v ) { - ' #ifdef USE_MORPHNORMALS', + const cache = this.cache; - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', + if ( v.x !== undefined ) { - ' #else', + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', + gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - ' #endif', + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; - '#endif', + } - '#ifdef USE_SKINNING', + } else { - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', + if ( arraysEqual( cache, v ) ) return; - '#endif', + gl.uniform4fv( this.addr, v ); - '\n' + copyArray( cache, v ); - ].filter( filterEmptyLine ).join( '\n' ); + } - prefixFragment = [ + } - customExtensions, + // Single matrix (from flat array or THREE.MatrixN) - generatePrecision( parameters ), + function setValueM2( gl, v ) { - '#define SHADER_NAME ' + parameters.shaderName, + const cache = this.cache; + const elements = v.elements; - customDefines, + if ( elements === undefined ) { - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer + if ( arraysEqual( cache, v ) ) return; - '#define GAMMA_FACTOR ' + gammaFactorDefine, + gl.uniformMatrix2fv( this.addr, false, v ); - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', + copyArray( cache, v ); - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + } else { - parameters.sheen ? '#define USE_SHEEN' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + if ( arraysEqual( cache, elements ) ) return; - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', + mat2array.set( elements ); - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', + gl.uniformMatrix2fv( this.addr, false, mat2array ); - parameters.flatShading ? '#define FLAT_SHADED' : '', + copyArray( cache, elements ); - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + } - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + } - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', + function setValueM3( gl, v ) { - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', + const cache = this.cache; + const elements = v.elements; - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + if ( elements === undefined ) { - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', + if ( arraysEqual( cache, v ) ) return; - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', + gl.uniformMatrix3fv( this.addr, false, v ); - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', + copyArray( cache, v ); - parameters.dithering ? '#define DITHERING' : '', + } else { - ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), + if ( arraysEqual( cache, elements ) ) return; - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', + mat3array.set( elements ); - '\n' + gl.uniformMatrix3fv( this.addr, false, mat3array ); - ].filter( filterEmptyLine ).join( '\n' ); + copyArray( cache, elements ); } - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); + } - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); + function setValueM4( gl, v ) { - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); + const cache = this.cache; + const elements = v.elements; - if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { + if ( elements === undefined ) { - // GLSL 3.0 conversion for built-in materials and ShaderMaterial + if ( arraysEqual( cache, v ) ) return; - versionString = '#version 300 es\n'; + gl.uniformMatrix4fv( this.addr, false, v ); - prefixVertex = [ - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; + copyArray( cache, v ); - prefixFragment = [ - '#define varying in', - ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', - ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; + } else { - } + if ( arraysEqual( cache, elements ) ) return; - const vertexGlsl = versionString + prefixVertex + vertexShader; - const fragmentGlsl = versionString + prefixFragment + fragmentShader; + mat4array.set( elements ); - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); + gl.uniformMatrix4fv( this.addr, false, mat4array ); - const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); + copyArray( cache, elements ); - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); + } - // Force a particular attribute to index 0. + } - if ( parameters.index0AttributeName !== undefined ) { + // Single integer / boolean - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); + function setValueV1i( gl, v ) { - } else if ( parameters.morphTargets === true ) { + const cache = this.cache; - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); + if ( cache[ 0 ] === v ) return; - } + gl.uniform1i( this.addr, v ); - gl.linkProgram( program ); + cache[ 0 ] = v; - // check for link errors - if ( renderer.debug.checkShaderErrors ) { + } - const programLog = gl.getProgramInfoLog( program ).trim(); - const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); + // Single integer / boolean vector (from flat array) - let runnable = true; - let haveDiagnostics = true; + function setValueV2i( gl, v ) { - if ( gl.getProgramParameter( program, 35714 ) === false ) { + const cache = this.cache; - runnable = false; + if ( arraysEqual( cache, v ) ) return; - const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); + gl.uniform2iv( this.addr, v ); - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); + copyArray( cache, v ); - } else if ( programLog !== '' ) { + } - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + function setValueV3i( gl, v ) { - } else if ( vertexLog === '' || fragmentLog === '' ) { + const cache = this.cache; - haveDiagnostics = false; + if ( arraysEqual( cache, v ) ) return; - } + gl.uniform3iv( this.addr, v ); - if ( haveDiagnostics ) { + copyArray( cache, v ); - this.diagnostics = { + } - runnable: runnable, + function setValueV4i( gl, v ) { - programLog: programLog, + const cache = this.cache; - vertexShader: { + if ( arraysEqual( cache, v ) ) return; - log: vertexLog, - prefix: prefixVertex + gl.uniform4iv( this.addr, v ); - }, + copyArray( cache, v ); - fragmentShader: { + } - log: fragmentLog, - prefix: prefixFragment + // Single unsigned integer - } + function setValueV1ui( gl, v ) { - }; + const cache = this.cache; - } + if ( cache[ 0 ] === v ) return; - } + gl.uniform1ui( this.addr, v ); - // Clean up + cache[ 0 ] = v; - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); + } - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); + // Single unsigned integer vector (from flat array) - // set up caching for uniform locations + function setValueV2ui( gl, v ) { - let cachedUniforms; + const cache = this.cache; - this.getUniforms = function () { + if ( arraysEqual( cache, v ) ) return; - if ( cachedUniforms === undefined ) { + gl.uniform2uiv( this.addr, v ); - cachedUniforms = new WebGLUniforms( gl, program ); + copyArray( cache, v ); - } + } - return cachedUniforms; + function setValueV3ui( gl, v ) { - }; + const cache = this.cache; - // set up caching for attribute locations + if ( arraysEqual( cache, v ) ) return; - let cachedAttributes; + gl.uniform3uiv( this.addr, v ); - this.getAttributes = function () { + copyArray( cache, v ); - if ( cachedAttributes === undefined ) { + } - cachedAttributes = fetchAttributeLocations( gl, program ); + function setValueV4ui( gl, v ) { - } + const cache = this.cache; - return cachedAttributes; + if ( arraysEqual( cache, v ) ) return; - }; + gl.uniform4uiv( this.addr, v ); - // free resource + copyArray( cache, v ); - this.destroy = function () { + } - bindingStates.releaseStatesOfProgram( this ); - gl.deleteProgram( program ); - this.program = undefined; + // Single texture (2D / Cube) - }; + function setValueT1( gl, v, textures ) { - // + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; + if ( cache[ 0 ] !== unit ) { - return this; + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - } + } - function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) { + textures.safeSetTexture2D( v || emptyTexture, unit ); - const programs = []; + } - const isWebGL2 = capabilities.isWebGL2; - const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const floatVertexTextures = capabilities.floatVertexTextures; - const maxVertexUniforms = capabilities.maxVertexUniforms; - const vertexTextures = capabilities.vertexTextures; + function setValueT3D1( gl, v, textures ) { - let precision = capabilities.precision; + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - const shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; + if ( cache[ 0 ] !== unit ) { - const parameterNames = [ - 'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', - 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', - 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap', - 'roughnessMap', 'metalnessMap', 'gradientMap', - 'alphaMap', 'combine', 'vertexColors', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', - 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', - 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', - 'maxMorphTargets', 'maxMorphNormals', 'premultipliedAlpha', - 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', - 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', - 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', - 'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', - 'sheen', 'transmissionMap' - ]; + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - function getMaxBones( object ) { + } - const skeleton = object.skeleton; - const bones = skeleton.bones; + textures.setTexture3D( v || emptyTexture3d, unit ); - if ( floatVertexTextures ) { + } - return 1024; + function setValueT6( gl, v, textures ) { - } else { + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) + if ( cache[ 0 ] !== unit ) { - const nVertexUniforms = maxVertexUniforms; - const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - const maxBones = Math.min( nVertexMatrices, bones.length ); + } - if ( maxBones < bones.length ) { + textures.safeSetTextureCube( v || emptyCubeTexture, unit ); - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; + } - } + function setValueT2DArray1( gl, v, textures ) { - return maxBones; + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - } + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; } - function getTextureEncodingFromMap( map ) { + textures.setTexture2DArray( v || emptyTexture2dArray, unit ); - let encoding; + } - if ( map && map.isTexture ) { + // Helper to pick the right setter for the singular case - encoding = map.encoding; + function getSingularSetter( type ) { - } else if ( map && map.isWebGLRenderTarget ) { + switch ( type ) { - console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' ); - encoding = map.texture.encoding; + case 0x1406: return setValueV1f; // FLOAT + case 0x8b50: return setValueV2f; // _VEC2 + case 0x8b51: return setValueV3f; // _VEC3 + case 0x8b52: return setValueV4f; // _VEC4 - } else { + case 0x8b5a: return setValueM2; // _MAT2 + case 0x8b5b: return setValueM3; // _MAT3 + case 0x8b5c: return setValueM4; // _MAT4 - encoding = LinearEncoding; + case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - } + case 0x1405: return setValueV1ui; // UINT + case 0x8dc6: return setValueV2ui; // _VEC2 + case 0x8dc7: return setValueV3ui; // _VEC3 + case 0x8dc8: return setValueV4ui; // _VEC4 - return encoding; + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1; + + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3D1; + + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6; + + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArray1; } - function getParameters( material, lights, shadows, scene, object ) { + } - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const envMap = cubemaps.get( material.envMap || environment ); + // Array of scalars - const shaderID = shaderIDs[ material.type ]; + function setValueV1fArray( gl, v ) { - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + gl.uniform1fv( this.addr, v ); - const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; + } - if ( material.precision !== null ) { + // Array of vectors (from flat array or array of THREE.VectorN) - precision = capabilities.getMaxPrecision( material.precision ); + function setValueV2fArray( gl, v ) { - if ( precision !== material.precision ) { + const data = flatten( v, this.size, 2 ); - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); + gl.uniform2fv( this.addr, data ); - } + } - } + function setValueV3fArray( gl, v ) { - let vertexShader, fragmentShader; + const data = flatten( v, this.size, 3 ); - if ( shaderID ) { + gl.uniform3fv( this.addr, data ); - const shader = ShaderLib[ shaderID ]; + } - vertexShader = shader.vertexShader; - fragmentShader = shader.fragmentShader; + function setValueV4fArray( gl, v ) { - } else { + const data = flatten( v, this.size, 4 ); - vertexShader = material.vertexShader; - fragmentShader = material.fragmentShader; + gl.uniform4fv( this.addr, data ); - } + } - const currentRenderTarget = renderer.getRenderTarget(); + // Array of matrices (from flat array or array of THREE.MatrixN) - const parameters = { + function setValueM2Array( gl, v ) { - isWebGL2: isWebGL2, + const data = flatten( v, this.size, 4 ); - shaderID: shaderID, - shaderName: material.type, + gl.uniformMatrix2fv( this.addr, false, data ); - vertexShader: vertexShader, - fragmentShader: fragmentShader, - defines: material.defines, + } - isRawShaderMaterial: material.isRawShaderMaterial === true, - glslVersion: material.glslVersion, + function setValueM3Array( gl, v ) { - precision: precision, + const data = flatten( v, this.size, 9 ); - instancing: object.isInstancedMesh === true, - instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, + gl.uniformMatrix3fv( this.addr, false, data ); - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatMap: !! material.clearcoatMap, - clearcoatRoughnessMap: !! material.clearcoatRoughnessMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, + } - gradientMap: !! material.gradientMap, + function setValueM4Array( gl, v ) { - sheen: !! material.sheen, + const data = flatten( v, this.size, 16 ); - transmissionMap: !! material.transmissionMap, + gl.uniformMatrix4fv( this.addr, false, data ); - combine: material.combine, + } - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmissionMap ) && !! material.displacementMap, + // Array of integer / boolean - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), + function setValueV1iArray( gl, v ) { - flatShading: material.flatShading, + gl.uniform1iv( this.addr, v ); - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, + } - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, + // Array of integer / boolean vectors (from flat array) - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, + function setValueV2iArray( gl, v ) { - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, + gl.uniform2iv( this.addr, v ); - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, + } - numClippingPlanes: clipping.numPlanes, - numClipIntersection: clipping.numIntersection, + function setValueV3iArray( gl, v ) { - dithering: material.dithering, + gl.uniform3iv( this.addr, v ); - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, + } - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, + function setValueV4iArray( gl, v ) { - premultipliedAlpha: material.premultipliedAlpha, + gl.uniform4iv( this.addr, v ); - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, + } - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, + // Array of unsigned integer - index0AttributeName: material.index0AttributeName, + function setValueV1uiArray( gl, v ) { - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, + gl.uniform1uiv( this.addr, v ); - rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), - rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), - rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), + } - customProgramCacheKey: material.customProgramCacheKey() + // Array of unsigned integer vectors (from flat array) - }; + function setValueV2uiArray( gl, v ) { - return parameters; + gl.uniform2uiv( this.addr, v ); - } + } - function getProgramCacheKey( parameters ) { + function setValueV3uiArray( gl, v ) { - const array = []; + gl.uniform3uiv( this.addr, v ); - if ( parameters.shaderID ) { + } - array.push( parameters.shaderID ); + function setValueV4uiArray( gl, v ) { - } else { + gl.uniform4uiv( this.addr, v ); - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); + } - } - if ( parameters.defines !== undefined ) { + // Array of textures (2D / Cube) - for ( const name in parameters.defines ) { + function setValueT1Array( gl, v, textures ) { - array.push( name ); - array.push( parameters.defines[ name ] ); + const n = v.length; - } + const units = allocTexUnits( textures, n ); - } + gl.uniform1iv( this.addr, units ); - if ( parameters.isRawShaderMaterial === false ) { + for ( let i = 0; i !== n; ++ i ) { - for ( let i = 0; i < parameterNames.length; i ++ ) { + textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); - array.push( parameters[ parameterNames[ i ] ] ); + } - } + } - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); + function setValueT6Array( gl, v, textures ) { - } + const n = v.length; - array.push( parameters.customProgramCacheKey ); + const units = allocTexUnits( textures, n ); - return array.join(); + gl.uniform1iv( this.addr, units ); + + for ( let i = 0; i !== n; ++ i ) { + + textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } - function getUniforms( material ) { + } - const shaderID = shaderIDs[ material.type ]; - let uniforms; + // Helper to pick the right setter for a pure (bottom-level) array - if ( shaderID ) { + function getPureArraySetter( type ) { - const shader = ShaderLib[ shaderID ]; - uniforms = UniformsUtils.clone( shader.uniforms ); + switch ( type ) { - } else { + case 0x1406: return setValueV1fArray; // FLOAT + case 0x8b50: return setValueV2fArray; // _VEC2 + case 0x8b51: return setValueV3fArray; // _VEC3 + case 0x8b52: return setValueV4fArray; // _VEC4 - uniforms = material.uniforms; + case 0x8b5a: return setValueM2Array; // _MAT2 + case 0x8b5b: return setValueM3Array; // _MAT3 + case 0x8b5c: return setValueM4Array; // _MAT4 - } + case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - return uniforms; + case 0x1405: return setValueV1uiArray; // UINT + case 0x8dc6: return setValueV2uiArray; // _VEC2 + case 0x8dc7: return setValueV3uiArray; // _VEC3 + case 0x8dc8: return setValueV4uiArray; // _VEC4 + + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1Array; + + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6Array; } - function acquireProgram( parameters, cacheKey ) { + } - let program; + // --- Uniform Classes --- - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { + function SingleUniform( id, activeInfo, addr ) { - const preexistingProgram = programs[ p ]; + this.id = id; + this.addr = addr; + this.cache = []; + this.setValue = getSingularSetter( activeInfo.type ); - if ( preexistingProgram.cacheKey === cacheKey ) { + // this.path = activeInfo.name; // DEBUG - program = preexistingProgram; - ++ program.usedTimes; + } - break; + function PureArrayUniform( id, activeInfo, addr ) { - } + this.id = id; + this.addr = addr; + this.cache = []; + this.size = activeInfo.size; + this.setValue = getPureArraySetter( activeInfo.type ); - } + // this.path = activeInfo.name; // DEBUG - if ( program === undefined ) { + } - program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); - programs.push( program ); + PureArrayUniform.prototype.updateCache = function ( data ) { - } + const cache = this.cache; - return program; + if ( data instanceof Float32Array && cache.length !== data.length ) { + + this.cache = new Float32Array( data.length ); } - function releaseProgram( program ) { + copyArray( cache, data ); - if ( -- program.usedTimes === 0 ) { + }; - // Remove from unordered set - const i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); + function StructuredUniform( id ) { - // Free WebGL resources - program.destroy(); + this.id = id; - } + this.seq = []; + this.map = {}; - } + } - return { - getParameters: getParameters, - getProgramCacheKey: getProgramCacheKey, - getUniforms: getUniforms, - acquireProgram: acquireProgram, - releaseProgram: releaseProgram, - // Exposed for resource monitoring & error feedback via renderer.info: - programs: programs - }; + StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - } + const seq = this.seq; - function WebGLProperties() { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - let properties = new WeakMap(); + const u = seq[ i ]; + u.setValue( gl, value[ u.id ], textures ); - function get( object ) { + } - let map = properties.get( object ); + }; - if ( map === undefined ) { + // --- Top-level --- - map = {}; - properties.set( object, map ); + // Parser - builds up the property tree from the path strings - } + const RePathPart = /(\w+)(\])?(\[|\.)?/g; - return map; + // extracts + // - the identifier (member name or array index) + // - followed by an optional right bracket (found when array index) + // - followed by an optional left bracket or dot (type of subscript) + // + // Note: These portions can be read in a non-overlapping fashion and + // allow straightforward parsing of the hierarchy that WebGL encodes + // in the uniform names. - } + function addUniform( container, uniformObject ) { - function remove( object ) { + container.seq.push( uniformObject ); + container.map[ uniformObject.id ] = uniformObject; - properties.delete( object ); + } - } + function parseUniform( activeInfo, addr, container ) { - function update( object, key, value ) { + const path = activeInfo.name, + pathLength = path.length; - properties.get( object )[ key ] = value; + // reset RegExp object, because of the early exit of a previous run + RePathPart.lastIndex = 0; - } + while ( true ) { - function dispose() { + const match = RePathPart.exec( path ), + matchEnd = RePathPart.lastIndex; - properties = new WeakMap(); + let id = match[ 1 ]; + const idIsIndex = match[ 2 ] === ']', + subscript = match[ 3 ]; - } + if ( idIsIndex ) id = id | 0; // convert to integer - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; + if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - } + // bare name or "pure" bottom-level array "[0]" suffix - function painterSortStable( a, b ) { + addUniform( container, subscript === undefined ? + new SingleUniform( id, activeInfo, addr ) : + new PureArrayUniform( id, activeInfo, addr ) ); - if ( a.groupOrder !== b.groupOrder ) { + break; - return a.groupOrder - b.groupOrder; + } else { - } else if ( a.renderOrder !== b.renderOrder ) { + // step into inner node / create it in case it doesn't exist - return a.renderOrder - b.renderOrder; + const map = container.map; + let next = map[ id ]; - } else if ( a.program !== b.program ) { + if ( next === undefined ) { - return a.program.id - b.program.id; + next = new StructuredUniform( id ); + addUniform( container, next ); - } else if ( a.material.id !== b.material.id ) { + } - return a.material.id - b.material.id; + container = next; - } else if ( a.z !== b.z ) { + } - return a.z - b.z; + } - } else { + } - return a.id - b.id; + // Root Container + + function WebGLUniforms( gl, program ) { + + this.seq = []; + this.map = {}; + + const n = gl.getProgramParameter( program, 35718 ); + + for ( let i = 0; i < n; ++ i ) { + + const info = gl.getActiveUniform( program, i ), + addr = gl.getUniformLocation( program, info.name ); + + parseUniform( info, addr, this ); } } - function reversePainterSortStable( a, b ) { + WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - if ( a.groupOrder !== b.groupOrder ) { + const u = this.map[ name ]; - return a.groupOrder - b.groupOrder; + if ( u !== undefined ) u.setValue( gl, value, textures ); - } else if ( a.renderOrder !== b.renderOrder ) { + }; - return a.renderOrder - b.renderOrder; + WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - } else if ( a.z !== b.z ) { + const v = object[ name ]; - return b.z - a.z; + if ( v !== undefined ) this.setValue( gl, name, v ); - } else { + }; - return a.id - b.id; - } + // Static interface - } + WebGLUniforms.upload = function ( gl, seq, values, textures ) { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - function WebGLRenderList( properties ) { + const u = seq[ i ], + v = values[ u.id ]; - const renderItems = []; - let renderItemsIndex = 0; + if ( v.needsUpdate !== false ) { - const opaque = []; - const transparent = []; + // note: always updating when .needsUpdate is undefined + u.setValue( gl, v.value, textures ); - const defaultProgram = { id: - 1 }; + } - function init() { + } - renderItemsIndex = 0; + }; - opaque.length = 0; - transparent.length = 0; + WebGLUniforms.seqWithValue = function ( seq, values ) { + + const r = []; + + for ( let i = 0, n = seq.length; i !== n; ++ i ) { + + const u = seq[ i ]; + if ( u.id in values ) r.push( u ); } - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { + return r; - let renderItem = renderItems[ renderItemsIndex ]; - const materialProperties = properties.get( material ); + }; - if ( renderItem === undefined ) { + function WebGLShader( gl, type, string ) { - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: materialProperties.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; + const shader = gl.createShader( type ); - renderItems[ renderItemsIndex ] = renderItem; + gl.shaderSource( shader, string ); + gl.compileShader( shader ); - } else { + return shader; - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = materialProperties.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; + } - } + let programIdCount = 0; - renderItemsIndex ++; + function addLineNumbers( string ) { - return renderItem; + const lines = string.split( '\n' ); + + for ( let i = 0; i < lines.length; i ++ ) { + + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; } - function push( object, geometry, material, groupOrder, z, group ) { + return lines.join( '\n' ); - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + } + + function getEncodingComponents( encoding ) { + + switch ( encoding ) { - ( material.transparent === true ? transparent : opaque ).push( renderItem ); + case LinearEncoding: + return [ 'Linear', '( value )' ]; + case sRGBEncoding: + return [ 'sRGB', '( value )' ]; + case RGBEEncoding: + return [ 'RGBE', '( value )' ]; + case RGBM7Encoding: + return [ 'RGBM', '( value, 7.0 )' ]; + case RGBM16Encoding: + return [ 'RGBM', '( value, 16.0 )' ]; + case RGBDEncoding: + return [ 'RGBD', '( value, 256.0 )' ]; + case GammaEncoding: + return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; + case LogLuvEncoding: + return [ 'LogLuv', '( value )' ]; + default: + console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); + return [ 'Linear', '( value )' ]; } - function unshift( object, geometry, material, groupOrder, z, group ) { + } - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + function getShaderErrors( gl, shader, type ) { - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); + const status = gl.getShaderParameter( shader, 35713 ); + const errors = gl.getShaderInfoLog( shader ).trim(); - } + if ( status && errors === '' ) return ''; - function sort( customOpaqueSort, customTransparentSort ) { + // --enable-privileged-webgl-extension + // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); - if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); + return type.toUpperCase() + '\n\n' + errors + '\n\n' + addLineNumbers( gl.getShaderSource( shader ) ); - } + } - function finish() { + function getTexelDecodingFunction( functionName, encoding ) { - // Clear references from inactive renderItems in the list + const components = getEncodingComponents( encoding ); + return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { + } - const renderItem = renderItems[ i ]; + function getTexelEncodingFunction( functionName, encoding ) { - if ( renderItem.id === null ) break; + const components = getEncodingComponents( encoding ); + return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; + } - } + function getToneMappingFunction( functionName, toneMapping ) { - } + let toneMappingName; - return { + switch ( toneMapping ) { - opaque: opaque, - transparent: transparent, + case LinearToneMapping: + toneMappingName = 'Linear'; + break; - init: init, - push: push, - unshift: unshift, - finish: finish, + case ReinhardToneMapping: + toneMappingName = 'Reinhard'; + break; - sort: sort - }; + case CineonToneMapping: + toneMappingName = 'OptimizedCineon'; + break; - } + case ACESFilmicToneMapping: + toneMappingName = 'ACESFilmic'; + break; - function WebGLRenderLists( properties ) { + case CustomToneMapping: + toneMappingName = 'Custom'; + break; - let lists = new WeakMap(); + default: + console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); + toneMappingName = 'Linear'; - function get( scene, camera ) { + } - const cameras = lists.get( scene ); - let list; + return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; - if ( cameras === undefined ) { + } - list = new WebGLRenderList( properties ); - lists.set( scene, new WeakMap() ); - lists.get( scene ).set( camera, list ); + function generateExtensions( parameters ) { - } else { + const chunks = [ + ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', + ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', + ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' + ]; - list = cameras.get( camera ); - if ( list === undefined ) { + return chunks.filter( filterEmptyLine ).join( '\n' ); - list = new WebGLRenderList( properties ); - cameras.set( camera, list ); + } - } + function generateDefines( defines ) { - } + const chunks = []; - return list; + for ( const name in defines ) { - } + const value = defines[ name ]; - function dispose() { + if ( value === false ) continue; - lists = new WeakMap(); + chunks.push( '#define ' + name + ' ' + value ); } - return { - get: get, - dispose: dispose - }; + return chunks.join( '\n' ); } - function UniformsCache() { + function fetchAttributeLocations( gl, program ) { - const lights = {}; + const attributes = {}; - return { + const n = gl.getProgramParameter( program, 35721 ); - get: function ( light ) { + for ( let i = 0; i < n; i ++ ) { - if ( lights[ light.id ] !== undefined ) { + const info = gl.getActiveAttrib( program, i ); + const name = info.name; - return lights[ light.id ]; + let locationSize = 1; + if ( info.type === 35674 ) locationSize = 2; + if ( info.type === 35675 ) locationSize = 3; + if ( info.type === 35676 ) locationSize = 4; - } + // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - let uniforms; + attributes[ name ] = { + type: info.type, + location: gl.getAttribLocation( program, name ), + locationSize: locationSize + }; - switch ( light.type ) { + } - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; + return attributes; - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; + } - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; + function filterEmptyLine( string ) { - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; + return string !== ''; - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; + } - } + function replaceLightNums( string, parameters ) { - lights[ light.id ] = uniforms; + return string + .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) + .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) + .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) + .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) + .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) + .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) + .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) + .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - return uniforms; + } - } + function replaceClippingPlaneNums( string, parameters ) { - }; + return string + .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) + .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); } - function ShadowUniformsCache() { + // Resolve Includes - const lights = {}; + const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - return { + function resolveIncludes( string ) { - get: function ( light ) { + return string.replace( includePattern, includeReplacer ); - if ( lights[ light.id ] !== undefined ) { + } - return lights[ light.id ]; + function includeReplacer( match, include ) { - } + const string = ShaderChunk[ include ]; - let uniforms; + if ( string === undefined ) { - switch ( light.type ) { + throw new Error( 'Can not resolve #include <' + include + '>' ); - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; + } - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; + return resolveIncludes( string ); - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; + } - // TODO (abelnation): set RectAreaLight shadow uniforms + // Unroll Loops - } + const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; + const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - lights[ light.id ] = uniforms; + function unrollLoops( string ) { - return uniforms; + return string + .replace( unrollLoopPattern, loopReplacer ) + .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - } + } - }; + function deprecatedLoopReplacer( match, start, end, snippet ) { + + console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); + return loopReplacer( match, start, end, snippet ); } + function loopReplacer( match, start, end, snippet ) { + let string = ''; - let nextVersion = 0; + for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - function shadowCastingLightsFirst( lightA, lightB ) { + string += snippet + .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) + .replace( /UNROLLED_LOOP_INDEX/g, i ); - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); + } + + return string; } - function WebGLLights( extensions, capabilities ) { + // - const cache = new UniformsCache(); + function generatePrecision( parameters ) { - const shadowCache = ShadowUniformsCache(); + let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; - const state = { + if ( parameters.precision === 'highp' ) { - version: 0, + precisionstring += '\n#define HIGH_PRECISION'; - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, + } else if ( parameters.precision === 'mediump' ) { - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, + precisionstring += '\n#define MEDIUM_PRECISION'; - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - rectAreaLTC1: null, - rectAreaLTC2: null, - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] + } else if ( parameters.precision === 'lowp' ) { - }; + precisionstring += '\n#define LOW_PRECISION'; - for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); + } - const vector3 = new Vector3(); - const matrix4 = new Matrix4(); - const matrix42 = new Matrix4(); + return precisionstring; - function setup( lights ) { + } - let r = 0, g = 0, b = 0; + function generateShadowMapTypeDefine( parameters ) { - for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); + let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; + if ( parameters.shadowMapType === PCFShadowMap ) { - let numDirectionalShadows = 0; - let numPointShadows = 0; - let numSpotShadows = 0; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - lights.sort( shadowCastingLightsFirst ); + } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - for ( let i = 0, l = lights.length; i < l; i ++ ) { + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - const light = lights[ i ]; + } else if ( parameters.shadowMapType === VSMShadowMap ) { - const color = light.color; - const intensity = light.intensity; - const distance = light.distance; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; + } - if ( light.isAmbientLight ) { + return shadowMapTypeDefine; - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; + } - } else if ( light.isLightProbe ) { + function generateEnvMapTypeDefine( parameters ) { - for ( let j = 0; j < 9; j ++ ) { + let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); + if ( parameters.envMap ) { - } + switch ( parameters.envMapMode ) { - } else if ( light.isDirectionalLight ) { + case CubeReflectionMapping: + case CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; - const uniforms = cache.get( light ); + case CubeUVReflectionMapping: + case CubeUVRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + } - if ( light.castShadow ) { + } - const shadow = light.shadow; + return envMapTypeDefine; - const shadowUniforms = shadowCache.get( light ); + } - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; + function generateEnvMapModeDefine( parameters ) { - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - numDirectionalShadows ++; + if ( parameters.envMap ) { - } + switch ( parameters.envMapMode ) { - state.directional[ directionalLength ] = uniforms; + case CubeRefractionMapping: + case CubeUVRefractionMapping: - directionalLength ++; + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; - } else if ( light.isSpotLight ) { + } - const uniforms = cache.get( light ); + } - uniforms.position.setFromMatrixPosition( light.matrixWorld ); + return envMapModeDefine; - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; + } - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; + function generateEnvMapBlendingDefine( parameters ) { - if ( light.castShadow ) { + let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - const shadow = light.shadow; + if ( parameters.envMap ) { - const shadowUniforms = shadowCache.get( light ); + switch ( parameters.combine ) { - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; + case MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + case MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; - numSpotShadows ++; + case AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; - } + } - state.spot[ spotLength ] = uniforms; + } - spotLength ++; + return envMapBlendingDefine; - } else if ( light.isRectAreaLight ) { + } - const uniforms = cache.get( light ); + function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); + // TODO Send this event to Three.js DevTools + // console.log( 'WebGLProgram', cacheKey ); - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); + const gl = renderer.getContext(); - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + const defines = parameters.defines; - state.rectArea[ rectAreaLength ] = uniforms; + let vertexShader = parameters.vertexShader; + let fragmentShader = parameters.fragmentShader; - rectAreaLength ++; + const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); + const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); + const envMapModeDefine = generateEnvMapModeDefine( parameters ); + const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); - } else if ( light.isPointLight ) { - const uniforms = cache.get( light ); + const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; + const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - if ( light.castShadow ) { + const customDefines = generateDefines( defines ); - const shadow = light.shadow; + const program = gl.createProgram(); - const shadowUniforms = shadowCache.get( light ); + let prefixVertex, prefixFragment; + let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; + if ( parameters.isRawShaderMaterial ) { - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; + prefixVertex = [ - numPointShadows ++; + customDefines - } + ].filter( filterEmptyLine ).join( '\n' ); - state.point[ pointLength ] = uniforms; + if ( prefixVertex.length > 0 ) { - pointLength ++; + prefixVertex += '\n'; - } else if ( light.isHemisphereLight ) { + } - const uniforms = cache.get( light ); + prefixFragment = [ - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); + customExtensions, + customDefines - state.hemi[ hemiLength ] = uniforms; + ].filter( filterEmptyLine ).join( '\n' ); - hemiLength ++; + if ( prefixFragment.length > 0 ) { - } + prefixFragment += '\n'; } - if ( rectAreaLength > 0 ) { - - if ( capabilities.isWebGL2 ) { + } else { - // WebGL 2 + prefixVertex = [ - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + generatePrecision( parameters ), - } else { + '#define SHADER_NAME ' + parameters.shaderName, - // WebGL 1 + customDefines, - if ( extensions.has( 'OES_texture_float_linear' ) === true ) { + parameters.instancing ? '#define USE_INSTANCING' : '', + parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { + '#define GAMMA_FACTOR ' + gammaFactorDefine, - state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; - state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; + '#define MAX_BONES ' + parameters.maxBones, + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - } else { + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', + ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - } + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - } + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULARCOLORMAP' : '', - } + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - const hash = state.hash; + parameters.sheenColorMap ? '#define USE_SHEENCOLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEENROUGHNESSMAP' : '', - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { + parameters.vertexTangents ? '#define USE_TANGENT' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUvs ? '#define USE_UV' : '', + parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; + parameters.flatShading ? '#define FLAT_SHADED' : '', - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + ( parameters.morphTargets && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE' : '', + ( parameters.morphTargets && parameters.isWebGL2 ) ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - state.version = nextVersion ++; + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - } + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - } + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - function setupView( lights, camera ) { + '#ifdef USE_INSTANCING', - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; + ' attribute mat4 instanceMatrix;', - const viewMatrix = camera.matrixWorldInverse; + '#endif', - for ( let i = 0, l = lights.length; i < l; i ++ ) { + '#ifdef USE_INSTANCING_COLOR', - const light = lights[ i ]; + ' attribute vec3 instanceColor;', - if ( light.isDirectionalLight ) { + '#endif', - const uniforms = state.directional[ directionalLength ]; + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + '#ifdef USE_TANGENT', - directionalLength ++; + ' attribute vec4 tangent;', - } else if ( light.isSpotLight ) { + '#endif', - const uniforms = state.spot[ spotLength ]; + '#if defined( USE_COLOR_ALPHA )', - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + ' attribute vec4 color;', - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + '#elif defined( USE_COLOR )', - spotLength ++; + ' attribute vec3 color;', - } else if ( light.isRectAreaLight ) { + '#endif', - const uniforms = state.rectArea[ rectAreaLength ]; + '#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )', - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); + ' #ifdef USE_MORPHNORMALS', - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); + ' #else', - rectAreaLength ++; + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', - } else if ( light.isPointLight ) { + ' #endif', - const uniforms = state.point[ pointLength ]; + '#endif', - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + '#ifdef USE_SKINNING', - pointLength ++; + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', - } else if ( light.isHemisphereLight ) { + '#endif', - const uniforms = state.hemi[ hemiLength ]; + '\n' - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); + ].filter( filterEmptyLine ).join( '\n' ); - hemiLength ++; + prefixFragment = [ - } + customExtensions, - } + generatePrecision( parameters ), - } + '#define SHADER_NAME ' + parameters.shaderName, - return { - setup: setup, - setupView: setupView, - state: state - }; + customDefines, - } + '#define GAMMA_FACTOR ' + gammaFactorDefine, - function WebGLRenderState( extensions, capabilities ) { + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - const lights = new WebGLLights( extensions, capabilities ); + parameters.map ? '#define USE_MAP' : '', + parameters.matcap ? '#define USE_MATCAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', + ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - const lightsArray = []; - const shadowsArray = []; + parameters.clearcoat ? '#define USE_CLEARCOAT' : '', + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - function init() { + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULARCOLORMAP' : '', + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - lightsArray.length = 0; - shadowsArray.length = 0; + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.alphaTest ? '#define USE_ALPHATEST' : '', - } + parameters.sheen ? '#define USE_SHEEN' : '', + parameters.sheenColorMap ? '#define USE_SHEENCOLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEENROUGHNESSMAP' : '', - function pushLight( light ) { + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - lightsArray.push( light ); + parameters.vertexTangents ? '#define USE_TANGENT' : '', + parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUvs ? '#define USE_UV' : '', + parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - } + parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - function pushShadow( shadowLight ) { + parameters.flatShading ? '#define FLAT_SHADED' : '', - shadowsArray.push( shadowLight ); + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - } + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - function setupLights() { + parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - lights.setup( lightsArray ); + parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - } + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - function setupLightsView( camera ) { + ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', - lights.setupView( lightsArray, camera ); + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - } + ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', + ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below + ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - const state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, + parameters.dithering ? '#define DITHERING' : '', + parameters.format === RGBFormat ? '#define OPAQUE' : '', - lights: lights - }; + ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below + parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', + parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', + parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', + parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', + parameters.specularColorMap ? getTexelDecodingFunction( 'specularColorMapTexelToLinear', parameters.specularColorMapEncoding ) : '', + parameters.sheenColorMap ? getTexelDecodingFunction( 'sheenColorMapTexelToLinear', parameters.sheenColorMapEncoding ) : '', + parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', + getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), - return { - init: init, - state: state, - setupLights: setupLights, - setupLightsView: setupLightsView, + parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - pushLight: pushLight, - pushShadow: pushShadow - }; + '\n' - } + ].filter( filterEmptyLine ).join( '\n' ); - function WebGLRenderStates( extensions, capabilities ) { + } - let renderStates = new WeakMap(); - - function get( scene, renderCallDepth = 0 ) { - - let renderState; + vertexShader = resolveIncludes( vertexShader ); + vertexShader = replaceLightNums( vertexShader, parameters ); + vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - if ( renderStates.has( scene ) === false ) { + fragmentShader = resolveIncludes( fragmentShader ); + fragmentShader = replaceLightNums( fragmentShader, parameters ); + fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.set( scene, [] ); - renderStates.get( scene ).push( renderState ); + vertexShader = unrollLoops( vertexShader ); + fragmentShader = unrollLoops( fragmentShader ); - } else { + if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - if ( renderCallDepth >= renderStates.get( scene ).length ) { + // GLSL 3.0 conversion for built-in materials and ShaderMaterial - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.get( scene ).push( renderState ); + versionString = '#version 300 es\n'; - } else { + prefixVertex = [ + 'precision mediump sampler2DArray;', + '#define attribute in', + '#define varying out', + '#define texture2D texture' + ].join( '\n' ) + '\n' + prefixVertex; - renderState = renderStates.get( scene )[ renderCallDepth ]; + prefixFragment = [ + '#define varying in', + ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', + ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', + '#define gl_FragDepthEXT gl_FragDepth', + '#define texture2D texture', + '#define textureCube texture', + '#define texture2DProj textureProj', + '#define texture2DLodEXT textureLod', + '#define texture2DProjLodEXT textureProjLod', + '#define textureCubeLodEXT textureLod', + '#define texture2DGradEXT textureGrad', + '#define texture2DProjGradEXT textureProjGrad', + '#define textureCubeGradEXT textureGrad' + ].join( '\n' ) + '\n' + prefixFragment; - } + } - } + const vertexGlsl = versionString + prefixVertex + vertexShader; + const fragmentGlsl = versionString + prefixFragment + fragmentShader; - return renderState; + // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); - } + const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); + const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - function dispose() { + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); - renderStates = new WeakMap(); + // Force a particular attribute to index 0. - } + if ( parameters.index0AttributeName !== undefined ) { - return { - get: get, - dispose: dispose - }; + gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - } + } else if ( parameters.morphTargets === true ) { - /** - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); - function MeshDepthMaterial( parameters ) { + } - Material.call( this ); + gl.linkProgram( program ); - this.type = 'MeshDepthMaterial'; + // check for link errors + if ( renderer.debug.checkShaderErrors ) { - this.depthPacking = BasicDepthPacking; + const programLog = gl.getProgramInfoLog( program ).trim(); + const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); + const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - this.skinning = false; - this.morphTargets = false; + let runnable = true; + let haveDiagnostics = true; - this.map = null; + if ( gl.getProgramParameter( program, 35714 ) === false ) { - this.alphaMap = null; + runnable = false; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); + const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - this.wireframe = false; - this.wireframeLinewidth = 1; + console.error( + 'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + + 'VALIDATE_STATUS ' + gl.getProgramParameter( program, 35715 ) + '\n\n' + + 'Program Info Log: ' + programLog + '\n' + + vertexErrors + '\n' + + fragmentErrors + ); - this.fog = false; + } else if ( programLog !== '' ) { - this.setValues( parameters ); + console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog ); - } + } else if ( vertexLog === '' || fragmentLog === '' ) { - MeshDepthMaterial.prototype = Object.create( Material.prototype ); - MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; + haveDiagnostics = false; - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; + } - MeshDepthMaterial.prototype.copy = function ( source ) { + if ( haveDiagnostics ) { - Material.prototype.copy.call( this, source ); + this.diagnostics = { - this.depthPacking = source.depthPacking; + runnable: runnable, - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + programLog: programLog, - this.map = source.map; + vertexShader: { - this.alphaMap = source.alphaMap; + log: vertexLog, + prefix: prefixVertex - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + }, - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + fragmentShader: { - return this; + log: fragmentLog, + prefix: prefixFragment - }; + } - /** - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ + }; - function MeshDistanceMaterial( parameters ) { + } - Material.call( this ); + } - this.type = 'MeshDistanceMaterial'; + // Clean up - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; + // Crashes in iOS9 and iOS10. #18402 + // gl.detachShader( program, glVertexShader ); + // gl.detachShader( program, glFragmentShader ); - this.skinning = false; - this.morphTargets = false; + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); - this.map = null; + // set up caching for uniform locations - this.alphaMap = null; + let cachedUniforms; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + this.getUniforms = function () { - this.fog = false; + if ( cachedUniforms === undefined ) { - this.setValues( parameters ); + cachedUniforms = new WebGLUniforms( gl, program ); - } + } - MeshDistanceMaterial.prototype = Object.create( Material.prototype ); - MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; + return cachedUniforms; - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; + }; - MeshDistanceMaterial.prototype.copy = function ( source ) { + // set up caching for attribute locations - Material.prototype.copy.call( this, source ); + let cachedAttributes; - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; + this.getAttributes = function () { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + if ( cachedAttributes === undefined ) { - this.map = source.map; + cachedAttributes = fetchAttributeLocations( gl, program ); - this.alphaMap = source.alphaMap; + } - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + return cachedAttributes; - return this; + }; - }; + // free resource - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; + this.destroy = function () { - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; + bindingStates.releaseStatesOfProgram( this ); - function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { + gl.deleteProgram( program ); + this.program = undefined; - let _frustum = new Frustum(); + }; - const _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), + // - _viewport = new Vector4(), + this.name = parameters.shaderName; + this.id = programIdCount ++; + this.cacheKey = cacheKey; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; - _depthMaterials = [], - _distanceMaterials = [], + return this; - _materialCache = {}; + } - const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; + function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { - const shadowMaterialVertical = new ShaderMaterial( { + const programs = []; - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, + const isWebGL2 = capabilities.isWebGL2; + const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; + const floatVertexTextures = capabilities.floatVertexTextures; + const maxVertexUniforms = capabilities.maxVertexUniforms; + const vertexTextures = capabilities.vertexTextures; - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, + let precision = capabilities.precision; - vertexShader: vsm_vert, + const shaderIDs = { + MeshDepthMaterial: 'depth', + MeshDistanceMaterial: 'distanceRGBA', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + MeshToonMaterial: 'toon', + MeshStandardMaterial: 'physical', + MeshPhysicalMaterial: 'physical', + MeshMatcapMaterial: 'matcap', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points', + ShadowMaterial: 'shadow', + SpriteMaterial: 'sprite' + }; - fragmentShader: vsm_frag + const parameterNames = [ + 'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', + 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', + 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', + 'objectSpaceNormalMap', 'tangentSpaceNormalMap', + 'clearcoat', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', + 'displacementMap', 'specularMap', , 'roughnessMap', 'metalnessMap', 'gradientMap', + 'alphaMap', 'alphaTest', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', + 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', + 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'morphTargetsCount', 'premultipliedAlpha', + 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', + 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', + 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', + 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', 'format', + 'specularIntensityMap', 'specularColorMap', 'specularColorMapEncoding', + 'transmission', 'transmissionMap', 'thicknessMap', + 'sheen', 'sheenColorMap', 'sheenColorMapEncoding', 'sheenRoughnessMap' + ]; - } ); + function getMaxBones( object ) { - const shadowMaterialHorizontal = shadowMaterialVertical.clone(); - shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; + const skeleton = object.skeleton; + const bones = skeleton.bones; - const fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - 'position', - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); + if ( floatVertexTextures ) { - const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); + return 1024; - const scope = this; + } else { - this.enabled = false; + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) - this.autoUpdate = true; - this.needsUpdate = false; + const nVertexUniforms = maxVertexUniforms; + const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - this.type = PCFShadowMap; + const maxBones = Math.min( nVertexMatrices, bones.length ); - this.render = function ( lights, scene, camera ) { + if ( maxBones < bones.length ) { - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); + return 0; - if ( lights.length === 0 ) return; + } - const currentRenderTarget = _renderer.getRenderTarget(); - const activeCubeFace = _renderer.getActiveCubeFace(); - const activeMipmapLevel = _renderer.getActiveMipmapLevel(); + return maxBones; - const _state = _renderer.state; + } - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); + } - // render depth map + function getTextureEncodingFromMap( map ) { - for ( let i = 0, il = lights.length; i < il; i ++ ) { + let encoding; - const light = lights[ i ]; - const shadow = light.shadow; + if ( map && map.isTexture ) { - if ( shadow === undefined ) { + encoding = map.encoding; - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; + } else if ( map && map.isWebGLRenderTarget ) { - } + console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' ); + encoding = map.texture.encoding; - if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; + } else { - _shadowMapSize.copy( shadow.mapSize ); + encoding = LinearEncoding; - const shadowFrameExtents = shadow.getFrameExtents(); + } - _shadowMapSize.multiply( shadowFrameExtents ); + if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) { - _viewportSize.copy( shadow.mapSize ); + encoding = LinearEncoding; // disable inline decode for sRGB textures in WebGL 2 - if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) { + } - if ( _shadowMapSize.x > maxTextureSize ) { + return encoding; - _viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; + } - } + function getParameters( material, lights, shadows, scene, object ) { - if ( _shadowMapSize.y > maxTextureSize ) { + const fog = scene.fog; + const environment = material.isMeshStandardMaterial ? scene.environment : null; - _viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); - } + const shaderID = shaderIDs[ material.type ]; - } + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; - const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; + if ( material.precision !== null ) { - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + precision = capabilities.getMaxPrecision( material.precision ); - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + if ( precision !== material.precision ) { - shadow.camera.updateProjectionMatrix(); + console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } - if ( shadow.map === null ) { - - const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; + } - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + let vertexShader, fragmentShader; - shadow.camera.updateProjectionMatrix(); + if ( shaderID ) { - } + const shader = ShaderLib[ shaderID ]; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); + vertexShader = shader.vertexShader; + fragmentShader = shader.fragmentShader; - const viewportCount = shadow.getViewportCount(); + } else { - for ( let vp = 0; vp < viewportCount; vp ++ ) { + vertexShader = material.vertexShader; + fragmentShader = material.fragmentShader; - const viewport = shadow.getViewport( vp ); + } - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); + const currentRenderTarget = renderer.getRenderTarget(); - _state.viewport( _viewport ); + const useAlphaTest = material.alphaTest > 0; + const useClearcoat = material.clearcoat > 0; - shadow.updateMatrices( light, vp ); + const parameters = { - _frustum = shadow.getFrustum(); + isWebGL2: isWebGL2, - renderObject( scene, camera, shadow.camera, light, this.type ); + shaderID: shaderID, + shaderName: material.type, - } + vertexShader: vertexShader, + fragmentShader: fragmentShader, + defines: material.defines, - // do blur pass for VSM + isRawShaderMaterial: material.isRawShaderMaterial === true, + glslVersion: material.glslVersion, - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + precision: precision, - VSMPass( shadow, camera ); + instancing: object.isInstancedMesh === true, + instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, - } + supportsVertexTextures: vertexTextures, + outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, + map: !! material.map, + mapEncoding: getTextureEncodingFromMap( material.map ), + matcap: !! material.matcap, + matcapEncoding: getTextureEncodingFromMap( material.matcap ), + envMap: !! envMap, + envMapMode: envMap && envMap.mapping, + envMapEncoding: getTextureEncodingFromMap( envMap ), + envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), + lightMap: !! material.lightMap, + lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, + tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - shadow.needsUpdate = false; + clearcoat: useClearcoat, + clearcoatMap: useClearcoat && !! material.clearcoatMap, + clearcoatRoughnessMap: useClearcoat && !! material.clearcoatRoughnessMap, + clearcoatNormalMap: useClearcoat && !! material.clearcoatNormalMap, - } + displacementMap: !! material.displacementMap, + roughnessMap: !! material.roughnessMap, + metalnessMap: !! material.metalnessMap, + specularMap: !! material.specularMap, + specularIntensityMap: !! material.specularIntensityMap, + specularColorMap: !! material.specularColorMap, + specularColorMapEncoding: getTextureEncodingFromMap( material.specularColorMap ), - scope.needsUpdate = false; + alphaMap: !! material.alphaMap, + alphaTest: useAlphaTest, - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); + gradientMap: !! material.gradientMap, - }; + sheen: material.sheen > 0, + sheenColorMap: !! material.sheenColorMap, + sheenColorMapEncoding: getTextureEncodingFromMap( material.sheenColorMap ), + sheenRoughnessMap: !! material.sheenRoughnessMap, - function VSMPass( shadow, camera ) { + transmission: material.transmission > 0, + transmissionMap: !! material.transmissionMap, + thicknessMap: !! material.thicknessMap, - const geometry = _objects.update( fullScreenMesh ); + combine: material.combine, - // vertical pass + vertexTangents: ( !! material.normalMap && !! object.geometry && !! object.geometry.attributes.tangent ), + vertexColors: material.vertexColors, + vertexAlphas: material.vertexColors === true && !! object.geometry && !! object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, + vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || !! material.sheenColorMap || material.sheenRoughnessMap, + uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || material.transmission > 0 || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || !! material.sheen > 0 || !! material.sheenColorMap || !! material.sheenRoughnessMap ) && !! material.displacementMap, - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); + fog: !! fog, + useFog: material.fog, + fogExp2: ( fog && fog.isFogExp2 ), - // horizontal pass + flatShading: !! material.flatShading, - shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: logarithmicDepthBuffer, - } + skinning: object.isSkinnedMesh === true && maxBones > 0, + maxBones: maxBones, + useVertexTexture: floatVertexTextures, - function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) { + morphTargets: !! object.geometry && !! object.geometry.morphAttributes.position, + morphNormals: !! object.geometry && !! object.geometry.morphAttributes.normal, + morphTargetsCount: ( !! object.geometry && !! object.geometry.morphAttributes.position ) ? object.geometry.morphAttributes.position.length : 0, - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numRectAreaLights: lights.rectArea.length, + numHemiLights: lights.hemi.length, - let material = _depthMaterials[ index ]; + numDirLightShadows: lights.directionalShadowMap.length, + numPointLightShadows: lights.pointShadowMap.length, + numSpotLightShadows: lights.spotShadowMap.length, - if ( material === undefined ) { + numClippingPlanes: clipping.numPlanes, + numClipIntersection: clipping.numIntersection, - material = new MeshDepthMaterial( { + format: material.format, + dithering: material.dithering, - depthPacking: RGBADepthPacking, + shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, + shadowMapType: renderer.shadowMap.type, - morphTargets: useMorphing, - skinning: useSkinning + toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, + physicallyCorrectLights: renderer.physicallyCorrectLights, - } ); + premultipliedAlpha: material.premultipliedAlpha, - _depthMaterials[ index ] = material; + doubleSided: material.side === DoubleSide, + flipSided: material.side === BackSide, - } + depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - return material; + index0AttributeName: material.index0AttributeName, - } + extensionDerivatives: material.extensions && material.extensions.derivatives, + extensionFragDepth: material.extensions && material.extensions.fragDepth, + extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, + extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) { + rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), + rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), + rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + customProgramCacheKey: material.customProgramCacheKey() - let material = _distanceMaterials[ index ]; + }; - if ( material === undefined ) { + return parameters; - material = new MeshDistanceMaterial( { + } - morphTargets: useMorphing, - skinning: useSkinning + function getProgramCacheKey( parameters ) { - } ); + const array = []; - _distanceMaterials[ index ] = material; + if ( parameters.shaderID ) { - } + array.push( parameters.shaderID ); - return material; + } else { - } + array.push( hashString( parameters.fragmentShader ) ); + array.push( hashString( parameters.vertexShader ) ); - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { + } - let result = null; + if ( parameters.defines !== undefined ) { - let getMaterialVariant = getDepthMaterialVariant; - let customMaterial = object.customDepthMaterial; + for ( const name in parameters.defines ) { - if ( light.isPointLight === true ) { + array.push( name ); + array.push( parameters.defines[ name ] ); - getMaterialVariant = getDistanceMaterialVariant; - customMaterial = object.customDistanceMaterial; + } } - if ( customMaterial === undefined ) { - - let useMorphing = false; + if ( parameters.isRawShaderMaterial === false ) { - if ( material.morphTargets === true ) { + for ( let i = 0; i < parameterNames.length; i ++ ) { - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; + array.push( parameters[ parameterNames[ i ] ] ); } - let useSkinning = false; - - if ( object.isSkinnedMesh === true ) { + array.push( renderer.outputEncoding ); + array.push( renderer.gammaFactor ); - if ( material.skinning === true ) { + } - useSkinning = true; + array.push( parameters.customProgramCacheKey ); - } else { + return array.join(); - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); + } - } + function getUniforms( material ) { - } + const shaderID = shaderIDs[ material.type ]; + let uniforms; - const useInstancing = object.isInstancedMesh === true; + if ( shaderID ) { - result = getMaterialVariant( useMorphing, useSkinning, useInstancing ); + const shader = ShaderLib[ shaderID ]; + uniforms = UniformsUtils.clone( shader.uniforms ); } else { - result = customMaterial; + uniforms = material.uniforms; } - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { + return uniforms; - // in this case we need a unique material instance reflecting the - // appropriate state - - const keyA = result.uuid, keyB = material.uuid; + } - let materialsForVariant = _materialCache[ keyA ]; + function acquireProgram( parameters, cacheKey ) { - if ( materialsForVariant === undefined ) { + let program; - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; + // Check if code has been already compiled + for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - } + const preexistingProgram = programs[ p ]; - let cachedMaterial = materialsForVariant[ keyB ]; + if ( preexistingProgram.cacheKey === cacheKey ) { - if ( cachedMaterial === undefined ) { + program = preexistingProgram; + ++ program.usedTimes; - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; + break; } - result = cachedMaterial; - } - result.visible = material.visible; - result.wireframe = material.wireframe; - - if ( type === VSMShadowMap ) { + if ( program === undefined ) { - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; + program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); + programs.push( program ); - } else { + } - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; + return program; - } + } - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; + function releaseProgram( program ) { - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; + if ( -- program.usedTimes === 0 ) { - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { + // Remove from unordered set + const i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; + // Free WebGL resources + program.destroy(); } - return result; - } - function renderObject( object, camera, shadowCamera, light, type ) { + return { + getParameters: getParameters, + getProgramCacheKey: getProgramCacheKey, + getUniforms: getUniforms, + acquireProgram: acquireProgram, + releaseProgram: releaseProgram, + // Exposed for resource monitoring & error feedback via renderer.info: + programs: programs + }; - if ( object.visible === false ) return; + } - const visible = object.layers.test( camera.layers ); + function WebGLProperties() { - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { + let properties = new WeakMap(); - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { + function get( object ) { - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + let map = properties.get( object ); - const geometry = _objects.update( object ); - const material = object.material; + if ( map === undefined ) { - if ( Array.isArray( material ) ) { + map = {}; + properties.set( object, map ); - const groups = geometry.groups; + } - for ( let k = 0, kl = groups.length; k < kl; k ++ ) { + return map; - const group = groups[ k ]; - const groupMaterial = material[ group.materialIndex ]; + } - if ( groupMaterial && groupMaterial.visible ) { + function remove( object ) { - const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); + properties.delete( object ); - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); + } - } + function update( object, key, value ) { - } + properties.get( object )[ key ] = value; - } else if ( material.visible ) { + } - const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); + function dispose() { - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); + properties = new WeakMap(); - } + } - } + return { + get: get, + remove: remove, + update: update, + dispose: dispose + }; - } + } - const children = object.children; + function painterSortStable( a, b ) { - for ( let i = 0, l = children.length; i < l; i ++ ) { + if ( a.groupOrder !== b.groupOrder ) { - renderObject( children[ i ], camera, shadowCamera, light, type ); + return a.groupOrder - b.groupOrder; - } + } else if ( a.renderOrder !== b.renderOrder ) { - } + return a.renderOrder - b.renderOrder; - } + } else if ( a.program !== b.program ) { - function WebGLState( gl, extensions, capabilities ) { + return a.program.id - b.program.id; - const isWebGL2 = capabilities.isWebGL2; + } else if ( a.material.id !== b.material.id ) { - function ColorBuffer() { + return a.material.id - b.material.id; - let locked = false; + } else if ( a.z !== b.z ) { - const color = new Vector4(); - let currentColorMask = null; - const currentColorClear = new Vector4( 0, 0, 0, 0 ); + return a.z - b.z; - return { + } else { - setMask: function ( colorMask ) { + return a.id - b.id; - if ( currentColorMask !== colorMask && ! locked ) { + } - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; + } - } + function reversePainterSortStable( a, b ) { - }, + if ( a.groupOrder !== b.groupOrder ) { - setLocked: function ( lock ) { + return a.groupOrder - b.groupOrder; - locked = lock; + } else if ( a.renderOrder !== b.renderOrder ) { - }, + return a.renderOrder - b.renderOrder; - setClear: function ( r, g, b, a, premultipliedAlpha ) { + } else if ( a.z !== b.z ) { - if ( premultipliedAlpha === true ) { + return b.z - a.z; - r *= a; g *= a; b *= a; + } else { - } + return a.id - b.id; - color.set( r, g, b, a ); + } - if ( currentColorClear.equals( color ) === false ) { + } - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); - } + function WebGLRenderList( properties ) { - }, + const renderItems = []; + let renderItemsIndex = 0; - reset: function () { + const opaque = []; + const transmissive = []; + const transparent = []; - locked = false; + const defaultProgram = { id: - 1 }; - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state + function init() { - } + renderItemsIndex = 0; - }; + opaque.length = 0; + transmissive.length = 0; + transparent.length = 0; } - function DepthBuffer() { + function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - let locked = false; + let renderItem = renderItems[ renderItemsIndex ]; + const materialProperties = properties.get( material ); - let currentDepthMask = null; - let currentDepthFunc = null; - let currentDepthClear = null; + if ( renderItem === undefined ) { - return { + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + program: materialProperties.program || defaultProgram, + groupOrder: groupOrder, + renderOrder: object.renderOrder, + z: z, + group: group + }; - setTest: function ( depthTest ) { + renderItems[ renderItemsIndex ] = renderItem; - if ( depthTest ) { + } else { - enable( 2929 ); + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.program = materialProperties.program || defaultProgram; + renderItem.groupOrder = groupOrder; + renderItem.renderOrder = object.renderOrder; + renderItem.z = z; + renderItem.group = group; - } else { + } - disable( 2929 ); + renderItemsIndex ++; - } + return renderItem; - }, + } - setMask: function ( depthMask ) { + function push( object, geometry, material, groupOrder, z, group ) { - if ( currentDepthMask !== depthMask && ! locked ) { + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - gl.depthMask( depthMask ); - currentDepthMask = depthMask; + if ( material.transmission > 0.0 ) { - } + transmissive.push( renderItem ); - }, + } else if ( material.transparent === true ) { - setFunc: function ( depthFunc ) { + transparent.push( renderItem ); - if ( currentDepthFunc !== depthFunc ) { + } else { - if ( depthFunc ) { + opaque.push( renderItem ); - switch ( depthFunc ) { + } - case NeverDepth: + } - gl.depthFunc( 512 ); - break; + function unshift( object, geometry, material, groupOrder, z, group ) { - case AlwaysDepth: + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - gl.depthFunc( 519 ); - break; + if ( material.transmission > 0.0 ) { - case LessDepth: + transmissive.unshift( renderItem ); - gl.depthFunc( 513 ); - break; + } else if ( material.transparent === true ) { - case LessEqualDepth: + transparent.unshift( renderItem ); - gl.depthFunc( 515 ); - break; + } else { - case EqualDepth: + opaque.unshift( renderItem ); - gl.depthFunc( 514 ); - break; + } - case GreaterEqualDepth: + } - gl.depthFunc( 518 ); - break; + function sort( customOpaqueSort, customTransparentSort ) { - case GreaterDepth: + if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); + if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); + if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - gl.depthFunc( 516 ); - break; + } - case NotEqualDepth: + function finish() { - gl.depthFunc( 517 ); - break; + // Clear references from inactive renderItems in the list - default: + for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - gl.depthFunc( 515 ); + const renderItem = renderItems[ i ]; - } + if ( renderItem.id === null ) break; - } else { + renderItem.id = null; + renderItem.object = null; + renderItem.geometry = null; + renderItem.material = null; + renderItem.program = null; + renderItem.group = null; - gl.depthFunc( 515 ); + } - } + } - currentDepthFunc = depthFunc; + return { - } + opaque: opaque, + transmissive: transmissive, + transparent: transparent, - }, + init: init, + push: push, + unshift: unshift, + finish: finish, - setLocked: function ( lock ) { + sort: sort + }; - locked = lock; + } - }, + function WebGLRenderLists( properties ) { - setClear: function ( depth ) { + let lists = new WeakMap(); - if ( currentDepthClear !== depth ) { + function get( scene, renderCallDepth ) { - gl.clearDepth( depth ); - currentDepthClear = depth; + let list; - } + if ( lists.has( scene ) === false ) { - }, + list = new WebGLRenderList( properties ); + lists.set( scene, [ list ] ); - reset: function () { + } else { - locked = false; + if ( renderCallDepth >= lists.get( scene ).length ) { - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; + list = new WebGLRenderList( properties ); + lists.get( scene ).push( list ); - } + } else { - }; + list = lists.get( scene )[ renderCallDepth ]; - } + } - function StencilBuffer() { + } - let locked = false; + return list; - let currentStencilMask = null; - let currentStencilFunc = null; - let currentStencilRef = null; - let currentStencilFuncMask = null; - let currentStencilFail = null; - let currentStencilZFail = null; - let currentStencilZPass = null; - let currentStencilClear = null; + } - return { + function dispose() { - setTest: function ( stencilTest ) { + lists = new WeakMap(); - if ( ! locked ) { + } - if ( stencilTest ) { + return { + get: get, + dispose: dispose + }; - enable( 2960 ); + } - } else { + function UniformsCache() { - disable( 2960 ); + const lights = {}; - } + return { - } + get: function ( light ) { - }, + if ( lights[ light.id ] !== undefined ) { - setMask: function ( stencilMask ) { + return lights[ light.id ]; - if ( currentStencilMask !== stencilMask && ! locked ) { + } - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; + let uniforms; - } + switch ( light.type ) { - }, + case 'DirectionalLight': + uniforms = { + direction: new Vector3(), + color: new Color() + }; + break; - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { + case 'SpotLight': + uniforms = { + position: new Vector3(), + direction: new Vector3(), + color: new Color(), + distance: 0, + coneCos: 0, + penumbraCos: 0, + decay: 0 + }; + break; - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { + case 'PointLight': + uniforms = { + position: new Vector3(), + color: new Color(), + distance: 0, + decay: 0 + }; + break; - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); + case 'HemisphereLight': + uniforms = { + direction: new Vector3(), + skyColor: new Color(), + groundColor: new Color() + }; + break; - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; + case 'RectAreaLight': + uniforms = { + color: new Color(), + position: new Vector3(), + halfWidth: new Vector3(), + halfHeight: new Vector3() + }; + break; - } + } - }, + lights[ light.id ] = uniforms; - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { + return uniforms; - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { + } - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); + }; - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; + } - } + function ShadowUniformsCache() { - }, + const lights = {}; - setLocked: function ( lock ) { + return { - locked = lock; + get: function ( light ) { - }, + if ( lights[ light.id ] !== undefined ) { - setClear: function ( stencil ) { + return lights[ light.id ]; - if ( currentStencilClear !== stencil ) { + } - gl.clearStencil( stencil ); - currentStencilClear = stencil; + let uniforms; - } + switch ( light.type ) { - }, + case 'DirectionalLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - reset: function () { + case 'SpotLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - locked = false; + case 'PointLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2(), + shadowCameraNear: 1, + shadowCameraFar: 1000 + }; + break; - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; + // TODO (abelnation): set RectAreaLight shadow uniforms } - }; + lights[ light.id ] = uniforms; - } + return uniforms; - // + } - const colorBuffer = new ColorBuffer(); - const depthBuffer = new DepthBuffer(); - const stencilBuffer = new StencilBuffer(); + }; - let enabledCapabilities = {}; + } - let currentProgram = null; - let currentBlendingEnabled = null; - let currentBlending = null; - let currentBlendEquation = null; - let currentBlendSrc = null; - let currentBlendDst = null; - let currentBlendEquationAlpha = null; - let currentBlendSrcAlpha = null; - let currentBlendDstAlpha = null; - let currentPremultipledAlpha = false; - let currentFlipSided = null; - let currentCullFace = null; + let nextVersion = 0; - let currentLineWidth = null; + function shadowCastingLightsFirst( lightA, lightB ) { - let currentPolygonOffsetFactor = null; - let currentPolygonOffsetUnits = null; + return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - const maxTextures = gl.getParameter( 35661 ); + } - let lineWidthAvailable = false; - let version = 0; - const glVersion = gl.getParameter( 7938 ); + function WebGLLights( extensions, capabilities ) { - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { + const cache = new UniformsCache(); - version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); + const shadowCache = ShadowUniformsCache(); - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { + const state = { - version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); + version: 0, - } + hash: { + directionalLength: - 1, + pointLength: - 1, + spotLength: - 1, + rectAreaLength: - 1, + hemiLength: - 1, - let currentTextureSlot = null; - let currentBoundTextures = {}; + numDirectionalShadows: - 1, + numPointShadows: - 1, + numSpotShadows: - 1 + }, - const currentScissor = new Vector4(); - const currentViewport = new Vector4(); + ambient: [ 0, 0, 0 ], + probe: [], + directional: [], + directionalShadow: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadow: [], + spotShadowMap: [], + spotShadowMatrix: [], + rectArea: [], + rectAreaLTC1: null, + rectAreaLTC2: null, + point: [], + pointShadow: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [] - function createTexture( type, target, count ) { + }; - const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - const texture = gl.createTexture(); + for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); + const vector3 = new Vector3(); + const matrix4 = new Matrix4(); + const matrix42 = new Matrix4(); - for ( let i = 0; i < count; i ++ ) { + function setup( lights, physicallyCorrectLights ) { - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); + let r = 0, g = 0, b = 0; - } + for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - return texture; + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - } + let numDirectionalShadows = 0; + let numPointShadows = 0; + let numSpotShadows = 0; - const emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); + lights.sort( shadowCastingLightsFirst ); - // init + // artist-friendly light intensity scaling factor + const scaleFactor = ( physicallyCorrectLights !== true ) ? Math.PI : 1; - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); + for ( let i = 0, l = lights.length; i < l; i ++ ) { - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); + const light = lights[ i ]; - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); + const color = light.color; + const intensity = light.intensity; + const distance = light.distance; - setBlending( NoBlending ); + const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - // + if ( light.isAmbientLight ) { - function enable( id ) { + r += color.r * intensity * scaleFactor; + g += color.g * intensity * scaleFactor; + b += color.b * intensity * scaleFactor; - if ( enabledCapabilities[ id ] !== true ) { + } else if ( light.isLightProbe ) { - gl.enable( id ); - enabledCapabilities[ id ] = true; + for ( let j = 0; j < 9; j ++ ) { - } + state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - } + } - function disable( id ) { + } else if ( light.isDirectionalLight ) { - if ( enabledCapabilities[ id ] !== false ) { + const uniforms = cache.get( light ); - gl.disable( id ); - enabledCapabilities[ id ] = false; + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); - } + if ( light.castShadow ) { - } + const shadow = light.shadow; - function useProgram( program ) { + const shadowUniforms = shadowCache.get( light ); - if ( currentProgram !== program ) { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - gl.useProgram( program ); + state.directionalShadow[ directionalLength ] = shadowUniforms; + state.directionalShadowMap[ directionalLength ] = shadowMap; + state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - currentProgram = program; + numDirectionalShadows ++; - return true; + } - } + state.directional[ directionalLength ] = uniforms; - return false; + directionalLength ++; - } + } else if ( light.isSpotLight ) { - const equationToGL = { - [ AddEquation ]: 32774, - [ SubtractEquation ]: 32778, - [ ReverseSubtractEquation ]: 32779 - }; + const uniforms = cache.get( light ); - if ( isWebGL2 ) { + uniforms.position.setFromMatrixPosition( light.matrixWorld ); - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; + uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); + uniforms.distance = distance; - } else { + uniforms.coneCos = Math.cos( light.angle ); + uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); + uniforms.decay = light.decay; - const extension = extensions.get( 'EXT_blend_minmax' ); + if ( light.castShadow ) { - if ( extension !== null ) { + const shadow = light.shadow; - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; + const shadowUniforms = shadowCache.get( light ); - } + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - } + state.spotShadow[ spotLength ] = shadowUniforms; + state.spotShadowMap[ spotLength ] = shadowMap; + state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - const factorToGL = { - [ ZeroFactor ]: 0, - [ OneFactor ]: 1, - [ SrcColorFactor ]: 768, - [ SrcAlphaFactor ]: 770, - [ SrcAlphaSaturateFactor ]: 776, - [ DstColorFactor ]: 774, - [ DstAlphaFactor ]: 772, - [ OneMinusSrcColorFactor ]: 769, - [ OneMinusSrcAlphaFactor ]: 771, - [ OneMinusDstColorFactor ]: 775, - [ OneMinusDstAlphaFactor ]: 773 - }; + numSpotShadows ++; - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { + } - if ( blending === NoBlending ) { + state.spot[ spotLength ] = uniforms; - if ( currentBlendingEnabled ) { + spotLength ++; - disable( 3042 ); - currentBlendingEnabled = false; + } else if ( light.isRectAreaLight ) { - } + const uniforms = cache.get( light ); - return; + // (a) intensity is the total visible light emitted + //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - } + // (b) intensity is the brightness of the light + uniforms.color.copy( color ).multiplyScalar( intensity ); - if ( ! currentBlendingEnabled ) { + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - enable( 3042 ); - currentBlendingEnabled = true; + state.rectArea[ rectAreaLength ] = uniforms; - } + rectAreaLength ++; - if ( blending !== CustomBlending ) { + } else if ( light.isPointLight ) { - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { + const uniforms = cache.get( light ); - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); + uniforms.distance = light.distance; + uniforms.decay = light.decay; - gl.blendEquation( 32774 ); + if ( light.castShadow ) { - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; + const shadow = light.shadow; - } + const shadowUniforms = shadowCache.get( light ); - if ( premultipliedAlpha ) { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; + shadowUniforms.shadowCameraNear = shadow.camera.near; + shadowUniforms.shadowCameraFar = shadow.camera.far; - switch ( blending ) { + state.pointShadow[ pointLength ] = shadowUniforms; + state.pointShadowMap[ pointLength ] = shadowMap; + state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; + numPointShadows ++; - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; + } - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; + state.point[ pointLength ] = uniforms; - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; + pointLength ++; - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + } else if ( light.isHemisphereLight ) { - } + const uniforms = cache.get( light ); - } else { + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); - switch ( blending ) { + state.hemi[ hemiLength ] = uniforms; - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; + hemiLength ++; - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; + } - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; + } - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; + if ( rectAreaLength > 0 ) { - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + if ( capabilities.isWebGL2 ) { - } + // WebGL 2 - } + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; + } else { - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; + // WebGL 1 - } + if ( extensions.has( 'OES_texture_float_linear' ) === true ) { - return; + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - } + } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { - // custom blending + state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; + state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; + } else { - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); + } - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; + } } - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + state.ambient[ 0 ] = r; + state.ambient[ 1 ] = g; + state.ambient[ 2 ] = b; - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); + const hash = state.hash; - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; + if ( hash.directionalLength !== directionalLength || + hash.pointLength !== pointLength || + hash.spotLength !== spotLength || + hash.rectAreaLength !== rectAreaLength || + hash.hemiLength !== hemiLength || + hash.numDirectionalShadows !== numDirectionalShadows || + hash.numPointShadows !== numPointShadows || + hash.numSpotShadows !== numSpotShadows ) { - } + state.directional.length = directionalLength; + state.spot.length = spotLength; + state.rectArea.length = rectAreaLength; + state.point.length = pointLength; + state.hemi.length = hemiLength; - currentBlending = blending; - currentPremultipledAlpha = null; + state.directionalShadow.length = numDirectionalShadows; + state.directionalShadowMap.length = numDirectionalShadows; + state.pointShadow.length = numPointShadows; + state.pointShadowMap.length = numPointShadows; + state.spotShadow.length = numSpotShadows; + state.spotShadowMap.length = numSpotShadows; + state.directionalShadowMatrix.length = numDirectionalShadows; + state.pointShadowMatrix.length = numPointShadows; + state.spotShadowMatrix.length = numSpotShadows; - } + hash.directionalLength = directionalLength; + hash.pointLength = pointLength; + hash.spotLength = spotLength; + hash.rectAreaLength = rectAreaLength; + hash.hemiLength = hemiLength; - function setMaterial( material, frontFaceCW ) { + hash.numDirectionalShadows = numDirectionalShadows; + hash.numPointShadows = numPointShadows; + hash.numSpotShadows = numSpotShadows; - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); + state.version = nextVersion ++; - let flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; + } - setFlipSided( flipSided ); + } - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); + function setupView( lights, camera ) { - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - const stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { + const viewMatrix = camera.matrixWorldInverse; - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); + for ( let i = 0, l = lights.length; i < l; i ++ ) { - } + const light = lights[ i ]; - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + if ( light.isDirectionalLight ) { - } + const uniforms = state.directional[ directionalLength ]; - // + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - function setFlipSided( flipSided ) { + directionalLength ++; - if ( currentFlipSided !== flipSided ) { + } else if ( light.isSpotLight ) { - if ( flipSided ) { + const uniforms = state.spot[ spotLength ]; - gl.frontFace( 2304 ); + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } else { + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - gl.frontFace( 2305 ); + spotLength ++; - } + } else if ( light.isRectAreaLight ) { - currentFlipSided = flipSided; + const uniforms = state.rectArea[ rectAreaLength ]; - } + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } + // extract local rotation of light to derive width/height half vectors + matrix42.identity(); + matrix4.copy( light.matrixWorld ); + matrix4.premultiply( viewMatrix ); + matrix42.extractRotation( matrix4 ); - function setCullFace( cullFace ) { + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - if ( cullFace !== CullFaceNone ) { + uniforms.halfWidth.applyMatrix4( matrix42 ); + uniforms.halfHeight.applyMatrix4( matrix42 ); - enable( 2884 ); + rectAreaLength ++; - if ( cullFace !== currentCullFace ) { + } else if ( light.isPointLight ) { - if ( cullFace === CullFaceBack ) { + const uniforms = state.point[ pointLength ]; - gl.cullFace( 1029 ); + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } else if ( cullFace === CullFaceFront ) { + pointLength ++; - gl.cullFace( 1028 ); + } else if ( light.isHemisphereLight ) { - } else { + const uniforms = state.hemi[ hemiLength ]; - gl.cullFace( 1032 ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + uniforms.direction.normalize(); - } + hemiLength ++; } - } else { - - disable( 2884 ); - } - currentCullFace = cullFace; - } - function setLineWidth( width ) { + return { + setup: setup, + setupView: setupView, + state: state + }; - if ( width !== currentLineWidth ) { + } - if ( lineWidthAvailable ) gl.lineWidth( width ); + function WebGLRenderState( extensions, capabilities ) { - currentLineWidth = width; + const lights = new WebGLLights( extensions, capabilities ); - } + const lightsArray = []; + const shadowsArray = []; - } + function init() { - function setPolygonOffset( polygonOffset, factor, units ) { + lightsArray.length = 0; + shadowsArray.length = 0; - if ( polygonOffset ) { + } - enable( 32823 ); + function pushLight( light ) { - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { + lightsArray.push( light ); - gl.polygonOffset( factor, units ); + } - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; + function pushShadow( shadowLight ) { - } + shadowsArray.push( shadowLight ); - } else { + } - disable( 32823 ); + function setupLights( physicallyCorrectLights ) { - } + lights.setup( lightsArray, physicallyCorrectLights ); } - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { + function setupLightsView( camera ) { - enable( 3089 ); + lights.setupView( lightsArray, camera ); - } else { + } - disable( 3089 ); + const state = { + lightsArray: lightsArray, + shadowsArray: shadowsArray, - } + lights: lights + }; - } + return { + init: init, + state: state, + setupLights: setupLights, + setupLightsView: setupLightsView, - // texture + pushLight: pushLight, + pushShadow: pushShadow + }; - function activeTexture( webglSlot ) { + } - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; + function WebGLRenderStates( extensions, capabilities ) { - if ( currentTextureSlot !== webglSlot ) { + let renderStates = new WeakMap(); - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; + function get( scene, renderCallDepth = 0 ) { - } + let renderState; - } + if ( renderStates.has( scene ) === false ) { - function bindTexture( webglType, webglTexture ) { + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.set( scene, [ renderState ] ); - if ( currentTextureSlot === null ) { + } else { - activeTexture(); + if ( renderCallDepth >= renderStates.get( scene ).length ) { - } + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.get( scene ).push( renderState ); - let boundTexture = currentBoundTextures[ currentTextureSlot ]; + } else { - if ( boundTexture === undefined ) { + renderState = renderStates.get( scene )[ renderCallDepth ]; - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; + } } - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + return renderState; - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); + } - boundTexture.type = webglType; - boundTexture.texture = webglTexture; + function dispose() { - } + renderStates = new WeakMap(); } - function unbindTexture() { - - const boundTexture = currentBoundTextures[ currentTextureSlot ]; + return { + get: get, + dispose: dispose + }; - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { + } - gl.bindTexture( boundTexture.type, null ); + /** + * parameters = { + * + * opacity: , + * + * map: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - boundTexture.type = undefined; - boundTexture.texture = undefined; + class MeshDepthMaterial extends Material { - } + constructor( parameters ) { - } + super(); - function compressedTexImage2D() { + this.type = 'MeshDepthMaterial'; - try { + this.depthPacking = BasicDepthPacking; - gl.compressedTexImage2D.apply( gl, arguments ); + this.map = null; - } catch ( error ) { + this.alphaMap = null; - console.error( 'THREE.WebGLState:', error ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } + this.wireframe = false; + this.wireframeLinewidth = 1; - } + this.fog = false; - function texImage2D() { + this.setValues( parameters ); - try { + } - gl.texImage2D.apply( gl, arguments ); + copy( source ) { - } catch ( error ) { + super.copy( source ); - console.error( 'THREE.WebGLState:', error ); + this.depthPacking = source.depthPacking; - } + this.map = source.map; - } + this.alphaMap = source.alphaMap; - function texImage3D() { + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - try { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - gl.texImage3D.apply( gl, arguments ); + return this; - } catch ( error ) { + } - console.error( 'THREE.WebGLState:', error ); + } - } + MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - } + /** + * parameters = { + * + * referencePosition: , + * nearDistance: , + * farDistance: , + * + * map: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: + * + * } + */ - // + class MeshDistanceMaterial extends Material { - function scissor( scissor ) { + constructor( parameters ) { - if ( currentScissor.equals( scissor ) === false ) { + super(); - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); + this.type = 'MeshDistanceMaterial'; - } + this.referencePosition = new Vector3(); + this.nearDistance = 1; + this.farDistance = 1000; - } + this.map = null; - function viewport( viewport ) { + this.alphaMap = null; - if ( currentViewport.equals( viewport ) === false ) { + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); + this.fog = false; - } + this.setValues( parameters ); } - // + copy( source ) { - function reset() { + super.copy( source ); - enabledCapabilities = {}; + this.referencePosition.copy( source.referencePosition ); + this.nearDistance = source.nearDistance; + this.farDistance = source.farDistance; - currentTextureSlot = null; - currentBoundTextures = {}; + this.map = source.map; - currentProgram = null; + this.alphaMap = source.alphaMap; - currentBlendingEnabled = null; - currentBlending = null; - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - currentPremultipledAlpha = false; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - currentFlipSided = null; - currentCullFace = null; + return this; - currentLineWidth = null; + } - currentPolygonOffsetFactor = null; - currentPolygonOffsetUnits = null; + } - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); + MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - } + const vertex = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - return { + const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, + function WebGLShadowMap( _renderer, _objects, _capabilities ) { - enable: enable, - disable: disable, + let _frustum = new Frustum(); - useProgram: useProgram, + const _shadowMapSize = new Vector2(), + _viewportSize = new Vector2(), - setBlending: setBlending, - setMaterial: setMaterial, + _viewport = new Vector4(), - setFlipSided: setFlipSided, - setCullFace: setCullFace, + _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), + _distanceMaterial = new MeshDistanceMaterial(), - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, + _materialCache = {}, - setScissorTest: setScissorTest, + _maxTextureSize = _capabilities.maxTextureSize; - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, + const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - scissor: scissor, - viewport: viewport, + const shadowMaterialVertical = new ShaderMaterial( { + defines: { + VSM_SAMPLES: 8 + }, + uniforms: { + shadow_pass: { value: null }, + resolution: { value: new Vector2() }, + radius: { value: 4.0 } + }, - reset: reset + vertexShader: vertex, + fragmentShader: fragment - }; + } ); - } + const shadowMaterialHorizontal = shadowMaterialVertical.clone(); + shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { + const fullScreenTri = new BufferGeometry(); + fullScreenTri.setAttribute( + 'position', + new BufferAttribute( + new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), + 3 + ) + ); - const isWebGL2 = capabilities.isWebGL2; - const maxTextures = capabilities.maxTextures; - const maxCubemapSize = capabilities.maxCubemapSize; - const maxTextureSize = capabilities.maxTextureSize; - const maxSamples = capabilities.maxSamples; + const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - const _videoTextures = new WeakMap(); - let _canvas; + const scope = this; - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). + this.enabled = false; - let useOffscreenCanvas = false; + this.autoUpdate = true; + this.needsUpdate = false; - try { + this.type = PCFShadowMap; - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; + this.render = function ( lights, scene, camera ) { - } catch ( err ) { + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - // Ignore any errors + if ( lights.length === 0 ) return; - } + const currentRenderTarget = _renderer.getRenderTarget(); + const activeCubeFace = _renderer.getActiveCubeFace(); + const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - function createCanvas( width, height ) { + const _state = _renderer.state; - // Use OffscreenCanvas when available. Specially needed in web workers + // Set GL state for depth map. + _state.setBlending( NoBlending ); + _state.buffers.color.setClear( 1, 1, 1, 1 ); + _state.buffers.depth.setTest( true ); + _state.setScissorTest( false ); - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + // render depth map - } + for ( let i = 0, il = lights.length; i < il; i ++ ) { - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { + const light = lights[ i ]; + const shadow = light.shadow; - let scale = 1; + if ( shadow === undefined ) { - // handle case if texture exceeds max size + console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); + continue; - if ( image.width > maxSize || image.height > maxSize ) { + } - scale = maxSize / Math.max( image.width, image.height ); + if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - } + _shadowMapSize.copy( shadow.mapSize ); - // only perform resize if necessary + const shadowFrameExtents = shadow.getFrameExtents(); - if ( scale < 1 || needsPowerOfTwo === true ) { + _shadowMapSize.multiply( shadowFrameExtents ); - // only perform resize for certain image types + _viewportSize.copy( shadow.mapSize ); - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { + if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { - const floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor; + if ( _shadowMapSize.x > _maxTextureSize ) { - const width = floor( scale * image.width ); - const height = floor( scale * image.height ); + _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); + _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; + shadow.mapSize.x = _viewportSize.x; - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); + } - // cube textures can't reuse the same canvas + if ( _shadowMapSize.y > _maxTextureSize ) { - const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; + _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); + _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; + shadow.mapSize.y = _viewportSize.y; - canvas.width = width; - canvas.height = height; + } - const context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); + } - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); + if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - return canvas; + const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; - } else { + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + shadow.map.texture.name = light.name + '.shadowMap'; - if ( 'data' in image ) { + shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); + shadow.camera.updateProjectionMatrix(); - } + } - return image; + if ( shadow.map === null ) { - } + const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - } + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + shadow.map.texture.name = light.name + '.shadowMap'; - return image; + shadow.camera.updateProjectionMatrix(); - } + } - function isPowerOfTwo( image ) { + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); - return MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height ); + const viewportCount = shadow.getViewportCount(); - } + for ( let vp = 0; vp < viewportCount; vp ++ ) { - function textureNeedsPowerOfTwo( texture ) { + const viewport = shadow.getViewport( vp ); - if ( isWebGL2 ) return false; + _viewport.set( + _viewportSize.x * viewport.x, + _viewportSize.y * viewport.y, + _viewportSize.x * viewport.z, + _viewportSize.y * viewport.w + ); - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); + _state.viewport( _viewport ); - } + shadow.updateMatrices( light, vp ); - function textureNeedsGenerateMipmaps( texture, supportsMips ) { + _frustum = shadow.getFrustum(); - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; + renderObject( scene, camera, shadow.camera, light, this.type ); - } + } - function generateMipmap( target, texture, width, height ) { + // do blur pass for VSM - _gl.generateMipmap( target ); + if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - const textureProperties = properties.get( texture ); + VSMPass( shadow, camera ); - // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 - textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; + } - } + shadow.needsUpdate = false; - function getInternalFormat( internalFormatName, glFormat, glType ) { + } - if ( isWebGL2 === false ) return glFormat; + scope.needsUpdate = false; - if ( internalFormatName !== null ) { + _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; + }; - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); + function VSMPass( shadow, camera ) { - } + const geometry = _objects.update( fullScreenMesh ); - let internalFormat = glFormat; + if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { - if ( glFormat === 6403 ) { + shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; + shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; + shadowMaterialVertical.needsUpdate = true; + shadowMaterialHorizontal.needsUpdate = true; } - if ( glFormat === 6407 ) { + // vertical pass - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; + shadowMaterialVertical.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.mapPass ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - } + // horizontal pass - if ( glFormat === 6408 ) { + shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; + shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; + shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; + } - } + function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { + let result = null; - extensions.get( 'EXT_color_buffer_float' ); + const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; - } + if ( customMaterial !== undefined ) { - return internalFormat; + result = customMaterial; - } + } else { - // Fallback filters for non-power-of-2 textures + result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; - function filterFallback( f ) { + } - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { + if ( ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) || + ( material.displacementMap && material.displacementScale !== 0 ) || + ( material.alphaMap && material.alphaTest > 0 ) ) { - return 9728; + // in this case we need a unique material instance reflecting the + // appropriate state - } + const keyA = result.uuid, keyB = material.uuid; - return 9729; + let materialsForVariant = _materialCache[ keyA ]; - } + if ( materialsForVariant === undefined ) { - // + materialsForVariant = {}; + _materialCache[ keyA ] = materialsForVariant; - function onTextureDispose( event ) { + } - const texture = event.target; + let cachedMaterial = materialsForVariant[ keyB ]; - texture.removeEventListener( 'dispose', onTextureDispose ); + if ( cachedMaterial === undefined ) { - deallocateTexture( texture ); + cachedMaterial = result.clone(); + materialsForVariant[ keyB ] = cachedMaterial; - if ( texture.isVideoTexture ) { + } - _videoTextures.delete( texture ); + result = cachedMaterial; } - info.memory.textures --; + result.visible = material.visible; + result.wireframe = material.wireframe; - } + if ( type === VSMShadowMap ) { - function onRenderTargetDispose( event ) { + result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - const renderTarget = event.target; + } else { - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; - deallocateRenderTarget( renderTarget ); + } - info.memory.textures --; + result.alphaMap = material.alphaMap; + result.alphaTest = material.alphaTest; - } + result.clipShadows = material.clipShadows; + result.clippingPlanes = material.clippingPlanes; + result.clipIntersection = material.clipIntersection; - // + result.displacementMap = material.displacementMap; + result.displacementScale = material.displacementScale; + result.displacementBias = material.displacementBias; - function deallocateTexture( texture ) { + result.wireframeLinewidth = material.wireframeLinewidth; + result.linewidth = material.linewidth; - const textureProperties = properties.get( texture ); + if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - if ( textureProperties.__webglInit === undefined ) return; + result.referencePosition.setFromMatrixPosition( light.matrixWorld ); + result.nearDistance = shadowCameraNear; + result.farDistance = shadowCameraFar; - _gl.deleteTexture( textureProperties.__webglTexture ); + } - properties.remove( texture ); + return result; } - function deallocateRenderTarget( renderTarget ) { + function renderObject( object, camera, shadowCamera, light, type ) { - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( renderTarget.texture ); + if ( object.visible === false ) return; - if ( ! renderTarget ) return; + const visible = object.layers.test( camera.layers ); - if ( textureProperties.__webglTexture !== undefined ) { + if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - _gl.deleteTexture( textureProperties.__webglTexture ); + if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - } + object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - if ( renderTarget.depthTexture ) { + const geometry = _objects.update( object ); + const material = object.material; - renderTarget.depthTexture.dispose(); + if ( Array.isArray( material ) ) { - } + const groups = geometry.groups; - if ( renderTarget.isWebGLCubeRenderTarget ) { + for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - for ( let i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - - } + const group = groups[ k ]; + const groupMaterial = material[ group.materialIndex ]; - } else { + if ( groupMaterial && groupMaterial.visible ) { - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); - if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); + const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - } + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - properties.remove( renderTarget.texture ); - properties.remove( renderTarget ); + } - } + } - // + } else if ( material.visible ) { - let textureUnits = 0; + const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); - function resetTextureUnits() { + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - textureUnits = 0; + } - } + } - function allocateTextureUnit() { + } - const textureUnit = textureUnits; + const children = object.children; - if ( textureUnit >= maxTextures ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); + renderObject( children[ i ], camera, shadowCamera, light, type ); } - textureUnits += 1; - - return textureUnit; - } - // + } - function setTexture2D( texture, slot ) { + function WebGLState( gl, extensions, capabilities ) { - const textureProperties = properties.get( texture ); + const isWebGL2 = capabilities.isWebGL2; - if ( texture.isVideoTexture ) updateVideoTexture( texture ); + function ColorBuffer() { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + let locked = false; - const image = texture.image; + const color = new Vector4(); + let currentColorMask = null; + const currentColorClear = new Vector4( 0, 0, 0, 0 ); - if ( image === undefined ) { + return { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); + setMask: function ( colorMask ) { - } else if ( image.complete === false ) { + if ( currentColorMask !== colorMask && ! locked ) { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); + gl.colorMask( colorMask, colorMask, colorMask, colorMask ); + currentColorMask = colorMask; - } else { + } - uploadTexture( textureProperties, texture, slot ); - return; + }, - } + setLocked: function ( lock ) { - } + locked = lock; - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); + }, - } + setClear: function ( r, g, b, a, premultipliedAlpha ) { - function setTexture2DArray( texture, slot ) { + if ( premultipliedAlpha === true ) { - const textureProperties = properties.get( texture ); + r *= a; g *= a; b *= a; - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + } - uploadTexture( textureProperties, texture, slot ); - return; + color.set( r, g, b, a ); - } + if ( currentColorClear.equals( color ) === false ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); + gl.clearColor( r, g, b, a ); + currentColorClear.copy( color ); - } + } - function setTexture3D( texture, slot ) { + }, - const textureProperties = properties.get( texture ); + reset: function () { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + locked = false; - uploadTexture( textureProperties, texture, slot ); - return; + currentColorMask = null; + currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - } + } - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); + }; } - function setTextureCube( texture, slot ) { + function DepthBuffer() { - const textureProperties = properties.get( texture ); + let locked = false; - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + let currentDepthMask = null; + let currentDepthFunc = null; + let currentDepthClear = null; - uploadCubeTexture( textureProperties, texture, slot ); - return; + return { - } + setTest: function ( depthTest ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + if ( depthTest ) { - } + enable( 2929 ); - const wrappingToGL = { - [ RepeatWrapping ]: 10497, - [ ClampToEdgeWrapping ]: 33071, - [ MirroredRepeatWrapping ]: 33648 - }; + } else { - const filterToGL = { - [ NearestFilter ]: 9728, - [ NearestMipmapNearestFilter ]: 9984, - [ NearestMipmapLinearFilter ]: 9986, + disable( 2929 ); - [ LinearFilter ]: 9729, - [ LinearMipmapNearestFilter ]: 9985, - [ LinearMipmapLinearFilter ]: 9987 - }; + } - function setTextureParameters( textureType, texture, supportsMips ) { + }, - if ( supportsMips ) { + setMask: function ( depthMask ) { - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); + if ( currentDepthMask !== depthMask && ! locked ) { - if ( textureType === 32879 || textureType === 35866 ) { + gl.depthMask( depthMask ); + currentDepthMask = depthMask; - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); + } - } + }, - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); + setFunc: function ( depthFunc ) { - } else { + if ( currentDepthFunc !== depthFunc ) { - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); + if ( depthFunc ) { - if ( textureType === 32879 || textureType === 35866 ) { + switch ( depthFunc ) { - _gl.texParameteri( textureType, 32882, 33071 ); + case NeverDepth: - } + gl.depthFunc( 512 ); + break; - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { + case AlwaysDepth: - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); + gl.depthFunc( 519 ); + break; - } + case LessDepth: - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); + gl.depthFunc( 513 ); + break; - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { + case LessEqualDepth: - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); + gl.depthFunc( 515 ); + break; - } + case EqualDepth: - } + gl.depthFunc( 514 ); + break; - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + case GreaterEqualDepth: - if ( extension ) { + gl.depthFunc( 518 ); + break; - if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === HalfFloatType && ( isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) return; + case GreaterDepth: - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { + gl.depthFunc( 516 ); + break; - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; + case NotEqualDepth: - } + gl.depthFunc( 517 ); + break; - } + default: - } + gl.depthFunc( 515 ); - function initTexture( textureProperties, texture ) { + } - if ( textureProperties.__webglInit === undefined ) { + } else { - textureProperties.__webglInit = true; + gl.depthFunc( 515 ); - texture.addEventListener( 'dispose', onTextureDispose ); + } - textureProperties.__webglTexture = _gl.createTexture(); + currentDepthFunc = depthFunc; - info.memory.textures ++; + } - } + }, - } + setLocked: function ( lock ) { - function uploadTexture( textureProperties, texture, slot ) { + locked = lock; - let textureType = 3553; + }, - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; + setClear: function ( depth ) { - initTexture( textureProperties, texture ); + if ( currentDepthClear !== depth ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); + gl.clearDepth( depth ); + currentDepthClear = depth; - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); + } - const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + }, - const supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ); + reset: function () { - let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + locked = false; - setTextureParameters( textureType, texture, supportsMips ); + currentDepthMask = null; + currentDepthFunc = null; + currentDepthClear = null; - let mipmap; - const mipmaps = texture.mipmaps; + } - if ( texture.isDepthTexture ) { + }; - // populate depth texture with dummy data + } - glInternalFormat = 6402; + function StencilBuffer() { - if ( isWebGL2 ) { + let locked = false; - if ( texture.type === FloatType ) { + let currentStencilMask = null; + let currentStencilFunc = null; + let currentStencilRef = null; + let currentStencilFuncMask = null; + let currentStencilFail = null; + let currentStencilZFail = null; + let currentStencilZPass = null; + let currentStencilClear = null; - glInternalFormat = 36012; + return { - } else if ( texture.type === UnsignedIntType ) { + setTest: function ( stencilTest ) { - glInternalFormat = 33190; + if ( ! locked ) { - } else if ( texture.type === UnsignedInt248Type ) { + if ( stencilTest ) { - glInternalFormat = 35056; + enable( 2960 ); - } else { + } else { - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D + disable( 2960 ); + + } } - } else { + }, - if ( texture.type === FloatType ) { + setMask: function ( stencilMask ) { - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); + if ( currentStencilMask !== stencilMask && ! locked ) { - } + gl.stencilMask( stencilMask ); + currentStencilMask = stencilMask; - } + } - // validation checks for WebGL 1 + }, - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { + setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { + if ( currentStencilFunc !== stencilFunc || + currentStencilRef !== stencilRef || + currentStencilFuncMask !== stencilMask ) { - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); + gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilFuncMask = stencilMask; } - } - - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { + }, - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; + setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { + if ( currentStencilFail !== stencilFail || + currentStencilZFail !== stencilZFail || + currentStencilZPass !== stencilZPass ) { - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); + gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; } - } - - // + }, - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); + setLocked: function ( lock ) { - } else if ( texture.isDataTexture ) { + locked = lock; - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + }, - if ( mipmaps.length > 0 && supportsMips ) { + setClear: function ( stencil ) { - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + if ( currentStencilClear !== stencil ) { - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + gl.clearStencil( stencil ); + currentStencilClear = stencil; } - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + }, - } else { + reset: function () { - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + locked = false; + + currentStencilMask = null; + currentStencilFunc = null; + currentStencilRef = null; + currentStencilFuncMask = null; + currentStencilFail = null; + currentStencilZFail = null; + currentStencilZPass = null; + currentStencilClear = null; } - } else if ( texture.isCompressedTexture ) { + }; - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + } - mipmap = mipmaps[ i ]; + // - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + const colorBuffer = new ColorBuffer(); + const depthBuffer = new DepthBuffer(); + const stencilBuffer = new StencilBuffer(); - if ( glFormat !== null ) { + let enabledCapabilities = {}; - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + let xrFramebuffer = null; + let currentBoundFramebuffers = {}; - } else { + let currentProgram = null; - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); + let currentBlendingEnabled = false; + let currentBlending = null; + let currentBlendEquation = null; + let currentBlendSrc = null; + let currentBlendDst = null; + let currentBlendEquationAlpha = null; + let currentBlendSrcAlpha = null; + let currentBlendDstAlpha = null; + let currentPremultipledAlpha = false; - } + let currentFlipSided = null; + let currentCullFace = null; - } else { + let currentLineWidth = null; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + let currentPolygonOffsetFactor = null; + let currentPolygonOffsetUnits = null; - } + const maxTextures = gl.getParameter( 35661 ); - } + let lineWidthAvailable = false; + let version = 0; + const glVersion = gl.getParameter( 7938 ); - textureProperties.__maxMipLevel = mipmaps.length - 1; + if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - } else if ( texture.isDataTexture2DArray ) { + version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 1.0 ); - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - } else if ( texture.isDataTexture3D ) { + version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 2.0 ); - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + } - } else { + let currentTextureSlot = null; + let currentBoundTextures = {}; - // regular Texture (image, video, canvas) + const scissorParam = gl.getParameter( 3088 ); + const viewportParam = gl.getParameter( 2978 ); - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + const currentScissor = new Vector4().fromArray( scissorParam ); + const currentViewport = new Vector4().fromArray( viewportParam ); - if ( mipmaps.length > 0 && supportsMips ) { + function createTexture( type, target, count ) { - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. + const texture = gl.createTexture(); - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); + gl.bindTexture( type, texture ); + gl.texParameteri( type, 10241, 9728 ); + gl.texParameteri( type, 10240, 9728 ); - } + for ( let i = 0; i < count; i ++ ) { - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - } else { + } - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; + return texture; - } + } - } + const emptyTextures = {}; + emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); + emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + // init - generateMipmap( textureType, texture, image.width, image.height ); + colorBuffer.setClear( 0, 0, 0, 1 ); + depthBuffer.setClear( 1 ); + stencilBuffer.setClear( 0 ); - } + enable( 2929 ); + depthBuffer.setFunc( LessEqualDepth ); - textureProperties.__version = texture.version; + setFlipSided( false ); + setCullFace( CullFaceBack ); + enable( 2884 ); - if ( texture.onUpdate ) texture.onUpdate( texture ); + setBlending( NoBlending ); - } + // - function uploadCubeTexture( textureProperties, texture, slot ) { + function enable( id ) { - if ( texture.image.length !== 6 ) return; + if ( enabledCapabilities[ id ] !== true ) { - initTexture( textureProperties, texture ); + gl.enable( id ); + enabledCapabilities[ id ] = true; - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + } - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); + } - const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); + function disable( id ) { - const cubeImage = []; + if ( enabledCapabilities[ id ] !== false ) { - for ( let i = 0; i < 6; i ++ ) { + gl.disable( id ); + enabledCapabilities[ id ] = false; - if ( ! isCompressed && ! isDataTexture ) { + } - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); + } - } else { + function bindXRFramebuffer( framebuffer ) { - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + if ( framebuffer !== xrFramebuffer ) { - } + gl.bindFramebuffer( 36160, framebuffer ); + + xrFramebuffer = framebuffer; } - const image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( 34067, texture, supportsMips ); - - let mipmaps; - - if ( isCompressed ) { - - for ( let i = 0; i < 6; i ++ ) { + } - mipmaps = cubeImage[ i ].mipmaps; + function bindFramebuffer( target, framebuffer ) { - for ( let j = 0; j < mipmaps.length; j ++ ) { + if ( framebuffer === null && xrFramebuffer !== null ) framebuffer = xrFramebuffer; // use active XR framebuffer if available - const mipmap = mipmaps[ j ]; + if ( currentBoundFramebuffers[ target ] !== framebuffer ) { - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + gl.bindFramebuffer( target, framebuffer ); - if ( glFormat !== null ) { + currentBoundFramebuffers[ target ] = framebuffer; - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + if ( isWebGL2 ) { - } else { + // 36009 is equivalent to 36160 - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); + if ( target === 36009 ) { - } + currentBoundFramebuffers[ 36160 ] = framebuffer; - } else { + } - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + if ( target === 36160 ) { - } + currentBoundFramebuffers[ 36009 ] = framebuffer; } } - textureProperties.__maxMipLevel = mipmaps.length - 1; + return true; - } else { + } - mipmaps = texture.mipmaps; + return false; - for ( let i = 0; i < 6; i ++ ) { + } - if ( isDataTexture ) { + function useProgram( program ) { - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + if ( currentProgram !== program ) { - for ( let j = 0; j < mipmaps.length; j ++ ) { + gl.useProgram( program ); - const mipmap = mipmaps[ j ]; - const mipmapImage = mipmap.image[ i ].image; + currentProgram = program; - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); + return true; - } + } - } else { + return false; - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); + } - for ( let j = 0; j < mipmaps.length; j ++ ) { + const equationToGL = { + [ AddEquation ]: 32774, + [ SubtractEquation ]: 32778, + [ ReverseSubtractEquation ]: 32779 + }; - const mipmap = mipmaps[ j ]; + if ( isWebGL2 ) { - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); + equationToGL[ MinEquation ] = 32775; + equationToGL[ MaxEquation ] = 32776; - } + } else { - } + const extension = extensions.get( 'EXT_blend_minmax' ); - } + if ( extension !== null ) { - textureProperties.__maxMipLevel = mipmaps.length; + equationToGL[ MinEquation ] = extension.MIN_EXT; + equationToGL[ MaxEquation ] = extension.MAX_EXT; } - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + } - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); + const factorToGL = { + [ ZeroFactor ]: 0, + [ OneFactor ]: 1, + [ SrcColorFactor ]: 768, + [ SrcAlphaFactor ]: 770, + [ SrcAlphaSaturateFactor ]: 776, + [ DstColorFactor ]: 774, + [ DstAlphaFactor ]: 772, + [ OneMinusSrcColorFactor ]: 769, + [ OneMinusSrcAlphaFactor ]: 771, + [ OneMinusDstColorFactor ]: 775, + [ OneMinusDstAlphaFactor ]: 773 + }; - } + function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - textureProperties.__version = texture.version; + if ( blending === NoBlending ) { - if ( texture.onUpdate ) texture.onUpdate( texture ); + if ( currentBlendingEnabled === true ) { - } + disable( 3042 ); + currentBlendingEnabled = false; - // Render targets + } - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { + return; - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( 36160, null ); + } - } + if ( currentBlendingEnabled === false ) { - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { + enable( 3042 ); + currentBlendingEnabled = true; - _gl.bindRenderbuffer( 36161, renderbuffer ); + } - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + if ( blending !== CustomBlending ) { - let glInternalFormat = 33189; + if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - if ( isMultisample ) { + if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - const depthTexture = renderTarget.depthTexture; + gl.blendEquation( 32774 ); - if ( depthTexture && depthTexture.isDepthTexture ) { + currentBlendEquation = AddEquation; + currentBlendEquationAlpha = AddEquation; - if ( depthTexture.type === FloatType ) { + } - glInternalFormat = 36012; + if ( premultipliedAlpha ) { - } else if ( depthTexture.type === UnsignedIntType ) { + switch ( blending ) { - glInternalFormat = 33190; + case NormalBlending: + gl.blendFuncSeparate( 1, 771, 1, 771 ); + break; - } + case AdditiveBlending: + gl.blendFunc( 1, 1 ); + break; - } + case SubtractiveBlending: + gl.blendFuncSeparate( 0, 0, 769, 771 ); + break; - const samples = getRenderTargetSamples( renderTarget ); + case MultiplyBlending: + gl.blendFuncSeparate( 0, 768, 0, 770 ); + break; - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - } else { + } - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + } else { - } + switch ( blending ) { - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); + case NormalBlending: + gl.blendFuncSeparate( 770, 771, 1, 771 ); + break; - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + case AdditiveBlending: + gl.blendFunc( 770, 1 ); + break; - if ( isMultisample ) { + case SubtractiveBlending: + gl.blendFunc( 0, 769 ); + break; - const samples = getRenderTargetSamples( renderTarget ); + case MultiplyBlending: + gl.blendFunc( 0, 768 ); + break; - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - } else { + } - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); + } + + currentBlendSrc = null; + currentBlendDst = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; } + return; - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); + } - } else { + // custom blending - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - if ( isMultisample ) { + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - const samples = getRenderTargetSamples( renderTarget ); + gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - } else { + } - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - } + gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); + + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; } - _gl.bindRenderbuffer( 36161, null ); + currentBlending = blending; + currentPremultipledAlpha = null; } - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { + function setMaterial( material, frontFaceCW ) { - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); + material.side === DoubleSide + ? disable( 2884 ) + : enable( 2884 ); - _gl.bindFramebuffer( 36160, framebuffer ); + let flipSided = ( material.side === BackSide ); + if ( frontFaceCW ) flipSided = ! flipSided; - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { + setFlipSided( flipSided ); - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); + ( material.blending === NormalBlending && material.transparent === false ) + ? setBlending( NoBlending ) + : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - } + depthBuffer.setFunc( material.depthFunc ); + depthBuffer.setTest( material.depthTest ); + depthBuffer.setMask( material.depthWrite ); + colorBuffer.setMask( material.colorWrite ); - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { + const stencilWrite = material.stencilWrite; + stencilBuffer.setTest( stencilWrite ); + if ( stencilWrite ) { - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; + stencilBuffer.setMask( material.stencilWriteMask ); + stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); + stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); } - setTexture2D( renderTarget.depthTexture, 0 ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + material.alphaToCoverage === true + ? enable( 32926 ) + : disable( 32926 ); - if ( renderTarget.depthTexture.format === DepthFormat ) { + } - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); + // - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + function setFlipSided( flipSided ) { - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); + if ( currentFlipSided !== flipSided ) { - } else { + if ( flipSided ) { - throw new Error( 'Unknown depthTexture format' ); + gl.frontFace( 2304 ); + + } else { + + gl.frontFace( 2305 ); + + } + + currentFlipSided = flipSided; } } - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); + function setCullFace( cullFace ) { - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + if ( cullFace !== CullFaceNone ) { - if ( renderTarget.depthTexture ) { + enable( 2884 ); - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); + if ( cullFace !== currentCullFace ) { - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); + if ( cullFace === CullFaceBack ) { - } else { + gl.cullFace( 1029 ); - if ( isCube ) { + } else if ( cullFace === CullFaceFront ) { - renderTargetProperties.__webglDepthbuffer = []; + gl.cullFace( 1028 ); - for ( let i = 0; i < 6; i ++ ) { + } else { - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); + gl.cullFace( 1032 ); } - } else { + } - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); + } else { - } + disable( 2884 ); } - _gl.bindFramebuffer( 36160, null ); + currentCullFace = cullFace; } - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( renderTarget.texture ); - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; + function setLineWidth( width ) { - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; + if ( width !== currentLineWidth ) { - // Handles WebGL2 RGBFormat fallback - #18858 + if ( lineWidthAvailable ) gl.lineWidth( width ); - if ( isWebGL2 && renderTarget.texture.format === RGBFormat && ( renderTarget.texture.type === FloatType || renderTarget.texture.type === HalfFloatType ) ) { + currentLineWidth = width; - renderTarget.texture.format = RGBAFormat; + } - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); + } - } + function setPolygonOffset( polygonOffset, factor, units ) { - // Setup framebuffer + if ( polygonOffset ) { - if ( isCube ) { + enable( 32823 ); - renderTargetProperties.__webglFramebuffer = []; + if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - for ( let i = 0; i < 6; i ++ ) { + gl.polygonOffset( factor, units ); - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; } } else { - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - - if ( isMultisample ) { + disable( 32823 ); - if ( isWebGL2 ) { + } - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + } - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); + function setScissorTest( scissorTest ) { - const glFormat = utils.convert( renderTarget.texture.format ); - const glType = utils.convert( renderTarget.texture.type ); - const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - const samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + if ( scissorTest ) { - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); + enable( 3089 ); - if ( renderTarget.depthBuffer ) { + } else { - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); + disable( 3089 ); - } + } - _gl.bindFramebuffer( 36160, null ); + } + // texture - } else { + function activeTexture( webglSlot ) { - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; - } + if ( currentTextureSlot !== webglSlot ) { - } + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; } - // Setup color buffer - - if ( isCube ) { + } - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, renderTarget.texture, supportsMips ); + function bindTexture( webglType, webglTexture ) { - for ( let i = 0; i < 6; i ++ ) { + if ( currentTextureSlot === null ) { - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); + activeTexture(); - } + } - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { + let boundTexture = currentBoundTextures[ currentTextureSlot ]; - generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height ); + if ( boundTexture === undefined ) { - } + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; - state.bindTexture( 34067, null ); + } - } else { + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - state.bindTexture( 3553, textureProperties.__webglTexture ); - setTextureParameters( 3553, renderTarget.texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 ); + gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { + boundTexture.type = webglType; + boundTexture.texture = webglTexture; - generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height ); + } - } + } - state.bindTexture( 3553, null ); + function unbindTexture() { - } + const boundTexture = currentBoundTextures[ currentTextureSlot ]; - // Setup depth and stencil buffers + if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - if ( renderTarget.depthBuffer ) { + gl.bindTexture( boundTexture.type, null ); - setupDepthRenderbuffer( renderTarget ); + boundTexture.type = undefined; + boundTexture.texture = undefined; } } - function updateRenderTargetMipmap( renderTarget ) { + function compressedTexImage2D() { - const texture = renderTarget.texture; - const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; + try { - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + gl.compressedTexImage2D.apply( gl, arguments ); - const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - const webglTexture = properties.get( texture ).__webglTexture; + } catch ( error ) { - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); + console.error( 'THREE.WebGLState:', error ); } } - function updateMultisampleRenderTarget( renderTarget ) { + function texImage2D() { - if ( renderTarget.isWebGLMultisampleRenderTarget ) { + try { - if ( isWebGL2 ) { + gl.texImage2D.apply( gl, arguments ); - const renderTargetProperties = properties.get( renderTarget ); + } catch ( error ) { - _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); + console.error( 'THREE.WebGLState:', error ); - const width = renderTarget.width; - const height = renderTarget.height; - let mask = 16384; + } - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; + } - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); + function texImage3D() { - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); // see #18905 + try { - } else { + gl.texImage3D.apply( gl, arguments ); - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + } catch ( error ) { - } + console.error( 'THREE.WebGLState:', error ); } } - function getRenderTargetSamples( renderTarget ) { + // - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; + function scissor( scissor ) { - } + if ( currentScissor.equals( scissor ) === false ) { - function updateVideoTexture( texture ) { + gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); + currentScissor.copy( scissor ); - const frame = info.render.frame; + } - // Check the last frame we updated the VideoTexture + } - if ( _videoTextures.get( texture ) !== frame ) { + function viewport( viewport ) { - _videoTextures.set( texture, frame ); - texture.update(); + if ( currentViewport.equals( viewport ) === false ) { + + gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); + currentViewport.copy( viewport ); } } - // backwards compatibility + // - let warnedTexture2D = false; - let warnedTextureCube = false; + function reset() { - function safeSetTexture2D( texture, slot ) { + // reset state - if ( texture && texture.isWebGLRenderTarget ) { + gl.disable( 3042 ); + gl.disable( 2884 ); + gl.disable( 2929 ); + gl.disable( 32823 ); + gl.disable( 3089 ); + gl.disable( 2960 ); + gl.disable( 32926 ); - if ( warnedTexture2D === false ) { + gl.blendEquation( 32774 ); + gl.blendFunc( 1, 0 ); + gl.blendFuncSeparate( 1, 0, 1, 0 ); - console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' ); - warnedTexture2D = true; + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); - } + gl.depthMask( true ); + gl.depthFunc( 513 ); + gl.clearDepth( 1 ); - texture = texture.texture; + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( 519, 0, 0xffffffff ); + gl.stencilOp( 7680, 7680, 7680 ); + gl.clearStencil( 0 ); - } + gl.cullFace( 1029 ); + gl.frontFace( 2305 ); - setTexture2D( texture, slot ); + gl.polygonOffset( 0, 0 ); - } + gl.activeTexture( 33984 ); - function safeSetTextureCube( texture, slot ) { + gl.bindFramebuffer( 36160, null ); - if ( texture && texture.isWebGLCubeRenderTarget ) { + if ( isWebGL2 === true ) { - if ( warnedTextureCube === false ) { + gl.bindFramebuffer( 36009, null ); + gl.bindFramebuffer( 36008, null ); - console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' ); - warnedTextureCube = true; + } - } + gl.useProgram( null ); - texture = texture.texture; + gl.lineWidth( 1 ); - } + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); + // reset internals - setTextureCube( texture, slot ); + enabledCapabilities = {}; - } + currentTextureSlot = null; + currentBoundTextures = {}; - // + xrFramebuffer = null; + currentBoundFramebuffers = {}; - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; + currentProgram = null; - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + currentBlendingEnabled = false; + currentBlending = null; + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + currentPremultipledAlpha = false; - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; + currentFlipSided = null; + currentCullFace = null; - } + currentLineWidth = null; - function WebGLUtils( gl, extensions, capabilities ) { + currentPolygonOffsetFactor = null; + currentPolygonOffsetUnits = null; - const isWebGL2 = capabilities.isWebGL2; + currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); - function convert( p ) { + colorBuffer.reset(); + depthBuffer.reset(); + stencilBuffer.reset(); - let extension; + } - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; + return { - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; + buffers: { + color: colorBuffer, + depth: depthBuffer, + stencil: stencilBuffer + }, - if ( p === HalfFloatType ) { + enable: enable, + disable: disable, - if ( isWebGL2 ) return 5131; + bindFramebuffer: bindFramebuffer, + bindXRFramebuffer: bindXRFramebuffer, - extension = extensions.get( 'OES_texture_half_float' ); + useProgram: useProgram, - if ( extension !== null ) { + setBlending: setBlending, + setMaterial: setMaterial, - return extension.HALF_FLOAT_OES; + setFlipSided: setFlipSided, + setCullFace: setCullFace, - } else { + setLineWidth: setLineWidth, + setPolygonOffset: setPolygonOffset, - return null; + setScissorTest: setScissorTest, - } + activeTexture: activeTexture, + bindTexture: bindTexture, + unbindTexture: unbindTexture, + compressedTexImage2D: compressedTexImage2D, + texImage2D: texImage2D, + texImage3D: texImage3D, - } + scissor: scissor, + viewport: viewport, - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; + reset: reset - // WebGL2 formats. + }; - if ( p === RedIntegerFormat ) return 36244; - if ( p === RGFormat ) return 33319; - if ( p === RGIntegerFormat ) return 33320; - if ( p === RGBIntegerFormat ) return 36248; - if ( p === RGBAIntegerFormat ) return 36249; + } - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { + function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + const isWebGL2 = capabilities.isWebGL2; + const maxTextures = capabilities.maxTextures; + const maxCubemapSize = capabilities.maxCubemapSize; + const maxTextureSize = capabilities.maxTextureSize; + const maxSamples = capabilities.maxSamples; - if ( extension !== null ) { + const _videoTextures = new WeakMap(); + let _canvas; - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, + // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! + // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - } else { + let useOffscreenCanvas = false; - return null; + try { - } + useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' + && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; - } + } catch ( err ) { - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { + // Ignore any errors - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + } - if ( extension !== null ) { + function createCanvas( width, height ) { - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + // Use OffscreenCanvas when available. Specially needed in web workers - } else { + return useOffscreenCanvas ? + new OffscreenCanvas( width, height ) : createElementNS( 'canvas' ); - return null; + } - } + function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - } + let scale = 1; - if ( p === RGB_ETC1_Format ) { + // handle case if texture exceeds max size - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); + if ( image.width > maxSize || image.height > maxSize ) { - if ( extension !== null ) { + scale = maxSize / Math.max( image.width, image.height ); - return extension.COMPRESSED_RGB_ETC1_WEBGL; + } - } else { + // only perform resize if necessary - return null; + if ( scale < 1 || needsPowerOfTwo === true ) { - } + // only perform resize for certain image types - } + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { + const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); + const width = floor( scale * image.width ); + const height = floor( scale * image.height ); - if ( extension !== null ) { + if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; - if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; + // cube textures can't reuse the same canvas - } + const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - } + canvas.width = width; + canvas.height = height; - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); + console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - if ( extension !== null ) { + return canvas; - // TODO Complete? + } else { - return p; + if ( 'data' in image ) { - } else { + console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - return null; + } + + return image; } } - if ( p === RGBA_BPTC_Format ) { - - extension = extensions.get( 'EXT_texture_compression_bptc' ); - - if ( extension !== null ) { + return image; - // TODO Complete? + } - return p; + function isPowerOfTwo$1( image ) { - } else { + return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); - return null; + } - } + function textureNeedsPowerOfTwo( texture ) { - } + if ( isWebGL2 ) return false; - if ( p === UnsignedInt248Type ) { + return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || + ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - if ( isWebGL2 ) return 34042; + } - extension = extensions.get( 'WEBGL_depth_texture' ); + function textureNeedsGenerateMipmaps( texture, supportsMips ) { - if ( extension !== null ) { + return texture.generateMipmaps && supportsMips && + texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - return extension.UNSIGNED_INT_24_8_WEBGL; + } - } else { + function generateMipmap( target, texture, width, height, depth = 1 ) { - return null; + _gl.generateMipmap( target ); - } + const textureProperties = properties.get( texture ); - } + textureProperties.__maxMipLevel = Math.log2( Math.max( width, height, depth ) ); } - return { convert: convert }; - - } - - function ArrayCamera( array = [] ) { + function getInternalFormat( internalFormatName, glFormat, glType, encoding ) { - PerspectiveCamera.call( this ); + if ( isWebGL2 === false ) return glFormat; - this.cameras = array; + if ( internalFormatName !== null ) { - } + if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { + console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); - constructor: ArrayCamera, + } - isArrayCamera: true + let internalFormat = glFormat; - } ); + if ( glFormat === 6403 ) { - function Group() { + if ( glType === 5126 ) internalFormat = 33326; + if ( glType === 5131 ) internalFormat = 33325; + if ( glType === 5121 ) internalFormat = 33321; - Object3D.call( this ); + } - this.type = 'Group'; + if ( glFormat === 6407 ) { - } + if ( glType === 5126 ) internalFormat = 34837; + if ( glType === 5131 ) internalFormat = 34843; + if ( glType === 5121 ) internalFormat = 32849; - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { + } - constructor: Group, + if ( glFormat === 6408 ) { - isGroup: true + if ( glType === 5126 ) internalFormat = 34836; + if ( glType === 5131 ) internalFormat = 34842; + if ( glType === 5121 ) internalFormat = ( encoding === sRGBEncoding ) ? 35907 : 32856; - } ); + } - function WebXRController() { + if ( internalFormat === 33325 || internalFormat === 33326 || + internalFormat === 34842 || internalFormat === 34836 ) { - this._targetRay = null; - this._grip = null; - this._hand = null; + extensions.get( 'EXT_color_buffer_float' ); - } + } - Object.assign( WebXRController.prototype, { + return internalFormat; - constructor: WebXRController, + } - getHandSpace: function () { + // Fallback filters for non-power-of-2 textures - if ( this._hand === null ) { + function filterFallback( f ) { - this._hand = new Group(); - this._hand.matrixAutoUpdate = false; - this._hand.visible = false; + if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - this._hand.joints = {}; - this._hand.inputState = { pinching: false }; + return 9728; } - return this._hand; - - }, - - getTargetRaySpace: function () { + return 9729; - if ( this._targetRay === null ) { + } - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; + // - } + function onTextureDispose( event ) { - return this._targetRay; + const texture = event.target; - }, + texture.removeEventListener( 'dispose', onTextureDispose ); - getGripSpace: function () { + deallocateTexture( texture ); - if ( this._grip === null ) { + if ( texture.isVideoTexture ) { - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; + _videoTextures.delete( texture ); } - return this._grip; + info.memory.textures --; - }, + } - dispatchEvent: function ( event ) { + function onRenderTargetDispose( event ) { - if ( this._targetRay !== null ) { + const renderTarget = event.target; - this._targetRay.dispatchEvent( event ); + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - } + deallocateRenderTarget( renderTarget ); - if ( this._grip !== null ) { + } - this._grip.dispatchEvent( event ); + // - } + function deallocateTexture( texture ) { - if ( this._hand !== null ) { + const textureProperties = properties.get( texture ); - this._hand.dispatchEvent( event ); + if ( textureProperties.__webglInit === undefined ) return; - } + _gl.deleteTexture( textureProperties.__webglTexture ); - return this; + properties.remove( texture ); - }, + } - disconnect: function ( inputSource ) { + function deallocateRenderTarget( renderTarget ) { - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); + const texture = renderTarget.texture; - if ( this._targetRay !== null ) { + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - this._targetRay.visible = false; + if ( ! renderTarget ) return; - } + if ( textureProperties.__webglTexture !== undefined ) { - if ( this._grip !== null ) { + _gl.deleteTexture( textureProperties.__webglTexture ); - this._grip.visible = false; + info.memory.textures --; } - if ( this._hand !== null ) { + if ( renderTarget.depthTexture ) { - this._hand.visible = false; + renderTarget.depthTexture.dispose(); } - return this; - - }, - - update: function ( inputSource, frame, referenceSpace ) { - - let inputPose = null; - let gripPose = null; - let handPose = null; - - const targetRay = this._targetRay; - const grip = this._grip; - const hand = this._hand; - - if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { + if ( renderTarget.isWebGLCubeRenderTarget ) { - if ( hand && inputSource.hand ) { + for ( let i = 0; i < 6; i ++ ) { - handPose = true; + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - for ( const inputjoint of inputSource.hand.values() ) { + } - // Update the joints groups with the XRJoint poses - const jointPose = frame.getJointPose( inputjoint, referenceSpace ); + } else { - if ( hand.joints[ inputjoint.jointName ] === undefined ) { + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); + if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); + if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); + if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); - // The transform of this joint will be updated with the joint pose on each frame - const joint = new Group(); - joint.matrixAutoUpdate = false; - joint.visible = false; - hand.joints[ inputjoint.jointName ] = joint; - // ?? - hand.add( joint ); + } - } + if ( renderTarget.isWebGLMultipleRenderTargets ) { - const joint = hand.joints[ inputjoint.jointName ]; + for ( let i = 0, il = texture.length; i < il; i ++ ) { - if ( jointPose !== null ) { + const attachmentProperties = properties.get( texture[ i ] ); - joint.matrix.fromArray( jointPose.transform.matrix ); - joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); - joint.jointRadius = jointPose.radius; + if ( attachmentProperties.__webglTexture ) { - } + _gl.deleteTexture( attachmentProperties.__webglTexture ); - joint.visible = jointPose !== null; + info.memory.textures --; } - // Custom events + properties.remove( texture[ i ] ); - // Check pinchz - const indexTip = hand.joints[ 'index-finger-tip' ]; - const thumbTip = hand.joints[ 'thumb-tip' ]; - const distance = indexTip.position.distanceTo( thumbTip.position ); + } - const distanceToPinch = 0.02; - const threshold = 0.005; + } - if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { + properties.remove( texture ); + properties.remove( renderTarget ); - hand.inputState.pinching = false; - this.dispatchEvent( { - type: 'pinchend', - handedness: inputSource.handedness, - target: this - } ); + } - } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { + // - hand.inputState.pinching = true; - this.dispatchEvent( { - type: 'pinchstart', - handedness: inputSource.handedness, - target: this - } ); + let textureUnits = 0; - } + function resetTextureUnits() { - } else { + textureUnits = 0; - if ( targetRay !== null ) { + } - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); + function allocateTextureUnit() { - if ( inputPose !== null ) { + const textureUnit = textureUnits; - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); + if ( textureUnit >= maxTextures ) { - } + console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - } + } - if ( grip !== null && inputSource.gripSpace ) { + textureUnits += 1; - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); + return textureUnit; - if ( gripPose !== null ) { + } - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); + // - } + function setTexture2D( texture, slot ) { - } + const textureProperties = properties.get( texture ); - } + if ( texture.isVideoTexture ) updateVideoTexture( texture ); - } + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - if ( targetRay !== null ) { + const image = texture.image; - targetRay.visible = ( inputPose !== null ); + if ( image === undefined ) { - } + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); - if ( grip !== null ) { + } else if ( image.complete === false ) { - grip.visible = ( gripPose !== null ); + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - } + } else { - if ( hand !== null ) { + uploadTexture( textureProperties, texture, slot ); + return; - hand.visible = ( handPose !== null ); + } } - return this; + state.activeTexture( 33984 + slot ); + state.bindTexture( 3553, textureProperties.__webglTexture ); } - } ); + function setTexture2DArray( texture, slot ) { - function WebXRManager( renderer, gl ) { + const textureProperties = properties.get( texture ); - const scope = this; + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - let session = null; + uploadTexture( textureProperties, texture, slot ); + return; - let framebufferScaleFactor = 1.0; + } - let referenceSpace = null; - let referenceSpaceType = 'local-floor'; + state.activeTexture( 33984 + slot ); + state.bindTexture( 35866, textureProperties.__webglTexture ); - let pose = null; + } - const controllers = []; - const inputSourcesMap = new Map(); + function setTexture3D( texture, slot ) { - // + const textureProperties = properties.get( texture ); - const cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - const cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); + uploadTexture( textureProperties, texture, slot ); + return; - const cameras = [ cameraL, cameraR ]; + } - const cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 32879, textureProperties.__webglTexture ); - let _currentDepthNear = null; - let _currentDepthFar = null; + } - // + function setTextureCube( texture, slot ) { - this.enabled = false; + const textureProperties = properties.get( texture ); - this.isPresenting = false; + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - this.getController = function ( index ) { + uploadCubeTexture( textureProperties, texture, slot ); + return; - let controller = controllers[ index ]; + } - if ( controller === undefined ) { + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); - controller = new WebXRController(); - controllers[ index ] = controller; + } - } + const wrappingToGL = { + [ RepeatWrapping ]: 10497, + [ ClampToEdgeWrapping ]: 33071, + [ MirroredRepeatWrapping ]: 33648 + }; - return controller.getTargetRaySpace(); + const filterToGL = { + [ NearestFilter ]: 9728, + [ NearestMipmapNearestFilter ]: 9984, + [ NearestMipmapLinearFilter ]: 9986, + [ LinearFilter ]: 9729, + [ LinearMipmapNearestFilter ]: 9985, + [ LinearMipmapLinearFilter ]: 9987 }; - this.getControllerGrip = function ( index ) { + function setTextureParameters( textureType, texture, supportsMips ) { - let controller = controllers[ index ]; + if ( supportsMips ) { - if ( controller === undefined ) { + _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); + _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - controller = new WebXRController(); - controllers[ index ] = controller; + if ( textureType === 32879 || textureType === 35866 ) { - } + _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); - return controller.getGripSpace(); + } - }; + _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); + _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - this.getHand = function ( index ) { + } else { - let controller = controllers[ index ]; + _gl.texParameteri( textureType, 10242, 33071 ); + _gl.texParameteri( textureType, 10243, 33071 ); - if ( controller === undefined ) { + if ( textureType === 32879 || textureType === 35866 ) { - controller = new WebXRController(); - controllers[ index ] = controller; + _gl.texParameteri( textureType, 32882, 33071 ); - } + } - return controller.getHandSpace(); + if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - }; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - // + } - function onSessionEvent( event ) { + _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - const controller = inputSourcesMap.get( event.inputSource ); + if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - if ( controller ) { + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - controller.dispatchEvent( { type: event.type, data: event.inputSource } ); + } } - } + if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - function onSessionEnd() { + const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - inputSourcesMap.forEach( function ( controller, inputSource ) { + if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 + if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only - controller.disconnect( inputSource ); + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - } ); + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; - inputSourcesMap.clear(); + } - _currentDepthNear = null; - _currentDepthFar = null; + } - // + } - renderer.setFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 - animation.stop(); + function initTexture( textureProperties, texture ) { - scope.isPresenting = false; + if ( textureProperties.__webglInit === undefined ) { - scope.dispatchEvent( { type: 'sessionend' } ); + textureProperties.__webglInit = true; - } + texture.addEventListener( 'dispose', onTextureDispose ); - this.setFramebufferScaleFactor = function ( value ) { + textureProperties.__webglTexture = _gl.createTexture(); - framebufferScaleFactor = value; + info.memory.textures ++; - if ( scope.isPresenting === true ) { + } - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); + } - } + function uploadTexture( textureProperties, texture, slot ) { - }; + let textureType = 3553; - this.setReferenceSpaceType = function ( value ) { + if ( texture.isDataTexture2DArray ) textureType = 35866; + if ( texture.isDataTexture3D ) textureType = 32879; - referenceSpaceType = value; + initTexture( textureProperties, texture ); - if ( scope.isPresenting === true ) { + state.activeTexture( 33984 + slot ); + state.bindTexture( textureType, textureProperties.__webglTexture ); - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); + _gl.pixelStorei( 37440, texture.flipY ); + _gl.pixelStorei( 37441, texture.premultiplyAlpha ); + _gl.pixelStorei( 3317, texture.unpackAlignment ); + _gl.pixelStorei( 37443, 0 ); - } + const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; + const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); - }; + const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format ); - this.getReferenceSpace = function () { + let glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); - return referenceSpace; + setTextureParameters( textureType, texture, supportsMips ); - }; + let mipmap; + const mipmaps = texture.mipmaps; - this.getSession = function () { + if ( texture.isDepthTexture ) { - return session; + // populate depth texture with dummy data - }; + glInternalFormat = 6402; - this.setSession = async function ( value ) { + if ( isWebGL2 ) { - session = value; + if ( texture.type === FloatType ) { - if ( session !== null ) { + glInternalFormat = 36012; - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - session.addEventListener( 'inputsourceschange', onInputSourcesChange ); + } else if ( texture.type === UnsignedIntType ) { - const attributes = gl.getContextAttributes(); + glInternalFormat = 33190; - if ( attributes.xrCompatible !== true ) { + } else if ( texture.type === UnsignedInt248Type ) { - await gl.makeXRCompatible(); + glInternalFormat = 35056; - } + } else { - const layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; + glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - // eslint-disable-next-line no-undef - const baseLayer = new XRWebGLLayer( session, gl, layerInit ); + } - session.updateRenderState( { baseLayer: baseLayer } ); + } else { - referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); + if ( texture.type === FloatType ) { - animation.setContext( session ); - animation.start(); + console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - scope.isPresenting = true; + } - scope.dispatchEvent( { type: 'sessionstart' } ); + } - } + // validation checks for WebGL 1 - }; + if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - function onInputSourcesChange( event ) { + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - const inputSources = session.inputSources; + console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - // Assign inputSources to available controllers + texture.type = UnsignedShortType; + glType = utils.convert( texture.type ); - for ( let i = 0; i < controllers.length; i ++ ) { + } - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); + } - } + if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - // Notify disconnected + // Depth stencil textures need the DEPTH_STENCIL internal format + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + glInternalFormat = 34041; - for ( let i = 0; i < event.removed.length; i ++ ) { + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedInt248Type ) { - const inputSource = event.removed[ i ]; - const controller = inputSourcesMap.get( inputSource ); + console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - if ( controller ) { + texture.type = UnsignedInt248Type; + glType = utils.convert( texture.type ); - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); + } } - } + // - // Notify connected + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - for ( let i = 0; i < event.added.length; i ++ ) { + } else if ( texture.isDataTexture ) { - const inputSource = event.added[ i ]; - const controller = inputSourcesMap.get( inputSource ); + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - if ( controller ) { + if ( mipmaps.length > 0 && supportsMips ) { - controller.dispatchEvent( { type: 'connected', data: inputSource } ); + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - } + mipmap = mipmaps[ i ]; + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } + } - } + texture.generateMipmaps = false; + textureProperties.__maxMipLevel = mipmaps.length - 1; - // + } else { - const cameraLPos = new Vector3(); - const cameraRPos = new Vector3(); + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); + textureProperties.__maxMipLevel = 0; - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { + } - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); + } else if ( texture.isCompressedTexture ) { - const ipd = cameraLPos.distanceTo( cameraRPos ); + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - const projL = cameraL.projectionMatrix.elements; - const projR = cameraR.projectionMatrix.elements; + mipmap = mipmaps[ i ]; - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; + if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - const left = near * leftFov; - const right = near * rightFov; + if ( glFormat !== null ) { - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - const zOffset = ipd / ( - leftFov + rightFov ); - const xOffset = zOffset * - leftFov; + state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + } else { - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - const near2 = near + zOffset; - const far2 = far + zOffset; - const left2 = left - xOffset; - const right2 = right + ( ipd - xOffset ); - const top2 = topFov * far / far2 * near2; - const bottom2 = bottomFov * far / far2 * near2; + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); + } - } + } else { - function updateCamera( camera, parent ) { + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - if ( parent === null ) { + } - camera.matrixWorld.copy( camera.matrix ); + } - } else { + textureProperties.__maxMipLevel = mipmaps.length - 1; - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); + } else if ( texture.isDataTexture2DArray ) { - } + state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); + textureProperties.__maxMipLevel = 0; - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + } else if ( texture.isDataTexture3D ) { - } + state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); + textureProperties.__maxMipLevel = 0; - this.getCamera = function ( camera ) { + } else { - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; + // regular Texture (image, video, canvas) - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - // Note that the new renderState won't apply until the next frame. See #18320 + if ( mipmaps.length > 0 && supportsMips ) { - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; + mipmap = mipmaps[ i ]; + state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - } + } - const parent = camera.parent; - const cameras = cameraVR.cameras; + texture.generateMipmaps = false; + textureProperties.__maxMipLevel = mipmaps.length - 1; - updateCamera( cameraVR, parent ); + } else { - for ( let i = 0; i < cameras.length; i ++ ) { + state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); + textureProperties.__maxMipLevel = 0; - updateCamera( cameras[ i ], parent ); + } } - // update camera and its children + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - camera.matrixWorld.copy( cameraVR.matrixWorld ); - camera.matrix.copy( cameraVR.matrix ); - camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + generateMipmap( textureType, texture, image.width, image.height ); - const children = camera.children; + } - for ( let i = 0, l = children.length; i < l; i ++ ) { + textureProperties.__version = texture.version; - children[ i ].updateMatrixWorld( true ); + if ( texture.onUpdate ) texture.onUpdate( texture ); - } + } - // update projection matrix for proper view frustum culling + function uploadCubeTexture( textureProperties, texture, slot ) { - if ( cameras.length === 2 ) { + if ( texture.image.length !== 6 ) return; - setProjectionFromUnion( cameraVR, cameraL, cameraR ); + initTexture( textureProperties, texture ); - } else { + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); - // assume single camera setup (AR) + _gl.pixelStorei( 37440, texture.flipY ); + _gl.pixelStorei( 37441, texture.premultiplyAlpha ); + _gl.pixelStorei( 3317, texture.unpackAlignment ); + _gl.pixelStorei( 37443, 0 ); - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); + const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); + const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - } + const cubeImage = []; - return cameraVR; + for ( let i = 0; i < 6; i ++ ) { - }; + if ( ! isCompressed && ! isDataTexture ) { - // Animation Loop + cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - let onAnimationFrameCallback = null; + } else { - function onAnimationFrame( time, frame ) { + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - pose = frame.getViewerPose( referenceSpace ); + } + + } + + const image = cubeImage[ 0 ], + supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format ), + glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); - if ( pose !== null ) { + setTextureParameters( 34067, texture, supportsMips ); - const views = pose.views; - const baseLayer = session.renderState.baseLayer; + let mipmaps; - renderer.setFramebuffer( baseLayer.framebuffer ); + if ( isCompressed ) { - let cameraVRNeedsUpdate = false; + for ( let i = 0; i < 6; i ++ ) { - // check if it's necessary to rebuild cameraVR's camera list + mipmaps = cubeImage[ i ].mipmaps; - if ( views.length !== cameraVR.cameras.length ) { + for ( let j = 0; j < mipmaps.length; j ++ ) { - cameraVR.cameras.length = 0; - cameraVRNeedsUpdate = true; + const mipmap = mipmaps[ j ]; - } + if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - for ( let i = 0; i < views.length; i ++ ) { + if ( glFormat !== null ) { - const view = views[ i ]; - const viewport = baseLayer.getViewport( view ); + state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - const camera = cameras[ i ]; - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); + } else { - if ( i === 0 ) { + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - cameraVR.matrix.copy( camera.matrix ); + } - } + } else { - if ( cameraVRNeedsUpdate === true ) { + state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - cameraVR.cameras.push( camera ); + } } } - } - - // + textureProperties.__maxMipLevel = mipmaps.length - 1; - const inputSources = session.inputSources; + } else { - for ( let i = 0; i < controllers.length; i ++ ) { + mipmaps = texture.mipmaps; - const controller = controllers[ i ]; - const inputSource = inputSources[ i ]; + for ( let i = 0; i < 6; i ++ ) { - controller.update( inputSource, frame, referenceSpace ); + if ( isDataTexture ) { - } + state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); + for ( let j = 0; j < mipmaps.length; j ++ ) { - } + const mipmap = mipmaps[ j ]; + const mipmapImage = mipmap.image[ i ].image; - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - this.setAnimationLoop = function ( callback ) { + } - onAnimationFrameCallback = callback; + } else { - }; + state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - this.dispose = function () {}; + for ( let j = 0; j < mipmaps.length; j ++ ) { - } + const mipmap = mipmaps[ j ]; - Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); + state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - function WebGLMaterials( properties ) { + } - function refreshFogUniforms( uniforms, fog ) { + } - uniforms.fogColor.value.copy( fog.color ); + } - if ( fog.isFog ) { + textureProperties.__maxMipLevel = mipmaps.length; - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + } - } else if ( fog.isFogExp2 ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - uniforms.fogDensity.value = fog.density; + // We assume images for cube map have the same size. + generateMipmap( 34067, texture, image.width, image.height ); } - } + textureProperties.__version = texture.version; - function refreshMaterialUniforms( uniforms, material, pixelRatio, height ) { + if ( texture.onUpdate ) texture.onUpdate( texture ); - if ( material.isMeshBasicMaterial ) { + } - refreshUniformsCommon( uniforms, material ); + // Render targets - } else if ( material.isMeshLambertMaterial ) { + // Setup storage for target texture and bind it to correct framebuffer + function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); + const glFormat = utils.convert( texture.format ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); - } else if ( material.isMeshToonMaterial ) { + if ( textureTarget === 32879 || textureTarget === 35866 ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); + state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); - } else if ( material.isMeshPhongMaterial ) { + } else { - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); + state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - } else if ( material.isMeshStandardMaterial ) { + } - refreshUniformsCommon( uniforms, material ); + state.bindFramebuffer( 36160, framebuffer ); + _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); + state.bindFramebuffer( 36160, null ); - if ( material.isMeshPhysicalMaterial ) { + } - refreshUniformsPhysical( uniforms, material ); + // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - } else { + _gl.bindRenderbuffer( 36161, renderbuffer ); - refreshUniformsStandard( uniforms, material ); + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - } + let glInternalFormat = 33189; - } else if ( material.isMeshMatcapMaterial ) { + if ( isMultisample ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); + const depthTexture = renderTarget.depthTexture; - } else if ( material.isMeshDepthMaterial ) { + if ( depthTexture && depthTexture.isDepthTexture ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); + if ( depthTexture.type === FloatType ) { - } else if ( material.isMeshDistanceMaterial ) { + glInternalFormat = 36012; - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); + } else if ( depthTexture.type === UnsignedIntType ) { - } else if ( material.isMeshNormalMaterial ) { + glInternalFormat = 33190; - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); + } - } else if ( material.isLineBasicMaterial ) { + } - refreshUniformsLine( uniforms, material ); + const samples = getRenderTargetSamples( renderTarget ); - if ( material.isLineDashedMaterial ) { + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - refreshUniformsDash( uniforms, material ); + } else { + + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); } - } else if ( material.isPointsMaterial ) { + _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - refreshUniformsPoints( uniforms, material, pixelRatio, height ); + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - } else if ( material.isSpriteMaterial ) { + if ( isMultisample ) { - refreshUniformsSprites( uniforms, material ); + const samples = getRenderTargetSamples( renderTarget ); - } else if ( material.isShadowMaterial ) { + _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + } else { - } else if ( material.isShaderMaterial ) { + _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - material.uniformsNeedUpdate = false; // #15581 + } - } - } + _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - function refreshUniformsCommon( uniforms, material ) { + } else { - uniforms.opacity.value = material.opacity; + // Use the first texture for MRT so far + const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[ 0 ] : renderTarget.texture; - if ( material.color ) { + const glFormat = utils.convert( texture.format ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); - uniforms.diffuse.value.copy( material.color ); + if ( isMultisample ) { - } + const samples = getRenderTargetSamples( renderTarget ); - if ( material.emissive ) { + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); + } else { + + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + + } } - if ( material.map ) { + _gl.bindRenderbuffer( 36161, null ); - uniforms.map.value = material.map; + } - } + // Setup resources for a Depth Texture for a FBO (needs an extension) + function setupDepthTexture( framebuffer, renderTarget ) { - if ( material.alphaMap ) { + const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); + if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - uniforms.alphaMap.value = material.alphaMap; + state.bindFramebuffer( 36160, framebuffer ); + + if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { + + throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); } - if ( material.specularMap ) { + // upload an empty depth texture with framebuffer size + if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || + renderTarget.depthTexture.image.width !== renderTarget.width || + renderTarget.depthTexture.image.height !== renderTarget.height ) { - uniforms.specularMap.value = material.specularMap; + renderTarget.depthTexture.image.width = renderTarget.width; + renderTarget.depthTexture.image.height = renderTarget.height; + renderTarget.depthTexture.needsUpdate = true; } - const envMap = properties.get( material ).envMap; - - if ( envMap ) { + setTexture2D( renderTarget.depthTexture, 0 ); - uniforms.envMap.value = envMap; + const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; - uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap._needsFlipEnvMap ) ? - 1 : 1; + if ( renderTarget.depthTexture.format === DepthFormat ) { - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; + _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - const maxMipLevel = properties.get( envMap ).__maxMipLevel; + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - if ( maxMipLevel !== undefined ) { + _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - uniforms.maxMipLevel.value = maxMipLevel; + } else { - } + throw new Error( 'Unknown depthTexture format' ); } - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; + } - } + // Setup GL resources for a non-texture depth buffer + function setupDepthRenderbuffer( renderTarget ) { - if ( material.aoMap ) { + const renderTargetProperties = properties.get( renderTarget ); - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - } + if ( renderTarget.depthTexture ) { - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. displacementMap map - // 4. normal map - // 5. bump map - // 6. roughnessMap map - // 7. metalnessMap map - // 8. alphaMap map - // 9. emissiveMap map - // 10. clearcoat map - // 11. clearcoat normal map - // 12. clearcoat roughnessMap map + if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - let uvScaleMap; + setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - if ( material.map ) { + } else { - uvScaleMap = material.map; + if ( isCube ) { - } else if ( material.specularMap ) { + renderTargetProperties.__webglDepthbuffer = []; - uvScaleMap = material.specularMap; + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.displacementMap ) { + state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); + renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - uvScaleMap = material.displacementMap; + } - } else if ( material.normalMap ) { + } else { - uvScaleMap = material.normalMap; + state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); - } else if ( material.bumpMap ) { + } - uvScaleMap = material.bumpMap; + } - } else if ( material.roughnessMap ) { + state.bindFramebuffer( 36160, null ); - uvScaleMap = material.roughnessMap; + } - } else if ( material.metalnessMap ) { + // Set up GL resources for the render target + function setupRenderTarget( renderTarget ) { - uvScaleMap = material.metalnessMap; + const texture = renderTarget.texture; - } else if ( material.alphaMap ) { + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - uvScaleMap = material.alphaMap; + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - } else if ( material.emissiveMap ) { + if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { - uvScaleMap = material.emissiveMap; + textureProperties.__webglTexture = _gl.createTexture(); + textureProperties.__version = texture.version; + info.memory.textures ++; - } else if ( material.clearcoatMap ) { + } - uvScaleMap = material.clearcoatMap; + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); + const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); + const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - } else if ( material.clearcoatNormalMap ) { + // Handles WebGL2 RGBFormat fallback - #18858 - uvScaleMap = material.clearcoatNormalMap; + if ( isWebGL2 && texture.format === RGBFormat && ( texture.type === FloatType || texture.type === HalfFloatType ) ) { - } else if ( material.clearcoatRoughnessMap ) { + texture.format = RGBAFormat; - uvScaleMap = material.clearcoatRoughnessMap; + console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); } - if ( uvScaleMap !== undefined ) { - - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { + // Setup framebuffer - uvScaleMap = uvScaleMap.texture; + if ( isCube ) { - } + renderTargetProperties.__webglFramebuffer = []; - if ( uvScaleMap.matrixAutoUpdate === true ) { + for ( let i = 0; i < 6; i ++ ) { - uvScaleMap.updateMatrix(); + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + } else { - } + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map + if ( isMultipleRenderTargets ) { - let uv2ScaleMap; + if ( capabilities.drawBuffers ) { - if ( material.aoMap ) { + const textures = renderTarget.texture; - uv2ScaleMap = material.aoMap; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - } else if ( material.lightMap ) { + const attachmentProperties = properties.get( textures[ i ] ); - uv2ScaleMap = material.lightMap; + if ( attachmentProperties.__webglTexture === undefined ) { - } + attachmentProperties.__webglTexture = _gl.createTexture(); - if ( uv2ScaleMap !== undefined ) { + info.memory.textures ++; - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { + } - uv2ScaleMap = uv2ScaleMap.texture; - - } - - if ( uv2ScaleMap.matrixAutoUpdate === true ) { + } - uv2ScaleMap.updateMatrix(); + } else { - } + console.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' ); - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); + } - } + } else if ( isMultisample ) { - } + if ( isWebGL2 ) { - function refreshUniformsLine( uniforms, material ) { + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - } + const glFormat = utils.convert( texture.format ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const samples = getRenderTargetSamples( renderTarget ); + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - function refreshUniformsDash( uniforms, material ) { + state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); + _gl.bindRenderbuffer( 36161, null ); - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + if ( renderTarget.depthBuffer ) { - } + renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { + } - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; + state.bindFramebuffer( 36160, null ); - if ( material.map ) { - uniforms.map.value = material.map; + } else { - } + console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - if ( material.alphaMap ) { + } - uniforms.alphaMap.value = material.alphaMap; + } } - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map + // Setup color buffer - let uvScaleMap; + if ( isCube ) { - if ( material.map ) { + state.bindTexture( 34067, textureProperties.__webglTexture ); + setTextureParameters( 34067, texture, supportsMips ); - uvScaleMap = material.map; + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.alphaMap ) { + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, 36064, 34069 + i ); - uvScaleMap = material.alphaMap; + } - } + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - if ( uvScaleMap !== undefined ) { + generateMipmap( 34067, texture, renderTarget.width, renderTarget.height ); - if ( uvScaleMap.matrixAutoUpdate === true ) { + } - uvScaleMap.updateMatrix(); + state.unbindTexture(); - } + } else if ( isMultipleRenderTargets ) { - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + const textures = renderTarget.texture; - } + for ( let i = 0, il = textures.length; i < il; i ++ ) { - } + const attachment = textures[ i ]; + const attachmentProperties = properties.get( attachment ); - function refreshUniformsSprites( uniforms, material ) { + state.bindTexture( 3553, attachmentProperties.__webglTexture ); + setTextureParameters( 3553, attachment, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, 36064 + i, 3553 ); - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; + if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { - if ( material.map ) { + generateMipmap( 3553, attachment, renderTarget.width, renderTarget.height ); - uniforms.map.value = material.map; + } - } + } - if ( material.alphaMap ) { + state.unbindTexture(); - uniforms.alphaMap.value = material.alphaMap; + } else { - } + let glTextureType = 3553; - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map + if ( isRenderTarget3D ) { - let uvScaleMap; + // Render targets containing layers, i.e: Texture 3D and 2d arrays - if ( material.map ) { + if ( isWebGL2 ) { - uvScaleMap = material.map; + const isTexture3D = texture.isDataTexture3D; + glTextureType = isTexture3D ? 32879 : 35866; - } else if ( material.alphaMap ) { + } else { - uvScaleMap = material.alphaMap; + console.warn( 'THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.' ); - } + } - if ( uvScaleMap !== undefined ) { + } - if ( uvScaleMap.matrixAutoUpdate === true ) { + state.bindTexture( glTextureType, textureProperties.__webglTexture ); + setTextureParameters( glTextureType, texture, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, 36064, glTextureType ); - uvScaleMap.updateMatrix(); + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + + generateMipmap( glTextureType, texture, renderTarget.width, renderTarget.height, renderTarget.depth ); } - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + state.unbindTexture(); } - } - - function refreshUniformsLambert( uniforms, material ) { + // Setup depth and stencil buffers - if ( material.emissiveMap ) { + if ( renderTarget.depthBuffer ) { - uniforms.emissiveMap.value = material.emissiveMap; + setupDepthRenderbuffer( renderTarget ); } } - function refreshUniformsPhong( uniforms, material ) { + function updateRenderTargetMipmap( renderTarget ) { - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - if ( material.emissiveMap ) { + const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; - uniforms.emissiveMap.value = material.emissiveMap; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - } + const texture = textures[ i ]; - if ( material.bumpMap ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; + const webglTexture = properties.get( texture ).__webglTexture; + + state.bindTexture( target, webglTexture ); + generateMipmap( target, texture, renderTarget.width, renderTarget.height ); + state.unbindTexture(); + + } } - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + function updateMultisampleRenderTarget( renderTarget ) { - } + if ( renderTarget.isWebGLMultisampleRenderTarget ) { - if ( material.displacementMap ) { + if ( isWebGL2 ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + const width = renderTarget.width; + const height = renderTarget.height; + let mask = 16384; - } + if ( renderTarget.depthBuffer ) mask |= 256; + if ( renderTarget.stencilBuffer ) mask |= 1024; - } + const renderTargetProperties = properties.get( renderTarget ); - function refreshUniformsToon( uniforms, material ) { + state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); + state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - if ( material.gradientMap ) { + _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - uniforms.gradientMap.value = material.gradientMap; + state.bindFramebuffer( 36008, null ); + state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); - } + } else { - if ( material.emissiveMap ) { + console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - uniforms.emissiveMap.value = material.emissiveMap; + } } - if ( material.bumpMap ) { + } - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + function getRenderTargetSamples( renderTarget ) { - } + return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? + Math.min( maxSamples, renderTarget.samples ) : 0; - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + function updateVideoTexture( texture ) { - } + const frame = info.render.frame; - if ( material.displacementMap ) { + // Check the last frame we updated the VideoTexture - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + if ( _videoTextures.get( texture ) !== frame ) { + + _videoTextures.set( texture, frame ); + texture.update(); } } - function refreshUniformsStandard( uniforms, material ) { + // backwards compatibility - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; + let warnedTexture2D = false; + let warnedTextureCube = false; - if ( material.roughnessMap ) { + function safeSetTexture2D( texture, slot ) { - uniforms.roughnessMap.value = material.roughnessMap; + if ( texture && texture.isWebGLRenderTarget ) { - } + if ( warnedTexture2D === false ) { - if ( material.metalnessMap ) { + console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' ); + warnedTexture2D = true; - uniforms.metalnessMap.value = material.metalnessMap; + } + + texture = texture.texture; } - if ( material.emissiveMap ) { + setTexture2D( texture, slot ); - uniforms.emissiveMap.value = material.emissiveMap; + } - } + function safeSetTextureCube( texture, slot ) { - if ( material.bumpMap ) { + if ( texture && texture.isWebGLCubeRenderTarget ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + if ( warnedTextureCube === false ) { - } + console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' ); + warnedTextureCube = true; - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + texture = texture.texture; } - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - } + setTextureCube( texture, slot ); - const envMap = properties.get( material ).envMap; + } - if ( envMap ) { + // - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; + this.allocateTextureUnit = allocateTextureUnit; + this.resetTextureUnits = resetTextureUnits; - } + this.setTexture2D = setTexture2D; + this.setTexture2DArray = setTexture2DArray; + this.setTexture3D = setTexture3D; + this.setTextureCube = setTextureCube; + this.setupRenderTarget = setupRenderTarget; + this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; - } + this.safeSetTexture2D = safeSetTexture2D; + this.safeSetTextureCube = safeSetTextureCube; - function refreshUniformsPhysical( uniforms, material ) { + } - refreshUniformsStandard( uniforms, material ); + function WebGLUtils( gl, extensions, capabilities ) { - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common + const isWebGL2 = capabilities.isWebGL2; - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) uniforms.sheen.value.copy( material.sheen ); + function convert( p ) { - if ( material.clearcoatMap ) { + let extension; - uniforms.clearcoatMap.value = material.clearcoatMap; + if ( p === UnsignedByteType ) return 5121; + if ( p === UnsignedShort4444Type ) return 32819; + if ( p === UnsignedShort5551Type ) return 32820; + if ( p === UnsignedShort565Type ) return 33635; - } + if ( p === ByteType ) return 5120; + if ( p === ShortType ) return 5122; + if ( p === UnsignedShortType ) return 5123; + if ( p === IntType ) return 5124; + if ( p === UnsignedIntType ) return 5125; + if ( p === FloatType ) return 5126; - if ( material.clearcoatRoughnessMap ) { + if ( p === HalfFloatType ) { - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; + if ( isWebGL2 ) return 5131; - } + extension = extensions.get( 'OES_texture_half_float' ); - if ( material.clearcoatNormalMap ) { + if ( extension !== null ) { - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; + return extension.HALF_FLOAT_OES; - if ( material.side === BackSide ) { + } else { - uniforms.clearcoatNormalScale.value.negate(); + return null; } } - uniforms.transmission.value = material.transmission; - - if ( material.transmissionMap ) { + if ( p === AlphaFormat ) return 6406; + if ( p === RGBFormat ) return 6407; + if ( p === RGBAFormat ) return 6408; + if ( p === LuminanceFormat ) return 6409; + if ( p === LuminanceAlphaFormat ) return 6410; + if ( p === DepthFormat ) return 6402; + if ( p === DepthStencilFormat ) return 34041; + if ( p === RedFormat ) return 6403; - uniforms.transmissionMap.value = material.transmissionMap; + // WebGL2 formats. - } + if ( p === RedIntegerFormat ) return 36244; + if ( p === RGFormat ) return 33319; + if ( p === RGIntegerFormat ) return 33320; + if ( p === RGBIntegerFormat ) return 36248; + if ( p === RGBAIntegerFormat ) return 36249; - } + if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || + p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - function refreshUniformsMatcap( uniforms, material ) { + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - if ( material.matcap ) { + if ( extension !== null ) { - uniforms.matcap.value = material.matcap; + if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - } + } else { - if ( material.bumpMap ) { + return null; - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + } } - if ( material.normalMap ) { + if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || + p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - } + if ( extension !== null ) { - if ( material.displacementMap ) { + if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } else { - } + return null; - } + } - function refreshUniformsDepth( uniforms, material ) { + } - if ( material.displacementMap ) { + if ( p === RGB_ETC1_Format ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - } + if ( extension !== null ) { - } + return extension.COMPRESSED_RGB_ETC1_WEBGL; - function refreshUniformsDistance( uniforms, material ) { + } else { - if ( material.displacementMap ) { + return null; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } } - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; + if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - } + extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - function refreshUniformsNormal( uniforms, material ) { + if ( extension !== null ) { - if ( material.bumpMap ) { + if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; + if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + } } - if ( material.normalMap ) { + if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || + p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || + p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || + p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || + p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || + p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || + p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || + p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || + p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || + p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - } + if ( extension !== null ) { - if ( material.displacementMap ) { + // TODO Complete? - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + return p; - } + } else { - } + return null; - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; + } - } + } - function createCanvasElement() { + if ( p === RGBA_BPTC_Format ) { - const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.style.display = 'block'; - return canvas; + extension = extensions.get( 'EXT_texture_compression_bptc' ); - } + if ( extension !== null ) { - function WebGLRenderer( parameters ) { + // TODO Complete? - parameters = parameters || {}; + return p; - const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), - _context = parameters.context !== undefined ? parameters.context : null, + } else { - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; + return null; - let currentRenderList = null; - let currentRenderState = null; + } - // render() can be called from within a callback triggered by another render. - // We track this so that the nested render call gets its state isolated from the parent render call. + } - const renderStateStack = []; + if ( p === UnsignedInt248Type ) { - // public properties + if ( isWebGL2 ) return 34042; - this.domElement = _canvas; + extension = extensions.get( 'WEBGL_depth_texture' ); - // Debug configuration container - this.debug = { + if ( extension !== null ) { - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; + return extension.UNSIGNED_INT_24_8_WEBGL; - // clearing + } else { - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + return null; - // scene graph + } - this.sortObjects = true; + } - // user-defined clipping + } - this.clippingPlanes = []; - this.localClippingEnabled = false; + return { convert: convert }; - // physically based shading + } - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; + class ArrayCamera extends PerspectiveCamera { - // physical lights + constructor( array = [] ) { - this.physicallyCorrectLights = false; + super(); - // tone mapping + this.cameras = array; - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; + } - // morphs + } - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; + ArrayCamera.prototype.isArrayCamera = true; - // internal properties + class Group extends Object3D { - const _this = this; + constructor() { - let _isContextLost = false; + super(); - // internal state cache + this.type = 'Group'; - let _framebuffer = null; + } - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentFramebuffer = null; - let _currentMaterialId = - 1; + } - let _currentCamera = null; + Group.prototype.isGroup = true; - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; + const _moveEvent = { type: 'move' }; - // + class WebXRController { - let _width = _canvas.width; - let _height = _canvas.height; + constructor() { - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; + this._targetRay = null; + this._grip = null; + this._hand = null; - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; + } - // frustum + getHandSpace() { - const _frustum = new Frustum(); + if ( this._hand === null ) { - // clipping + this._hand = new Group(); + this._hand.matrixAutoUpdate = false; + this._hand.visible = false; - let _clippingEnabled = false; - let _localClippingEnabled = false; + this._hand.joints = {}; + this._hand.inputState = { pinching: false }; - // camera matrices cache + } - const _projScreenMatrix = new Matrix4(); + return this._hand; - const _vector3 = new Vector3(); + } - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; + getTargetRaySpace() { - function getTargetPixelRatio() { + if ( this._targetRay === null ) { - return _currentRenderTarget === null ? _pixelRatio : 1; + this._targetRay = new Group(); + this._targetRay.matrixAutoUpdate = false; + this._targetRay.visible = false; + this._targetRay.hasLinearVelocity = false; + this._targetRay.linearVelocity = new Vector3(); + this._targetRay.hasAngularVelocity = false; + this._targetRay.angularVelocity = new Vector3(); - } + } - // initialize + return this._targetRay; - let _gl = _context; + } - function getContext( contextNames, contextAttributes ) { + getGripSpace() { - for ( let i = 0; i < contextNames.length; i ++ ) { + if ( this._grip === null ) { - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; + this._grip = new Group(); + this._grip.matrixAutoUpdate = false; + this._grip.visible = false; + this._grip.hasLinearVelocity = false; + this._grip.linearVelocity = new Vector3(); + this._grip.hasAngularVelocity = false; + this._grip.angularVelocity = new Vector3(); } - return null; + return this._grip; } - try { - - const contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; + dispatchEvent( event ) { - // event listeners must be registered before WebGL context is created, see #12753 + if ( this._targetRay !== null ) { - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); + this._targetRay.dispatchEvent( event ); - if ( _gl === null ) { + } - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; + if ( this._grip !== null ) { - if ( _this.isWebGL1Renderer === true ) { + this._grip.dispatchEvent( event ); - contextNames.shift(); + } - } + if ( this._hand !== null ) { - _gl = getContext( contextNames, contextAttributes ); + this._hand.dispatchEvent( event ); - if ( _gl === null ) { + } - if ( getContext( contextNames ) ) { + return this; - throw new Error( 'Error creating WebGL context with your selected attributes.' ); + } - } else { + disconnect( inputSource ) { - throw new Error( 'Error creating WebGL context.' ); + this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - } + if ( this._targetRay !== null ) { - } + this._targetRay.visible = false; } - // Some experimental-webgl implementations do not have getShaderPrecisionFormat + if ( this._grip !== null ) { - if ( _gl.getShaderPrecisionFormat === undefined ) { + this._grip.visible = false; - _gl.getShaderPrecisionFormat = function () { + } - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; + if ( this._hand !== null ) { - }; + this._hand.visible = false; } - } catch ( error ) { - - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; + return this; } - let extensions, capabilities, state, info; - let properties, textures, cubemaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping; + update( inputSource, frame, referenceSpace ) { - let background, morphtargets, bufferRenderer, indexedBufferRenderer; + let inputPose = null; + let gripPose = null; + let handPose = null; - let utils, bindingStates; + const targetRay = this._targetRay; + const grip = this._grip; + const hand = this._hand; - function initGLContext() { + if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { - extensions = new WebGLExtensions( _gl ); + if ( targetRay !== null ) { - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); + inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - extensions.init( capabilities ); + if ( inputPose !== null ) { - utils = new WebGLUtils( _gl, extensions, capabilities ); + targetRay.matrix.fromArray( inputPose.transform.matrix ); + targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - state = new WebGLState( _gl, extensions, capabilities ); - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + if ( inputPose.linearVelocity ) { - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists( properties ); - renderStates = new WebGLRenderStates( extensions, capabilities ); - background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); + targetRay.hasLinearVelocity = true; + targetRay.linearVelocity.copy( inputPose.linearVelocity ); - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); + } else { - info.programs = programCache.programs; + targetRay.hasLinearVelocity = false; - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.state = state; - _this.info = info; + } - } + if ( inputPose.angularVelocity ) { - initGLContext(); + targetRay.hasAngularVelocity = true; + targetRay.angularVelocity.copy( inputPose.angularVelocity ); - // xr + } else { - const xr = new WebXRManager( _this, _gl ); + targetRay.hasAngularVelocity = false; - this.xr = xr; + } - // shadow map + this.dispatchEvent( _moveEvent ); - const shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); + } - this.shadowMap = shadowMap; + } - // API + if ( hand && inputSource.hand ) { - this.getContext = function () { + handPose = true; - return _gl; + for ( const inputjoint of inputSource.hand.values() ) { - }; + // Update the joints groups with the XRJoint poses + const jointPose = frame.getJointPose( inputjoint, referenceSpace ); - this.getContextAttributes = function () { + if ( hand.joints[ inputjoint.jointName ] === undefined ) { - return _gl.getContextAttributes(); + // The transform of this joint will be updated with the joint pose on each frame + const joint = new Group(); + joint.matrixAutoUpdate = false; + joint.visible = false; + hand.joints[ inputjoint.jointName ] = joint; + // ?? + hand.add( joint ); - }; + } - this.forceContextLoss = function () { + const joint = hand.joints[ inputjoint.jointName ]; - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); + if ( jointPose !== null ) { - }; + joint.matrix.fromArray( jointPose.transform.matrix ); + joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); + joint.jointRadius = jointPose.radius; - this.forceContextRestore = function () { + } - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); + joint.visible = jointPose !== null; - }; + } - this.getPixelRatio = function () { + // Custom events - return _pixelRatio; + // Check pinchz + const indexTip = hand.joints[ 'index-finger-tip' ]; + const thumbTip = hand.joints[ 'thumb-tip' ]; + const distance = indexTip.position.distanceTo( thumbTip.position ); - }; + const distanceToPinch = 0.02; + const threshold = 0.005; - this.setPixelRatio = function ( value ) { + if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - if ( value === undefined ) return; + hand.inputState.pinching = false; + this.dispatchEvent( { + type: 'pinchend', + handedness: inputSource.handedness, + target: this + } ); - _pixelRatio = value; + } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - this.setSize( _width, _height, false ); + hand.inputState.pinching = true; + this.dispatchEvent( { + type: 'pinchstart', + handedness: inputSource.handedness, + target: this + } ); - }; + } - this.getSize = function ( target ) { + } else { - if ( target === undefined ) { + if ( grip !== null && inputSource.gripSpace ) { - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); + gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - target = new Vector2(); + if ( gripPose !== null ) { - } + grip.matrix.fromArray( gripPose.transform.matrix ); + grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - return target.set( _width, _height ); + if ( gripPose.linearVelocity ) { - }; + grip.hasLinearVelocity = true; + grip.linearVelocity.copy( gripPose.linearVelocity ); - this.setSize = function ( width, height, updateStyle ) { + } else { - if ( xr.isPresenting ) { + grip.hasLinearVelocity = false; - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; + } - } + if ( gripPose.angularVelocity ) { - _width = width; - _height = height; + grip.hasAngularVelocity = true; + grip.angularVelocity.copy( gripPose.angularVelocity ); - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); + } else { - if ( updateStyle !== false ) { + grip.hasAngularVelocity = false; - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + } - } + } - this.setViewport( 0, 0, width, height ); + } - }; + } - this.getDrawingBufferSize = function ( target ) { + } - if ( target === undefined ) { + if ( targetRay !== null ) { + + targetRay.visible = ( inputPose !== null ); + + } - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); + if ( grip !== null ) { - target = new Vector2(); + grip.visible = ( gripPose !== null ); } - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); + if ( hand !== null ) { - }; + hand.visible = ( handPose !== null ); - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { + } - _width = width; - _height = height; + return this; - _pixelRatio = pixelRatio; + } - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); + } - this.setViewport( 0, 0, width, height ); + class WebXRManager extends EventDispatcher { - }; + constructor( renderer, gl ) { - this.getCurrentViewport = function ( target ) { + super(); - if ( target === undefined ) { + const scope = this; + const state = renderer.state; + + let session = null; + let framebufferScaleFactor = 1.0; + + let referenceSpace = null; + let referenceSpaceType = 'local-floor'; + + let pose = null; + let glBinding = null; + let glFramebuffer = null; + let glProjLayer = null; + let glBaseLayer = null; + let isMultisample = false; + let glMultisampledFramebuffer = null; + let glColorRenderbuffer = null; + let glDepthRenderbuffer = null; + let xrFrame = null; + let depthStyle = null; + let clearStyle = null; + + const controllers = []; + const inputSourcesMap = new Map(); - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); + // - target = new Vector4(); + const cameraL = new PerspectiveCamera(); + cameraL.layers.enable( 1 ); + cameraL.viewport = new Vector4(); - } + const cameraR = new PerspectiveCamera(); + cameraR.layers.enable( 2 ); + cameraR.viewport = new Vector4(); - return target.copy( _currentViewport ); + const cameras = [ cameraL, cameraR ]; - }; + const cameraVR = new ArrayCamera(); + cameraVR.layers.enable( 1 ); + cameraVR.layers.enable( 2 ); - this.getViewport = function ( target ) { + let _currentDepthNear = null; + let _currentDepthFar = null; - return target.copy( _viewport ); + // - }; + this.cameraAutoUpdate = true; + this.enabled = false; - this.setViewport = function ( x, y, width, height ) { + this.isPresenting = false; - if ( x.isVector4 ) { + this.getController = function ( index ) { - _viewport.set( x.x, x.y, x.z, x.w ); + let controller = controllers[ index ]; - } else { + if ( controller === undefined ) { - _viewport.set( x, y, width, height ); + controller = new WebXRController(); + controllers[ index ] = controller; - } + } - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + return controller.getTargetRaySpace(); - }; + }; - this.getScissor = function ( target ) { + this.getControllerGrip = function ( index ) { - return target.copy( _scissor ); + let controller = controllers[ index ]; - }; + if ( controller === undefined ) { - this.setScissor = function ( x, y, width, height ) { + controller = new WebXRController(); + controllers[ index ] = controller; - if ( x.isVector4 ) { + } - _scissor.set( x.x, x.y, x.z, x.w ); + return controller.getGripSpace(); - } else { + }; - _scissor.set( x, y, width, height ); + this.getHand = function ( index ) { - } + let controller = controllers[ index ]; - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); + if ( controller === undefined ) { - }; + controller = new WebXRController(); + controllers[ index ] = controller; - this.getScissorTest = function () { + } - return _scissorTest; + return controller.getHandSpace(); - }; + }; - this.setScissorTest = function ( boolean ) { + // - state.setScissorTest( _scissorTest = boolean ); + function onSessionEvent( event ) { - }; + const controller = inputSourcesMap.get( event.inputSource ); - this.setOpaqueSort = function ( method ) { + if ( controller ) { - _opaqueSort = method; + controller.dispatchEvent( { type: event.type, data: event.inputSource } ); - }; + } - this.setTransparentSort = function ( method ) { + } - _transparentSort = method; + function onSessionEnd() { - }; + inputSourcesMap.forEach( function ( controller, inputSource ) { - // Clearing + controller.disconnect( inputSource ); - this.getClearColor = function ( target ) { + } ); - if ( target === undefined ) { + inputSourcesMap.clear(); - console.warn( 'WebGLRenderer: .getClearColor() now requires a Color as an argument' ); + _currentDepthNear = null; + _currentDepthFar = null; - target = new Color(); + // restore framebuffer/rendering state - } + state.bindXRFramebuffer( null ); + renderer.setRenderTarget( renderer.getRenderTarget() ); - return target.copy( background.getClearColor() ); + if ( glFramebuffer ) gl.deleteFramebuffer( glFramebuffer ); + if ( glMultisampledFramebuffer ) gl.deleteFramebuffer( glMultisampledFramebuffer ); + if ( glColorRenderbuffer ) gl.deleteRenderbuffer( glColorRenderbuffer ); + if ( glDepthRenderbuffer ) gl.deleteRenderbuffer( glDepthRenderbuffer ); + glFramebuffer = null; + glMultisampledFramebuffer = null; + glColorRenderbuffer = null; + glDepthRenderbuffer = null; + glBaseLayer = null; + glProjLayer = null; + glBinding = null; + session = null; - }; + // - this.setClearColor = function () { + animation.stop(); - background.setClearColor.apply( background, arguments ); + scope.isPresenting = false; - }; + scope.dispatchEvent( { type: 'sessionend' } ); - this.getClearAlpha = function () { + } - return background.getClearAlpha(); + this.setFramebufferScaleFactor = function ( value ) { - }; + framebufferScaleFactor = value; - this.setClearAlpha = function () { + if ( scope.isPresenting === true ) { - background.setClearAlpha.apply( background, arguments ); + console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - }; + } - this.clear = function ( color, depth, stencil ) { + }; - let bits = 0; + this.setReferenceSpaceType = function ( value ) { - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; + referenceSpaceType = value; - _gl.clear( bits ); + if ( scope.isPresenting === true ) { - }; + console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - this.clearColor = function () { + } - this.clear( true, false, false ); + }; - }; + this.getReferenceSpace = function () { - this.clearDepth = function () { + return referenceSpace; - this.clear( false, true, false ); + }; - }; + this.getBaseLayer = function () { - this.clearStencil = function () { + return glProjLayer !== null ? glProjLayer : glBaseLayer; - this.clear( false, false, true ); + }; - }; + this.getBinding = function () { - // + return glBinding; - this.dispose = function () { + }; - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); + this.getFrame = function () { - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - objects.dispose(); - bindingStates.dispose(); + return xrFrame; - xr.dispose(); + }; - animation.stop(); + this.getSession = function () { - }; + return session; - // Events + }; - function onContextLost( event ) { + this.setSession = async function ( value ) { - event.preventDefault(); + session = value; - console.log( 'THREE.WebGLRenderer: Context Lost.' ); + if ( session !== null ) { - _isContextLost = true; + session.addEventListener( 'select', onSessionEvent ); + session.addEventListener( 'selectstart', onSessionEvent ); + session.addEventListener( 'selectend', onSessionEvent ); + session.addEventListener( 'squeeze', onSessionEvent ); + session.addEventListener( 'squeezestart', onSessionEvent ); + session.addEventListener( 'squeezeend', onSessionEvent ); + session.addEventListener( 'end', onSessionEnd ); + session.addEventListener( 'inputsourceschange', onInputSourcesChange ); - } + const attributes = gl.getContextAttributes(); - function onContextRestore( /* event */ ) { + if ( attributes.xrCompatible !== true ) { - console.log( 'THREE.WebGLRenderer: Context Restored.' ); + await gl.makeXRCompatible(); - _isContextLost = false; + } - initGLContext(); + if ( session.renderState.layers === undefined ) { - } + const layerInit = { + antialias: attributes.antialias, + alpha: attributes.alpha, + depth: attributes.depth, + stencil: attributes.stencil, + framebufferScaleFactor: framebufferScaleFactor + }; - function onMaterialDispose( event ) { + glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - const material = event.target; + session.updateRenderState( { baseLayer: glBaseLayer } ); - material.removeEventListener( 'dispose', onMaterialDispose ); + } else if ( gl instanceof WebGLRenderingContext ) { - deallocateMaterial( material ); + // Use old style webgl layer because we can't use MSAA + // WebGL2 support. - } + const layerInit = { + antialias: true, + alpha: attributes.alpha, + depth: attributes.depth, + stencil: attributes.stencil, + framebufferScaleFactor: framebufferScaleFactor + }; - // Buffer deallocation + glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - function deallocateMaterial( material ) { + session.updateRenderState( { layers: [ glBaseLayer ] } ); + + } else { - releaseMaterialProgramReference( material ); + isMultisample = attributes.antialias; + let depthFormat = null; - properties.remove( material ); - } + if ( attributes.depth ) { + clearStyle = 256; - function releaseMaterialProgramReference( material ) { + if ( attributes.stencil ) clearStyle |= 1024; - const programInfo = properties.get( material ).program; + depthStyle = attributes.stencil ? 33306 : 36096; + depthFormat = attributes.stencil ? 35056 : 33190; - if ( programInfo !== undefined ) { + } - programCache.releaseProgram( programInfo ); + const projectionlayerInit = { + colorFormat: attributes.alpha ? 32856 : 32849, + depthFormat: depthFormat, + scaleFactor: framebufferScaleFactor + }; - } + glBinding = new XRWebGLBinding( session, gl ); - } + glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); - // Buffer rendering + glFramebuffer = gl.createFramebuffer(); - function renderObjectImmediate( object, program ) { + session.updateRenderState( { layers: [ glProjLayer ] } ); - object.render( function ( object ) { + if ( isMultisample ) { - _this.renderBufferImmediate( object, program ); + glMultisampledFramebuffer = gl.createFramebuffer(); + glColorRenderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer( 36161, glColorRenderbuffer ); + gl.renderbufferStorageMultisample( + 36161, + 4, + 32856, + glProjLayer.textureWidth, + glProjLayer.textureHeight ); + state.bindFramebuffer( 36160, glMultisampledFramebuffer ); + gl.framebufferRenderbuffer( 36160, 36064, 36161, glColorRenderbuffer ); + gl.bindRenderbuffer( 36161, null ); - } ); + if ( depthFormat !== null ) { - } + glDepthRenderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer( 36161, glDepthRenderbuffer ); + gl.renderbufferStorageMultisample( 36161, 4, depthFormat, glProjLayer.textureWidth, glProjLayer.textureHeight ); + gl.framebufferRenderbuffer( 36160, depthStyle, 36161, glDepthRenderbuffer ); + gl.bindRenderbuffer( 36161, null ); - this.renderBufferImmediate = function ( object, program ) { + } - bindingStates.initAttributes(); + state.bindFramebuffer( 36160, null ); - const buffers = properties.get( object ); + } - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + } - const programAttributes = program.getAttributes(); + referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); - if ( object.hasPositions ) { + animation.setContext( session ); + animation.start(); - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); + scope.isPresenting = true; - bindingStates.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); + scope.dispatchEvent( { type: 'sessionstart' } ); - } + } - if ( object.hasNormals ) { + }; - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); + function onInputSourcesChange( event ) { - bindingStates.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); + const inputSources = session.inputSources; - } + // Assign inputSources to available controllers - if ( object.hasUvs ) { + for ( let i = 0; i < controllers.length; i ++ ) { - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); + inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - bindingStates.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); + } - } + // Notify disconnected - if ( object.hasColors ) { + for ( let i = 0; i < event.removed.length; i ++ ) { - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); + const inputSource = event.removed[ i ]; + const controller = inputSourcesMap.get( inputSource ); - bindingStates.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); + if ( controller ) { - } + controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); + inputSourcesMap.delete( inputSource ); - bindingStates.disableUnusedAttributes(); + } - _gl.drawArrays( 4, 0, object.count ); + } - object.count = 0; + // Notify connected - }; + for ( let i = 0; i < event.added.length; i ++ ) { - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { + const inputSource = event.added[ i ]; + const controller = inputSourcesMap.get( inputSource ); - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) + if ( controller ) { - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); + controller.dispatchEvent( { type: 'connected', data: inputSource } ); + + } - const program = setProgram( camera, scene, material, object ); + } - state.setMaterial( material, frontFaceCW ); + } // - let index = geometry.index; - const position = geometry.attributes.position; + const cameraLPos = new Vector3(); + const cameraRPos = new Vector3(); - // + /** + * Assumes 2 cameras that are parallel and share an X-axis, and that + * the cameras' projection and world matrices have already been set. + * And that near and far planes are identical for both cameras. + * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 + */ + function setProjectionFromUnion( camera, cameraL, cameraR ) { - if ( index === null ) { + cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); + cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - if ( position === undefined || position.count === 0 ) return; + const ipd = cameraLPos.distanceTo( cameraRPos ); - } else if ( index.count === 0 ) { + const projL = cameraL.projectionMatrix.elements; + const projR = cameraR.projectionMatrix.elements; - return; + // VR systems will have identical far and near planes, and + // most likely identical top and bottom frustum extents. + // Use the left camera for these values. + const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); + const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); + const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; + const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - } + const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; + const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; + const left = near * leftFov; + const right = near * rightFov; - // + // Calculate the new camera's position offset from the + // left camera. xOffset should be roughly half `ipd`. + const zOffset = ipd / ( - leftFov + rightFov ); + const xOffset = zOffset * - leftFov; - let rangeFactor = 1; + // TODO: Better way to apply this offset? + cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); + camera.translateX( xOffset ); + camera.translateZ( zOffset ); + camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - if ( material.wireframe === true ) { + // Find the union of the frustum values of the cameras and scale + // the values so that the near plane's position does not change in world space, + // although must now be relative to the new union camera. + const near2 = near + zOffset; + const far2 = far + zOffset; + const left2 = left - xOffset; + const right2 = right + ( ipd - xOffset ); + const top2 = topFov * far / far2 * near2; + const bottom2 = bottomFov * far / far2 * near2; - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; + camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); } - if ( material.morphTargets || material.morphNormals ) { - - morphtargets.update( object, geometry, material, program ); + function updateCamera( camera, parent ) { - } + if ( parent === null ) { - bindingStates.setup( object, material, program, geometry, index ); + camera.matrixWorld.copy( camera.matrix ); - let attribute; - let renderer = bufferRenderer; + } else { - if ( index !== null ) { + camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - attribute = attributes.get( index ); + } - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); } - // - - const dataCount = ( index !== null ) ? index.count : position.count; + this.updateCamera = function ( camera ) { - const rangeStart = geometry.drawRange.start * rangeFactor; - const rangeCount = geometry.drawRange.count * rangeFactor; + if ( session === null ) return; - const groupStart = group !== null ? group.start * rangeFactor : 0; - const groupCount = group !== null ? group.count * rangeFactor : Infinity; + cameraVR.near = cameraR.near = cameraL.near = camera.near; + cameraVR.far = cameraR.far = cameraL.far = camera.far; - const drawStart = Math.max( rangeStart, groupStart ); - const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + // Note that the new renderState won't apply until the next frame. See #18320 - if ( drawCount === 0 ) return; + session.updateRenderState( { + depthNear: cameraVR.near, + depthFar: cameraVR.far + } ); - // + _currentDepthNear = cameraVR.near; + _currentDepthFar = cameraVR.far; - if ( object.isMesh ) { + } - if ( material.wireframe === true ) { + const parent = camera.parent; + const cameras = cameraVR.cameras; - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); + updateCamera( cameraVR, parent ); - } else { + for ( let i = 0; i < cameras.length; i ++ ) { - renderer.setMode( 4 ); + updateCamera( cameras[ i ], parent ); } - } else if ( object.isLine ) { + cameraVR.matrixWorld.decompose( cameraVR.position, cameraVR.quaternion, cameraVR.scale ); - let lineWidth = material.linewidth; + // update user camera and its children - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + camera.position.copy( cameraVR.position ); + camera.quaternion.copy( cameraVR.quaternion ); + camera.scale.copy( cameraVR.scale ); + camera.matrix.copy( cameraVR.matrix ); + camera.matrixWorld.copy( cameraVR.matrixWorld ); - state.setLineWidth( lineWidth * getTargetPixelRatio() ); + const children = camera.children; - if ( object.isLineSegments ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - renderer.setMode( 1 ); + children[ i ].updateMatrixWorld( true ); - } else if ( object.isLineLoop ) { + } - renderer.setMode( 2 ); + // update projection matrix for proper view frustum culling - } else { + if ( cameras.length === 2 ) { - renderer.setMode( 3 ); + setProjectionFromUnion( cameraVR, cameraL, cameraR ); - } + } else { - } else if ( object.isPoints ) { + // assume single camera setup (AR) - renderer.setMode( 0 ); + cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - } else if ( object.isSprite ) { + } - renderer.setMode( 4 ); + }; - } + this.getCamera = function () { - if ( object.isInstancedMesh ) { + return cameraVR; - renderer.renderInstances( drawStart, drawCount, object.count ); + }; - } else if ( geometry.isInstancedBufferGeometry ) { + this.getFoveation = function () { - const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); + if ( glProjLayer !== null ) { - renderer.renderInstances( drawStart, drawCount, instanceCount ); + return glProjLayer.fixedFoveation; - } else { + } - renderer.render( drawStart, drawCount ); + if ( glBaseLayer !== null ) { - } + return glBaseLayer.fixedFoveation; - }; + } - // Compile + return undefined; - this.compile = function ( scene, camera ) { + }; - currentRenderState = renderStates.get( scene ); - currentRenderState.init(); + this.setFoveation = function ( foveation ) { - scene.traverseVisible( function ( object ) { + // 0 = no foveation = full resolution + // 1 = maximum foveation = the edges render at lower resolution - if ( object.isLight && object.layers.test( camera.layers ) ) { + if ( glProjLayer !== null ) { - currentRenderState.pushLight( object ); + glProjLayer.fixedFoveation = foveation; - if ( object.castShadow ) { + } - currentRenderState.pushShadow( object ); + if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { - } + glBaseLayer.fixedFoveation = foveation; } - } ); + }; - currentRenderState.setupLights(); + // Animation Loop - const compiled = new WeakMap(); + let onAnimationFrameCallback = null; - scene.traverse( function ( object ) { + function onAnimationFrame( time, frame ) { - const material = object.material; + pose = frame.getViewerPose( referenceSpace ); + xrFrame = frame; - if ( material ) { + if ( pose !== null ) { - if ( Array.isArray( material ) ) { + const views = pose.views; - for ( let i = 0; i < material.length; i ++ ) { + if ( glBaseLayer !== null ) { - const material2 = material[ i ]; + state.bindXRFramebuffer( glBaseLayer.framebuffer ); - if ( compiled.has( material2 ) === false ) { + } - initMaterial( material2, scene, object ); - compiled.set( material2 ); + let cameraVRNeedsUpdate = false; - } + // check if it's necessary to rebuild cameraVR's camera list - } + if ( views.length !== cameraVR.cameras.length ) { - } else if ( compiled.has( material ) === false ) { + cameraVR.cameras.length = 0; - initMaterial( material, scene, object ); - compiled.set( material ); + cameraVRNeedsUpdate = true; } - } + for ( let i = 0; i < views.length; i ++ ) { - } ); + const view = views[ i ]; - }; + let viewport = null; - // Animation Loop + if ( glBaseLayer !== null ) { - let onAnimationFrameCallback = null; + viewport = glBaseLayer.getViewport( view ); - function onAnimationFrame( time ) { + } else { - if ( xr.isPresenting ) return; - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); + const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); - } + state.bindXRFramebuffer( glFramebuffer ); - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + if ( glSubImage.depthStencilTexture !== undefined ) { - if ( typeof window !== 'undefined' ) animation.setContext( window ); + gl.framebufferTexture2D( 36160, depthStyle, 3553, glSubImage.depthStencilTexture, 0 ); - this.setAnimationLoop = function ( callback ) { + } - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); + gl.framebufferTexture2D( 36160, 36064, 3553, glSubImage.colorTexture, 0 ); - ( callback === null ) ? animation.stop() : animation.start(); + viewport = glSubImage.viewport; - }; + } - // Rendering + const camera = cameras[ i ]; - this.render = function ( scene, camera ) { + camera.matrix.fromArray( view.transform.matrix ); + camera.projectionMatrix.fromArray( view.projectionMatrix ); + camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - let renderTarget, forceClear; + if ( i === 0 ) { - if ( arguments[ 2 ] !== undefined ) { + cameraVR.matrix.copy( camera.matrix ); - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; + } - } + if ( cameraVRNeedsUpdate === true ) { - if ( arguments[ 3 ] !== undefined ) { + cameraVR.cameras.push( camera ); - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; + } - } + } - if ( camera !== undefined && camera.isCamera !== true ) { + if ( isMultisample ) { - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + state.bindXRFramebuffer( glMultisampledFramebuffer ); - } + if ( clearStyle !== null ) gl.clear( clearStyle ); - if ( _isContextLost === true ) return; + } - // reset caching for this frame + } - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; + // - // update scene graph + const inputSources = session.inputSources; - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + for ( let i = 0; i < controllers.length; i ++ ) { - // update camera matrices and frustum + const controller = controllers[ i ]; + const inputSource = inputSources[ i ]; - if ( camera.parent === null ) camera.updateMatrixWorld(); + controller.update( inputSource, frame, referenceSpace ); - if ( xr.enabled === true && xr.isPresenting === true ) { + } - camera = xr.getCamera( camera ); + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - } + if ( isMultisample ) { - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); + const width = glProjLayer.textureWidth; + const height = glProjLayer.textureHeight; - currentRenderState = renderStates.get( scene, renderStateStack.length ); - currentRenderState.init(); + state.bindFramebuffer( 36008, glMultisampledFramebuffer ); + state.bindFramebuffer( 36009, glFramebuffer ); + // Invalidate the depth here to avoid flush of the depth data to main memory. + gl.invalidateFramebuffer( 36008, [ depthStyle ] ); + gl.invalidateFramebuffer( 36009, [ depthStyle ] ); + gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, 16384, 9728 ); + // Invalidate the MSAA buffer because it's not needed anymore. + gl.invalidateFramebuffer( 36008, [ 36064 ] ); + state.bindFramebuffer( 36008, null ); + state.bindFramebuffer( 36009, null ); - renderStateStack.push( currentRenderState ); + state.bindFramebuffer( 36160, glMultisampledFramebuffer ); - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); + } - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); + xrFrame = null; - currentRenderList = renderLists.get( scene, camera ); - currentRenderList.init(); + } - projectObject( scene, camera, 0, _this.sortObjects ); + const animation = new WebGLAnimation(); - currentRenderList.finish(); + animation.setAnimationLoop( onAnimationFrame ); - if ( _this.sortObjects === true ) { + this.setAnimationLoop = function ( callback ) { - currentRenderList.sort( _opaqueSort, _transparentSort ); + onAnimationFrameCallback = callback; - } + }; - // + this.dispose = function () {}; - if ( _clippingEnabled === true ) clipping.beginShadows(); + } - const shadowsArray = currentRenderState.state.shadowsArray; + } - shadowMap.render( shadowsArray, scene, camera ); + function WebGLMaterials( properties ) { - currentRenderState.setupLights(); - currentRenderState.setupLightsView( camera ); + function refreshFogUniforms( uniforms, fog ) { - if ( _clippingEnabled === true ) clipping.endShadows(); + uniforms.fogColor.value.copy( fog.color ); - // + if ( fog.isFog ) { - if ( this.info.autoReset === true ) this.info.reset(); + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; - if ( renderTarget !== undefined ) { + } else if ( fog.isFogExp2 ) { - this.setRenderTarget( renderTarget ); + uniforms.fogDensity.value = fog.density; } - // + } - background.render( currentRenderList, scene, camera, forceClear ); + function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { - // render scene + if ( material.isMeshBasicMaterial ) { - const opaqueObjects = currentRenderList.opaque; - const transparentObjects = currentRenderList.transparent; + refreshUniformsCommon( uniforms, material ); - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); + } else if ( material.isMeshLambertMaterial ) { - // + refreshUniformsCommon( uniforms, material ); + refreshUniformsLambert( uniforms, material ); - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); + } else if ( material.isMeshToonMaterial ) { - // + refreshUniformsCommon( uniforms, material ); + refreshUniformsToon( uniforms, material ); - if ( _currentRenderTarget !== null ) { + } else if ( material.isMeshPhongMaterial ) { - // Generate mipmap if we're using any kind of mipmap filtering + refreshUniformsCommon( uniforms, material ); + refreshUniformsPhong( uniforms, material ); - textures.updateRenderTargetMipmap( _currentRenderTarget ); + } else if ( material.isMeshStandardMaterial ) { - // resolve multisample renderbuffers to a single-sample texture if necessary + refreshUniformsCommon( uniforms, material ); - textures.updateMultisampleRenderTarget( _currentRenderTarget ); + if ( material.isMeshPhysicalMaterial ) { - } + refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); - // Ensure depth buffer writing is enabled so it can be cleared on next render + } else { - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); + refreshUniformsStandard( uniforms, material ); - state.setPolygonOffset( false ); + } - // _gl.finish(); + } else if ( material.isMeshMatcapMaterial ) { - renderStateStack.pop(); - if ( renderStateStack.length > 0 ) { + refreshUniformsCommon( uniforms, material ); + refreshUniformsMatcap( uniforms, material ); - currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; + } else if ( material.isMeshDepthMaterial ) { - } else { + refreshUniformsCommon( uniforms, material ); + refreshUniformsDepth( uniforms, material ); - currentRenderState = null; + } else if ( material.isMeshDistanceMaterial ) { - } + refreshUniformsCommon( uniforms, material ); + refreshUniformsDistance( uniforms, material ); - currentRenderList = null; + } else if ( material.isMeshNormalMaterial ) { - }; + refreshUniformsCommon( uniforms, material ); + refreshUniformsNormal( uniforms, material ); - function projectObject( object, camera, groupOrder, sortObjects ) { + } else if ( material.isLineBasicMaterial ) { - if ( object.visible === false ) return; + refreshUniformsLine( uniforms, material ); - const visible = object.layers.test( camera.layers ); + if ( material.isLineDashedMaterial ) { - if ( visible ) { + refreshUniformsDash( uniforms, material ); - if ( object.isGroup ) { + } - groupOrder = object.renderOrder; + } else if ( material.isPointsMaterial ) { - } else if ( object.isLOD ) { + refreshUniformsPoints( uniforms, material, pixelRatio, height ); - if ( object.autoUpdate === true ) object.update( camera ); + } else if ( material.isSpriteMaterial ) { - } else if ( object.isLight ) { + refreshUniformsSprites( uniforms, material ); - currentRenderState.pushLight( object ); + } else if ( material.isShadowMaterial ) { - if ( object.castShadow ) { + uniforms.color.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - currentRenderState.pushShadow( object ); + } else if ( material.isShaderMaterial ) { - } + material.uniformsNeedUpdate = false; // #15581 - } else if ( object.isSprite ) { + } - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { + } - if ( sortObjects ) { + function refreshUniformsCommon( uniforms, material ) { - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + uniforms.opacity.value = material.opacity; - } + if ( material.color ) { - const geometry = objects.update( object ); - const material = object.material; + uniforms.diffuse.value.copy( material.color ); - if ( material.visible ) { + } - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + if ( material.emissive ) { - } + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - } + } - } else if ( object.isImmediateRenderObject ) { + if ( material.map ) { - if ( sortObjects ) { + uniforms.map.value = material.map; - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + } - } + if ( material.alphaMap ) { - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); + uniforms.alphaMap.value = material.alphaMap; - } else if ( object.isMesh || object.isLine || object.isPoints ) { + } - if ( object.isSkinnedMesh ) { + if ( material.specularMap ) { - // update skeleton only once in a frame + uniforms.specularMap.value = material.specularMap; - if ( object.skeleton.frame !== info.render.frame ) { + } - object.skeleton.update(); - object.skeleton.frame = info.render.frame; + if ( material.alphaTest > 0 ) { - } + uniforms.alphaTest.value = material.alphaTest; - } + } - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + const envMap = properties.get( material ).envMap; - if ( sortObjects ) { + if ( envMap ) { - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + uniforms.envMap.value = envMap; - } + uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; - const geometry = objects.update( object ); - const material = object.material; + uniforms.reflectivity.value = material.reflectivity; + uniforms.ior.value = material.ior; + uniforms.refractionRatio.value = material.refractionRatio; - if ( Array.isArray( material ) ) { + const maxMipLevel = properties.get( envMap ).__maxMipLevel; - const groups = geometry.groups; + if ( maxMipLevel !== undefined ) { - for ( let i = 0, l = groups.length; i < l; i ++ ) { + uniforms.maxMipLevel.value = maxMipLevel; - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; + } - if ( groupMaterial && groupMaterial.visible ) { + } - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + if ( material.lightMap ) { - } + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; - } + } - } else if ( material.visible ) { + if ( material.aoMap ) { - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; - } + } - } + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. displacementMap map + // 4. normal map + // 5. bump map + // 6. roughnessMap map + // 7. metalnessMap map + // 8. alphaMap map + // 9. emissiveMap map + // 10. clearcoat map + // 11. clearcoat normal map + // 12. clearcoat roughnessMap map + // 13. specular intensity map + // 14. specular tint map + // 15. transmission map + // 16. thickness map - } + let uvScaleMap; - } + if ( material.map ) { - const children = object.children; + uvScaleMap = material.map; - for ( let i = 0, l = children.length; i < l; i ++ ) { + } else if ( material.specularMap ) { - projectObject( children[ i ], camera, groupOrder, sortObjects ); + uvScaleMap = material.specularMap; - } + } else if ( material.displacementMap ) { - } + uvScaleMap = material.displacementMap; - function renderObjects( renderList, scene, camera ) { + } else if ( material.normalMap ) { - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; + uvScaleMap = material.normalMap; - for ( let i = 0, l = renderList.length; i < l; i ++ ) { + } else if ( material.bumpMap ) { - const renderItem = renderList[ i ]; + uvScaleMap = material.bumpMap; - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; + } else if ( material.roughnessMap ) { - if ( camera.isArrayCamera ) { + uvScaleMap = material.roughnessMap; - const cameras = camera.cameras; + } else if ( material.metalnessMap ) { - for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { + uvScaleMap = material.metalnessMap; - const camera2 = cameras[ j ]; + } else if ( material.alphaMap ) { - if ( object.layers.test( camera2.layers ) ) { + uvScaleMap = material.alphaMap; - state.viewport( _currentViewport.copy( camera2.viewport ) ); + } else if ( material.emissiveMap ) { - currentRenderState.setupLightsView( camera2 ); + uvScaleMap = material.emissiveMap; - renderObject( object, scene, camera2, geometry, material, group ); + } else if ( material.clearcoatMap ) { - } + uvScaleMap = material.clearcoatMap; - } + } else if ( material.clearcoatNormalMap ) { - } else { + uvScaleMap = material.clearcoatNormalMap; - renderObject( object, scene, camera, geometry, material, group ); + } else if ( material.clearcoatRoughnessMap ) { - } + uvScaleMap = material.clearcoatRoughnessMap; - } + } else if ( material.specularIntensityMap ) { - } + uvScaleMap = material.specularIntensityMap; - function renderObject( object, scene, camera, geometry, material, group ) { + } else if ( material.specularColorMap ) { - object.onBeforeRender( _this, scene, camera, geometry, material, group ); + uvScaleMap = material.specularColorMap; - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + } else if ( material.transmissionMap ) { - if ( object.isImmediateRenderObject ) { + uvScaleMap = material.transmissionMap; - const program = setProgram( camera, scene, material, object ); + } else if ( material.thicknessMap ) { - state.setMaterial( material ); + uvScaleMap = material.thicknessMap; - bindingStates.reset(); + } else if ( material.sheenColorMap ) { - renderObjectImmediate( object, program ); + uvScaleMap = material.sheenColorMap; - } else { + } else if ( material.sheenRoughnessMap ) { - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); + uvScaleMap = material.sheenRoughnessMap; } - object.onAfterRender( _this, scene, camera, geometry, material, group ); + if ( uvScaleMap !== undefined ) { - } + // backwards compatibility + if ( uvScaleMap.isWebGLRenderTarget ) { - function initMaterial( material, scene, object ) { + uvScaleMap = uvScaleMap.texture; - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + } - const materialProperties = properties.get( material ); + if ( uvScaleMap.matrixAutoUpdate === true ) { - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; + uvScaleMap.updateMatrix(); - const lightsStateVersion = lights.state.version; + } - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - let program = materialProperties.program; - let programChange = true; + } - // always update environment and fog - changing these trigger an initMaterial call, but it's possible that the program doesn't change + // uv repeat and offset setting priorities for uv2 + // 1. ao map + // 2. light map - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = cubemaps.get( material.envMap || materialProperties.environment ); + let uv2ScaleMap; - if ( program === undefined ) { + if ( material.aoMap ) { - // new material - material.addEventListener( 'dispose', onMaterialDispose ); + uv2ScaleMap = material.aoMap; - } else if ( program.cacheKey !== programCacheKey ) { + } else if ( material.lightMap ) { - // changed glsl or parameters - releaseMaterialProgramReference( material ); + uv2ScaleMap = material.lightMap; - } else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) { + } - programChange = false; + if ( uv2ScaleMap !== undefined ) { - } else if ( parameters.shaderID !== undefined ) { + // backwards compatibility + if ( uv2ScaleMap.isWebGLRenderTarget ) { - // same glsl and uniform list - return; + uv2ScaleMap = uv2ScaleMap.texture; - } else { + } - // only rebuild uniform list - programChange = false; + if ( uv2ScaleMap.matrixAutoUpdate === true ) { - } + uv2ScaleMap.updateMatrix(); - if ( programChange ) { + } - parameters.uniforms = programCache.getUniforms( material ); + uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); - material.onBeforeCompile( parameters, _this ); + } - program = programCache.acquireProgram( parameters, programCacheKey ); + } - materialProperties.program = program; - materialProperties.uniforms = parameters.uniforms; - materialProperties.outputEncoding = parameters.outputEncoding; + function refreshUniformsLine( uniforms, material ) { - } + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - const uniforms = materialProperties.uniforms; + } - if ( ! material.isShaderMaterial && - ! material.isRawShaderMaterial || - material.clipping === true ) { + function refreshUniformsDash( uniforms, material ) { - materialProperties.numClippingPlanes = clipping.numPlanes; - materialProperties.numIntersection = clipping.numIntersection; - uniforms.clippingPlanes = clipping.uniform; + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; - } + } - // store the light setup it was created for + function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * pixelRatio; + uniforms.scale.value = height * 0.5; - if ( materialProperties.needsLights ) { + if ( material.map ) { - // wire up the material to this renderer's lighting state + uniforms.map.value = material.map; - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; + } - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms + if ( material.alphaMap ) { + + uniforms.alphaMap.value = material.alphaMap; } - const progUniforms = materialProperties.program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); + if ( material.alphaTest > 0 ) { - materialProperties.uniformsList = uniformsList; + uniforms.alphaTest.value = material.alphaTest; - } + } - function setProgram( camera, scene, material, object ) { + // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + let uvScaleMap; - textures.resetTextureUnits(); + if ( material.map ) { - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - const envMap = cubemaps.get( material.envMap || environment ); + uvScaleMap = material.map; - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; + } else if ( material.alphaMap ) { - if ( _clippingEnabled === true ) { + uvScaleMap = material.alphaMap; - if ( _localClippingEnabled === true || camera !== _currentCamera ) { + } - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; + if ( uvScaleMap !== undefined ) { - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); + if ( uvScaleMap.matrixAutoUpdate === true ) { + + uvScaleMap.updateMatrix(); } + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + } - if ( material.version === materialProperties.__version ) { + } - if ( material.fog && materialProperties.fog !== fog ) { + function refreshUniformsSprites( uniforms, material ) { - initMaterial( material, scene, object ); + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.rotation.value = material.rotation; - } else if ( materialProperties.environment !== environment ) { + if ( material.map ) { - initMaterial( material, scene, object ); + uniforms.map.value = material.map; - } else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { + } - initMaterial( material, scene, object ); + if ( material.alphaMap ) { - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { + uniforms.alphaMap.value = material.alphaMap; - initMaterial( material, scene, object ); + } - } else if ( materialProperties.outputEncoding !== encoding ) { + if ( material.alphaTest > 0 ) { - initMaterial( material, scene, object ); + uniforms.alphaTest.value = material.alphaTest; - } else if ( materialProperties.envMap !== envMap ) { + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map - initMaterial( material, scene, object ); + let uvScaleMap; - } + if ( material.map ) { - } else { + uvScaleMap = material.map; - initMaterial( material, scene, object ); - materialProperties.__version = material.version; + } else if ( material.alphaMap ) { + + uvScaleMap = material.alphaMap; } - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; + if ( uvScaleMap !== undefined ) { - const program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; + if ( uvScaleMap.matrixAutoUpdate === true ) { - if ( state.useProgram( program.program ) ) { + uvScaleMap.updateMatrix(); - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; + } + + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } - if ( material.id !== _currentMaterialId ) { + } - _currentMaterialId = material.id; + function refreshUniformsLambert( uniforms, material ) { - refreshMaterial = true; + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; } - if ( refreshProgram || _currentCamera !== camera ) { + } - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); + function refreshUniformsPhong( uniforms, material ) { - if ( capabilities.logarithmicDepthBuffer ) { + uniforms.specular.value.copy( material.specular ); + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - - } + if ( material.emissiveMap ) { - if ( _currentCamera !== camera ) { + uniforms.emissiveMap.value = material.emissiveMap; - _currentCamera = camera; + } - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: + if ( material.bumpMap ) { - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - } + } - // load material specific uniforms - // (shader material also gets them for the sake of genericity) + if ( material.normalMap ) { - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - const uCamPos = p_uniforms.map.cameraPosition; + } - if ( uCamPos !== undefined ) { + if ( material.displacementMap ) { - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - } + } - } + } - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { + function refreshUniformsToon( uniforms, material ) { - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); + if ( material.gradientMap ) { - } + uniforms.gradientMap.value = material.gradientMap; - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - material.skinning ) { + } - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + if ( material.emissiveMap ) { - } + uniforms.emissiveMap.value = material.emissiveMap; } - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures + if ( material.bumpMap ) { - if ( material.skinning ) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); + } - const skeleton = object.skeleton; + if ( material.normalMap ) { - if ( skeleton ) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - const bones = skeleton.bones; + } - if ( capabilities.floatVertexTextures ) { + if ( material.displacementMap ) { - if ( skeleton.boneTexture === null ) { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + } + } - let size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = MathUtils.ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); + function refreshUniformsStandard( uniforms, material ) { - const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values + uniforms.roughness.value = material.roughness; + uniforms.metalness.value = material.metalness; - const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); + if ( material.roughnessMap ) { - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; + uniforms.roughnessMap.value = material.roughnessMap; - } + } - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); + if ( material.metalnessMap ) { - } else { + uniforms.metalnessMap.value = material.metalnessMap; - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); + } - } + if ( material.emissiveMap ) { - } + uniforms.emissiveMap.value = material.emissiveMap; } - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { + if ( material.bumpMap ) { - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } - if ( refreshMaterial ) { - - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - - if ( materialProperties.needsLights ) { + if ( material.normalMap ) { - // the current material requires lighting info + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required + } - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); + if ( material.displacementMap ) { - } + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - // refresh uniforms common to several materials + } - if ( fog && material.fog ) { + const envMap = properties.get( material ).envMap; - materials.refreshFogUniforms( m_uniforms, fog ); + if ( envMap ) { - } + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height ); + } - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + } - } + function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { + refreshUniformsStandard( uniforms, material ); - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; + uniforms.ior.value = material.ior; // also part of uniforms common - } + if ( material.sheen > 0 ) { - if ( material.isSpriteMaterial ) { + uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); - p_uniforms.setValue( _gl, 'center', object.center ); + uniforms.sheenRoughness.value = material.sheenRoughness; - } + if ( material.sheenColorMap ) { - // common matrices + uniforms.sheenColorMap.value = material.sheenColorMap; - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); + } - return program; + if ( material.sheenRoughnessMap ) { - } + uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + } - function markUniformsLightsNeedsUpdate( uniforms, value ) { + } - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; + if ( material.clearcoat > 0 ) { - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; + uniforms.clearcoat.value = material.clearcoat; + uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - } + if ( material.clearcoatMap ) { - function materialNeedsLights( material ) { + uniforms.clearcoatMap.value = material.clearcoatMap; - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); + } - } + if ( material.clearcoatRoughnessMap ) { - // - this.setFramebuffer = function ( value ) { + uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - if ( _framebuffer !== value && _currentRenderTarget === null ) _gl.bindFramebuffer( 36160, value ); + } - _framebuffer = value; + if ( material.clearcoatNormalMap ) { - }; + uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); + uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - this.getActiveCubeFace = function () { + if ( material.side === BackSide ) { - return _currentActiveCubeFace; + uniforms.clearcoatNormalScale.value.negate(); - }; + } - this.getActiveMipmapLevel = function () { + } - return _currentActiveMipmapLevel; + } - }; + if ( material.transmission > 0 ) { - this.getRenderList = function () { + uniforms.transmission.value = material.transmission; + uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; + uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); - return currentRenderList; + if ( material.transmissionMap ) { - }; + uniforms.transmissionMap.value = material.transmissionMap; - this.setRenderList = function ( renderList ) { + } - currentRenderList = renderList; + uniforms.thickness.value = material.thickness; - }; + if ( material.thicknessMap ) { - this.getRenderTarget = function () { + uniforms.thicknessMap.value = material.thicknessMap; - return _currentRenderTarget; + } - }; + uniforms.attenuationDistance.value = material.attenuationDistance; + uniforms.attenuationColor.value.copy( material.attenuationColor ); - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { + } - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; + uniforms.specularIntensity.value = material.specularIntensity; + uniforms.specularColor.value.copy( material.specularColor ); - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + if ( material.specularIntensityMap ) { - textures.setupRenderTarget( renderTarget ); + uniforms.specularIntensityMap.value = material.specularIntensityMap; } - let framebuffer = _framebuffer; - let isCube = false; + if ( material.specularColorMap ) { - if ( renderTarget ) { + uniforms.specularColorMap.value = material.specularColorMap; - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; + } - if ( renderTarget.isWebGLCubeRenderTarget ) { + } - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; + function refreshUniformsMatcap( uniforms, material ) { - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + if ( material.matcap ) { - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + uniforms.matcap.value = material.matcap; - } else { + } - framebuffer = __webglFramebuffer; + if ( material.bumpMap ) { - } + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; + } - } else { + if ( material.normalMap ) { - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } - if ( _currentFramebuffer !== framebuffer ) { + if ( material.displacementMap ) { - _gl.bindFramebuffer( 36160, framebuffer ); - _currentFramebuffer = framebuffer; + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; } - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); + } - if ( isCube ) { + function refreshUniformsDepth( uniforms, material ) { - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); + if ( material.displacementMap ) { + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; } - }; + } - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { + function refreshUniformsDistance( uniforms, material ) { - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + if ( material.displacementMap ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; } - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + uniforms.referencePosition.value.copy( material.referencePosition ); + uniforms.nearDistance.value = material.nearDistance; + uniforms.farDistance.value = material.farDistance; - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + } - framebuffer = framebuffer[ activeCubeFaceIndex ]; + function refreshUniformsNormal( uniforms, material ) { - } + if ( material.bumpMap ) { - if ( framebuffer ) { + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - let restore = false; + } - if ( framebuffer !== _currentFramebuffer ) { + if ( material.normalMap ) { - _gl.bindFramebuffer( 36160, framebuffer ); + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - restore = true; + } - } + if ( material.displacementMap ) { - try { + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; + } - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { + } - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; + return { + refreshFogUniforms: refreshFogUniforms, + refreshMaterialUniforms: refreshMaterialUniforms + }; - } + } - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); + function createCanvasElement() { - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { + const canvas = createElementNS( 'canvas' ); + canvas.style.display = 'block'; + return canvas; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; + } - } + function WebGLRenderer( parameters = {} ) { - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { + const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), + _context = parameters.context !== undefined ? parameters.context : null, - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', + _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + let currentRenderList = null; + let currentRenderState = null; - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); + // render() can be called from within a callback triggered by another render. + // We track this so that the nested render call gets its list and state isolated from the parent render call. - } + const renderListStack = []; + const renderStateStack = []; - } else { + // public properties - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + this.domElement = _canvas; - } + // Debug configuration container + this.debug = { - } finally { + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: true + }; - if ( restore ) { + // clearing - _gl.bindFramebuffer( 36160, _currentFramebuffer ); + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - } + // scene graph - } + this.sortObjects = true; - } + // user-defined clipping - }; + this.clippingPlanes = []; + this.localClippingEnabled = false; - this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { + // physically based shading - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); - const glFormat = utils.convert( texture.format ); + this.gammaFactor = 2.0; // for backwards compatibility + this.outputEncoding = LinearEncoding; - textures.setTexture2D( texture, 0 ); + // physical lights - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); + this.physicallyCorrectLights = false; - state.unbindTexture(); + // tone mapping - }; + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { + // internal properties - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); + const _this = this; - textures.setTexture2D( dstTexture, 0 ); + let _isContextLost = false; - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); + // internal state cache - if ( srcTexture.isDataTexture ) { + let _currentActiveCubeFace = 0; + let _currentActiveMipmapLevel = 0; + let _currentRenderTarget = null; + let _currentMaterialId = - 1; - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); + let _currentCamera = null; - } else { + const _currentViewport = new Vector4(); + const _currentScissor = new Vector4(); + let _currentScissorTest = null; - if ( srcTexture.isCompressedTexture ) { + // - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); + let _width = _canvas.width; + let _height = _canvas.height; - } else { + let _pixelRatio = 1; + let _opaqueSort = null; + let _transparentSort = null; - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); + const _viewport = new Vector4( 0, 0, _width, _height ); + const _scissor = new Vector4( 0, 0, _width, _height ); + let _scissorTest = false; - } + // - } + const _currentDrawBuffers = []; - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); + // frustum - state.unbindTexture(); + const _frustum = new Frustum(); - }; + // clipping - this.initTexture = function ( texture ) { + let _clippingEnabled = false; + let _localClippingEnabled = false; - textures.setTexture2D( texture, 0 ); + // transmission - state.unbindTexture(); + let _transmissionRenderTarget = null; - }; + // camera matrices cache - this.resetState = function () { + const _projScreenMatrix = new Matrix4(); - state.reset(); - bindingStates.reset(); + const _vector3 = new Vector3(); - }; + const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + function getTargetPixelRatio() { - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + return _currentRenderTarget === null ? _pixelRatio : 1; } - } + // initialize - function WebGL1Renderer( parameters ) { + let _gl = _context; - WebGLRenderer.call( this, parameters ); + function getContext( contextNames, contextAttributes ) { - } + for ( let i = 0; i < contextNames.length; i ++ ) { - WebGL1Renderer.prototype = Object.assign( Object.create( WebGLRenderer.prototype ), { + const contextName = contextNames[ i ]; + const context = _canvas.getContext( contextName, contextAttributes ); + if ( context !== null ) return context; - constructor: WebGL1Renderer, + } - isWebGL1Renderer: true + return null; - } ); + } - class Scene extends Object3D { + try { - constructor() { + const contextAttributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer, + powerPreference: _powerPreference, + failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat + }; - super(); + // event listeners must be registered before WebGL context is created, see #12753 - Object.defineProperty( this, 'isScene', { value: true } ); + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - this.type = 'Scene'; + if ( _gl === null ) { - this.background = null; - this.environment = null; - this.fog = null; + const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - this.overrideMaterial = null; + if ( _this.isWebGL1Renderer === true ) { - this.autoUpdate = true; // checked by the renderer + contextNames.shift(); - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + } - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + _gl = getContext( contextNames, contextAttributes ); - } + if ( _gl === null ) { - } + if ( getContext( contextNames ) ) { - copy( source, recursive ) { + throw new Error( 'Error creating WebGL context with your selected attributes.' ); - super.copy( source, recursive ); + } else { - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.environment !== null ) this.environment = source.environment.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); + throw new Error( 'Error creating WebGL context.' ); - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); + } - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; - - return this; + } - } + } - toJSON( meta ) { + // Some experimental-webgl implementations do not have getShaderPrecisionFormat - const data = super.toJSON( meta ); + if ( _gl.getShaderPrecisionFormat === undefined ) { - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.environment !== null ) data.object.environment = this.environment.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); + _gl.getShaderPrecisionFormat = function () { - return data; + return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - } + }; - } + } - function InterleavedBuffer( array, stride ) { + } catch ( error ) { - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; + console.error( 'THREE.WebGLRenderer: ' + error.message ); + throw error; - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; + } - this.version = 0; + let extensions, capabilities, state, info; + let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; + let programCache, materials, renderLists, renderStates, clipping, shadowMap; - this.uuid = MathUtils.generateUUID(); + let background, morphtargets, bufferRenderer, indexedBufferRenderer; - } + let utils, bindingStates; - Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { + function initGLContext() { - set: function ( value ) { + extensions = new WebGLExtensions( _gl ); - if ( value === true ) this.version ++; + capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - } + extensions.init( capabilities ); - } ); + utils = new WebGLUtils( _gl, extensions, capabilities ); - Object.assign( InterleavedBuffer.prototype, { + state = new WebGLState( _gl, extensions, capabilities ); - isInterleavedBuffer: true, + _currentDrawBuffers[ 0 ] = 1029; - onUploadCallback: function () {}, + info = new WebGLInfo( _gl ); + properties = new WebGLProperties(); + textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); + cubemaps = new WebGLCubeMaps( _this ); + cubeuvmaps = new WebGLCubeUVMaps( _this ); + attributes = new WebGLAttributes( _gl, capabilities ); + bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); + geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); + objects = new WebGLObjects( _gl, geometries, attributes, info ); + morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); + clipping = new WebGLClipping( properties ); + programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); + materials = new WebGLMaterials( properties ); + renderLists = new WebGLRenderLists( properties ); + renderStates = new WebGLRenderStates( extensions, capabilities ); + background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); + shadowMap = new WebGLShadowMap( _this, objects, capabilities ); - setUsage: function ( value ) { + bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); + indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - this.usage = value; + info.programs = programCache.programs; - return this; + _this.capabilities = capabilities; + _this.extensions = extensions; + _this.properties = properties; + _this.renderLists = renderLists; + _this.shadowMap = shadowMap; + _this.state = state; + _this.info = info; - }, + } - copy: function ( source ) { + initGLContext(); - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; + // xr - return this; + const xr = new WebXRManager( _this, _gl ); - }, + this.xr = xr; - copyAt: function ( index1, attribute, index2 ) { + // API - index1 *= this.stride; - index2 *= attribute.stride; + this.getContext = function () { - for ( let i = 0, l = this.stride; i < l; i ++ ) { + return _gl; - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + }; - } + this.getContextAttributes = function () { - return this; + return _gl.getContextAttributes(); - }, + }; - set: function ( value, offset = 0 ) { + this.forceContextLoss = function () { - this.array.set( value, offset ); + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.loseContext(); - return this; + }; - }, + this.forceContextRestore = function () { - clone: function ( data ) { + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.restoreContext(); - if ( data.arrayBuffers === undefined ) { + }; - data.arrayBuffers = {}; + this.getPixelRatio = function () { - } + return _pixelRatio; - if ( this.array.buffer._uuid === undefined ) { + }; - this.array.buffer._uuid = MathUtils.generateUUID(); + this.setPixelRatio = function ( value ) { - } + if ( value === undefined ) return; - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { + _pixelRatio = value; - data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; + this.setSize( _width, _height, false ); - } + }; - const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); + this.getSize = function ( target ) { - const ib = new InterleavedBuffer( array, this.stride ); - ib.setUsage( this.usage ); + return target.set( _width, _height ); - return ib; + }; - }, + this.setSize = function ( width, height, updateStyle ) { - onUpload: function ( callback ) { + if ( xr.isPresenting ) { - this.onUploadCallback = callback; + console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); + return; - return this; + } - }, + _width = width; + _height = height; - toJSON: function ( data ) { + _canvas.width = Math.floor( width * _pixelRatio ); + _canvas.height = Math.floor( height * _pixelRatio ); - if ( data.arrayBuffers === undefined ) { + if ( updateStyle !== false ) { - data.arrayBuffers = {}; + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; } - // generate UUID for array buffer if necessary + this.setViewport( 0, 0, width, height ); - if ( this.array.buffer._uuid === undefined ) { + }; - this.array.buffer._uuid = MathUtils.generateUUID(); + this.getDrawingBufferSize = function ( target ) { - } + return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { + }; - data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); + this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - } + _width = width; + _height = height; - // + _pixelRatio = pixelRatio; - return { - uuid: this.uuid, - buffer: this.array.buffer._uuid, - type: this.array.constructor.name, - stride: this.stride - }; + _canvas.width = Math.floor( width * pixelRatio ); + _canvas.height = Math.floor( height * pixelRatio ); - } + this.setViewport( 0, 0, width, height ); - } ); + }; - const _vector$6 = new Vector3(); + this.getCurrentViewport = function ( target ) { + + return target.copy( _currentViewport ); - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { + }; - this.name = ''; + this.getViewport = function ( target ) { - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; + return target.copy( _viewport ); - this.normalized = normalized === true; + }; - } + this.setViewport = function ( x, y, width, height ) { - Object.defineProperties( InterleavedBufferAttribute.prototype, { + if ( x.isVector4 ) { - count: { + _viewport.set( x.x, x.y, x.z, x.w ); - get: function () { + } else { - return this.data.count; + _viewport.set( x, y, width, height ); } - }, + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - array: { + }; - get: function () { + this.getScissor = function ( target ) { + + return target.copy( _scissor ); - return this.data.array; + }; - } + this.setScissor = function ( x, y, width, height ) { - }, + if ( x.isVector4 ) { - needsUpdate: { + _scissor.set( x.x, x.y, x.z, x.w ); - set: function ( value ) { + } else { - this.data.needsUpdate = value; + _scissor.set( x, y, width, height ); } - } + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - } ); + }; - Object.assign( InterleavedBufferAttribute.prototype, { + this.getScissorTest = function () { - isInterleavedBufferAttribute: true, + return _scissorTest; - applyMatrix4: function ( m ) { + }; - for ( let i = 0, l = this.data.count; i < l; i ++ ) { + this.setScissorTest = function ( boolean ) { - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + state.setScissorTest( _scissorTest = boolean ); - _vector$6.applyMatrix4( m ); + }; - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + this.setOpaqueSort = function ( method ) { - } + _opaqueSort = method; - return this; + }; - }, + this.setTransparentSort = function ( method ) { - setX: function ( index, x ) { + _transparentSort = method; - this.data.array[ index * this.data.stride + this.offset ] = x; + }; - return this; + // Clearing - }, + this.getClearColor = function ( target ) { - setY: function ( index, y ) { + return target.copy( background.getClearColor() ); - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + }; - return this; + this.setClearColor = function () { - }, + background.setClearColor.apply( background, arguments ); - setZ: function ( index, z ) { + }; - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + this.getClearAlpha = function () { - return this; + return background.getClearAlpha(); - }, + }; - setW: function ( index, w ) { + this.setClearAlpha = function () { - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + background.setClearAlpha.apply( background, arguments ); - return this; + }; - }, + this.clear = function ( color, depth, stencil ) { - getX: function ( index ) { + let bits = 0; - return this.data.array[ index * this.data.stride + this.offset ]; + if ( color === undefined || color ) bits |= 16384; + if ( depth === undefined || depth ) bits |= 256; + if ( stencil === undefined || stencil ) bits |= 1024; - }, + _gl.clear( bits ); - getY: function ( index ) { + }; - return this.data.array[ index * this.data.stride + this.offset + 1 ]; + this.clearColor = function () { - }, + this.clear( true, false, false ); - getZ: function ( index ) { + }; - return this.data.array[ index * this.data.stride + this.offset + 2 ]; + this.clearDepth = function () { - }, + this.clear( false, true, false ); - getW: function ( index ) { + }; - return this.data.array[ index * this.data.stride + this.offset + 3 ]; + this.clearStencil = function () { - }, + this.clear( false, false, true ); - setXY: function ( index, x, y ) { + }; - index = index * this.data.stride + this.offset; + // - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; + this.dispose = function () { - return this; + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - }, + renderLists.dispose(); + renderStates.dispose(); + properties.dispose(); + cubemaps.dispose(); + cubeuvmaps.dispose(); + objects.dispose(); + bindingStates.dispose(); - setXYZ: function ( index, x, y, z ) { + xr.dispose(); - index = index * this.data.stride + this.offset; + xr.removeEventListener( 'sessionstart', onXRSessionStart ); + xr.removeEventListener( 'sessionend', onXRSessionEnd ); - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; + if ( _transmissionRenderTarget ) { - return this; + _transmissionRenderTarget.dispose(); + _transmissionRenderTarget = null; - }, + } - setXYZW: function ( index, x, y, z, w ) { + animation.stop(); - index = index * this.data.stride + this.offset; + }; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; + // Events - return this; + function onContextLost( event ) { - }, + event.preventDefault(); - clone: function ( data ) { + console.log( 'THREE.WebGLRenderer: Context Lost.' ); - if ( data === undefined ) { + _isContextLost = true; - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); + } - const array = []; + function onContextRestore( /* event */ ) { - for ( let i = 0; i < this.count; i ++ ) { + console.log( 'THREE.WebGLRenderer: Context Restored.' ); - const index = i * this.data.stride + this.offset; + _isContextLost = false; - for ( let j = 0; j < this.itemSize; j ++ ) { + const infoAutoReset = info.autoReset; + const shadowMapEnabled = shadowMap.enabled; + const shadowMapAutoUpdate = shadowMap.autoUpdate; + const shadowMapNeedsUpdate = shadowMap.needsUpdate; + const shadowMapType = shadowMap.type; - array.push( this.data.array[ index + j ] ); + initGLContext(); - } + info.autoReset = infoAutoReset; + shadowMap.enabled = shadowMapEnabled; + shadowMap.autoUpdate = shadowMapAutoUpdate; + shadowMap.needsUpdate = shadowMapNeedsUpdate; + shadowMap.type = shadowMapType; - } + } - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); + function onMaterialDispose( event ) { - } else { + const material = event.target; - if ( data.interleavedBuffers === undefined ) { + material.removeEventListener( 'dispose', onMaterialDispose ); - data.interleavedBuffers = {}; + deallocateMaterial( material ); - } + } - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + // Buffer deallocation - data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); + function deallocateMaterial( material ) { - } + releaseMaterialProgramReferences( material ); - return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); + properties.remove( material ); - } + } - }, - toJSON: function ( data ) { + function releaseMaterialProgramReferences( material ) { - if ( data === undefined ) { + const programs = properties.get( material ).programs; - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); + if ( programs !== undefined ) { - const array = []; + programs.forEach( function ( program ) { - for ( let i = 0; i < this.count; i ++ ) { + programCache.releaseProgram( program ); - const index = i * this.data.stride + this.offset; + } ); - for ( let j = 0; j < this.itemSize; j ++ ) { + } - array.push( this.data.array[ index + j ] ); + } - } + // Buffer rendering - } + this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - // deinterleave data and save it as an ordinary buffer attribute for now + if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; + const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - } else { + const program = setProgram( camera, scene, geometry, material, object ); - // save as true interlaved attribtue + state.setMaterial( material, frontFaceCW ); - if ( data.interleavedBuffers === undefined ) { + // - data.interleavedBuffers = {}; + let index = geometry.index; + const position = geometry.attributes.position; - } + // - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + if ( index === null ) { - data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); + if ( position === undefined || position.count === 0 ) return; - } + } else if ( index.count === 0 ) { - return { - isInterleavedBufferAttribute: true, - itemSize: this.itemSize, - data: this.data.uuid, - offset: this.offset, - normalized: this.normalized - }; + return; } - } + // - } ); + let rangeFactor = 1; - /** - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ + if ( material.wireframe === true ) { - function SpriteMaterial( parameters ) { + index = geometries.getWireframeAttribute( geometry ); + rangeFactor = 2; - Material.call( this ); + } - this.type = 'SpriteMaterial'; + bindingStates.setup( object, material, program, geometry, index ); - this.color = new Color( 0xffffff ); + let attribute; + let renderer = bufferRenderer; - this.map = null; + if ( index !== null ) { - this.alphaMap = null; + attribute = attributes.get( index ); - this.rotation = 0; + renderer = indexedBufferRenderer; + renderer.setIndex( attribute ); - this.sizeAttenuation = true; + } - this.transparent = true; + // - this.setValues( parameters ); + const dataCount = ( index !== null ) ? index.count : position.count; - } + const rangeStart = geometry.drawRange.start * rangeFactor; + const rangeCount = geometry.drawRange.count * rangeFactor; - SpriteMaterial.prototype = Object.create( Material.prototype ); - SpriteMaterial.prototype.constructor = SpriteMaterial; - SpriteMaterial.prototype.isSpriteMaterial = true; + const groupStart = group !== null ? group.start * rangeFactor : 0; + const groupCount = group !== null ? group.count * rangeFactor : Infinity; - SpriteMaterial.prototype.copy = function ( source ) { + const drawStart = Math.max( rangeStart, groupStart ); + const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - Material.prototype.copy.call( this, source ); + const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - this.color.copy( source.color ); + if ( drawCount === 0 ) return; - this.map = source.map; + // - this.alphaMap = source.alphaMap; + if ( object.isMesh ) { - this.rotation = source.rotation; + if ( material.wireframe === true ) { - this.sizeAttenuation = source.sizeAttenuation; + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( 1 ); - return this; + } else { - }; + renderer.setMode( 4 ); - let _geometry; + } - const _intersectPoint = new Vector3(); - const _worldScale = new Vector3(); - const _mvPosition = new Vector3(); + } else if ( object.isLine ) { - const _alignedPosition = new Vector2(); - const _rotatedPosition = new Vector2(); - const _viewWorldMatrix = new Matrix4(); + let lineWidth = material.linewidth; - const _vA$1 = new Vector3(); - const _vB$1 = new Vector3(); - const _vC$1 = new Vector3(); + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - const _uvA$1 = new Vector2(); - const _uvB$1 = new Vector2(); - const _uvC$1 = new Vector2(); + state.setLineWidth( lineWidth * getTargetPixelRatio() ); - function Sprite( material ) { + if ( object.isLineSegments ) { - Object3D.call( this ); + renderer.setMode( 1 ); - this.type = 'Sprite'; + } else if ( object.isLineLoop ) { - if ( _geometry === undefined ) { + renderer.setMode( 2 ); - _geometry = new BufferGeometry(); + } else { - const float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); + renderer.setMode( 3 ); - const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); + } - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); + } else if ( object.isPoints ) { - } + renderer.setMode( 0 ); - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); + } else if ( object.isSprite ) { - this.center = new Vector2( 0.5, 0.5 ); + renderer.setMode( 4 ); - } + } + + if ( object.isInstancedMesh ) { - Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { + renderer.renderInstances( drawStart, drawCount, object.count ); - constructor: Sprite, + } else if ( geometry.isInstancedBufferGeometry ) { - isSprite: true, + const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - raycast: function ( raycaster, intersects ) { + renderer.renderInstances( drawStart, drawCount, instanceCount ); - if ( raycaster.camera === null ) { + } else { - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); + renderer.render( drawStart, drawCount ); } - _worldScale.setFromMatrixScale( this.matrixWorld ); + }; - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); + // Compile - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + this.compile = function ( scene, camera ) { - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { + currentRenderState = renderStates.get( scene ); + currentRenderState.init(); - _worldScale.multiplyScalar( - _mvPosition.z ); + renderStateStack.push( currentRenderState ); - } + scene.traverseVisible( function ( object ) { - const rotation = this.material.rotation; - let sin, cos; + if ( object.isLight && object.layers.test( camera.layers ) ) { - if ( rotation !== 0 ) { + currentRenderState.pushLight( object ); - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); + if ( object.castShadow ) { - } + currentRenderState.pushShadow( object ); - const center = this.center; + } - transformVertex( _vA$1.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB$1.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + } - _uvA$1.set( 0, 0 ); - _uvB$1.set( 1, 0 ); - _uvC$1.set( 1, 1 ); + } ); - // check first triangle - let intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint ); + currentRenderState.setupLights( _this.physicallyCorrectLights ); - if ( intersect === null ) { + scene.traverse( function ( object ) { - // check second triangle - transformVertex( _vB$1.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB$1.set( 0, 1 ); + const material = object.material; - intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint ); - if ( intersect === null ) { + if ( material ) { - return; + if ( Array.isArray( material ) ) { - } + for ( let i = 0; i < material.length; i ++ ) { - } + const material2 = material[ i ]; - const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); + getProgram( material2, scene, object ); - if ( distance < raycaster.near || distance > raycaster.far ) return; + } - intersects.push( { + } else { - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ), - face: null, - object: this + getProgram( material, scene, object ); - } ); + } - }, + } - copy: function ( source ) { + } ); - Object3D.prototype.copy.call( this, source ); + renderStateStack.pop(); + currentRenderState = null; - if ( source.center !== undefined ) this.center.copy( source.center ); + }; - this.material = source.material; + // Animation Loop - return this; + let onAnimationFrameCallback = null; - } + function onAnimationFrame( time ) { - } ); + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { + } - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); + function onXRSessionStart() { - // to check if rotation is not zero - if ( sin !== undefined ) { + animation.stop(); - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); + } - } else { + function onXRSessionEnd() { - _rotatedPosition.copy( _alignedPosition ); + animation.start(); } + const animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; - - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); + if ( typeof window !== 'undefined' ) animation.setContext( window ); - } + this.setAnimationLoop = function ( callback ) { - const _v1$4 = new Vector3(); - const _v2$2 = new Vector3(); + onAnimationFrameCallback = callback; + xr.setAnimationLoop( callback ); - function LOD() { + ( callback === null ) ? animation.stop() : animation.start(); - Object3D.call( this ); + }; - this._currentLevel = 0; + xr.addEventListener( 'sessionstart', onXRSessionStart ); + xr.addEventListener( 'sessionend', onXRSessionEnd ); - this.type = 'LOD'; + // Rendering - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - } - } ); + this.render = function ( scene, camera ) { - this.autoUpdate = true; + if ( camera !== undefined && camera.isCamera !== true ) { - } + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { + } - constructor: LOD, + if ( _isContextLost === true ) return; - isLOD: true, + // update scene graph - copy: function ( source ) { + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - Object3D.prototype.copy.call( this, source, false ); + // update camera matrices and frustum - const levels = source.levels; + if ( camera.parent === null ) camera.updateMatrixWorld(); - for ( let i = 0, l = levels.length; i < l; i ++ ) { + if ( xr.enabled === true && xr.isPresenting === true ) { - const level = levels[ i ]; + if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); - this.addLevel( level.object.clone(), level.distance ); + camera = xr.getCamera(); // use XR camera for rendering } - this.autoUpdate = source.autoUpdate; + // + if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); - return this; + currentRenderState = renderStates.get( scene, renderStateStack.length ); + currentRenderState.init(); - }, + renderStateStack.push( currentRenderState ); - addLevel: function ( object, distance = 0 ) { + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromProjectionMatrix( _projScreenMatrix ); - distance = Math.abs( distance ); + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - const levels = this.levels; + currentRenderList = renderLists.get( scene, renderListStack.length ); + currentRenderList.init(); - let l; + renderListStack.push( currentRenderList ); - for ( l = 0; l < levels.length; l ++ ) { + projectObject( scene, camera, 0, _this.sortObjects ); - if ( distance < levels[ l ].distance ) { + currentRenderList.finish(); - break; + if ( _this.sortObjects === true ) { - } + currentRenderList.sort( _opaqueSort, _transparentSort ); } - levels.splice( l, 0, { distance: distance, object: object } ); + // + + if ( _clippingEnabled === true ) clipping.beginShadows(); - this.add( object ); + const shadowsArray = currentRenderState.state.shadowsArray; - return this; + shadowMap.render( shadowsArray, scene, camera ); - }, + if ( _clippingEnabled === true ) clipping.endShadows(); - getCurrentLevel: function () { + // - return this._currentLevel; + if ( this.info.autoReset === true ) this.info.reset(); - }, + // - getObjectForDistance: function ( distance ) { + background.render( currentRenderList, scene ); - const levels = this.levels; + // render scene - if ( levels.length > 0 ) { + currentRenderState.setupLights( _this.physicallyCorrectLights ); - let i, l; + if ( camera.isArrayCamera ) { - for ( i = 1, l = levels.length; i < l; i ++ ) { + const cameras = camera.cameras; - if ( distance < levels[ i ].distance ) { + for ( let i = 0, l = cameras.length; i < l; i ++ ) { - break; + const camera2 = cameras[ i ]; - } + renderScene( currentRenderList, scene, camera2, camera2.viewport ); } - return levels[ i - 1 ].object; - - } + } else { - return null; + renderScene( currentRenderList, scene, camera ); - }, + } - raycast: function ( raycaster, intersects ) { + // - const levels = this.levels; + if ( _currentRenderTarget !== null ) { - if ( levels.length > 0 ) { + // resolve multisample renderbuffers to a single-sample texture if necessary - _v1$4.setFromMatrixPosition( this.matrixWorld ); + textures.updateMultisampleRenderTarget( _currentRenderTarget ); - const distance = raycaster.ray.origin.distanceTo( _v1$4 ); + // Generate mipmap if we're using any kind of mipmap filtering - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + textures.updateRenderTargetMipmap( _currentRenderTarget ); } - }, - - update: function ( camera ) { + // - const levels = this.levels; + if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - if ( levels.length > 1 ) { + // Ensure depth buffer writing is enabled so it can be cleared on next render - _v1$4.setFromMatrixPosition( camera.matrixWorld ); - _v2$2.setFromMatrixPosition( this.matrixWorld ); + state.buffers.depth.setTest( true ); + state.buffers.depth.setMask( true ); + state.buffers.color.setMask( true ); - const distance = _v1$4.distanceTo( _v2$2 ) / camera.zoom; + state.setPolygonOffset( false ); - levels[ 0 ].object.visible = true; + // _gl.finish(); - let i, l; + bindingStates.resetDefaultState(); + _currentMaterialId = - 1; + _currentCamera = null; - for ( i = 1, l = levels.length; i < l; i ++ ) { + renderStateStack.pop(); - if ( distance >= levels[ i ].distance ) { + if ( renderStateStack.length > 0 ) { - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; + currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; - } else { + } else { - break; + currentRenderState = null; - } + } - } + renderListStack.pop(); - this._currentLevel = i - 1; + if ( renderListStack.length > 0 ) { - for ( ; i < l; i ++ ) { + currentRenderList = renderListStack[ renderListStack.length - 1 ]; - levels[ i ].object.visible = false; + } else { - } + currentRenderList = null; } - }, - - toJSON: function ( meta ) { + }; - const data = Object3D.prototype.toJSON.call( this, meta ); + function projectObject( object, camera, groupOrder, sortObjects ) { - if ( this.autoUpdate === false ) data.object.autoUpdate = false; + if ( object.visible === false ) return; - data.object.levels = []; + const visible = object.layers.test( camera.layers ); - const levels = this.levels; + if ( visible ) { - for ( let i = 0, l = levels.length; i < l; i ++ ) { + if ( object.isGroup ) { - const level = levels[ i ]; + groupOrder = object.renderOrder; - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); + } else if ( object.isLOD ) { - } + if ( object.autoUpdate === true ) object.update( camera ); - return data; + } else if ( object.isLight ) { - } + currentRenderState.pushLight( object ); - } ); + if ( object.castShadow ) { - const _basePosition = new Vector3(); + currentRenderState.pushShadow( object ); - const _skinIndex = new Vector4(); - const _skinWeight = new Vector4(); + } - const _vector$7 = new Vector3(); - const _matrix$1 = new Matrix4(); + } else if ( object.isSprite ) { - function SkinnedMesh( geometry, material ) { + if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - if ( geometry && geometry.isGeometry ) { + if ( sortObjects ) { - console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - } + } - Mesh.call( this, geometry, material ); + const geometry = objects.update( object ); + const material = object.material; - this.type = 'SkinnedMesh'; + if ( material.visible ) { - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - } + } - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { + } - constructor: SkinnedMesh, + } else if ( object.isMesh || object.isLine || object.isPoints ) { - isSkinnedMesh: true, + if ( object.isSkinnedMesh ) { - copy: function ( source ) { + // update skeleton only once in a frame - Mesh.prototype.copy.call( this, source ); + if ( object.skeleton.frame !== info.render.frame ) { - this.bindMode = source.bindMode; - this.bindMatrix.copy( source.bindMatrix ); - this.bindMatrixInverse.copy( source.bindMatrixInverse ); + object.skeleton.update(); + object.skeleton.frame = info.render.frame; - this.skeleton = source.skeleton; + } - return this; + } - }, + if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - bind: function ( skeleton, bindMatrix ) { + if ( sortObjects ) { - this.skeleton = skeleton; + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - if ( bindMatrix === undefined ) { + } - this.updateMatrixWorld( true ); + const geometry = objects.update( object ); + const material = object.material; - this.skeleton.calculateInverses(); + if ( Array.isArray( material ) ) { - bindMatrix = this.matrixWorld; + const groups = geometry.groups; - } + for ( let i = 0, l = groups.length; i < l; i ++ ) { - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.copy( bindMatrix ).invert(); + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - }, + if ( groupMaterial && groupMaterial.visible ) { - pose: function () { + currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - this.skeleton.pose(); + } - }, + } - normalizeSkinWeights: function () { + } else if ( material.visible ) { - const vector = new Vector4(); + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - const skinWeight = this.geometry.attributes.skinWeight; + } - for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { + } - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); + } - const scale = 1.0 / vector.manhattanLength(); + } - if ( scale !== Infinity ) { + const children = object.children; - vector.multiplyScalar( scale ); + for ( let i = 0, l = children.length; i < l; i ++ ) { - } else { + projectObject( children[ i ], camera, groupOrder, sortObjects ); - vector.set( 1, 0, 0, 0 ); // do something reasonable + } - } + } - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); + function renderScene( currentRenderList, scene, camera, viewport ) { - } + const opaqueObjects = currentRenderList.opaque; + const transmissiveObjects = currentRenderList.transmissive; + const transparentObjects = currentRenderList.transparent; - }, + currentRenderState.setupLightsView( camera ); - updateMatrixWorld: function ( force ) { + if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, scene, camera ); - Mesh.prototype.updateMatrixWorld.call( this, force ); + if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); - if ( this.bindMode === 'attached' ) { + if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); + if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); + if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - this.bindMatrixInverse.copy( this.matrixWorld ).invert(); + } - } else if ( this.bindMode === 'detached' ) { + function renderTransmissionPass( opaqueObjects, scene, camera ) { - this.bindMatrixInverse.copy( this.bindMatrix ).invert(); + if ( _transmissionRenderTarget === null ) { - } else { + const needsAntialias = _antialias === true && capabilities.isWebGL2 === true; + const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget; - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); + _transmissionRenderTarget = new renderTargetType( 1024, 1024, { + generateMipmaps: true, + type: utils.convert( HalfFloatType ) !== null ? HalfFloatType : UnsignedByteType, + minFilter: LinearMipmapLinearFilter, + magFilter: NearestFilter, + wrapS: ClampToEdgeWrapping, + wrapT: ClampToEdgeWrapping + } ); } - }, + const currentRenderTarget = _this.getRenderTarget(); + _this.setRenderTarget( _transmissionRenderTarget ); + _this.clear(); - boneTransform: function ( index, target ) { + // Turn off the features which can affect the frag color for opaque objects pass. + // Otherwise they are applied twice in opaque objects pass and transmission objects pass. + const currentToneMapping = _this.toneMapping; + _this.toneMapping = NoToneMapping; - const skeleton = this.skeleton; - const geometry = this.geometry; + renderObjects( opaqueObjects, scene, camera ); - _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); + _this.toneMapping = currentToneMapping; - _basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - target.set( 0, 0, 0 ); + _this.setRenderTarget( currentRenderTarget ); - for ( let i = 0; i < 4; i ++ ) { + } - const weight = _skinWeight.getComponent( i ); + function renderObjects( renderList, scene, camera ) { - if ( weight !== 0 ) { + const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - const boneIndex = _skinIndex.getComponent( i ); + for ( let i = 0, l = renderList.length; i < l; i ++ ) { + + const renderItem = renderList[ i ]; - _matrix$1.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = overrideMaterial === null ? renderItem.material : overrideMaterial; + const group = renderItem.group; - target.addScaledVector( _vector$7.copy( _basePosition ).applyMatrix4( _matrix$1 ), weight ); + if ( object.layers.test( camera.layers ) ) { + + renderObject( object, scene, camera, geometry, material, group ); } } - return target.applyMatrix4( this.bindMatrixInverse ); - } - } ); + function renderObject( object, scene, camera, geometry, material, group ) { + + object.onBeforeRender( _this, scene, camera, geometry, material, group ); - function Bone() { + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - Object3D.call( this ); + material.onBeforeRender( _this, scene, camera, geometry, object, group ); - this.type = 'Bone'; + if ( material.transparent === true && material.side === DoubleSide ) { - } + material.side = BackSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { + material.side = FrontSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - constructor: Bone, + material.side = DoubleSide; - isBone: true + } else { - } ); + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - const _offsetMatrix = new Matrix4(); - const _identityMatrix = new Matrix4(); + } - function Skeleton( bones = [], boneInverses = [] ) { + object.onAfterRender( _this, scene, camera, geometry, material, group ); - this.uuid = MathUtils.generateUUID(); + } - this.bones = bones.slice( 0 ); - this.boneInverses = boneInverses; - this.boneMatrices = null; + function getProgram( material, scene, object ) { - this.boneTexture = null; - this.boneTextureSize = 0; + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - this.frame = - 1; + const materialProperties = properties.get( material ); - this.init(); + const lights = currentRenderState.state.lights; + const shadowsArray = currentRenderState.state.shadowsArray; - } + const lightsStateVersion = lights.state.version; - Object.assign( Skeleton.prototype, { + const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); + const programCacheKey = programCache.getProgramCacheKey( parameters ); - init: function () { + let programs = materialProperties.programs; - const bones = this.bones; - const boneInverses = this.boneInverses; + // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - this.boneMatrices = new Float32Array( bones.length * 16 ); + materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.fog = scene.fog; + materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); - // calculate inverse bone matrices if necessary + if ( programs === undefined ) { - if ( boneInverses.length === 0 ) { + // new material - this.calculateInverses(); + material.addEventListener( 'dispose', onMaterialDispose ); - } else { + programs = new Map(); + materialProperties.programs = programs; - // handle special case + } - if ( bones.length !== boneInverses.length ) { + let program = programs.get( programCacheKey ); - console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); + if ( program !== undefined ) { - this.boneInverses = []; + // early out if program and light state is identical - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { - this.boneInverses.push( new Matrix4() ); + updateCommonMaterialProperties( material, parameters ); - } + return program; } - } + } else { - }, + parameters.uniforms = programCache.getUniforms( material ); - calculateInverses: function () { + material.onBuild( object, parameters, _this ); - this.boneInverses.length = 0; + material.onBeforeCompile( parameters, _this ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + program = programCache.acquireProgram( parameters, programCacheKey ); + programs.set( programCacheKey, program ); - const inverse = new Matrix4(); + materialProperties.uniforms = parameters.uniforms; - if ( this.bones[ i ] ) { + } - inverse.copy( this.bones[ i ].matrixWorld ).invert(); + const uniforms = materialProperties.uniforms; - } + if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { - this.boneInverses.push( inverse ); + uniforms.clippingPlanes = clipping.uniform; } - }, - - pose: function () { + updateCommonMaterialProperties( material, parameters ); - // recover the bind-time world matrices + // store the light setup it was created for - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + materialProperties.needsLights = materialNeedsLights( material ); + materialProperties.lightsStateVersion = lightsStateVersion; - const bone = this.bones[ i ]; + if ( materialProperties.needsLights ) { - if ( bone ) { + // wire up the material to this renderer's lighting state - bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); + uniforms.ambientLightColor.value = lights.state.ambient; + uniforms.lightProbe.value = lights.state.probe; + uniforms.directionalLights.value = lights.state.directional; + uniforms.directionalLightShadows.value = lights.state.directionalShadow; + uniforms.spotLights.value = lights.state.spot; + uniforms.spotLightShadows.value = lights.state.spotShadow; + uniforms.rectAreaLights.value = lights.state.rectArea; + uniforms.ltc_1.value = lights.state.rectAreaLTC1; + uniforms.ltc_2.value = lights.state.rectAreaLTC2; + uniforms.pointLights.value = lights.state.point; + uniforms.pointLightShadows.value = lights.state.pointShadow; + uniforms.hemisphereLights.value = lights.state.hemi; - } + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; + // TODO (abelnation): add area lights shadow info to uniforms } - // compute the local matrices, positions, rotations and scales + const progUniforms = program.getUniforms(); + const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + materialProperties.currentProgram = program; + materialProperties.uniformsList = uniformsList; - const bone = this.bones[ i ]; + return program; - if ( bone ) { + } - if ( bone.parent && bone.parent.isBone ) { + function updateCommonMaterialProperties( material, parameters ) { - bone.matrix.copy( bone.parent.matrixWorld ).invert(); - bone.matrix.multiply( bone.matrixWorld ); + const materialProperties = properties.get( material ); - } else { + materialProperties.outputEncoding = parameters.outputEncoding; + materialProperties.instancing = parameters.instancing; + materialProperties.skinning = parameters.skinning; + materialProperties.morphTargets = parameters.morphTargets; + materialProperties.morphNormals = parameters.morphNormals; + materialProperties.morphTargetsCount = parameters.morphTargetsCount; + materialProperties.numClippingPlanes = parameters.numClippingPlanes; + materialProperties.numIntersection = parameters.numClipIntersection; + materialProperties.vertexAlphas = parameters.vertexAlphas; + materialProperties.vertexTangents = parameters.vertexTangents; - bone.matrix.copy( bone.matrixWorld ); + } - } + function setProgram( camera, scene, geometry, material, object ) { - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - } + textures.resetTextureUnits(); - } + const fog = scene.fog; + const environment = material.isMeshStandardMaterial ? scene.environment : null; + const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; + const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; + const morphTargets = !! geometry.morphAttributes.position; + const morphNormals = !! geometry.morphAttributes.normal; + const morphTargetsCount = !! geometry.morphAttributes.position ? geometry.morphAttributes.position.length : 0; - }, + const materialProperties = properties.get( material ); + const lights = currentRenderState.state.lights; - update: function () { + if ( _clippingEnabled === true ) { - const bones = this.bones; - const boneInverses = this.boneInverses; - const boneMatrices = this.boneMatrices; - const boneTexture = this.boneTexture; + if ( _localClippingEnabled === true || camera !== _currentCamera ) { - // flatten bone matrices to array + const useCache = + camera === _currentCamera && + material.id === _currentMaterialId; - for ( let i = 0, il = bones.length; i < il; i ++ ) { + // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + clipping.setState( material, camera, useCache ); - // compute the offset between the current and the original transform + } - const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; + } - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); + // - } + let needsProgramChange = false; - if ( boneTexture !== null ) { + if ( material.version === materialProperties.__version ) { - boneTexture.needsUpdate = true; + if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - } + needsProgramChange = true; - }, + } else if ( materialProperties.outputEncoding !== encoding ) { - clone: function () { + needsProgramChange = true; - return new Skeleton( this.bones, this.boneInverses ); + } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { - }, + needsProgramChange = true; - getBoneByName: function ( name ) { + } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + needsProgramChange = true; - const bone = this.bones[ i ]; + } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { - if ( bone.name === name ) { + needsProgramChange = true; - return bone; + } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { - } + needsProgramChange = true; - } + } else if ( materialProperties.envMap !== envMap ) { - return undefined; + needsProgramChange = true; - }, + } else if ( material.fog && materialProperties.fog !== fog ) { - dispose: function ( ) { + needsProgramChange = true; - if ( this.boneTexture !== null ) { + } else if ( materialProperties.numClippingPlanes !== undefined && + ( materialProperties.numClippingPlanes !== clipping.numPlanes || + materialProperties.numIntersection !== clipping.numIntersection ) ) { - this.boneTexture.dispose(); + needsProgramChange = true; - this.boneTexture = null; + } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { - } + needsProgramChange = true; - }, + } else if ( materialProperties.vertexTangents !== vertexTangents ) { - fromJSON: function ( json, bones ) { + needsProgramChange = true; - this.uuid = json.uuid; + } else if ( materialProperties.morphTargets !== morphTargets ) { - for ( let i = 0, l = json.bones.length; i < l; i ++ ) { + needsProgramChange = true; - const uuid = json.bones[ i ]; - let bone = bones[ uuid ]; + } else if ( materialProperties.morphNormals !== morphNormals ) { - if ( bone === undefined ) { + needsProgramChange = true; - console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); - bone = new Bone(); + } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { + + needsProgramChange = true; } - this.bones.push( bone ); - this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); + } else { + + needsProgramChange = true; + materialProperties.__version = material.version; } - this.init(); - - return this; + // - }, + let program = materialProperties.currentProgram; - toJSON: function () { + if ( needsProgramChange === true ) { - const data = { - metadata: { - version: 4.5, - type: 'Skeleton', - generator: 'Skeleton.toJSON' - }, - bones: [], - boneInverses: [] - }; + program = getProgram( material, scene, object ); - data.uuid = this.uuid; + } - const bones = this.bones; - const boneInverses = this.boneInverses; + let refreshProgram = false; + let refreshMaterial = false; + let refreshLights = false; - for ( let i = 0, l = bones.length; i < l; i ++ ) { + const p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.uniforms; - const bone = bones[ i ]; - data.bones.push( bone.uuid ); + if ( state.useProgram( program.program ) ) { - const boneInverse = boneInverses[ i ]; - data.boneInverses.push( boneInverse.toArray() ); + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; } - return data; - - } + if ( material.id !== _currentMaterialId ) { - } ); + _currentMaterialId = material.id; - const _instanceLocalMatrix = new Matrix4(); - const _instanceWorldMatrix = new Matrix4(); + refreshMaterial = true; - const _instanceIntersects = []; + } - const _mesh = new Mesh(); + if ( refreshProgram || _currentCamera !== camera ) { - function InstancedMesh( geometry, material, count ) { + p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - Mesh.call( this, geometry, material ); + if ( capabilities.logarithmicDepthBuffer ) { - this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceColor = null; + p_uniforms.setValue( _gl, 'logDepthBufFC', + 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - this.count = count; + } - this.frustumCulled = false; + if ( _currentCamera !== camera ) { - } + _currentCamera = camera; - InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { + // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: - constructor: InstancedMesh, + refreshMaterial = true; // set to true on material change + refreshLights = true; // remains set until update done - isInstancedMesh: true, + } - copy: function ( source ) { + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - Mesh.prototype.copy.call( this, source ); + if ( material.isShaderMaterial || + material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshStandardMaterial || + material.envMap ) { - this.instanceMatrix.copy( source.instanceMatrix ); + const uCamPos = p_uniforms.map.cameraPosition; - if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); + if ( uCamPos !== undefined ) { - this.count = source.count; + uCamPos.setValue( _gl, + _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - return this; + } - }, + } - getColorAt: function ( index, color ) { + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial ) { - color.fromArray( this.instanceColor.array, index * 3 ); + p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - }, + } - getMatrixAt: function ( index, matrix ) { + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial || + material.isShadowMaterial || + object.isSkinnedMesh ) { - matrix.fromArray( this.instanceMatrix.array, index * 16 ); + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - }, + } - raycast: function ( raycaster, intersects ) { + } - const matrixWorld = this.matrixWorld; - const raycastTimes = this.count; + // skinning and morph target uniforms must be set even if material didn't change + // auto-setting of texture unit for bone and morph texture must go before other textures + // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures - _mesh.geometry = this.geometry; - _mesh.material = this.material; + if ( object.isSkinnedMesh ) { - if ( _mesh.material === undefined ) return; + p_uniforms.setOptional( _gl, object, 'bindMatrix' ); + p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { + const skeleton = object.skeleton; - // calculate the world matrix for each instance + if ( skeleton ) { - this.getMatrixAt( instanceId, _instanceLocalMatrix ); + if ( capabilities.floatVertexTextures ) { - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); + if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); - // the mesh represents this single instance + p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); + p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - _mesh.matrixWorld = _instanceWorldMatrix; + } else { - _mesh.raycast( raycaster, _instanceIntersects ); + p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - // process the result of raycast + } - for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { + } - const intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); + } - } + if ( !! geometry && ( geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined ) ) { - _instanceIntersects.length = 0; + morphtargets.update( object, geometry, material, program ); } - }, - - setColorAt: function ( index, color ) { - if ( this.instanceColor === null ) { + if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 ); + materialProperties.receiveShadow = object.receiveShadow; + p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); } - color.toArray( this.instanceColor.array, index * 3 ); + if ( refreshMaterial ) { - }, + p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - setMatrixAt: function ( index, matrix ) { + if ( materialProperties.needsLights ) { - matrix.toArray( this.instanceMatrix.array, index * 16 ); + // the current material requires lighting info - }, + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required - updateMorphTargets: function () { + markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - }, + } - dispose: function () { + // refresh uniforms common to several materials - this.dispatchEvent( { type: 'dispose' } ); + if ( fog && material.fog ) { - } + materials.refreshFogUniforms( m_uniforms, fog ); - } ); + } - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ + materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); - function LineBasicMaterial( parameters ) { + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - Material.call( this ); + } - this.type = 'LineBasicMaterial'; + if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - this.color = new Color( 0xffffff ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + material.uniformsNeedUpdate = false; - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; + } - this.morphTargets = false; + if ( material.isSpriteMaterial ) { - this.setValues( parameters ); + p_uniforms.setValue( _gl, 'center', object.center ); - } + } - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; + // common matrices - LineBasicMaterial.prototype.isLineBasicMaterial = true; + p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); + p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); + p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - LineBasicMaterial.prototype.copy = function ( source ) { + return program; - Material.prototype.copy.call( this, source ); + } - this.color.copy( source.color ); + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; + function markUniformsLightsNeedsUpdate( uniforms, value ) { - this.morphTargets = source.morphTargets; + uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; - return this; + uniforms.directionalLights.needsUpdate = value; + uniforms.directionalLightShadows.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.pointLightShadows.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.spotLightShadows.needsUpdate = value; + uniforms.rectAreaLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; - }; + } - const _start = new Vector3(); - const _end = new Vector3(); - const _inverseMatrix$1 = new Matrix4(); - const _ray$1 = new Ray(); - const _sphere$2 = new Sphere(); + function materialNeedsLights( material ) { - function Line( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { + return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || + material.isMeshStandardMaterial || material.isShadowMaterial || + ( material.isShaderMaterial && material.lights === true ); - Object3D.call( this ); + } - this.type = 'Line'; + this.getActiveCubeFace = function () { - this.geometry = geometry; - this.material = material; + return _currentActiveCubeFace; - this.updateMorphTargets(); + }; - } + this.getActiveMipmapLevel = function () { - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { + return _currentActiveMipmapLevel; - constructor: Line, + }; - isLine: true, + this.getRenderTarget = function () { - copy: function ( source ) { + return _currentRenderTarget; - Object3D.prototype.copy.call( this, source ); + }; - this.material = source.material; - this.geometry = source.geometry; + this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - return this; + _currentRenderTarget = renderTarget; + _currentActiveCubeFace = activeCubeFace; + _currentActiveMipmapLevel = activeMipmapLevel; - }, + if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - computeLineDistances: function () { + textures.setupRenderTarget( renderTarget ); - const geometry = this.geometry; + } - if ( geometry.isBufferGeometry ) { + let framebuffer = null; + let isCube = false; + let isRenderTarget3D = false; - // we assume non-indexed geometry + if ( renderTarget ) { - if ( geometry.index === null ) { + const texture = renderTarget.texture; - const positionAttribute = geometry.attributes.position; - const lineDistances = [ 0 ]; + if ( texture.isDataTexture3D || texture.isDataTexture2DArray ) { - for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { + isRenderTarget3D = true; - _start.fromBufferAttribute( positionAttribute, i - 1 ); - _end.fromBufferAttribute( positionAttribute, i ); + } - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start.distanceTo( _end ); + const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - } + if ( renderTarget.isWebGLCubeRenderTarget ) { - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + framebuffer = __webglFramebuffer[ activeCubeFace ]; + isCube = true; + + } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; } else { - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + framebuffer = __webglFramebuffer; } - } else if ( geometry.isGeometry ) { + _currentViewport.copy( renderTarget.viewport ); + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; - console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + } else { + + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); + _currentScissorTest = _scissorTest; } - return this; + const framebufferBound = state.bindFramebuffer( 36160, framebuffer ); - }, + if ( framebufferBound && capabilities.drawBuffers ) { - raycast: function ( raycaster, intersects ) { + let needsUpdate = false; - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Line.threshold; + if ( renderTarget ) { - // Checking boundingSphere distance to ray + if ( renderTarget.isWebGLMultipleRenderTargets ) { - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + const textures = renderTarget.texture; - _sphere$2.copy( geometry.boundingSphere ); - _sphere$2.applyMatrix4( matrixWorld ); - _sphere$2.radius += threshold; + if ( _currentDrawBuffers.length !== textures.length || _currentDrawBuffers[ 0 ] !== 36064 ) { - if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - // + _currentDrawBuffers[ i ] = 36064 + i; - _inverseMatrix$1.copy( matrixWorld ).invert(); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); + } - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; + _currentDrawBuffers.length = textures.length; - const vStart = new Vector3(); - const vEnd = new Vector3(); - const interSegment = new Vector3(); - const interRay = new Vector3(); - const step = this.isLineSegments ? 2 : 1; + needsUpdate = true; - if ( geometry.isBufferGeometry ) { + } - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; + } else { - if ( index !== null ) { + if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== 36064 ) { - const indices = index.array; + _currentDrawBuffers[ 0 ] = 36064; + _currentDrawBuffers.length = 1; - for ( let i = 0, l = indices.length - 1; i < l; i += step ) { + needsUpdate = true; - const a = indices[ i ]; - const b = indices[ i + 1 ]; + } - vStart.fromBufferAttribute( positionAttribute, a ); - vEnd.fromBufferAttribute( positionAttribute, b ); + } - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + } else { - if ( distSq > localThresholdSq ) continue; + if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== 1029 ) { - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + _currentDrawBuffers[ 0 ] = 1029; + _currentDrawBuffers.length = 1; - const distance = raycaster.ray.origin.distanceTo( interRay ); + needsUpdate = true; - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + } - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + if ( needsUpdate ) { - } ); + if ( capabilities.isWebGL2 ) { + + _gl.drawBuffers( _currentDrawBuffers ); + + } else { + + extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( _currentDrawBuffers ); } - } else { + } - for ( let i = 0, l = positionAttribute.count - 1; i < l; i += step ) { + } - vStart.fromBufferAttribute( positionAttribute, i ); - vEnd.fromBufferAttribute( positionAttribute, i + 1 ); + state.viewport( _currentViewport ); + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + if ( isCube ) { - if ( distSq > localThresholdSq ) continue; + const textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + } else if ( isRenderTarget3D ) { - const distance = raycaster.ray.origin.distanceTo( interRay ); + const textureProperties = properties.get( renderTarget.texture ); + const layer = activeCubeFace || 0; + _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + }; - } ); + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - } + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - } + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; - } else if ( geometry.isGeometry ) { + } - console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + + framebuffer = framebuffer[ activeCubeFaceIndex ]; } - }, + if ( framebuffer ) { - updateMorphTargets: function () { + state.bindFramebuffer( 36160, framebuffer ); - const geometry = this.geometry; + try { - if ( geometry.isBufferGeometry ) { + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - if ( keys.length > 0 ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; - const morphAttribute = morphAttributes[ keys[ 0 ] ]; + } - if ( morphAttribute !== undefined ) { + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; - const name = morphAttribute[ m ].name || String( m ); + } - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { + + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } - } + } else { - } + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - } else { + } - const morphTargets = geometry.morphTargets; + } finally { - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + // restore framebuffer of current render target if necessary - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( 36160, framebuffer ); } } - } + }; - } ); + this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - const _start$1 = new Vector3(); - const _end$1 = new Vector3(); + const levelScale = Math.pow( 2, - level ); + const width = Math.floor( texture.image.width * levelScale ); + const height = Math.floor( texture.image.height * levelScale ); - function LineSegments( geometry, material ) { + let glFormat = utils.convert( texture.format ); - Line.call( this, geometry, material ); + if ( capabilities.isWebGL2 ) { - this.type = 'LineSegments'; + // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=1120100 + // Not needed in Chrome 93+ - } + if ( glFormat === 6407 ) glFormat = 32849; + if ( glFormat === 6408 ) glFormat = 32856; - LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { + } - constructor: LineSegments, + textures.setTexture2D( texture, 0 ); - isLineSegments: true, + _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); - computeLineDistances: function () { + state.unbindTexture(); - const geometry = this.geometry; + }; - if ( geometry.isBufferGeometry ) { + this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { - // we assume non-indexed geometry + const width = srcTexture.image.width; + const height = srcTexture.image.height; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); - if ( geometry.index === null ) { + textures.setTexture2D( dstTexture, 0 ); - const positionAttribute = geometry.attributes.position; - const lineDistances = []; + // As another texture upload may have changed pixelStorei + // parameters, make sure they are correct for the dstTexture + _gl.pixelStorei( 37440, dstTexture.flipY ); + _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { + if ( srcTexture.isDataTexture ) { - _start$1.fromBufferAttribute( positionAttribute, i ); - _end$1.fromBufferAttribute( positionAttribute, i + 1 ); + _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); + } else { - } + if ( srcTexture.isCompressedTexture ) { - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); } else { - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); } - } else if ( geometry.isGeometry ) { - - console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - } - return this; + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - } + state.unbindTexture(); - } ); + }; - function LineLoop( geometry, material ) { + this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { - Line.call( this, geometry, material ); + if ( _this.isWebGL1Renderer ) { - this.type = 'LineLoop'; + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); + return; - } + } - LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { + const width = sourceBox.max.x - sourceBox.min.x + 1; + const height = sourceBox.max.y - sourceBox.min.y + 1; + const depth = sourceBox.max.z - sourceBox.min.z + 1; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); + let glTarget; - constructor: LineLoop, + if ( dstTexture.isDataTexture3D ) { - isLineLoop: true, + textures.setTexture3D( dstTexture, 0 ); + glTarget = 32879; - } ); + } else if ( dstTexture.isDataTexture2DArray ) { - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ + textures.setTexture2DArray( dstTexture, 0 ); + glTarget = 35866; - function PointsMaterial( parameters ) { + } else { - Material.call( this ); + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); + return; - this.type = 'PointsMaterial'; + } - this.color = new Color( 0xffffff ); + _gl.pixelStorei( 37440, dstTexture.flipY ); + _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - this.map = null; + const unpackRowLen = _gl.getParameter( 3314 ); + const unpackImageHeight = _gl.getParameter( 32878 ); + const unpackSkipPixels = _gl.getParameter( 3316 ); + const unpackSkipRows = _gl.getParameter( 3315 ); + const unpackSkipImages = _gl.getParameter( 32877 ); - this.alphaMap = null; + const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; - this.size = 1; - this.sizeAttenuation = true; + _gl.pixelStorei( 3314, image.width ); + _gl.pixelStorei( 32878, image.height ); + _gl.pixelStorei( 3316, sourceBox.min.x ); + _gl.pixelStorei( 3315, sourceBox.min.y ); + _gl.pixelStorei( 32877, sourceBox.min.z ); - this.morphTargets = false; + if ( srcTexture.isDataTexture || srcTexture.isDataTexture3D ) { - this.setValues( parameters ); + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); - } + } else { - PointsMaterial.prototype = Object.create( Material.prototype ); - PointsMaterial.prototype.constructor = PointsMaterial; + if ( srcTexture.isCompressedTexture ) { - PointsMaterial.prototype.isPointsMaterial = true; + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); + _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); - PointsMaterial.prototype.copy = function ( source ) { + } else { - Material.prototype.copy.call( this, source ); + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); - this.color.copy( source.color ); + } - this.map = source.map; + } - this.alphaMap = source.alphaMap; + _gl.pixelStorei( 3314, unpackRowLen ); + _gl.pixelStorei( 32878, unpackImageHeight ); + _gl.pixelStorei( 3316, unpackSkipPixels ); + _gl.pixelStorei( 3315, unpackSkipRows ); + _gl.pixelStorei( 32877, unpackSkipImages ); - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); - this.morphTargets = source.morphTargets; + state.unbindTexture(); - return this; + }; - }; + this.initTexture = function ( texture ) { - const _inverseMatrix$2 = new Matrix4(); - const _ray$2 = new Ray(); - const _sphere$3 = new Sphere(); - const _position$1 = new Vector3(); + textures.setTexture2D( texture, 0 ); - function Points( geometry = new BufferGeometry(), material = new PointsMaterial() ) { + state.unbindTexture(); - Object3D.call( this ); + }; - this.type = 'Points'; + this.resetState = function () { - this.geometry = geometry; - this.material = material; + _currentActiveCubeFace = 0; + _currentActiveMipmapLevel = 0; + _currentRenderTarget = null; - this.updateMorphTargets(); + state.reset(); + bindingStates.reset(); - } + }; - Points.prototype = Object.assign( Object.create( Object3D.prototype ), { + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - constructor: Points, + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - isPoints: true, + } - copy: function ( source ) { + } - Object3D.prototype.copy.call( this, source ); + WebGLRenderer.prototype.isWebGLRenderer = true; - this.material = source.material; - this.geometry = source.geometry; + class WebGL1Renderer extends WebGLRenderer {} - return this; + WebGL1Renderer.prototype.isWebGL1Renderer = true; - }, + class Scene extends Object3D { - raycast: function ( raycaster, intersects ) { + constructor() { - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Points.threshold; + super(); - // Checking boundingSphere distance to ray + this.type = 'Scene'; - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + this.background = null; + this.environment = null; + this.fog = null; - _sphere$3.copy( geometry.boundingSphere ); - _sphere$3.applyMatrix4( matrixWorld ); - _sphere$3.radius += threshold; + this.overrideMaterial = null; - if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; + this.autoUpdate = true; // checked by the renderer - // + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - _inverseMatrix$2.copy( matrixWorld ).invert(); - _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; + } - if ( geometry.isBufferGeometry ) { + } - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; + copy( source, recursive ) { - if ( index !== null ) { + super.copy( source, recursive ); - const indices = index.array; + if ( source.background !== null ) this.background = source.background.clone(); + if ( source.environment !== null ) this.environment = source.environment.clone(); + if ( source.fog !== null ) this.fog = source.fog.clone(); - for ( let i = 0, il = indices.length; i < il; i ++ ) { + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - const a = indices[ i ]; + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; - _position$1.fromBufferAttribute( positionAttribute, a ); + return this; - testPoint( _position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); + } - } + toJSON( meta ) { - } else { + const data = super.toJSON( meta ); - for ( let i = 0, l = positionAttribute.count; i < l; i ++ ) { + if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - _position$1.fromBufferAttribute( positionAttribute, i ); + return data; - testPoint( _position$1, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); + } - } + } - } + Scene.prototype.isScene = true; - } else { + class InterleavedBuffer { - console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + constructor( array, stride ) { - } + this.array = array; + this.stride = stride; + this.count = array !== undefined ? array.length / stride : 0; - }, + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; - updateMorphTargets: function () { + this.version = 0; - const geometry = this.geometry; + this.uuid = generateUUID(); - if ( geometry.isBufferGeometry ) { + } - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); + onUploadCallback() {} - if ( keys.length > 0 ) { + set needsUpdate( value ) { - const morphAttribute = morphAttributes[ keys[ 0 ] ]; + if ( value === true ) this.version ++; - if ( morphAttribute !== undefined ) { + } - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + setUsage( value ) { - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + this.usage = value; - const name = morphAttribute[ m ].name || String( m ); + return this; - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + } - } + copy( source ) { - } + this.array = new source.array.constructor( source.array ); + this.count = source.count; + this.stride = source.stride; + this.usage = source.usage; - } + return this; - } else { + } - const morphTargets = geometry.morphTargets; + copyAt( index1, attribute, index2 ) { - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + index1 *= this.stride; + index2 *= attribute.stride; - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + for ( let i = 0, l = this.stride; i < l; i ++ ) { - } + this.array[ index1 + i ] = attribute.array[ index2 + i ]; } + return this; + } - } ); + set( value, offset = 0 ) { - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { + this.array.set( value, offset ); - const rayPointDistanceSq = _ray$2.distanceSqToPoint( point ); + return this; - if ( rayPointDistanceSq < localThresholdSq ) { + } - const intersectPoint = new Vector3(); + clone( data ) { - _ray$2.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); + if ( data.arrayBuffers === undefined ) { - const distance = raycaster.ray.origin.distanceTo( intersectPoint ); + data.arrayBuffers = {}; - if ( distance < raycaster.near || distance > raycaster.far ) return; + } - intersects.push( { + if ( this.array.buffer._uuid === undefined ) { - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object + this.array.buffer._uuid = generateUUID(); - } ); + } - } + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - } + data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + } - Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - this.format = format !== undefined ? format : RGBFormat; + const ib = new this.constructor( array, this.stride ); + ib.setUsage( this.usage ); - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + return ib; - this.generateMipmaps = false; + } - const scope = this; + onUpload( callback ) { - function updateVideo() { + this.onUploadCallback = callback; - scope.needsUpdate = true; - video.requestVideoFrameCallback( updateVideo ); + return this; } - if ( 'requestVideoFrameCallback' in video ) { + toJSON( data ) { - video.requestVideoFrameCallback( updateVideo ); + if ( data.arrayBuffers === undefined ) { - } + data.arrayBuffers = {}; - } + } - VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { + // generate UUID for array buffer if necessary - constructor: VideoTexture, - - clone: function () { - - return new this.constructor( this.image ).copy( this ); + if ( this.array.buffer._uuid === undefined ) { - }, + this.array.buffer._uuid = generateUUID(); - isVideoTexture: true, + } - update: function () { + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - const video = this.image; - const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; + data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); - if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { + } - this.needsUpdate = true; + // - } + return { + uuid: this.uuid, + buffer: this.array.buffer._uuid, + type: this.array.constructor.name, + stride: this.stride + }; } - } ); + } - function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + InterleavedBuffer.prototype.isInterleavedBuffer = true; - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + const _vector$6 = /*@__PURE__*/ new Vector3(); - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; + class InterleavedBufferAttribute { - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) + constructor( interleavedBuffer, itemSize, offset, normalized = false ) { - this.flipY = false; + this.name = ''; - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; - this.generateMipmaps = false; + this.normalized = normalized === true; - } + } - CompressedTexture.prototype = Object.create( Texture.prototype ); - CompressedTexture.prototype.constructor = CompressedTexture; + get count() { - CompressedTexture.prototype.isCompressedTexture = true; + return this.data.count; - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + } - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + get array() { - this.needsUpdate = true; + return this.data.array; - } + } - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - CanvasTexture.prototype.isCanvasTexture = true; + set needsUpdate( value ) { - function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { + this.data.needsUpdate = value; - format = format !== undefined ? format : DepthFormat; + } - if ( format !== DepthFormat && format !== DepthStencilFormat ) { + applyMatrix4( m ) { - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); + for ( let i = 0, l = this.data.count; i < l; i ++ ) { - } + _vector$6.x = this.getX( i ); + _vector$6.y = this.getY( i ); + _vector$6.z = this.getZ( i ); - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; + _vector$6.applyMatrix4( m ); - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - this.image = { width: width, height: height }; + } - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + return this; - this.flipY = false; - this.generateMipmaps = false; + } - } + applyNormalMatrix( m ) { - DepthTexture.prototype = Object.create( Texture.prototype ); - DepthTexture.prototype.constructor = DepthTexture; - DepthTexture.prototype.isDepthTexture = true; + for ( let i = 0, l = this.count; i < l; i ++ ) { - class CircleGeometry extends BufferGeometry { + _vector$6.x = this.getX( i ); + _vector$6.y = this.getY( i ); + _vector$6.z = this.getZ( i ); - constructor( radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2 ) { + _vector$6.applyNormalMatrix( m ); - super(); + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - this.type = 'CircleGeometry'; + } - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + return this; - segments = Math.max( 3, segments ); + } - // buffers + transformDirection( m ) { - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; + for ( let i = 0, l = this.count; i < l; i ++ ) { - // helper variables + _vector$6.x = this.getX( i ); + _vector$6.y = this.getY( i ); + _vector$6.z = this.getZ( i ); - const vertex = new Vector3(); - const uv = new Vector2(); + _vector$6.transformDirection( m ); - // center point + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - vertices.push( 0, 0, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( 0.5, 0.5 ); + } - for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { + return this; - const segment = thetaStart + s / segments * thetaLength; + } - // vertex + setX( index, x ) { - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + this.data.array[ index * this.data.stride + this.offset ] = x; - vertices.push( vertex.x, vertex.y, vertex.z ); + return this; - // normal + } - normals.push( 0, 0, 1 ); + setY( index, y ) { - // uvs + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - uv.x = ( vertices[ i ] / radius + 1 ) / 2; - uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; + return this; - uvs.push( uv.x, uv.y ); + } - } + setZ( index, z ) { - // indices + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - for ( let i = 1; i <= segments; i ++ ) { + return this; - indices.push( i, i + 1, 0 ); + } - } + setW( index, w ) { - // build geometry + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + return this; } - } - - new Vector3(); - new Vector3(); - new Vector3(); - new Triangle(); + getX( index ) { - /** - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ + return this.data.array[ index * this.data.stride + this.offset ]; - const Earcut = { + } - triangulate: function ( data, holeIndices, dim ) { + getY( index ) { - dim = dim || 2; + return this.data.array[ index * this.data.stride + this.offset + 1 ]; - const hasHoles = holeIndices && holeIndices.length; - const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; - let outerNode = linkedList$1( data, 0, outerLen, dim, true ); - const triangles = []; + } - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; + getZ( index ) { - let minX, minY, maxX, maxY, x, y, invSize; + return this.data.array[ index * this.data.stride + this.offset + 2 ]; - if ( hasHoles ) outerNode = eliminateHoles$1( data, holeIndices, outerNode, dim ); + } - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { + getW( index ) { - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; + return this.data.array[ index * this.data.stride + this.offset + 3 ]; - for ( let i = dim; i < outerLen; i += dim ) { + } - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; + setXY( index, x, y ) { - } + index = index * this.data.stride + this.offset; - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; - } + return this; - earcutLinked$1( outerNode, triangles, dim, minX, minY, invSize ); + } - return triangles; + setXYZ( index, x, y, z ) { - } + index = index * this.data.stride + this.offset; - }; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList$1( data, start, end, dim, clockwise ) { + return this; - let i, last; + } - if ( clockwise === ( signedArea$2( data, start, end, dim ) > 0 ) ) { + setXYZW( index, x, y, z, w ) { - for ( i = start; i < end; i += dim ) last = insertNode$2( i, data[ i ], data[ i + 1 ], last ); + index = index * this.data.stride + this.offset; - } else { + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; - for ( i = end - dim; i >= start; i -= dim ) last = insertNode$2( i, data[ i ], data[ i + 1 ], last ); + return this; } - if ( last && equals$2( last, last.next ) ) { + clone( data ) { - removeNode$2( last ); - last = last.next; + if ( data === undefined ) { - } + console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - return last; + const array = []; - } + for ( let i = 0; i < this.count; i ++ ) { - // eliminate colinear or duplicate points - function filterPoints$1( start, end ) { + const index = i * this.data.stride + this.offset; - if ( ! start ) return start; - if ( ! end ) end = start; + for ( let j = 0; j < this.itemSize; j ++ ) { - let p = start, - again; - do { + array.push( this.data.array[ index + j ] ); - again = false; + } - if ( ! p.steiner && ( equals$2( p, p.next ) || area$1( p.prev, p, p.next ) === 0 ) ) { + } - removeNode$2( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; + return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); } else { - p = p.next; + if ( data.interleavedBuffers === undefined ) { - } + data.interleavedBuffers = {}; - } while ( again || p !== end ); + } - return end; + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - } + data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked$1( ear, triangles, dim, minX, minY, invSize, pass ) { + } - if ( ! ear ) return; + return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve$1( ear, minX, minY, invSize ); + } - let stop = ear, - prev, next; + } - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { + toJSON( data ) { - prev = ear.prev; - next = ear.next; + if ( data === undefined ) { - if ( invSize ? isEarHashed$1( ear, minX, minY, invSize ) : isEar$1( ear ) ) { + console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); + const array = []; - removeNode$2( ear ); + for ( let i = 0; i < this.count; i ++ ) { - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; + const index = i * this.data.stride + this.offset; - continue; + for ( let j = 0; j < this.itemSize; j ++ ) { - } + array.push( this.data.array[ index + j ] ); - ear = next; + } - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { + } - // try filtering points and slicing again - if ( ! pass ) { + // deinterleave data and save it as an ordinary buffer attribute for now - earcutLinked$1( filterPoints$1( ear ), triangles, dim, minX, minY, invSize, 1 ); + return { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: array, + normalized: this.normalized + }; - // if this didn't work, try curing all small self-intersections locally + } else { - } else if ( pass === 1 ) { + // save as true interlaved attribtue - ear = cureLocalIntersections$1( filterPoints$1( ear ), triangles, dim ); - earcutLinked$1( ear, triangles, dim, minX, minY, invSize, 2 ); + if ( data.interleavedBuffers === undefined ) { - // as a last resort, try splitting the remaining polygon into two + data.interleavedBuffers = {}; - } else if ( pass === 2 ) { + } - splitEarcut$1( ear, triangles, dim, minX, minY, invSize ); + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + + data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); } - break; + return { + isInterleavedBufferAttribute: true, + itemSize: this.itemSize, + data: this.data.uuid, + offset: this.offset, + normalized: this.normalized + }; } @@ -31342,7517 +30434,7396 @@ } - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar$1( ear ) { - - const a = ear.prev, - b = ear, - c = ear.next; - - if ( area$1( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; - // now make sure we don't have other points inside the potential ear - let p = ear.next.next; + /** + * parameters = { + * color: , + * map: new THREE.Texture( ), + * alphaMap: new THREE.Texture( ), + * rotation: , + * sizeAttenuation: + * } + */ - while ( p !== ear.prev ) { + class SpriteMaterial extends Material { - if ( pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area$1( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; + constructor( parameters ) { - } + super(); - return true; + this.type = 'SpriteMaterial'; - } + this.color = new Color( 0xffffff ); - function isEarHashed$1( ear, minX, minY, invSize ) { + this.map = null; - const a = ear.prev, - b = ear, - c = ear.next; + this.alphaMap = null; - if ( area$1( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + this.rotation = 0; - // triangle bbox; min & max are calculated like this for speed - const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); + this.sizeAttenuation = true; - // z-order range for the current triangle bbox; - const minZ = zOrder$1( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder$1( maxTX, maxTY, minX, minY, invSize ); + this.transparent = true; - let p = ear.prevZ, - n = ear.nextZ; + this.setValues( parameters ); - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { + } - if ( p !== ear.prev && p !== ear.next && - pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area$1( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + copy( source ) { - if ( n !== ear.prev && n !== ear.next && - pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area$1( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + super.copy( source ); - } + this.color.copy( source.color ); - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { + this.map = source.map; - if ( p !== ear.prev && p !== ear.next && - pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area$1( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + this.alphaMap = source.alphaMap; - } + this.rotation = source.rotation; - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { + this.sizeAttenuation = source.sizeAttenuation; - if ( n !== ear.prev && n !== ear.next && - pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area$1( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + return this; } - return true; - } - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections$1( start, triangles, dim ) { + SpriteMaterial.prototype.isSpriteMaterial = true; - let p = start; - do { + let _geometry; - const a = p.prev, - b = p.next.next; + const _intersectPoint = /*@__PURE__*/ new Vector3(); + const _worldScale = /*@__PURE__*/ new Vector3(); + const _mvPosition = /*@__PURE__*/ new Vector3(); - if ( ! equals$2( a, b ) && intersects$2( a, p, p.next, b ) && locallyInside$1( a, b ) && locallyInside$1( b, a ) ) { + const _alignedPosition = /*@__PURE__*/ new Vector2(); + const _rotatedPosition = /*@__PURE__*/ new Vector2(); + const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); + const _vA = /*@__PURE__*/ new Vector3(); + const _vB = /*@__PURE__*/ new Vector3(); + const _vC = /*@__PURE__*/ new Vector3(); - // remove two nodes involved - removeNode$2( p ); - removeNode$2( p.next ); + const _uvA = /*@__PURE__*/ new Vector2(); + const _uvB = /*@__PURE__*/ new Vector2(); + const _uvC = /*@__PURE__*/ new Vector2(); - p = start = b; + class Sprite extends Object3D { - } + constructor( material ) { - p = p.next; + super(); - } while ( p !== start ); + this.type = 'Sprite'; - return filterPoints$1( p ); + if ( _geometry === undefined ) { - } + _geometry = new BufferGeometry(); - // try splitting polygon into two and triangulate them independently - function splitEarcut$1( start, triangles, dim, minX, minY, invSize ) { + const float32Array = new Float32Array( [ + - 0.5, - 0.5, 0, 0, 0, + 0.5, - 0.5, 0, 1, 0, + 0.5, 0.5, 0, 1, 1, + - 0.5, 0.5, 0, 0, 1 + ] ); - // look for a valid diagonal that divides the polygon into two - let a = start; - do { + const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - let b = a.next.next; - while ( b !== a.prev ) { + _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); + _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); + _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - if ( a.i !== b.i && isValidDiagonal$1( a, b ) ) { + } - // split the polygon in two by the diagonal - let c = splitPolygon$1( a, b ); + this.geometry = _geometry; + this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - // filter colinear points around the cuts - a = filterPoints$1( a, a.next ); - c = filterPoints$1( c, c.next ); + this.center = new Vector2( 0.5, 0.5 ); - // run earcut on each half - earcutLinked$1( a, triangles, dim, minX, minY, invSize ); - earcutLinked$1( c, triangles, dim, minX, minY, invSize ); - return; + } - } + raycast( raycaster, intersects ) { - b = b.next; + if ( raycaster.camera === null ) { + + console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); } - a = a.next; + _worldScale.setFromMatrixScale( this.matrixWorld ); - } while ( a !== start ); + _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); + this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - } + _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles$1( data, holeIndices, outerNode, dim ) { + if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - const queue = []; - let i, len, start, end, list; + _worldScale.multiplyScalar( - _mvPosition.z ); - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { + } - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList$1( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost$1( list ) ); + const rotation = this.material.rotation; + let sin, cos; - } + if ( rotation !== 0 ) { - queue.sort( compareX$1 ); + cos = Math.cos( rotation ); + sin = Math.sin( rotation ); - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { + } - eliminateHole$1( queue[ i ], outerNode ); - outerNode = filterPoints$1( outerNode, outerNode.next ); + const center = this.center; - } + transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - return outerNode; + _uvA.set( 0, 0 ); + _uvB.set( 1, 0 ); + _uvC.set( 1, 1 ); - } + // check first triangle + let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); - function compareX$1( a, b ) { + if ( intersect === null ) { - return a.x - b.x; + // check second triangle + transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + _uvB.set( 0, 1 ); - } + intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); + if ( intersect === null ) { - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole$1( hole, outerNode ) { + return; - outerNode = findHoleBridge$1( hole, outerNode ); - if ( outerNode ) { + } - const b = splitPolygon$1( outerNode, hole ); + } - // filter collinear points around the cuts - filterPoints$1( outerNode, outerNode.next ); - filterPoints$1( b, b.next ); + const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - } + if ( distance < raycaster.near || distance > raycaster.far ) return; - } + intersects.push( { - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge$1( hole, outerNode ) { + distance: distance, + point: _intersectPoint.clone(), + uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), + face: null, + object: this - let p = outerNode; - const hx = hole.x; - const hy = hole.y; - let qx = - Infinity, m; + } ); - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { + } - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { + copy( source ) { - const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { + super.copy( source ); - qx = x; - if ( x === hx ) { + if ( source.center !== undefined ) this.center.copy( source.center ); - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; + this.material = source.material; - } + return this; - m = p.x < p.next.x ? p : p.next; + } - } + } - } + Sprite.prototype.isSprite = true; - p = p.next; + function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - } while ( p !== outerNode ); + // compute position in camera space + _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - if ( ! m ) return null; + // to check if rotation is not zero + if ( sin !== undefined ) { - if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint + _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); + _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point + } else { - const stop = m, - mx = m.x, - my = m.y; - let tanMin = Infinity, tan; + _rotatedPosition.copy( _alignedPosition ); - p = m; + } - do { - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle$1( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { + vertexPosition.copy( mvPosition ); + vertexPosition.x += _rotatedPosition.x; + vertexPosition.y += _rotatedPosition.y; - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential + // transform to world space + vertexPosition.applyMatrix4( _viewWorldMatrix ); - if ( locallyInside$1( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector$1( m, p ) ) ) ) ) ) { + } - m = p; - tanMin = tan; + const _basePosition = /*@__PURE__*/ new Vector3(); - } + const _skinIndex = /*@__PURE__*/ new Vector4(); + const _skinWeight = /*@__PURE__*/ new Vector4(); - } + const _vector$5 = /*@__PURE__*/ new Vector3(); + const _matrix = /*@__PURE__*/ new Matrix4(); - p = p.next; + class SkinnedMesh extends Mesh { - } while ( p !== stop ); + constructor( geometry, material ) { - return m; + super( geometry, material ); - } + this.type = 'SkinnedMesh'; - // whether sector in vertex m contains sector in vertex p in the same coordinates - function sectorContainsSector$1( m, p ) { + this.bindMode = 'attached'; + this.bindMatrix = new Matrix4(); + this.bindMatrixInverse = new Matrix4(); - return area$1( m.prev, m, p.prev ) < 0 && area$1( p.next, m, m.next ) < 0; + } - } + copy( source ) { - // interlink polygon nodes in z-order - function indexCurve$1( start, minX, minY, invSize ) { + super.copy( source ); - let p = start; - do { + this.bindMode = source.bindMode; + this.bindMatrix.copy( source.bindMatrix ); + this.bindMatrixInverse.copy( source.bindMatrixInverse ); - if ( p.z === null ) p.z = zOrder$1( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; + this.skeleton = source.skeleton; - } while ( p !== start ); + return this; - p.prevZ.nextZ = null; - p.prevZ = null; + } - sortLinked$1( p ); + bind( skeleton, bindMatrix ) { - } + this.skeleton = skeleton; - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked$1( list ) { + if ( bindMatrix === undefined ) { - let i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; + this.updateMatrixWorld( true ); - do { + this.skeleton.calculateInverses(); - p = list; - list = null; - tail = null; - numMerges = 0; + bindMatrix = this.matrixWorld; - while ( p ) { + } - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.copy( bindMatrix ).invert(); - pSize ++; - q = q.nextZ; - if ( ! q ) break; + } - } + pose() { - qSize = inSize; + this.skeleton.pose(); - while ( pSize > 0 || ( qSize > 0 && q ) ) { + } - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { + normalizeSkinWeights() { - e = p; - p = p.nextZ; - pSize --; + const vector = new Vector4(); - } else { + const skinWeight = this.geometry.attributes.skinWeight; - e = q; - q = q.nextZ; - qSize --; + for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - } + vector.x = skinWeight.getX( i ); + vector.y = skinWeight.getY( i ); + vector.z = skinWeight.getZ( i ); + vector.w = skinWeight.getW( i ); - if ( tail ) tail.nextZ = e; - else list = e; + const scale = 1.0 / vector.manhattanLength(); - e.prevZ = tail; - tail = e; + if ( scale !== Infinity ) { - } + vector.multiplyScalar( scale ); - p = q; + } else { - } + vector.set( 1, 0, 0, 0 ); // do something reasonable - tail.nextZ = null; - inSize *= 2; + } - } while ( numMerges > 1 ); + skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - return list; + } - } + } - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder$1( x, y, minX, minY, invSize ) { + updateMatrixWorld( force ) { - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; + super.updateMatrixWorld( force ); - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; + if ( this.bindMode === 'attached' ) { - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; + this.bindMatrixInverse.copy( this.matrixWorld ).invert(); - return x | ( y << 1 ); + } else if ( this.bindMode === 'detached' ) { - } + this.bindMatrixInverse.copy( this.bindMatrix ).invert(); - // find the leftmost node of a polygon ring - function getLeftmost$1( start ) { + } else { - let p = start, - leftmost = start; - do { + console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; + } - } while ( p !== start ); + } - return leftmost; + boneTransform( index, target ) { - } + const skeleton = this.skeleton; + const geometry = this.geometry; - // check if a point lies within a convex triangle - function pointInTriangle$1( ax, ay, bx, by, cx, cy, px, py ) { + _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); + _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; + _basePosition.copy( target ).applyMatrix4( this.bindMatrix ); - } + target.set( 0, 0, 0 ); - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal$1( a, b ) { + for ( let i = 0; i < 4; i ++ ) { - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon$1( a, b ) && // dones't intersect other edges - ( locallyInside$1( a, b ) && locallyInside$1( b, a ) && middleInside$1( a, b ) && // locally visible - ( area$1( a.prev, a, b.prev ) || area$1( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals$2( a, b ) && area$1( a.prev, a, a.next ) > 0 && area$1( b.prev, b, b.next ) > 0 ); // special zero-length case + const weight = _skinWeight.getComponent( i ); - } + if ( weight !== 0 ) { - // signed area of a triangle - function area$1( p, q, r ) { + const boneIndex = _skinIndex.getComponent( i ); - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); + _matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - } + target.addScaledVector( _vector$5.copy( _basePosition ).applyMatrix4( _matrix ), weight ); - // check if two points are equal - function equals$2( p1, p2 ) { + } - return p1.x === p2.x && p1.y === p2.y; + } - } + return target.applyMatrix4( this.bindMatrixInverse ); - // check if two segments intersect - function intersects$2( p1, q1, p2, q2 ) { + } - const o1 = sign$2( area$1( p1, q1, p2 ) ); - const o2 = sign$2( area$1( p1, q1, q2 ) ); - const o3 = sign$2( area$1( p2, q2, p1 ) ); - const o4 = sign$2( area$1( p2, q2, q1 ) ); + } - if ( o1 !== o2 && o3 !== o4 ) return true; // general case + SkinnedMesh.prototype.isSkinnedMesh = true; - if ( o1 === 0 && onSegment$1( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment$1( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment$1( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment$1( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + class Bone extends Object3D { - return false; + constructor() { - } + super(); - // for collinear points p, q, r, check if point q lies on segment pr - function onSegment$1( p, q, r ) { + this.type = 'Bone'; - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); + } } - function sign$2( num ) { - - return num > 0 ? 1 : num < 0 ? - 1 : 0; - - } + Bone.prototype.isBone = true; - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon$1( a, b ) { + class DataTexture extends Texture { - let p = a; - do { + constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding ) { - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects$2( p, p.next, a, b ) ) return true; - p = p.next; + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - } while ( p !== a ); + this.image = { data: data, width: width, height: height }; - return false; + this.magFilter = magFilter; + this.minFilter = minFilter; - } + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - // check if a polygon diagonal is locally inside the polygon - function locallyInside$1( a, b ) { + this.needsUpdate = true; - return area$1( a.prev, a, a.next ) < 0 ? - area$1( a, b, a.next ) >= 0 && area$1( a, a.prev, b ) >= 0 : - area$1( a, b, a.prev ) < 0 || area$1( a, a.next, b ) < 0; + } } - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside$1( a, b ) { + DataTexture.prototype.isDataTexture = true; - let p = a, - inside = false; - const px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { + class InstancedBufferAttribute extends BufferAttribute { - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; + constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { - } while ( p !== a ); + if ( typeof normalized === 'number' ) { - return inside; + meshPerAttribute = normalized; - } + normalized = false; - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon$1( a, b ) { + console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); - const a2 = new Node$1( a.i, a.x, a.y ), - b2 = new Node$1( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; + } - a.next = b; - b.prev = a; + super( array, itemSize, normalized ); - a2.next = an; - an.prev = a2; + this.meshPerAttribute = meshPerAttribute; - b2.next = a2; - a2.prev = b2; + } - bp.next = b2; - b2.prev = bp; + copy( source ) { - return b2; + super.copy( source ); - } + this.meshPerAttribute = source.meshPerAttribute; - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode$2( i, x, y, last ) { + return this; - const p = new Node$1( i, x, y ); + } - if ( ! last ) { + toJSON() { - p.prev = p; - p.next = p; + const data = super.toJSON(); - } else { + data.meshPerAttribute = this.meshPerAttribute; - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; + data.isInstancedBufferAttribute = true; - } + return data; - return p; + } } - function removeNode$2( p ) { + InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; - p.next.prev = p.prev; - p.prev.next = p.next; + const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); + const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; + const _instanceIntersects = []; - } + const _mesh = /*@__PURE__*/ new Mesh(); - function Node$1( i, x, y ) { + class InstancedMesh extends Mesh { - // vertex index in coordinates array - this.i = i; + constructor( geometry, material, count ) { - // vertex coordinates - this.x = x; - this.y = y; + super( geometry, material ); - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; + this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); + this.instanceColor = null; - // z-order curve value - this.z = null; + this.count = count; - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; + this.frustumCulled = false; - // indicates whether this is a steiner point - this.steiner = false; + } - } + copy( source ) { - function signedArea$2( data, start, end, dim ) { + super.copy( source ); - let sum = 0; - for ( let i = start, j = end - dim; i < end; i += dim ) { + this.instanceMatrix.copy( source.instanceMatrix ); - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; + if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); + + this.count = source.count; + + return this; } - return sum; + getColorAt( index, color ) { - } + color.fromArray( this.instanceColor.array, index * 3 ); - const ShapeUtils = { + } - // calculate area of the contour polygon + getMatrixAt( index, matrix ) { - area: function ( contour ) { + matrix.fromArray( this.instanceMatrix.array, index * 16 ); - const n = contour.length; - let a = 0.0; + } - for ( let p = n - 1, q = 0; q < n; p = q ++ ) { + raycast( raycaster, intersects ) { - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + const matrixWorld = this.matrixWorld; + const raycastTimes = this.count; - } + _mesh.geometry = this.geometry; + _mesh.material = this.material; - return a * 0.5; + if ( _mesh.material === undefined ) return; - }, + for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - isClockWise: function ( pts ) { + // calculate the world matrix for each instance - return ShapeUtils.area( pts ) < 0; + this.getMatrixAt( instanceId, _instanceLocalMatrix ); - }, + _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - triangulateShape: function ( contour, holes ) { + // the mesh represents this single instance - const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - const holeIndices = []; // array of hole indices - const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + _mesh.matrixWorld = _instanceWorldMatrix; - removeDupEndPts( contour ); - addContour( vertices, contour ); + _mesh.raycast( raycaster, _instanceIntersects ); - // + // process the result of raycast - let holeIndex = contour.length; + for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - holes.forEach( removeDupEndPts ); + const intersect = _instanceIntersects[ i ]; + intersect.instanceId = instanceId; + intersect.object = this; + intersects.push( intersect ); - for ( let i = 0; i < holes.length; i ++ ) { + } - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); + _instanceIntersects.length = 0; } - // - - const triangles = Earcut.triangulate( vertices, holeIndices ); + } - // + setColorAt( index, color ) { - for ( let i = 0; i < triangles.length; i += 3 ) { + if ( this.instanceColor === null ) { - faces.push( triangles.slice( i, i + 3 ) ); + this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); } - return faces; + color.toArray( this.instanceColor.array, index * 3 ); } - }; - - function removeDupEndPts( points ) { - - const l = points.length; - - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + setMatrixAt( index, matrix ) { - points.pop(); + matrix.toArray( this.instanceMatrix.array, index * 16 ); } - } + updateMorphTargets() { - function addContour( vertices, contour ) { + } - for ( let i = 0; i < contour.length; i ++ ) { + dispose() { - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); + this.dispatchEvent( { type: 'dispose' } ); } } + InstancedMesh.prototype.isInstancedMesh = true; + /** - * Creates extruded geometry from a path shape. - * * parameters = { + * color: , + * opacity: , * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * + * linewidth: , + * linecap: "round", + * linejoin: "round" * } */ - class ExtrudeGeometry extends BufferGeometry { + class LineBasicMaterial extends Material { - constructor( shapes, options ) { + constructor( parameters ) { super(); - this.type = 'ExtrudeGeometry'; + this.type = 'LineBasicMaterial'; - this.parameters = { - shapes: shapes, - options: options - }; + this.color = new Color( 0xffffff ); - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; - const scope = this; + this.setValues( parameters ); - const verticesArray = []; - const uvArray = []; + } - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - const shape = shapes[ i ]; - addShape( shape ); + copy( source ) { - } + super.copy( source ); - // build geometry + this.color.copy( source.color ); - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; - this.computeVertexNormals(); + return this; - // functions + } - function addShape( shape ) { + } - const placeholder = []; + LineBasicMaterial.prototype.isLineBasicMaterial = true; - // options + const _start$1 = /*@__PURE__*/ new Vector3(); + const _end$1 = /*@__PURE__*/ new Vector3(); + const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); + const _ray$1 = /*@__PURE__*/ new Ray(); + const _sphere$1 = /*@__PURE__*/ new Sphere(); - const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - const steps = options.steps !== undefined ? options.steps : 1; - let depth = options.depth !== undefined ? options.depth : 100; + class Line extends Object3D { - let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { - const extrudePath = options.extrudePath; + super(); - const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; + this.type = 'Line'; - // deprecated options + this.geometry = geometry; + this.material = material; - if ( options.amount !== undefined ) { + this.updateMorphTargets(); - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; + } - } + copy( source ) { - // + super.copy( source ); - let extrudePts, extrudeByPath = false; - let splineTube, binormal, normal, position2; + this.material = source.material; + this.geometry = source.geometry; - if ( extrudePath ) { + return this; - extrudePts = extrudePath.getSpacedPoints( steps ); + } - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + computeLineDistances() { - // SETUP TNB variables + const geometry = this.geometry; - // TODO1 - have a .isClosed in spline? + if ( geometry.isBufferGeometry ) { - splineTube = extrudePath.computeFrenetFrames( steps, false ); + // we assume non-indexed geometry - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + if ( geometry.index === null ) { - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); + const positionAttribute = geometry.attributes.position; + const lineDistances = [ 0 ]; - } + for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - // Safeguards if bevels are not enabled + _start$1.fromBufferAttribute( positionAttribute, i - 1 ); + _end$1.fromBufferAttribute( positionAttribute, i ); - if ( ! bevelEnabled ) { + lineDistances[ i ] = lineDistances[ i - 1 ]; + lineDistances[ i ] += _start$1.distanceTo( _end$1 ); - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; + } + + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + + } else { + + console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); } - // Variables initialization + } else if ( geometry.isGeometry ) { - const shapePoints = shape.extractPoints( curveSegments ); + console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - let vertices = shapePoints.shape; - const holes = shapePoints.holes; + } - const reverse = ! ShapeUtils.isClockWise( vertices ); + return this; - if ( reverse ) { + } - vertices = vertices.reverse(); + raycast( raycaster, intersects ) { - // Maybe we should also check if holes are in the opposite direction, just to be safe ... + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Line.threshold; + const drawRange = geometry.drawRange; - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + // Checking boundingSphere distance to ray - const ahole = holes[ h ]; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - if ( ShapeUtils.isClockWise( ahole ) ) { + _sphere$1.copy( geometry.boundingSphere ); + _sphere$1.applyMatrix4( matrixWorld ); + _sphere$1.radius += threshold; - holes[ h ] = ahole.reverse(); + if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; - } + // - } + _inverseMatrix$1.copy( matrixWorld ).invert(); + _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - } + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; + const vStart = new Vector3(); + const vEnd = new Vector3(); + const interSegment = new Vector3(); + const interRay = new Vector3(); + const step = this.isLineSegments ? 2 : 1; - const faces = ShapeUtils.triangulateShape( vertices, holes ); + if ( geometry.isBufferGeometry ) { - /* Vertices */ + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - const contour = vertices; // vertices has all points but contour has only points of circumference + if ( index !== null ) { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - const ahole = holes[ h ]; + for ( let i = start, l = end - 1; i < l; i += step ) { - vertices = vertices.concat( ahole ); + const a = index.getX( i ); + const b = index.getX( i + 1 ); - } + vStart.fromBufferAttribute( positionAttribute, a ); + vEnd.fromBufferAttribute( positionAttribute, b ); + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - function scalePt2( pt, vec, size ) { + if ( distSq > localThresholdSq ) continue; - if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - return vec.clone().multiplyScalar( size ).add( pt ); + const distance = raycaster.ray.origin.distanceTo( interRay ); - } + if ( distance < raycaster.near || distance > raycaster.far ) continue; - const vlen = vertices.length, flen = faces.length; + intersects.push( { + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - // Find directions for point movement + } ); + } - function getBevelVec( inPt, inPrev, inNext ) { + } else { - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt + for ( let i = start, l = end - 1; i < l; i += step ) { - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html + vStart.fromBufferAttribute( positionAttribute, i ); + vEnd.fromBufferAttribute( positionAttribute, i + 1 ); - const v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - const v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + if ( distSq > localThresholdSq ) continue; - // check for collinear edges - const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - if ( Math.abs( collinear0 ) > Number.EPSILON ) { + const distance = raycaster.ray.origin.distanceTo( interRay ); - // not collinear + if ( distance < raycaster.near || distance > raycaster.far ) continue; - // length of vectors for normalizing + intersects.push( { - const v_prev_len = Math.sqrt( v_prev_lensq ); - const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - // shift adjacent points by unit vectors to the left + } ); - const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + } - const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + } - // scaling factor for v_prev to intersection point + } else if ( geometry.isGeometry ) { - const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - // vector from inPt to intersection point + } - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + } - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { + updateMorphTargets() { - return new Vector2( v_trans_x, v_trans_y ); + const geometry = this.geometry; - } else { + if ( geometry.isBufferGeometry ) { - shrink_by = Math.sqrt( v_trans_lensq / 2 ); + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - } + if ( keys.length > 0 ) { - } else { + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - // handle special case of collinear edges + if ( morphAttribute !== undefined ) { - let direction_eq = false; // assumes: opposite + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - if ( v_prev_x > Number.EPSILON ) { + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - if ( v_next_x > Number.EPSILON ) { + const name = morphAttribute[ m ].name || String( m ); - direction_eq = true; + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; - } + } - } else { + } - if ( v_prev_x < - Number.EPSILON ) { + } - if ( v_next_x < - Number.EPSILON ) { + } else { - direction_eq = true; + const morphTargets = geometry.morphTargets; - } + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - } else { + console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + } - direction_eq = true; + } - } + } - } + } - } + Line.prototype.isLine = true; - if ( direction_eq ) { + const _start = /*@__PURE__*/ new Vector3(); + const _end = /*@__PURE__*/ new Vector3(); - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); + class LineSegments extends Line { - } else { + constructor( geometry, material ) { - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); + super( geometry, material ); - } + this.type = 'LineSegments'; - } + } - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + computeLineDistances() { - } + const geometry = this.geometry; + if ( geometry.isBufferGeometry ) { - const contourMovements = []; + // we assume non-indexed geometry - for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + if ( geometry.index === null ) { - if ( j === il ) j = 0; - if ( k === il ) k = 0; + const positionAttribute = geometry.attributes.position; + const lineDistances = []; - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + _start.fromBufferAttribute( positionAttribute, i ); + _end.fromBufferAttribute( positionAttribute, i + 1 ); - } + lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; + lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); - const holesMovements = []; - let oneHoleMovements, verticesMovements = contourMovements.concat(); + } - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - const ahole = holes[ h ]; + } else { - oneHoleMovements = []; + console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + } - if ( j === il ) j = 0; - if ( k === il ) k = 0; + } else if ( geometry.isGeometry ) { - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - } + } - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); + return this; - } + } + } - // Loop bevelSegments, 1 for the front, 1 for the back + LineSegments.prototype.isLineSegments = true; - for ( let b = 0; b < bevelSegments; b ++ ) { + class LineLoop extends Line { - //for ( b = bevelSegments; b > 0; b -- ) { + constructor( geometry, material ) { - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + super( geometry, material ); - // contract shape + this.type = 'LineLoop'; - for ( let i = 0, il = contour.length; i < il; i ++ ) { + } - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + } - v( vert.x, vert.y, - z ); + LineLoop.prototype.isLineLoop = true; - } + /** + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * alphaMap: new THREE.Texture( ), + * + * size: , + * sizeAttenuation: + * + * } + */ - // expand holes + class PointsMaterial extends Material { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + constructor( parameters ) { - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + super(); - for ( let i = 0, il = ahole.length; i < il; i ++ ) { + this.type = 'PointsMaterial'; - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + this.color = new Color( 0xffffff ); - v( vert.x, vert.y, - z ); + this.map = null; - } + this.alphaMap = null; - } + this.size = 1; + this.sizeAttenuation = true; - } + this.setValues( parameters ); - const bs = bevelSize + bevelOffset; + } - // Back facing vertices + copy( source ) { - for ( let i = 0; i < vlen; i ++ ) { + super.copy( source ); - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + this.color.copy( source.color ); - if ( ! extrudeByPath ) { + this.map = source.map; - v( vert.x, vert.y, 0 ); + this.alphaMap = source.alphaMap; - } else { + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + return this; - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + } - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + } - v( position2.x, position2.y, position2.z ); + PointsMaterial.prototype.isPointsMaterial = true; - } + const _inverseMatrix = /*@__PURE__*/ new Matrix4(); + const _ray = /*@__PURE__*/ new Ray(); + const _sphere = /*@__PURE__*/ new Sphere(); + const _position$2 = /*@__PURE__*/ new Vector3(); - } + class Points extends Object3D { - // Add stepped vertices... - // Including front facing vertices + constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { - for ( let s = 1; s <= steps; s ++ ) { + super(); - for ( let i = 0; i < vlen; i ++ ) { + this.type = 'Points'; - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + this.geometry = geometry; + this.material = material; - if ( ! extrudeByPath ) { + this.updateMorphTargets(); - v( vert.x, vert.y, depth / steps * s ); + } - } else { + copy( source ) { - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + super.copy( source ); - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + this.material = source.material; + this.geometry = source.geometry; - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + return this; - v( position2.x, position2.y, position2.z ); + } - } + raycast( raycaster, intersects ) { - } + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Points.threshold; + const drawRange = geometry.drawRange; - } + // Checking boundingSphere distance to ray + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - // Add bevel segments planes + _sphere.copy( geometry.boundingSphere ); + _sphere.applyMatrix4( matrixWorld ); + _sphere.radius += threshold; - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( let b = bevelSegments - 1; b >= 0; b -- ) { + if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + // - // contract shape + _inverseMatrix.copy( matrixWorld ).invert(); + _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - for ( let i = 0, il = contour.length; i < il; i ++ ) { + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); + if ( geometry.isBufferGeometry ) { - } + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - // expand holes + if ( index !== null ) { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + for ( let i = start, il = end; i < il; i ++ ) { - for ( let i = 0, il = ahole.length; i < il; i ++ ) { + const a = index.getX( i ); - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + _position$2.fromBufferAttribute( positionAttribute, a ); - if ( ! extrudeByPath ) { + testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - v( vert.x, vert.y, depth + z ); + } - } else { + } else { - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - } + for ( let i = start, l = end; i < l; i ++ ) { - } + _position$2.fromBufferAttribute( positionAttribute, i ); + + testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); } } - /* Faces */ + } else { - // Top and bottom faces + console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - buildLidFaces(); + } - // Sides faces + } - buildSideFaces(); + updateMorphTargets() { + const geometry = this.geometry; - ///// Internal functions + if ( geometry.isBufferGeometry ) { - function buildLidFaces() { + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - const start = verticesArray.length / 3; + if ( keys.length > 0 ) { - if ( bevelEnabled ) { + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - let layer = 0; // steps + 1 - let offset = vlen * layer; + if ( morphAttribute !== undefined ) { - // Bottom faces + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - for ( let i = 0; i < flen; i ++ ) { + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - const face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + const name = morphAttribute[ m ].name || String( m ); + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; } - layer = steps + bevelSegments * 2; - offset = vlen * layer; + } - // Top faces + } - for ( let i = 0; i < flen; i ++ ) { + } else { - const face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + const morphTargets = geometry.morphTargets; - } + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - } else { + console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - // Bottom faces + } - for ( let i = 0; i < flen; i ++ ) { + } - const face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + } - } + } - // Top faces + Points.prototype.isPoints = true; - for ( let i = 0; i < flen; i ++ ) { + function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - const face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - } + if ( rayPointDistanceSq < localThresholdSq ) { - } + const intersectPoint = new Vector3(); - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); + _ray.closestPointToPoint( point, intersectPoint ); + intersectPoint.applyMatrix4( matrixWorld ); - } + const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - // Create faces for the z-sides of the shape + if ( distance < raycaster.near || distance > raycaster.far ) return; - function buildSideFaces() { + intersects.push( { - const start = verticesArray.length / 3; - let layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint, + index: index, + face: null, + object: object - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + } ); - const ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + } - //, true - layeroffset += ahole.length; + } - } + class VideoTexture extends Texture { + constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); + super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.format = format !== undefined ? format : RGBFormat; - } + this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; + this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - function sidewalls( contour, layeroffset ) { + this.generateMipmaps = false; - let i = contour.length; + const scope = this; - while ( -- i >= 0 ) { + function updateVideo() { - const j = i; - let k = i - 1; - if ( k < 0 ) k = contour.length - 1; + scope.needsUpdate = true; + video.requestVideoFrameCallback( updateVideo ); - //console.log('b', i,j, i-1, k,vertices.length); + } - for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { + if ( 'requestVideoFrameCallback' in video ) { - const slen1 = vlen * s; - const slen2 = vlen * ( s + 1 ); + video.requestVideoFrameCallback( updateVideo ); - const a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + } - f4( a, b, c, d ); + } - } + clone() { - } + return new this.constructor( this.image ).copy( this ); - } + } - function v( x, y, z ) { + update() { - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); + const video = this.image; + const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - } + if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { + this.needsUpdate = true; - function f3( a, b, c ) { + } - addVertex( a ); - addVertex( b ); - addVertex( c ); + } - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + } - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); + VideoTexture.prototype.isVideoTexture = true; - } + class CompressedTexture extends Texture { - function f4( a, b, c, d ) { - - addVertex( a ); - addVertex( b ); - addVertex( d ); - - addVertex( b ); - addVertex( c ); - addVertex( d ); + constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); + this.flipY = false; - } + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - function addVertex( index ) { + this.generateMipmaps = false; - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); + } - } + } + CompressedTexture.prototype.isCompressedTexture = true; - function addUV( vector2 ) { + class CanvasTexture extends Texture { - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); + constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - } + super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } + this.needsUpdate = true; } - toJSON() { + } - const data = BufferGeometry.prototype.toJSON.call( this ); + CanvasTexture.prototype.isCanvasTexture = true; - const shapes = this.parameters.shapes; - const options = this.parameters.options; + class DepthTexture extends Texture { - return toJSON( shapes, options, data ); + constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - } + format = format !== undefined ? format : DepthFormat; - } + if ( format !== DepthFormat && format !== DepthStencilFormat ) { - const WorldUVGenerator = { + throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { + } - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; + if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; + if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - }, + this.image = { width: width, height: height }; - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { + this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const a_z = vertices[ indexA * 3 + 2 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const b_z = vertices[ indexB * 3 + 2 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - const c_z = vertices[ indexC * 3 + 2 ]; - const d_x = vertices[ indexD * 3 ]; - const d_y = vertices[ indexD * 3 + 1 ]; - const d_z = vertices[ indexD * 3 + 2 ]; + this.flipY = false; + this.generateMipmaps = false; - if ( Math.abs( a_y - b_y ) < 0.01 ) { + } - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; - } else { + } - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; + DepthTexture.prototype.isDepthTexture = true; - } + class CircleGeometry extends BufferGeometry { - } + constructor( radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2 ) { - }; + super(); - function toJSON( shapes, options, data ) { + this.type = 'CircleGeometry'; - data.shapes = []; + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - if ( Array.isArray( shapes ) ) { + segments = Math.max( 3, segments ); - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + // buffers - const shape = shapes[ i ]; + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - data.shapes.push( shape.uuid ); + // helper variables - } + const vertex = new Vector3(); + const uv = new Vector2(); - } else { + // center point - data.shapes.push( shapes.uuid ); + vertices.push( 0, 0, 0 ); + normals.push( 0, 0, 1 ); + uvs.push( 0.5, 0.5 ); - } + for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); + const segment = thetaStart + s / segments * thetaLength; - return data; + // vertex - } + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); - /** - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html - */ + vertices.push( vertex.x, vertex.y, vertex.z ); + + // normal - function ParametricGeometry( func, slices, stacks ) { + normals.push( 0, 0, 1 ); - BufferGeometry.call( this ); + // uvs - this.type = 'ParametricGeometry'; + uv.x = ( vertices[ i ] / radius + 1 ) / 2; + uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; + uvs.push( uv.x, uv.y ); - // buffers + } - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; + // indices - const EPS = 0.00001; + for ( let i = 1; i <= segments; i ++ ) { - const normal = new Vector3(); + indices.push( i, i + 1, 0 ); - const p0 = new Vector3(), p1 = new Vector3(); - const pu = new Vector3(), pv = new Vector3(); + } - if ( func.length < 3 ) { + // build geometry - console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' ); + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); } - // generate vertices, normals and uvs + static fromJSON( data ) { - const sliceCount = slices + 1; + return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); - for ( let i = 0; i <= stacks; i ++ ) { + } - const v = i / stacks; + } - for ( let j = 0; j <= slices; j ++ ) { + new Vector3(); + new Vector3(); + new Vector3(); + new Triangle(); - const u = j / slices; + /** + * Extensible curve object. + * + * Some common of curve methods: + * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) + * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following curves inherit from THREE.Curve: + * + * -- 2D curves -- + * THREE.ArcCurve + * THREE.CubicBezierCurve + * THREE.EllipseCurve + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.SplineCurve + * + * -- 3D curves -- + * THREE.CatmullRomCurve3 + * THREE.CubicBezierCurve3 + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * + * A series of curves can be represented as a THREE.CurvePath. + * + **/ - // vertex + class Curve { - func( u, v, p0 ); - vertices.push( p0.x, p0.y, p0.z ); + constructor() { - // normal + this.type = 'Curve'; - // approximate tangent vectors via finite differences + this.arcLengthDivisions = 200; - if ( u - EPS >= 0 ) { + } - func( u - EPS, v, p1 ); - pu.subVectors( p0, p1 ); + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] - } else { + getPoint( /* t, optionalTarget */ ) { - func( u + EPS, v, p1 ); - pu.subVectors( p1, p0 ); + console.warn( 'THREE.Curve: .getPoint() not implemented.' ); + return null; - } + } - if ( v - EPS >= 0 ) { + // Get point at relative position in curve according to arc length + // - u [0 .. 1] - func( u, v - EPS, p1 ); - pv.subVectors( p0, p1 ); + getPointAt( u, optionalTarget ) { - } else { + const t = this.getUtoTmapping( u ); + return this.getPoint( t, optionalTarget ); - func( u, v + EPS, p1 ); - pv.subVectors( p1, p0 ); + } - } + // Get sequence of points using getPoint( t ) - // cross product of tangent vectors returns surface normal + getPoints( divisions = 5 ) { - normal.crossVectors( pu, pv ).normalize(); - normals.push( normal.x, normal.y, normal.z ); + const points = []; - // uv + for ( let d = 0; d <= divisions; d ++ ) { - uvs.push( u, v ); + points.push( this.getPoint( d / divisions ) ); } - } + return points; - // generate indices + } - for ( let i = 0; i < stacks; i ++ ) { + // Get sequence of points using getPointAt( u ) - for ( let j = 0; j < slices; j ++ ) { + getSpacedPoints( divisions = 5 ) { - const a = i * sliceCount + j; - const b = i * sliceCount + j + 1; - const c = ( i + 1 ) * sliceCount + j + 1; - const d = ( i + 1 ) * sliceCount + j; + const points = []; - // faces one and two + for ( let d = 0; d <= divisions; d ++ ) { - indices.push( a, b, d ); - indices.push( b, c, d ); + points.push( this.getPointAt( d / divisions ) ); } + return points; + } - // build geometry + // Get total curve arc length - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + getLength() { - } + const lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; - ParametricGeometry.prototype = Object.create( BufferGeometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; + } - class ShapeGeometry extends BufferGeometry { + // Get list of cumulative segment lengths - constructor( shapes, curveSegments = 12 ) { + getLengths( divisions = this.arcLengthDivisions ) { - super(); - this.type = 'ShapeGeometry'; + if ( this.cacheArcLengths && + ( this.cacheArcLengths.length === divisions + 1 ) && + ! this.needsUpdate ) { - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; + return this.cacheArcLengths; - // buffers + } - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; + this.needsUpdate = false; - // helper variables + const cache = []; + let current, last = this.getPoint( 0 ); + let sum = 0; - let groupStart = 0; - let groupCount = 0; + cache.push( 0 ); - // allow single and array values for "shapes" parameter + for ( let p = 1; p <= divisions; p ++ ) { - if ( Array.isArray( shapes ) === false ) { + current = this.getPoint( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; - addShape( shapes ); + } - } else { + this.cacheArcLengths = cache; - for ( let i = 0; i < shapes.length; i ++ ) { + return cache; // { sums: cache, sum: sum }; Sum is in the last element. - addShape( shapes[ i ] ); + } - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support + updateArcLengths() { - groupStart += groupCount; - groupCount = 0; + this.needsUpdate = true; + this.getLengths(); - } + } - } + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - // build geometry + getUtoTmapping( u, distance ) { - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + const arcLengths = this.getLengths(); + let i = 0; + const il = arcLengths.length; - // helper functions + let targetArcLength; // The targeted u distance value to get - function addShape( shape ) { + if ( distance ) { - const indexOffset = vertices.length / 3; - const points = shape.extractPoints( curveSegments ); + targetArcLength = distance; - let shapeVertices = points.shape; - const shapeHoles = points.holes; + } else { - // check direction of vertices + targetArcLength = u * arcLengths[ il - 1 ]; - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { + } - shapeVertices = shapeVertices.reverse(); + // binary search for the index with largest value smaller than target u distance - } + let low = 0, high = il - 1, comparison; - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + while ( low <= high ) { - const shapeHole = shapeHoles[ i ]; + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { + comparison = arcLengths[ i ] - targetArcLength; - shapeHoles[ i ] = shapeHole.reverse(); + if ( comparison < 0 ) { - } + low = i + 1; - } + } else if ( comparison > 0 ) { - const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); + high = i - 1; - // join vertices of inner and outer paths to a single array + } else { - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + high = i; + break; - const shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); + // DONE } - // vertices, normals, uvs - - for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - - const vertex = shapeVertices[ i ]; + } - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs + i = high; - } + if ( arcLengths[ i ] === targetArcLength ) { - // incides + return i / ( il - 1 ); - for ( let i = 0, l = faces.length; i < l; i ++ ) { + } - const face = faces[ i ]; + // we could get finer grain at lengths, or use simple interpolation between two points - const a = face[ 0 ] + indexOffset; - const b = face[ 1 ] + indexOffset; - const c = face[ 2 ] + indexOffset; + const lengthBefore = arcLengths[ i ]; + const lengthAfter = arcLengths[ i + 1 ]; - indices.push( a, b, c ); - groupCount += 3; + const segmentLength = lengthAfter - lengthBefore; - } + // determine where we are between the 'before' and 'after' points - } + const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - } + // add that fractional amount to t - toJSON() { + const t = ( i + segmentFraction ) / ( il - 1 ); - const data = BufferGeometry.prototype.toJSON.call( this ); + return t; - const shapes = this.parameters.shapes; + } - return toJSON$1( shapes, data ); + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation - } + getTangent( t, optionalTarget ) { - } + const delta = 0.0001; + let t1 = t - delta; + let t2 = t + delta; - function toJSON$1( shapes, data ) { + // Capping in case of danger - data.shapes = []; + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - if ( Array.isArray( shapes ) ) { + const pt1 = this.getPoint( t1 ); + const pt2 = this.getPoint( t2 ); - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - const shape = shapes[ i ]; + tangent.copy( pt2 ).sub( pt1 ).normalize(); - data.shapes.push( shape.uuid ); + return tangent; - } + } - } else { + getTangentAt( u, optionalTarget ) { - data.shapes.push( shapes.uuid ); + const t = this.getUtoTmapping( u ); + return this.getTangent( t, optionalTarget ); } - return data; + computeFrenetFrames( segments, closed ) { - } + // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - class SphereGeometry extends BufferGeometry { + const normal = new Vector3(); - constructor( radius = 1, widthSegments = 8, heightSegments = 6, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { + const tangents = []; + const normals = []; + const binormals = []; - super(); - this.type = 'SphereGeometry'; + const vec = new Vector3(); + const mat = new Matrix4(); - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + // compute the tangent vectors for each segment on the curve - widthSegments = Math.max( 3, Math.floor( widthSegments ) ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) ); + for ( let i = 0; i <= segments; i ++ ) { - const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); + const u = i / segments; - let index = 0; - const grid = []; + tangents[ i ] = this.getTangentAt( u, new Vector3() ); - const vertex = new Vector3(); - const normal = new Vector3(); + } - // buffers + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the minimum tangent xyz component - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; + normals[ 0 ] = new Vector3(); + binormals[ 0 ] = new Vector3(); + let min = Number.MAX_VALUE; + const tx = Math.abs( tangents[ 0 ].x ); + const ty = Math.abs( tangents[ 0 ].y ); + const tz = Math.abs( tangents[ 0 ].z ); - // generate vertices, normals and uvs + if ( tx <= min ) { - for ( let iy = 0; iy <= heightSegments; iy ++ ) { + min = tx; + normal.set( 1, 0, 0 ); - const verticesRow = []; + } - const v = iy / heightSegments; + if ( ty <= min ) { - // special case for the poles + min = ty; + normal.set( 0, 1, 0 ); - let uOffset = 0; + } - if ( iy == 0 && thetaStart == 0 ) { + if ( tz <= min ) { - uOffset = 0.5 / widthSegments; + normal.set( 0, 0, 1 ); - } else if ( iy == heightSegments && thetaEnd == Math.PI ) { + } - uOffset = - 0.5 / widthSegments; + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - } + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - for ( let ix = 0; ix <= widthSegments; ix ++ ) { - const u = ix / widthSegments; + // compute the slowly-varying normal and binormal vectors for each segment on the curve - // vertex + for ( let i = 1; i <= segments; i ++ ) { - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + normals[ i ] = normals[ i - 1 ].clone(); - vertices.push( vertex.x, vertex.y, vertex.z ); + binormals[ i ] = binormals[ i - 1 ].clone(); - // normal + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - normal.copy( vertex ).normalize(); - normals.push( normal.x, normal.y, normal.z ); + if ( vec.length() > Number.EPSILON ) { - // uv + vec.normalize(); - uvs.push( u + uOffset, 1 - v ); + const theta = Math.acos( clamp$1( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - verticesRow.push( index ++ ); + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } - grid.push( verticesRow ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } - // indices + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - for ( let iy = 0; iy < heightSegments; iy ++ ) { + if ( closed === true ) { - for ( let ix = 0; ix < widthSegments; ix ++ ) { + let theta = Math.acos( clamp$1( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); + theta /= segments; - const a = grid[ iy ][ ix + 1 ]; - const b = grid[ iy ][ ix ]; - const c = grid[ iy + 1 ][ ix ]; - const d = grid[ iy + 1 ][ ix + 1 ]; + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); - if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); + theta = - theta; } - } + for ( let i = 1; i <= segments; i ++ ) { - // build geometry + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + } - } + } - } + return { + tangents: tangents, + normals: normals, + binormals: binormals + }; - /** - * parameters = { - * color: - * } - */ + } - function ShadowMaterial( parameters ) { + clone() { - Material.call( this ); + return new this.constructor().copy( this ); - this.type = 'ShadowMaterial'; + } - this.color = new Color( 0x000000 ); - this.transparent = true; + copy( source ) { - this.setValues( parameters ); + this.arcLengthDivisions = source.arcLengthDivisions; - } + return this; - ShadowMaterial.prototype = Object.create( Material.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; + } - ShadowMaterial.prototype.isShadowMaterial = true; + toJSON() { - ShadowMaterial.prototype.copy = function ( source ) { + const data = { + metadata: { + version: 4.5, + type: 'Curve', + generator: 'Curve.toJSON' + } + }; - Material.prototype.copy.call( this, source ); + data.arcLengthDivisions = this.arcLengthDivisions; + data.type = this.type; - this.color.copy( source.color ); + return data; - return this; + } - }; + fromJSON( json ) { - function RawShaderMaterial( parameters ) { + this.arcLengthDivisions = json.arcLengthDivisions; - ShaderMaterial.call( this, parameters ); + return this; - this.type = 'RawShaderMaterial'; + } } - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; + class EllipseCurve extends Curve { - RawShaderMaterial.prototype.isRawShaderMaterial = true; + constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { - /** - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + super(); - function MeshStandardMaterial( parameters ) { + this.type = 'EllipseCurve'; - Material.call( this ); + this.aX = aX; + this.aY = aY; - this.defines = { 'STANDARD': '' }; + this.xRadius = xRadius; + this.yRadius = yRadius; - this.type = 'MeshStandardMaterial'; + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; + this.aClockwise = aClockwise; - this.map = null; + this.aRotation = aRotation; - this.lightMap = null; - this.lightMapIntensity = 1.0; + } - this.aoMap = null; - this.aoMapIntensity = 1.0; + getPoint( t, optionalTarget ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + const point = optionalTarget || new Vector2(); - this.bumpMap = null; - this.bumpScale = 1; + const twoPi = Math.PI * 2; + let deltaAngle = this.aEndAngle - this.aStartAngle; + const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + // ensures that deltaAngle is 0 .. 2 PI + while ( deltaAngle < 0 ) deltaAngle += twoPi; + while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + if ( deltaAngle < Number.EPSILON ) { - this.roughnessMap = null; + if ( samePoints ) { - this.metalnessMap = null; + deltaAngle = 0; - this.alphaMap = null; + } else { - this.envMap = null; - this.envMapIntensity = 1.0; + deltaAngle = twoPi; - this.refractionRatio = 0.98; + } - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( this.aClockwise === true && ! samePoints ) { - this.vertexTangents = false; + if ( deltaAngle === twoPi ) { - this.setValues( parameters ); + deltaAngle = - twoPi; - } + } else { - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; + deltaAngle = deltaAngle - twoPi; - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + } - MeshStandardMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + const angle = this.aStartAngle + t * deltaAngle; + let x = this.aX + this.xRadius * Math.cos( angle ); + let y = this.aY + this.yRadius * Math.sin( angle ); - this.defines = { 'STANDARD': '' }; + if ( this.aRotation !== 0 ) { - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; + const cos = Math.cos( this.aRotation ); + const sin = Math.sin( this.aRotation ); - this.map = source.map; + const tx = x - this.aX; + const ty = y - this.aY; - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + // Rotate the point about the center of the ellipse. + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + } - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + return point.set( x, y ); - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + copy( source ) { - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + super.copy( source ); - this.roughnessMap = source.roughnessMap; + this.aX = source.aX; + this.aY = source.aY; - this.metalnessMap = source.metalnessMap; + this.xRadius = source.xRadius; + this.yRadius = source.yRadius; - this.alphaMap = source.alphaMap; + this.aStartAngle = source.aStartAngle; + this.aEndAngle = source.aEndAngle; - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; + this.aClockwise = source.aClockwise; - this.refractionRatio = source.refractionRatio; + this.aRotation = source.aRotation; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + return this; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + } - this.vertexTangents = source.vertexTangents; + toJSON() { - return this; + const data = super.toJSON(); - }; + data.aX = this.aX; + data.aY = this.aY; - /** - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * reflectivity: , - * ior: , - * - * sheen: , - * - * transmission: , - * transmissionMap: new THREE.Texture( ) - * } - */ + data.xRadius = this.xRadius; + data.yRadius = this.yRadius; - function MeshPhysicalMaterial( parameters ) { + data.aStartAngle = this.aStartAngle; + data.aEndAngle = this.aEndAngle; - MeshStandardMaterial.call( this ); + data.aClockwise = this.aClockwise; - this.defines = { + data.aRotation = this.aRotation; - 'STANDARD': '', - 'PHYSICAL': '' + return data; - }; + } - this.type = 'MeshPhysicalMaterial'; + fromJSON( json ) { - this.clearcoat = 0.0; - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; + super.fromJSON( json ); - this.reflectivity = 0.5; // maps to F0 = 0.04 + this.aX = json.aX; + this.aY = json.aY; - Object.defineProperty( this, 'ior', { - get: function () { + this.xRadius = json.xRadius; + this.yRadius = json.yRadius; - return ( 1 + 0.4 * this.reflectivity ) / ( 1 - 0.4 * this.reflectivity ); + this.aStartAngle = json.aStartAngle; + this.aEndAngle = json.aEndAngle; - }, - set: function ( ior ) { + this.aClockwise = json.aClockwise; - this.reflectivity = MathUtils.clamp( 2.5 * ( ior - 1 ) / ( ior + 1 ), 0, 1 ); + this.aRotation = json.aRotation; - } - } ); + return this; - this.sheen = null; // null will disable sheen bsdf + } - this.transmission = 0.0; - this.transmissionMap = null; + } - this.setValues( parameters ); + EllipseCurve.prototype.isEllipseCurve = true; - } + class ArcCurve extends EllipseCurve { - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; + constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; + super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - MeshPhysicalMaterial.prototype.copy = function ( source ) { + this.type = 'ArcCurve'; - MeshStandardMaterial.prototype.copy.call( this, source ); + } - this.defines = { + } - 'STANDARD': '', - 'PHYSICAL': '' + ArcCurve.prototype.isArcCurve = true; + + /** + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ - }; - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM - this.reflectivity = source.reflectivity; + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ - if ( source.sheen ) { + function CubicPoly() { - this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); + let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - } else { + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + function init( x0, x1, t0, t1 ) { - this.sheen = null; + c0 = x0; + c1 = t0; + c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + c3 = 2 * x0 - 2 * x1 + t0 + t1; } - this.transmission = source.transmission; - this.transmissionMap = source.transmissionMap; - - return this; + return { - }; + initCatmullRom: function ( x0, x1, x2, x3, tension ) { - /** - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - function MeshPhongMaterial( parameters ) { + }, - Material.call( this ); + initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - this.type = 'MeshPhongMaterial'; + // compute tangents when parameterized in [t1,t2] + let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; - this.map = null; + init( x1, x2, t1, t2 ); - this.lightMap = null; - this.lightMapIntensity = 1.0; + }, - this.aoMap = null; - this.aoMapIntensity = 1.0; + calc: function ( t ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + const t2 = t * t; + const t3 = t2 * t; + return c0 + c1 * t + c2 * t2 + c3 * t3; - this.bumpMap = null; - this.bumpScale = 1; + } - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + }; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + } - this.specularMap = null; + // - this.alphaMap = null; + const tmp = new Vector3(); + const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + class CatmullRomCurve3 extends Curve { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + super(); - this.setValues( parameters ); + this.type = 'CatmullRomCurve3'; - } + this.points = points; + this.closed = closed; + this.curveType = curveType; + this.tension = tension; - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; + } - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; + getPoint( t, optionalTarget = new Vector3() ) { - MeshPhongMaterial.prototype.copy = function ( source ) { + const point = optionalTarget; - Material.prototype.copy.call( this, source ); + const points = this.points; + const l = points.length; - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; + const p = ( l - ( this.closed ? 0 : 1 ) ) * t; + let intPoint = Math.floor( p ); + let weight = p - intPoint; - this.map = source.map; + if ( this.closed ) { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + } else if ( weight === 0 && intPoint === l - 1 ) { - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + intPoint = l - 2; + weight = 1; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + let p0, p3; // 4 points (p1 & p2 defined below) - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + if ( this.closed || intPoint > 0 ) { - this.specularMap = source.specularMap; + p0 = points[ ( intPoint - 1 ) % l ]; - this.alphaMap = source.alphaMap; + } else { - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + const p1 = points[ intPoint % l ]; + const p2 = points[ ( intPoint + 1 ) % l ]; - return this; + if ( this.closed || intPoint + 2 < l ) { - }; + p3 = points[ ( intPoint + 2 ) % l ]; - /** - * parameters = { - * color: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + } else { - function MeshToonMaterial( parameters ) { + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; - Material.call( this ); + } - this.defines = { 'TOON': '' }; + if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - this.type = 'MeshToonMaterial'; + // init Centripetal / Chordal Catmull-Rom + const pow = this.curveType === 'chordal' ? 0.5 : 0.25; + let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - this.color = new Color( 0xffffff ); + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; - this.map = null; - this.gradientMap = null; + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - this.lightMap = null; - this.lightMapIntensity = 1.0; + } else if ( this.curveType === 'catmullrom' ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + } - this.bumpMap = null; - this.bumpScale = 1; + point.set( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + return point; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + } - this.alphaMap = null; + copy( source ) { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + super.copy( source ); - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + this.points = []; - this.setValues( parameters ); + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - } + const point = source.points[ i ]; - MeshToonMaterial.prototype = Object.create( Material.prototype ); - MeshToonMaterial.prototype.constructor = MeshToonMaterial; + this.points.push( point.clone() ); - MeshToonMaterial.prototype.isMeshToonMaterial = true; + } - MeshToonMaterial.prototype.copy = function ( source ) { + this.closed = source.closed; + this.curveType = source.curveType; + this.tension = source.tension; - Material.prototype.copy.call( this, source ); + return this; - this.color.copy( source.color ); + } - this.map = source.map; - this.gradientMap = source.gradientMap; + toJSON() { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + const data = super.toJSON(); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + data.points = []; - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + const point = this.points[ i ]; + data.points.push( point.toArray() ); - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + } - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + data.closed = this.closed; + data.curveType = this.curveType; + data.tension = this.tension; - this.alphaMap = source.alphaMap; + return data; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + fromJSON( json ) { - return this; + super.fromJSON( json ); - }; + this.points = []; - /** - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - function MeshNormalMaterial( parameters ) { + const point = json.points[ i ]; + this.points.push( new Vector3().fromArray( point ) ); - Material.call( this ); + } - this.type = 'MeshNormalMaterial'; + this.closed = json.closed; + this.curveType = json.curveType; + this.tension = json.tension; - this.bumpMap = null; - this.bumpScale = 1; + return this; - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + } - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + } - this.wireframe = false; - this.wireframeLinewidth = 1; + CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - this.fog = false; + /** + * Bezier Curves formulas obtained from + * http://en.wikipedia.org/wiki/Bézier_curve + */ - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + function CatmullRom( t, p0, p1, p2, p3 ) { - this.setValues( parameters ); + const v0 = ( p2 - p0 ) * 0.5; + const v1 = ( p3 - p1 ) * 0.5; + const t2 = t * t; + const t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; + // - MeshNormalMaterial.prototype.copy = function ( source ) { + function QuadraticBezierP0( t, p ) { - Material.prototype.copy.call( this, source ); + const k = 1 - t; + return k * k * p; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + function QuadraticBezierP1( t, p ) { - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + return 2 * ( 1 - t ) * t * p; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + function QuadraticBezierP2( t, p ) { - return this; + return t * t * p; - }; + } - /** - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + function QuadraticBezier( t, p0, p1, p2 ) { - function MeshLambertMaterial( parameters ) { + return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + + QuadraticBezierP2( t, p2 ); - Material.call( this ); + } - this.type = 'MeshLambertMaterial'; + // - this.color = new Color( 0xffffff ); // diffuse + function CubicBezierP0( t, p ) { - this.map = null; + const k = 1 - t; + return k * k * k * p; - this.lightMap = null; - this.lightMapIntensity = 1.0; + } - this.aoMap = null; - this.aoMapIntensity = 1.0; + function CubicBezierP1( t, p ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + const k = 1 - t; + return 3 * k * k * t * p; - this.specularMap = null; + } - this.alphaMap = null; + function CubicBezierP2( t, p ) { - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + return 3 * ( 1 - t ) * t * t * p; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + function CubicBezierP3( t, p ) { - this.setValues( parameters ); + return t * t * t * p; } - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; + function CubicBezier( t, p0, p1, p2, p3 ) { - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; + return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + + CubicBezierP3( t, p3 ); - MeshLambertMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + class CubicBezierCurve extends Curve { - this.color.copy( source.color ); + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { - this.map = source.map; + super(); - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + this.type = 'CubicBezierCurve'; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + } - this.specularMap = source.specularMap; + getPoint( t, optionalTarget = new Vector2() ) { - this.alphaMap = source.alphaMap; + const point = optionalTarget; - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) + ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + return point; - return this; + } - }; + copy( source ) { - /** - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + super.copy( source ); - function MeshMatcapMaterial( parameters ) { + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - Material.call( this ); + return this; - this.defines = { 'MATCAP': '' }; + } - this.type = 'MeshMatcapMaterial'; + toJSON() { - this.color = new Color( 0xffffff ); // diffuse + const data = super.toJSON(); - this.matcap = null; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - this.map = null; + return data; - this.bumpMap = null; - this.bumpScale = 1; + } - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + fromJSON( json ) { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + super.fromJSON( json ); - this.alphaMap = null; + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + return this; - this.setValues( parameters ); + } } - MeshMatcapMaterial.prototype = Object.create( Material.prototype ); - MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial; + CubicBezierCurve.prototype.isCubicBezierCurve = true; - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; + class CubicBezierCurve3 extends Curve { - MeshMatcapMaterial.prototype.copy = function ( source ) { + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { - Material.prototype.copy.call( this, source ); + super(); - this.defines = { 'MATCAP': '' }; + this.type = 'CubicBezierCurve3'; - this.color.copy( source.color ); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - this.matcap = source.matcap; + } - this.map = source.map; + getPoint( t, optionalTarget = new Vector3() ) { - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + const point = optionalTarget; - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), + CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) + ); - this.alphaMap = source.alphaMap; + return point; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + } - return this; + copy( source ) { - }; + super.copy( source ); - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - function LineDashedMaterial( parameters ) { + return this; - LineBasicMaterial.call( this ); + } - this.type = 'LineDashedMaterial'; + toJSON() { - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + const data = super.toJSON(); - this.setValues( parameters ); + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - } + return data; - LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; + } - LineDashedMaterial.prototype.isLineDashedMaterial = true; + fromJSON( json ) { - LineDashedMaterial.prototype.copy = function ( source ) { + super.fromJSON( json ); - LineBasicMaterial.prototype.copy.call( this, source ); + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; + return this; - return this; + } - }; + } - var Materials = /*#__PURE__*/Object.freeze({ - __proto__: null, - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshToonMaterial: MeshToonMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshDistanceMaterial: MeshDistanceMaterial, - MeshBasicMaterial: MeshBasicMaterial, - MeshMatcapMaterial: MeshMatcapMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); + CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - const AnimationUtils = { + class LineCurve extends Curve { - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { + constructor( v1 = new Vector2(), v2 = new Vector2() ) { - if ( AnimationUtils.isTypedArray( array ) ) { + super(); - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); + this.type = 'LineCurve'; - } + this.v1 = v1; + this.v2 = v2; - return array.slice( from, to ); + } - }, + getPoint( t, optionalTarget = new Vector2() ) { - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { + const point = optionalTarget; - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; + if ( t === 1 ) { - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + point.copy( this.v2 ); - return new type( array ); // create typed array + } else { + + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); } - return Array.prototype.slice.call( array ); // create Array + return point; - }, + } - isTypedArray: function ( object ) { + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); + return this.getPoint( u, optionalTarget ); - }, + } - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { + getTangent( t, optionalTarget ) { - function compareTime( i, j ) { + const tangent = optionalTarget || new Vector2(); - return times[ i ] - times[ j ]; + tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - } + return tangent; - const n = times.length; - const result = new Array( n ); - for ( let i = 0; i !== n; ++ i ) result[ i ] = i; + } - result.sort( compareTime ); + copy( source ) { - return result; + super.copy( source ); - }, + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { + return this; - const nValues = values.length; - const result = new values.constructor( nValues ); + } - for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + toJSON() { - const srcOffset = order[ i ] * stride; + const data = super.toJSON(); - for ( let j = 0; j !== stride; ++ j ) { + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - result[ dstOffset ++ ] = values[ srcOffset + j ]; + return data; - } + } - } + fromJSON( json ) { - return result; + super.fromJSON( json ); - }, + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { + return this; - let i = 1, key = jsonKeys[ 0 ]; + } - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + } - key = jsonKeys[ i ++ ]; + LineCurve.prototype.isLineCurve = true; - } + class LineCurve3 extends Curve { - if ( key === undefined ) return; // no data + constructor( v1 = new Vector3(), v2 = new Vector3() ) { - let value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data + super(); - if ( Array.isArray( value ) ) { + this.type = 'LineCurve3'; + this.isLineCurve3 = true; - do { + this.v1 = v1; + this.v2 = v2; - value = key[ valuePropertyName ]; + } + getPoint( t, optionalTarget = new Vector3() ) { - if ( value !== undefined ) { + const point = optionalTarget; - times.push( key.time ); - values.push.apply( values, value ); // push all elements + if ( t === 1 ) { - } + point.copy( this.v2 ); - key = jsonKeys[ i ++ ]; + } else { - } while ( key !== undefined ); + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - } else if ( value.toArray !== undefined ) { + } - // ...assume THREE.Math-ish + return point; - do { + } + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - value = key[ valuePropertyName ]; + return this.getPoint( u, optionalTarget ); - if ( value !== undefined ) { + } + copy( source ) { - times.push( key.time ); - value.toArray( values, values.length ); + super.copy( source ); - } + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - key = jsonKeys[ i ++ ]; + return this; - } while ( key !== undefined ); + } + toJSON() { - } else { + const data = super.toJSON(); - // otherwise push as-is + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - do { + return data; - value = key[ valuePropertyName ]; + } + fromJSON( json ) { - if ( value !== undefined ) { + super.fromJSON( json ); - times.push( key.time ); - values.push( value ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - } + return this; - key = jsonKeys[ i ++ ]; + } - } while ( key !== undefined ); + } - } + class QuadraticBezierCurve extends Curve { - }, + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { - subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { + super(); - const clip = sourceClip.clone(); + this.type = 'QuadraticBezierCurve'; - clip.name = name; + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - const tracks = []; + } - for ( let i = 0; i < clip.tracks.length; ++ i ) { + getPoint( t, optionalTarget = new Vector2() ) { - const track = clip.tracks[ i ]; - const valueSize = track.getValueSize(); + const point = optionalTarget; - const times = []; - const values = []; + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - for ( let j = 0; j < track.times.length; ++ j ) { + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ) + ); - const frame = track.times[ j ] * fps; + return point; - if ( frame < startFrame || frame >= endFrame ) continue; + } - times.push( track.times[ j ] ); + copy( source ) { - for ( let k = 0; k < valueSize; ++ k ) { + super.copy( source ); - values.push( track.values[ j * valueSize + k ] ); + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - } + return this; - } + } - if ( times.length === 0 ) continue; + toJSON() { - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); + const data = super.toJSON(); - tracks.push( track ); + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - } + return data; - clip.tracks = tracks; + } - // find minimum .times value across all tracks in the trimmed clip + fromJSON( json ) { - let minStartTime = Infinity; + super.fromJSON( json ); - for ( let i = 0; i < clip.tracks.length; ++ i ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { + return this; - minStartTime = clip.tracks[ i ].times[ 0 ]; + } - } + } - } + QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - // shift all tracks such that clip begins at t=0 + class QuadraticBezierCurve3 extends Curve { - for ( let i = 0; i < clip.tracks.length; ++ i ) { + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { - clip.tracks[ i ].shift( - 1 * minStartTime ); + super(); - } + this.type = 'QuadraticBezierCurve3'; - clip.resetDuration(); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - return clip; + } - }, + getPoint( t, optionalTarget = new Vector3() ) { - makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { + const point = optionalTarget; - if ( fps <= 0 ) fps = 30; + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - const numTracks = referenceClip.tracks.length; - const referenceTime = referenceFrame / fps; + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ), + QuadraticBezier( t, v0.z, v1.z, v2.z ) + ); - // Make each track's values relative to the values at the reference frame - for ( let i = 0; i < numTracks; ++ i ) { + return point; - const referenceTrack = referenceClip.tracks[ i ]; - const referenceTrackType = referenceTrack.ValueTypeName; + } - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; + copy( source ) { - // Find the track in the target clip whose name and type matches the reference track - const targetTrack = targetClip.tracks.find( function ( track ) { + super.copy( source ); - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - } ); + return this; - if ( targetTrack === undefined ) continue; + } - let referenceOffset = 0; - const referenceValueSize = referenceTrack.getValueSize(); + toJSON() { - if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { + const data = super.toJSON(); - referenceOffset = referenceValueSize / 3; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - } + return data; - let targetOffset = 0; - const targetValueSize = targetTrack.getValueSize(); + } - if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { + fromJSON( json ) { - targetOffset = targetValueSize / 3; + super.fromJSON( json ); - } + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - const lastIndex = referenceTrack.times.length - 1; - let referenceValue; + return this; - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { + } - // Reference frame is earlier than the first keyframe, so just use the first keyframe - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); + } - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { + QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - // Reference frame is after the last keyframe, so just use the last keyframe - const startIndex = lastIndex * referenceValueSize + referenceOffset; - const endIndex = startIndex + referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); + class SplineCurve extends Curve { - } else { + constructor( points = [] ) { - // Interpolate to the reference value - const interpolant = referenceTrack.createInterpolant(); - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - interpolant.evaluate( referenceTime ); - referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); + super(); - } + this.type = 'SplineCurve'; - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { + this.points = points; - const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); + } - } + getPoint( t, optionalTarget = new Vector2() ) { - // Subtract the reference value from all of the track values + const point = optionalTarget; - const numTimes = targetTrack.times.length; - for ( let j = 0; j < numTimes; ++ j ) { + const points = this.points; + const p = ( points.length - 1 ) * t; - const valueStart = j * targetValueSize + targetOffset; + const intPoint = Math.floor( p ); + const weight = p - intPoint; - if ( referenceTrackType === 'quaternion' ) { + const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + const p1 = points[ intPoint ]; + const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); + point.set( + CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), + CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) + ); - } else { + return point; - const valueEnd = targetValueSize - targetOffset * 2; + } - // Subtract each value for all other numeric track types - for ( let k = 0; k < valueEnd; ++ k ) { + copy( source ) { - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; + super.copy( source ); - } + this.points = []; - } + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - } + const point = source.points[ i ]; - } + this.points.push( point.clone() ); - targetClip.blendMode = AdditiveAnimationBlendMode; + } - return targetClip; + return this; } - }; + toJSON() { - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - */ + const data = super.toJSON(); - function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + data.points = []; - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; + const point = this.points[ i ]; + data.points.push( point.toArray() ); - } + } - Object.assign( Interpolant.prototype, { + return data; - evaluate: function ( t ) { + } - const pp = this.parameterPositions; - let i1 = this._cachedIndex, - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; + fromJSON( json ) { - validate_interval: { + super.fromJSON( json ); - seek: { + this.points = []; - let right; + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - linear_scan: { + const point = json.points[ i ]; + this.points.push( new Vector2().fromArray( point ) ); - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { + } - for ( let giveUpAt = i1 + 2; ; ) { + return this; - if ( t1 === undefined ) { + } - if ( t < t0 ) break forward_scan; + } - // after end + SplineCurve.prototype.isSplineCurve = true; - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); + var Curves = /*#__PURE__*/Object.freeze({ + __proto__: null, + ArcCurve: ArcCurve, + CatmullRomCurve3: CatmullRomCurve3, + CubicBezierCurve: CubicBezierCurve, + CubicBezierCurve3: CubicBezierCurve3, + EllipseCurve: EllipseCurve, + LineCurve: LineCurve, + LineCurve3: LineCurve3, + QuadraticBezierCurve: QuadraticBezierCurve, + QuadraticBezierCurve3: QuadraticBezierCurve3, + SplineCurve: SplineCurve + }); - } + /************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ - if ( i1 === giveUpAt ) break; // this loop + class CurvePath extends Curve { - t0 = t1; - t1 = pp[ ++ i1 ]; + constructor() { - if ( t < t1 ) { + super(); - // we have arrived at the sought interval - break seek; + this.type = 'CurvePath'; - } + this.curves = []; + this.autoClose = false; // Automatically closes the path - } + } - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; + add( curve ) { - } + this.curves.push( curve ); - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { + } - // looping? + closePath() { - const t1global = pp[ 1 ]; + // Add a line curve if start and end of lines are not connected + const startPoint = this.curves[ 0 ].getPoint( 0 ); + const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - if ( t < t1global ) { + if ( ! startPoint.equals( endPoint ) ) { - i1 = 2; // + 1, using the scan for the details - t0 = t1global; + this.curves.push( new LineCurve( endPoint, startPoint ) ); - } + } - // linear reverse scan + } - for ( let giveUpAt = i1 - 2; ; ) { + // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: - if ( t0 === undefined ) { + // 1. Length of each sub path have to be known + // 2. Locate and identify type of curve + // 3. Get t for the curve + // 4. Return curve.getPointAt(t') - // before start + getPoint( t, optionalTarget ) { - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + const d = t * this.getLength(); + const curveLengths = this.getCurveLengths(); + let i = 0; - } + // To think about boundaries points. - if ( i1 === giveUpAt ) break; // this loop + while ( i < curveLengths.length ) { - t1 = t0; - t0 = pp[ -- i1 - 1 ]; + if ( curveLengths[ i ] >= d ) { - if ( t >= t0 ) { + const diff = curveLengths[ i ] - d; + const curve = this.curves[ i ]; - // we have arrived at the sought interval - break seek; + const segmentLength = curve.getLength(); + const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - } + return curve.getPointAt( u, optionalTarget ); - } + } - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; + i ++; - } + } - // the interval is valid + return null; - break validate_interval; + // loop where sum != 0, sum > d , sum+1 >> 1; + const lens = this.getCurveLengths(); + return lens[ lens.length - 1 ]; - if ( t < pp[ mid ] ) { + } - right = mid; + // cacheLengths must be recalculated. + updateArcLengths() { - } else { + this.needsUpdate = true; + this.cacheLengths = null; + this.getCurveLengths(); - i1 = mid + 1; + } - } + // Compute lengths and cache them + // We cannot overwrite getLengths() because UtoT mapping uses it. - } + getCurveLengths() { - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; + // We use cache values if curves and cache array are same length - // check boundary cases, again + if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) { - if ( t0 === undefined ) { + return this.cacheLengths; - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + } - } + // Get length of sub-curve + // Push sums into cached array - if ( t1 === undefined ) { + const lengths = []; + let sums = 0; - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); + for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - } + sums += this.curves[ i ].getLength(); + lengths.push( sums ); - } // seek + } - this._cachedIndex = i1; + this.cacheLengths = lengths; - this.intervalChanged_( i1, t0, t1 ); + return lengths; - } // validate_interval + } - return this.interpolate_( i1, t0, t, t1 ); + getSpacedPoints( divisions = 40 ) { - }, + const points = []; - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. + for ( let i = 0; i <= divisions; i ++ ) { - // --- Protected interface + points.push( this.getPoint( i / divisions ) ); - DefaultSettings_: {}, + } - getSettings_: function () { + if ( this.autoClose ) { - return this.settings || this.DefaultSettings_; + points.push( points[ 0 ] ); - }, + } - copySampleValue_: function ( index ) { + return points; - // copies a sample value to the result buffer + } - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; + getPoints( divisions = 12 ) { - for ( let i = 0; i !== stride; ++ i ) { + const points = []; + let last; - result[ i ] = values[ offset + i ]; + for ( let i = 0, curves = this.curves; i < curves.length; i ++ ) { - } + const curve = curves[ i ]; + const resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2 + : ( curve && ( curve.isLineCurve || curve.isLineCurve3 ) ) ? 1 + : ( curve && curve.isSplineCurve ) ? divisions * curve.points.length + : divisions; - return result; + const pts = curve.getPoints( resolution ); - }, + for ( let j = 0; j < pts.length; j ++ ) { - // Template methods for derived classes: + const point = pts[ j ]; - interpolate_: function ( /* i1, t0, t, t1 */ ) { + if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer + points.push( point ); + last = point; - }, + } - intervalChanged_: function ( /* i1, t0, t1 */ ) { + } - // empty + if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - } + points.push( points[ 0 ] ); - } ); + } - // DECLARE ALIAS AFTER assign prototype - Object.assign( Interpolant.prototype, { + return points; - //( 0, t, t0 ), returns this.resultBuffer - beforeStart_: Interpolant.prototype.copySampleValue_, + } - //( N-1, tN-1, t ), returns this.resultBuffer - afterEnd_: Interpolant.prototype.copySampleValue_, + copy( source ) { - } ); + super.copy( source ); - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - */ + this.curves = []; - function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + for ( let i = 0, l = source.curves.length; i < l; i ++ ) { - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + const curve = source.curves[ i ]; - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; + this.curves.push( curve.clone() ); - } + } - CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + this.autoClose = source.autoClose; - constructor: CubicInterpolant, + return this; - DefaultSettings_: { + } - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding + toJSON() { - }, + const data = super.toJSON(); - intervalChanged_: function ( i1, t0, t1 ) { + data.autoClose = this.autoClose; + data.curves = []; - const pp = this.parameterPositions; - let iPrev = i1 - 2, - iNext = i1 + 1, + for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; + const curve = this.curves[ i ]; + data.curves.push( curve.toJSON() ); - if ( tPrev === undefined ) { + } - switch ( this.getSettings_().endingStart ) { + return data; - case ZeroSlopeEnding: + } - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; + fromJSON( json ) { - break; - - case WrapAroundEnding: - - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - - break; + super.fromJSON( json ); - default: // ZeroCurvatureEnding + this.autoClose = json.autoClose; + this.curves = []; - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; + for ( let i = 0, l = json.curves.length; i < l; i ++ ) { - } + const curve = json.curves[ i ]; + this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } - if ( tNext === undefined ) { - - switch ( this.getSettings_().endingEnd ) { - - case ZeroSlopeEnding: + return this; - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; + } - break; + } - case WrapAroundEnding: + class Path extends CurvePath { - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; + constructor( points ) { - break; + super(); + this.type = 'Path'; - default: // ZeroCurvatureEnding + this.currentPoint = new Vector2(); - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; + if ( points ) { - } + this.setFromPoints( points ); } - const halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; - - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - }, - - interpolate_: function ( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, - - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; - - // evaluate polynomials + } - const sP = - wP * ppp + 2 * wP * pp - wP * p; - const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - const sN = wN * ppp - wN * pp; + setFromPoints( points ) { - // combine data linearly + this.moveTo( points[ 0 ].x, points[ 0 ].y ); - for ( let i = 0; i !== stride; ++ i ) { + for ( let i = 1, l = points.length; i < l; i ++ ) { - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; + this.lineTo( points[ i ].x, points[ i ].y ); } - return result; + return this; } - } ); + moveTo( x, y ) { - function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + return this; - } + } - LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + lineTo( x, y ) { - constructor: LinearInterpolant, + const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); + this.curves.push( curve ); - interpolate_: function ( i1, t0, t, t1 ) { + this.currentPoint.set( x, y ); - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + return this; - offset1 = i1 * stride, - offset0 = offset1 - stride, + } - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; + quadraticCurveTo( aCPx, aCPy, aX, aY ) { - for ( let i = 0; i !== stride; ++ i ) { + const curve = new QuadraticBezierCurve( + this.currentPoint.clone(), + new Vector2( aCPx, aCPy ), + new Vector2( aX, aY ) + ); - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; + this.curves.push( curve ); - } + this.currentPoint.set( aX, aY ); - return result; + return this; } - } ); + bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - */ + const curve = new CubicBezierCurve( + this.currentPoint.clone(), + new Vector2( aCP1x, aCP1y ), + new Vector2( aCP2x, aCP2y ), + new Vector2( aX, aY ) + ); - function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + this.curves.push( curve ); - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + this.currentPoint.set( aX, aY ); - } + return this; - DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + } - constructor: DiscreteInterpolant, + splineThru( pts /*Array of Vector*/ ) { - interpolate_: function ( i1 /*, t0, t, t1 */ ) { + const npts = [ this.currentPoint.clone() ].concat( pts ); - return this.copySampleValue_( i1 - 1 ); + const curve = new SplineCurve( npts ); + this.curves.push( curve ); - } + this.currentPoint.copy( pts[ pts.length - 1 ] ); - } ); + return this; - function KeyframeTrack( name, times, values, interpolation ) { + } - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); + arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - this.name = name; + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - this.setInterpolation( interpolation || this.DefaultInterpolation ); + return this; - } + } - // Static methods + absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - Object.assign( KeyframeTrack, { + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): + return this; - toJSON: function ( track ) { + } - const trackType = track.constructor; + ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - let json; + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - json = trackType.toJSON( track ); + return this; - } else { + } - // by default, we assume the data can be serialized as-is - json = { + absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) + const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - }; + if ( this.curves.length > 0 ) { - const interpolation = track.getInterpolation(); + // if a previous curve is present, attempt to join + const firstPoint = curve.getPoint( 0 ); - if ( interpolation !== track.DefaultInterpolation ) { + if ( ! firstPoint.equals( this.currentPoint ) ) { - json.interpolation = interpolation; + this.lineTo( firstPoint.x, firstPoint.y ); } } - json.type = track.ValueTypeName; // mandatory + this.curves.push( curve ); - return json; + const lastPoint = curve.getPoint( 1 ); + this.currentPoint.copy( lastPoint ); + + return this; } - } ); + copy( source ) { - Object.assign( KeyframeTrack.prototype, { + super.copy( source ); - constructor: KeyframeTrack, + this.currentPoint.copy( source.currentPoint ); - TimeBufferType: Float32Array, + return this; - ValueBufferType: Float32Array, + } - DefaultInterpolation: InterpolateLinear, + toJSON() { - InterpolantFactoryMethodDiscrete: function ( result ) { + const data = super.toJSON(); - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); + data.currentPoint = this.currentPoint.toArray(); - }, + return data; - InterpolantFactoryMethodLinear: function ( result ) { + } - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); + fromJSON( json ) { - }, + super.fromJSON( json ); - InterpolantFactoryMethodSmooth: function ( result ) { + this.currentPoint.fromArray( json.currentPoint ); - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); + return this; - }, + } - setInterpolation: function ( interpolation ) { + } - let factoryMethod; + class Shape extends Path { - switch ( interpolation ) { + constructor( points ) { - case InterpolateDiscrete: + super( points ); - factoryMethod = this.InterpolantFactoryMethodDiscrete; + this.uuid = generateUUID(); - break; + this.type = 'Shape'; - case InterpolateLinear: + this.holes = []; - factoryMethod = this.InterpolantFactoryMethodLinear; + } - break; + getPointsHoles( divisions ) { - case InterpolateSmooth: + const holesPts = []; - factoryMethod = this.InterpolantFactoryMethodSmooth; + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - break; + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } - if ( factoryMethod === undefined ) { + return holesPts; - const message = 'unsupported interpolation for ' + - this.ValueTypeName + ' keyframe track named ' + this.name; + } - if ( this.createInterpolant === undefined ) { + // get points of shape and holes (keypoints based on segments parameter) - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { + extractPoints( divisions ) { - this.setInterpolation( this.DefaultInterpolation ); + return { - } else { + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) - throw new Error( message ); // fatal, in this case + }; - } + } - } + copy( source ) { - console.warn( 'THREE.KeyframeTrack:', message ); - return this; + super.copy( source ); - } + this.holes = []; - this.createInterpolant = factoryMethod; + for ( let i = 0, l = source.holes.length; i < l; i ++ ) { - return this; + const hole = source.holes[ i ]; - }, + this.holes.push( hole.clone() ); - getInterpolation: function () { + } - switch ( this.createInterpolant ) { + return this; - case this.InterpolantFactoryMethodDiscrete: + } - return InterpolateDiscrete; + toJSON() { - case this.InterpolantFactoryMethodLinear: + const data = super.toJSON(); - return InterpolateLinear; + data.uuid = this.uuid; + data.holes = []; - case this.InterpolantFactoryMethodSmooth: + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - return InterpolateSmooth; + const hole = this.holes[ i ]; + data.holes.push( hole.toJSON() ); } - }, - - getValueSize: function () { - - return this.values.length / this.times.length; - - }, + return data; - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { + } - if ( timeOffset !== 0.0 ) { + fromJSON( json ) { - const times = this.times; + super.fromJSON( json ); - for ( let i = 0, n = times.length; i !== n; ++ i ) { + this.uuid = json.uuid; + this.holes = []; - times[ i ] += timeOffset; + for ( let i = 0, l = json.holes.length; i < l; i ++ ) { - } + const hole = json.holes[ i ]; + this.holes.push( new Path().fromJSON( hole ) ); } return this; - }, + } - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { + } - if ( timeScale !== 1.0 ) { + /** + * Port from https://github.com/mapbox/earcut (v2.2.2) + */ - const times = this.times; + const Earcut = { - for ( let i = 0, n = times.length; i !== n; ++ i ) { + triangulate: function ( data, holeIndices, dim = 2 ) { - times[ i ] *= timeScale; + const hasHoles = holeIndices && holeIndices.length; + const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; + let outerNode = linkedList$1( data, 0, outerLen, dim, true ); + const triangles = []; - } + if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; - } + let minX, minY, maxX, maxY, x, y, invSize; - return this; + if ( hasHoles ) outerNode = eliminateHoles$1( data, holeIndices, outerNode, dim ); - }, + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + if ( data.length > 80 * dim ) { - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function ( startTime, endTime ) { + minX = maxX = data[ 0 ]; + minY = maxY = data[ 1 ]; - const times = this.times, - nKeys = times.length; + for ( let i = dim; i < outerLen; i += dim ) { - let from = 0, - to = nKeys - 1; + x = data[ i ]; + y = data[ i + 1 ]; + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; - while ( from !== nKeys && times[ from ] < startTime ) { + } - ++ from; + // minX, minY and invSize are later used to transform coords into integers for z-order calculation + invSize = Math.max( maxX - minX, maxY - minY ); + invSize = invSize !== 0 ? 1 / invSize : 0; } - while ( to !== - 1 && times[ to ] > endTime ) { + earcutLinked$1( outerNode, triangles, dim, minX, minY, invSize ); - -- to; + return triangles; - } + } - ++ to; // inclusive -> exclusive bound + }; - if ( from !== 0 || to !== nKeys ) { + // create a circular doubly linked list from polygon points in the specified winding order + function linkedList$1( data, start, end, dim, clockwise ) { - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { + let i, last; - to = Math.max( to, 1 ); - from = to - 1; + if ( clockwise === ( signedArea$2( data, start, end, dim ) > 0 ) ) { - } + for ( i = start; i < end; i += dim ) last = insertNode$2( i, data[ i ], data[ i + 1 ], last ); - const stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); + } else { - } + for ( i = end - dim; i >= start; i -= dim ) last = insertNode$2( i, data[ i ], data[ i + 1 ], last ); - return this; + } - }, + if ( last && equals$2( last, last.next ) ) { - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { + removeNode$2( last ); + last = last.next; - let valid = true; + } - const valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { + return last; - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; + } - } + // eliminate colinear or duplicate points + function filterPoints$1( start, end ) { - const times = this.times, - values = this.values, + if ( ! start ) return start; + if ( ! end ) end = start; - nKeys = times.length; + let p = start, + again; + do { - if ( nKeys === 0 ) { + again = false; - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; + if ( ! p.steiner && ( equals$2( p, p.next ) || area$1( p.prev, p, p.next ) === 0 ) ) { - } + removeNode$2( p ); + p = end = p.prev; + if ( p === p.next ) break; + again = true; - let prevTime = null; + } else { - for ( let i = 0; i !== nKeys; i ++ ) { + p = p.next; - const currTime = times[ i ]; + } - if ( typeof currTime === 'number' && isNaN( currTime ) ) { + } while ( again || p !== end ); - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; + return end; - } + } - if ( prevTime !== null && prevTime > currTime ) { + // main ear slicing loop which triangulates a polygon (given as a linked list) + function earcutLinked$1( ear, triangles, dim, minX, minY, invSize, pass ) { - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; + if ( ! ear ) return; - } + // interlink polygon nodes in z-order + if ( ! pass && invSize ) indexCurve$1( ear, minX, minY, invSize ); - prevTime = currTime; + let stop = ear, + prev, next; - } + // iterate through ears, slicing them one by one + while ( ear.prev !== ear.next ) { - if ( values !== undefined ) { + prev = ear.prev; + next = ear.next; - if ( AnimationUtils.isTypedArray( values ) ) { + if ( invSize ? isEarHashed$1( ear, minX, minY, invSize ) : isEar$1( ear ) ) { - for ( let i = 0, n = values.length; i !== n; ++ i ) { + // cut off the triangle + triangles.push( prev.i / dim ); + triangles.push( ear.i / dim ); + triangles.push( next.i / dim ); - const value = values[ i ]; + removeNode$2( ear ); - if ( isNaN( value ) ) { + // skipping the next vertex leads to less sliver triangles + ear = next.next; + stop = next.next; - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; + continue; - } + } - } + ear = next; - } + // if we looped through the whole remaining polygon and can't find any more ears + if ( ear === stop ) { - } + // try filtering points and slicing again + if ( ! pass ) { - return valid; + earcutLinked$1( filterPoints$1( ear ), triangles, dim, minX, minY, invSize, 1 ); - }, + // if this didn't work, try curing all small self-intersections locally - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function () { + } else if ( pass === 1 ) { - // times or values may be shared with other tracks, so overwriting is unsafe - const times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), + ear = cureLocalIntersections$1( filterPoints$1( ear ), triangles, dim ); + earcutLinked$1( ear, triangles, dim, minX, minY, invSize, 2 ); - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, + // as a last resort, try splitting the remaining polygon into two - lastIndex = times.length - 1; + } else if ( pass === 2 ) { - let writeIndex = 1; + splitEarcut$1( ear, triangles, dim, minX, minY, invSize ); - for ( let i = 1; i < lastIndex; ++ i ) { + } - let keep = false; + break; - const time = times[ i ]; - const timeNext = times[ i + 1 ]; + } - // remove adjacent keyframes scheduled at the same time + } - if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { + } - if ( ! smoothInterpolation ) { + // check whether a polygon node forms a valid ear with adjacent nodes + function isEar$1( ear ) { - // remove unnecessary keyframes same as their neighbors + const a = ear.prev, + b = ear, + c = ear.next; - const offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; + if ( area$1( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - for ( let j = 0; j !== stride; ++ j ) { + // now make sure we don't have other points inside the potential ear + let p = ear.next.next; - const value = values[ offset + j ]; + while ( p !== ear.prev ) { - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { + if ( pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area$1( p.prev, p, p.next ) >= 0 ) return false; + p = p.next; - keep = true; - break; + } - } + return true; - } + } - } else { + function isEarHashed$1( ear, minX, minY, invSize ) { - keep = true; + const a = ear.prev, + b = ear, + c = ear.next; - } + if ( area$1( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - } + // triangle bbox; min & max are calculated like this for speed + const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), + minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), + maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), + maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - // in-place compaction + // z-order range for the current triangle bbox; + const minZ = zOrder$1( minTX, minTY, minX, minY, invSize ), + maxZ = zOrder$1( maxTX, maxTY, minX, minY, invSize ); - if ( keep ) { + let p = ear.prevZ, + n = ear.nextZ; - if ( i !== writeIndex ) { + // look for points inside the triangle in both directions + while ( p && p.z >= minZ && n && n.z <= maxZ ) { - times[ writeIndex ] = times[ i ]; + if ( p !== ear.prev && p !== ear.next && + pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area$1( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; - const readOffset = i * stride, - writeOffset = writeIndex * stride; + if ( n !== ear.prev && n !== ear.next && + pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && + area$1( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; - for ( let j = 0; j !== stride; ++ j ) { + } - values[ writeOffset + j ] = values[ readOffset + j ]; + // look for remaining points in decreasing z-order + while ( p && p.z >= minZ ) { - } + if ( p !== ear.prev && p !== ear.next && + pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area$1( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; - } + } - ++ writeIndex; + // look for remaining points in increasing z-order + while ( n && n.z <= maxZ ) { - } + if ( n !== ear.prev && n !== ear.next && + pointInTriangle$1( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && + area$1( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; - } + } - // flush last keyframe (compaction looks ahead) + return true; - if ( lastIndex > 0 ) { + } - times[ writeIndex ] = times[ lastIndex ]; + // go through all polygon nodes and cure small local self-intersections + function cureLocalIntersections$1( start, triangles, dim ) { - for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { + let p = start; + do { - values[ writeOffset + j ] = values[ readOffset + j ]; + const a = p.prev, + b = p.next.next; - } + if ( ! equals$2( a, b ) && intersects$2( a, p, p.next, b ) && locallyInside$1( a, b ) && locallyInside$1( b, a ) ) { - ++ writeIndex; + triangles.push( a.i / dim ); + triangles.push( p.i / dim ); + triangles.push( b.i / dim ); - } + // remove two nodes involved + removeNode$2( p ); + removeNode$2( p.next ); - if ( writeIndex !== times.length ) { + p = start = b; - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + } - } else { + p = p.next; - this.times = times; - this.values = values; + } while ( p !== start ); - } + return filterPoints$1( p ); - return this; + } - }, + // try splitting polygon into two and triangulate them independently + function splitEarcut$1( start, triangles, dim, minX, minY, invSize ) { + + // look for a valid diagonal that divides the polygon into two + let a = start; + do { - clone: function () { + let b = a.next.next; + while ( b !== a.prev ) { - const times = AnimationUtils.arraySlice( this.times, 0 ); - const values = AnimationUtils.arraySlice( this.values, 0 ); + if ( a.i !== b.i && isValidDiagonal$1( a, b ) ) { - const TypedKeyframeTrack = this.constructor; - const track = new TypedKeyframeTrack( this.name, times, values ); + // split the polygon in two by the diagonal + let c = splitPolygon$1( a, b ); - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; + // filter colinear points around the cuts + a = filterPoints$1( a, a.next ); + c = filterPoints$1( c, c.next ); - return track; + // run earcut on each half + earcutLinked$1( a, triangles, dim, minX, minY, invSize ); + earcutLinked$1( c, triangles, dim, minX, minY, invSize ); + return; - } + } - } ); + b = b.next; - /** - * A Track of Boolean keyframe values. - */ + } - function BooleanKeyframeTrack( name, times, values ) { + a = a.next; - KeyframeTrack.call( this, name, times, values ); + } while ( a !== start ); } - BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + // link every hole into the outer loop, producing a single-ring polygon without holes + function eliminateHoles$1( data, holeIndices, outerNode, dim ) { - constructor: BooleanKeyframeTrack, + const queue = []; + let i, len, start, end, list; - ValueTypeName: 'bool', - ValueBufferType: Array, + for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - DefaultInterpolation: InterpolateDiscrete, + start = holeIndices[ i ] * dim; + end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; + list = linkedList$1( data, start, end, dim, false ); + if ( list === list.next ) list.steiner = true; + queue.push( getLeftmost$1( list ) ); - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined + } - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". + queue.sort( compareX$1 ); - } ); + // process holes from left to right + for ( i = 0; i < queue.length; i ++ ) { - /** - * A Track of keyframe values that represent color. - */ + eliminateHole$1( queue[ i ], outerNode ); + outerNode = filterPoints$1( outerNode, outerNode.next ); - function ColorKeyframeTrack( name, times, values, interpolation ) { + } - KeyframeTrack.call( this, name, times, values, interpolation ); + return outerNode; } - ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: ColorKeyframeTrack, - - ValueTypeName: 'color' + function compareX$1( a, b ) { - // ValueBufferType is inherited + return a.x - b.x; - // DefaultInterpolation is inherited + } - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. + // find a bridge between vertices that connects hole with an outer ring and and link it + function eliminateHole$1( hole, outerNode ) { - } ); + outerNode = findHoleBridge$1( hole, outerNode ); + if ( outerNode ) { - /** - * A Track of numeric keyframe values. - */ + const b = splitPolygon$1( outerNode, hole ); - function NumberKeyframeTrack( name, times, values, interpolation ) { + // filter collinear points around the cuts + filterPoints$1( outerNode, outerNode.next ); + filterPoints$1( b, b.next ); - KeyframeTrack.call( this, name, times, values, interpolation ); + } } - NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: NumberKeyframeTrack, + // David Eberly's algorithm for finding a bridge between hole and outer polygon + function findHoleBridge$1( hole, outerNode ) { - ValueTypeName: 'number' + let p = outerNode; + const hx = hole.x; + const hy = hole.y; + let qx = - Infinity, m; - // ValueBufferType is inherited + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + do { - // DefaultInterpolation is inherited + if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - } ); + const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); + if ( x <= hx && x > qx ) { - /** - * Spherical linear unit quaternion interpolant. - */ + qx = x; + if ( x === hx ) { - function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + if ( hy === p.y ) return p; + if ( hy === p.next.y ) return p.next; - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + } - } + m = p.x < p.next.x ? p : p.next; - QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + } - constructor: QuaternionLinearInterpolant, + } - interpolate_: function ( i1, t0, t, t1 ) { + p = p.next; - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + } while ( p !== outerNode ); - alpha = ( t - t0 ) / ( t1 - t0 ); + if ( ! m ) return null; - let offset = i1 * stride; + if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint - for ( let end = offset + stride; offset !== end; offset += 4 ) { + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); + const stop = m, + mx = m.x, + my = m.y; + let tanMin = Infinity, tan; - } + p = m; - return result; + do { - } + if ( hx >= p.x && p.x >= mx && hx !== p.x && + pointInTriangle$1( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { - } ); + tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential - /** - * A Track of quaternion keyframe values. - */ + if ( locallyInside$1( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector$1( m, p ) ) ) ) ) ) { - function QuaternionKeyframeTrack( name, times, values, interpolation ) { + m = p; + tanMin = tan; - KeyframeTrack.call( this, name, times, values, interpolation ); + } - } + } - QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + p = p.next; - constructor: QuaternionKeyframeTrack, + } while ( p !== stop ); - ValueTypeName: 'quaternion', + return m; - // ValueBufferType is inherited + } - DefaultInterpolation: InterpolateLinear, + // whether sector in vertex m contains sector in vertex p in the same coordinates + function sectorContainsSector$1( m, p ) { - InterpolantFactoryMethodLinear: function ( result ) { + return area$1( m.prev, m, p.prev ) < 0 && area$1( p.next, m, m.next ) < 0; - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); + } - }, + // interlink polygon nodes in z-order + function indexCurve$1( start, minX, minY, invSize ) { - InterpolantFactoryMethodSmooth: undefined // not yet implemented + let p = start; + do { - } ); + if ( p.z === null ) p.z = zOrder$1( p.x, p.y, minX, minY, invSize ); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; - /** - * A Track that interpolates Strings - */ + } while ( p !== start ); - function StringKeyframeTrack( name, times, values, interpolation ) { + p.prevZ.nextZ = null; + p.prevZ = null; - KeyframeTrack.call( this, name, times, values, interpolation ); + sortLinked$1( p ); } - StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + // Simon Tatham's linked list merge sort algorithm + // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + function sortLinked$1( list ) { - constructor: StringKeyframeTrack, + let i, p, q, e, tail, numMerges, pSize, qSize, + inSize = 1; - ValueTypeName: 'string', - ValueBufferType: Array, + do { - DefaultInterpolation: InterpolateDiscrete, + p = list; + list = null; + tail = null; + numMerges = 0; - InterpolantFactoryMethodLinear: undefined, + while ( p ) { - InterpolantFactoryMethodSmooth: undefined + numMerges ++; + q = p; + pSize = 0; + for ( i = 0; i < inSize; i ++ ) { - } ); + pSize ++; + q = q.nextZ; + if ( ! q ) break; - /** - * A Track of vectored keyframe values. - */ + } - function VectorKeyframeTrack( name, times, values, interpolation ) { + qSize = inSize; - KeyframeTrack.call( this, name, times, values, interpolation ); + while ( pSize > 0 || ( qSize > 0 && q ) ) { - } + if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + e = p; + p = p.nextZ; + pSize --; - constructor: VectorKeyframeTrack, + } else { - ValueTypeName: 'vector' + e = q; + q = q.nextZ; + qSize --; - // ValueBufferType is inherited + } - // DefaultInterpolation is inherited + if ( tail ) tail.nextZ = e; + else list = e; - } ); + e.prevZ = tail; + tail = e; - function AnimationClip( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { + } - this.name = name; - this.tracks = tracks; - this.duration = duration; - this.blendMode = blendMode; + p = q; - this.uuid = MathUtils.generateUUID(); + } - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { + tail.nextZ = null; + inSize *= 2; - this.resetDuration(); + } while ( numMerges > 1 ); - } + return list; } - function getTrackTypeForValueTypeName( typeName ) { - - switch ( typeName.toLowerCase() ) { - - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': + // z-order of a point given coords and inverse of the longer side of data bbox + function zOrder$1( x, y, minX, minY, invSize ) { - return NumberKeyframeTrack; + // coords are transformed into non-negative 15-bit integer range + x = 32767 * ( x - minX ) * invSize; + y = 32767 * ( y - minY ) * invSize; - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': + x = ( x | ( x << 8 ) ) & 0x00FF00FF; + x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; + x = ( x | ( x << 2 ) ) & 0x33333333; + x = ( x | ( x << 1 ) ) & 0x55555555; - return VectorKeyframeTrack; + y = ( y | ( y << 8 ) ) & 0x00FF00FF; + y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; + y = ( y | ( y << 2 ) ) & 0x33333333; + y = ( y | ( y << 1 ) ) & 0x55555555; - case 'color': + return x | ( y << 1 ); - return ColorKeyframeTrack; + } - case 'quaternion': + // find the leftmost node of a polygon ring + function getLeftmost$1( start ) { - return QuaternionKeyframeTrack; + let p = start, + leftmost = start; + do { - case 'bool': - case 'boolean': + if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; + p = p.next; - return BooleanKeyframeTrack; + } while ( p !== start ); - case 'string': + return leftmost; - return StringKeyframeTrack; + } - } + // check if a point lies within a convex triangle + function pointInTriangle$1( ax, ay, bx, by, cx, cy, px, py ) { - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); + return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && + ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && + ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; } - function parseKeyframeTrack( json ) { - - if ( json.type === undefined ) { + // check if a diagonal between two polygon nodes is valid (lies in polygon interior) + function isValidDiagonal$1( a, b ) { - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); + return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon$1( a, b ) && // dones't intersect other edges + ( locallyInside$1( a, b ) && locallyInside$1( b, a ) && middleInside$1( a, b ) && // locally visible + ( area$1( a.prev, a, b.prev ) || area$1( a, b.prev, b ) ) || // does not create opposite-facing sectors + equals$2( a, b ) && area$1( a.prev, a, a.next ) > 0 && area$1( b.prev, b, b.next ) > 0 ); // special zero-length case - } + } - const trackType = getTrackTypeForValueTypeName( json.type ); + // signed area of a triangle + function area$1( p, q, r ) { - if ( json.times === undefined ) { + return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - const times = [], values = []; + } - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + // check if two points are equal + function equals$2( p1, p2 ) { - json.times = times; - json.values = values; + return p1.x === p2.x && p1.y === p2.y; - } + } - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { + // check if two segments intersect + function intersects$2( p1, q1, p2, q2 ) { - return trackType.parse( json ); + const o1 = sign$2( area$1( p1, q1, p2 ) ); + const o2 = sign$2( area$1( p1, q1, q2 ) ); + const o3 = sign$2( area$1( p2, q2, p1 ) ); + const o4 = sign$2( area$1( p2, q2, q1 ) ); - } else { + if ( o1 !== o2 && o3 !== o4 ) return true; // general case - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); + if ( o1 === 0 && onSegment$1( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if ( o2 === 0 && onSegment$1( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if ( o3 === 0 && onSegment$1( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if ( o4 === 0 && onSegment$1( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - } + return false; } - Object.assign( AnimationClip, { + // for collinear points p, q, r, check if point q lies on segment pr + function onSegment$1( p, q, r ) { - parse: function ( json ) { + return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - const tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); + } - for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { + function sign$2( num ) { - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); + return num > 0 ? 1 : num < 0 ? - 1 : 0; - } + } - const clip = new AnimationClip( json.name, json.duration, tracks, json.blendMode ); - clip.uuid = json.uuid; + // check if a polygon diagonal intersects any polygon segments + function intersectsPolygon$1( a, b ) { - return clip; + let p = a; + do { - }, + if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects$2( p, p.next, a, b ) ) return true; + p = p.next; - toJSON: function ( clip ) { + } while ( p !== a ); - const tracks = [], - clipTracks = clip.tracks; + return false; - const json = { + } - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode + // check if a polygon diagonal is locally inside the polygon + function locallyInside$1( a, b ) { - }; + return area$1( a.prev, a, a.next ) < 0 ? + area$1( a, b, a.next ) >= 0 && area$1( a, a.prev, b ) >= 0 : + area$1( a, b, a.prev ) < 0 || area$1( a, a.next, b ) < 0; - for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { + } - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + // check if the middle point of a polygon diagonal is inside the polygon + function middleInside$1( a, b ) { - } + let p = a, + inside = false; + const px = ( a.x + b.x ) / 2, + py = ( a.y + b.y ) / 2; + do { - return json; + if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && + ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) + inside = ! inside; + p = p.next; - }, + } while ( p !== a ); - CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { + return inside; - const numMorphTargets = morphTargetSequence.length; - const tracks = []; + } - for ( let i = 0; i < numMorphTargets; i ++ ) { + // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; + // if one belongs to the outer ring and another to a hole, it merges it into a single ring + function splitPolygon$1( a, b ) { - let times = []; - let values = []; + const a2 = new Node$1( a.i, a.x, a.y ), + b2 = new Node$1( b.i, b.x, b.y ), + an = a.next, + bp = b.prev; - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); + a.next = b; + b.prev = a; - values.push( 0, 1, 0 ); + a2.next = an; + an.prev = a2; - const order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); + b2.next = a2; + a2.prev = b2; - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { + bp.next = b2; + b2.prev = bp; - times.push( numMorphTargets ); - values.push( values[ 0 ] ); + return b2; - } + } - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); + // create a node and optionally link it with previous one (in a circular doubly linked list) + function insertNode$2( i, x, y, last ) { - } + const p = new Node$1( i, x, y ); - return new AnimationClip( name, - 1, tracks ); + if ( ! last ) { - }, + p.prev = p; + p.next = p; - findByName: function ( objectOrClipArray, name ) { + } else { - let clipArray = objectOrClipArray; + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; - if ( ! Array.isArray( objectOrClipArray ) ) { + } - const o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; + return p; - } + } - for ( let i = 0; i < clipArray.length; i ++ ) { + function removeNode$2( p ) { - if ( clipArray[ i ].name === name ) { + p.next.prev = p.prev; + p.prev.next = p.next; - return clipArray[ i ]; + if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; + if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; - } + } - } + function Node$1( i, x, y ) { - return null; + // vertex index in coordinates array + this.i = i; - }, + // vertex coordinates + this.x = x; + this.y = y; - CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { + // previous and next vertex nodes in a polygon ring + this.prev = null; + this.next = null; - const animationToMorphTargets = {}; + // z-order curve value + this.z = null; - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - const pattern = /^([\w-]*?)([\d]+)$/; + // previous and next nodes in z-order + this.prevZ = null; + this.nextZ = null; - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { + // indicates whether this is a steiner point + this.steiner = false; - const morphTarget = morphTargets[ i ]; - const parts = morphTarget.name.match( pattern ); + } - if ( parts && parts.length > 1 ) { + function signedArea$2( data, start, end, dim ) { - const name = parts[ 1 ]; + let sum = 0; + for ( let i = start, j = end - dim; i < end; i += dim ) { - let animationMorphTargets = animationToMorphTargets[ name ]; + sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); + j = i; - if ( ! animationMorphTargets ) { + } - animationToMorphTargets[ name ] = animationMorphTargets = []; + return sum; - } + } - animationMorphTargets.push( morphTarget ); + class ShapeUtils { - } + // calculate area of the contour polygon - } + static area( contour ) { - const clips = []; + const n = contour.length; + let a = 0.0; - for ( const name in animationToMorphTargets ) { + for ( let p = n - 1, q = 0; q < n; p = q ++ ) { - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } - return clips; - - }, - - // parse the animation.hierarchy format - parseAnimation: function ( animation, bones ) { - - if ( ! animation ) { + return a * 0.5; - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; + } - } + static isClockWise( pts ) { - const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { + return ShapeUtils.area( pts ) < 0; - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { + } - const times = []; - const values = []; + static triangulateShape( contour, holes ) { - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); + const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] + const holeIndices = []; // array of hole indices + const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { + removeDupEndPts( contour ); + addContour( vertices, contour ); - destTracks.push( new trackType( trackName, times, values ) ); + // - } + let holeIndex = contour.length; - } + holes.forEach( removeDupEndPts ); - }; + for ( let i = 0; i < holes.length; i ++ ) { - const tracks = []; + holeIndices.push( holeIndex ); + holeIndex += holes[ i ].length; + addContour( vertices, holes[ i ] ); - const clipName = animation.name || 'default'; - const fps = animation.fps || 30; - const blendMode = animation.blendMode; + } - // automatic length determination in AnimationClip. - let duration = animation.length || - 1; + // - const hierarchyTracks = animation.hierarchy || []; + const triangles = Earcut.triangulate( vertices, holeIndices ); - for ( let h = 0; h < hierarchyTracks.length; h ++ ) { + // - const animationKeys = hierarchyTracks[ h ].keys; + for ( let i = 0; i < triangles.length; i += 3 ) { - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; + faces.push( triangles.slice( i, i + 3 ) ); - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { + } - // figure out all morph targets used in this track - const morphTargetNames = {}; + return faces; - let k; + } - for ( k = 0; k < animationKeys.length; k ++ ) { + } - if ( animationKeys[ k ].morphTargets ) { + function removeDupEndPts( points ) { - for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { + const l = points.length; - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; + if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - } + points.pop(); - } + } - } + } - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( const morphTargetName in morphTargetNames ) { + function addContour( vertices, contour ) { - const times = []; - const values = []; + for ( let i = 0; i < contour.length; i ++ ) { - for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { + vertices.push( contour[ i ].x ); + vertices.push( contour[ i ].y ); - const animationKey = animationKeys[ k ]; + } - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); + } - } + /** + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * depth: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline (including bevelOffset) is bevel + * bevelOffset: , // how far from shape outline does bevel start + * bevelSegments: , // number of bevel layers + * + * extrudePath: // curve to extrude shape along + * + * UVGenerator: // object that provides UV generator functions + * + * } + */ - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); + class ExtrudeGeometry extends BufferGeometry { - } + constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) { - duration = morphTargetNames.length * ( fps || 1.0 ); + super(); - } else { + this.type = 'ExtrudeGeometry'; - // ...assume skeletal animation + this.parameters = { + shapes: shapes, + options: options + }; - const boneName = '.bones[' + bones[ h ].name + ']'; + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); + const scope = this; - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); + const verticesArray = []; + const uvArray = []; - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - } + const shape = shapes[ i ]; + addShape( shape ); } - if ( tracks.length === 0 ) { + // build geometry - return null; + this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); - } + this.computeVertexNormals(); - const clip = new AnimationClip( clipName, duration, tracks, blendMode ); + // functions - return clip; + function addShape( shape ) { - } + const placeholder = []; - } ); + // options - Object.assign( AnimationClip.prototype, { + const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + const steps = options.steps !== undefined ? options.steps : 1; + let depth = options.depth !== undefined ? options.depth : 1; - resetDuration: function () { + let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; + let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; + let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; + let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; + let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - const tracks = this.tracks; - let duration = 0; + const extrudePath = options.extrudePath; - for ( let i = 0, n = tracks.length; i !== n; ++ i ) { + const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - const track = this.tracks[ i ]; + // deprecated options - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); + if ( options.amount !== undefined ) { - } + console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); + depth = options.amount; - this.duration = duration; + } - return this; + // - }, + let extrudePts, extrudeByPath = false; + let splineTube, binormal, normal, position2; - trim: function () { + if ( extrudePath ) { - for ( let i = 0; i < this.tracks.length; i ++ ) { + extrudePts = extrudePath.getSpacedPoints( steps ); - this.tracks[ i ].trim( 0, this.duration ); + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion - } + // SETUP TNB variables - return this; + // TODO1 - have a .isClosed in spline? - }, + splineTube = extrudePath.computeFrenetFrames( steps, false ); - validate: function () { + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - let valid = true; + binormal = new Vector3(); + normal = new Vector3(); + position2 = new Vector3(); - for ( let i = 0; i < this.tracks.length; i ++ ) { + } - valid = valid && this.tracks[ i ].validate(); + // Safeguards if bevels are not enabled - } + if ( ! bevelEnabled ) { - return valid; + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + bevelOffset = 0; - }, + } - optimize: function () { + // Variables initialization - for ( let i = 0; i < this.tracks.length; i ++ ) { + const shapePoints = shape.extractPoints( curveSegments ); - this.tracks[ i ].optimize(); + let vertices = shapePoints.shape; + const holes = shapePoints.holes; - } + const reverse = ! ShapeUtils.isClockWise( vertices ); - return this; + if ( reverse ) { - }, + vertices = vertices.reverse(); - clone: function () { + // Maybe we should also check if holes are in the opposite direction, just to be safe ... - const tracks = []; + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - for ( let i = 0; i < this.tracks.length; i ++ ) { + const ahole = holes[ h ]; - tracks.push( this.tracks[ i ].clone() ); + if ( ShapeUtils.isClockWise( ahole ) ) { - } + holes[ h ] = ahole.reverse(); - return new AnimationClip( this.name, this.duration, tracks, this.blendMode ); + } - }, + } - toJSON: function () { + } - return AnimationClip.toJSON( this ); - } + const faces = ShapeUtils.triangulateShape( vertices, holes ); - } ); + /* Vertices */ - const Cache = { + const contour = vertices; // vertices has all points but contour has only points of circumference - enabled: false, + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - files: {}, + const ahole = holes[ h ]; - add: function ( key, file ) { + vertices = vertices.concat( ahole ); - if ( this.enabled === false ) return; + } - // console.log( 'THREE.Cache', 'Adding key:', key ); - this.files[ key ] = file; + function scalePt2( pt, vec, size ) { - }, + if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); - get: function ( key ) { + return vec.clone().multiplyScalar( size ).add( pt ); - if ( this.enabled === false ) return; + } - // console.log( 'THREE.Cache', 'Checking key:', key ); + const vlen = vertices.length, flen = faces.length; - return this.files[ key ]; - }, + // Find directions for point movement - remove: function ( key ) { - delete this.files[ key ]; + function getBevelVec( inPt, inPrev, inNext ) { - }, + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. - clear: function () { + let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - this.files = {}; + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html - } + const v_prev_x = inPt.x - inPrev.x, + v_prev_y = inPt.y - inPrev.y; + const v_next_x = inNext.x - inPt.x, + v_next_y = inNext.y - inPt.y; - }; + const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for collinear edges + const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - function LoadingManager( onLoad, onProgress, onError ) { + if ( Math.abs( collinear0 ) > Number.EPSILON ) { - const scope = this; + // not collinear - let isLoading = false; - let itemsLoaded = 0; - let itemsTotal = 0; - let urlModifier = undefined; - const handlers = []; + // length of vectors for normalizing - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor + const v_prev_len = Math.sqrt( v_prev_lensq ); + const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + // shift adjacent points by unit vectors to the left - this.itemStart = function ( url ) { + const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - itemsTotal ++; + const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - if ( isLoading === false ) { + // scaling factor for v_prev to intersection point - if ( scope.onStart !== undefined ) { + const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - scope.onStart( url, itemsLoaded, itemsTotal ); + // vector from inPt to intersection point - } + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - } + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { - isLoading = true; + return new Vector2( v_trans_x, v_trans_y ); - }; + } else { - this.itemEnd = function ( url ) { + shrink_by = Math.sqrt( v_trans_lensq / 2 ); - itemsLoaded ++; + } - if ( scope.onProgress !== undefined ) { + } else { - scope.onProgress( url, itemsLoaded, itemsTotal ); + // handle special case of collinear edges - } + let direction_eq = false; // assumes: opposite - if ( itemsLoaded === itemsTotal ) { + if ( v_prev_x > Number.EPSILON ) { - isLoading = false; + if ( v_next_x > Number.EPSILON ) { - if ( scope.onLoad !== undefined ) { + direction_eq = true; - scope.onLoad(); + } - } + } else { - } + if ( v_prev_x < - Number.EPSILON ) { - }; + if ( v_next_x < - Number.EPSILON ) { - this.itemError = function ( url ) { + direction_eq = true; - if ( scope.onError !== undefined ) { + } - scope.onError( url ); + } else { - } + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - }; + direction_eq = true; - this.resolveURL = function ( url ) { + } - if ( urlModifier ) { + } - return urlModifier( url ); + } - } + if ( direction_eq ) { - return url; + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); - }; + } else { - this.setURLModifier = function ( transform ) { + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); - urlModifier = transform; + } - return this; + } - }; + return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - this.addHandler = function ( regex, loader ) { + } - handlers.push( regex, loader ); - return this; + const contourMovements = []; - }; + for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - this.removeHandler = function ( regex ) { + if ( j === il ) j = 0; + if ( k === il ) k = 0; - const index = handlers.indexOf( regex ); + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) - if ( index !== - 1 ) { + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - handlers.splice( index, 2 ); + } - } + const holesMovements = []; + let oneHoleMovements, verticesMovements = contourMovements.concat(); - return this; + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - }; + const ahole = holes[ h ]; - this.getHandler = function ( file ) { + oneHoleMovements = []; - for ( let i = 0, l = handlers.length; i < l; i += 2 ) { + for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - const regex = handlers[ i ]; - const loader = handlers[ i + 1 ]; + if ( j === il ) j = 0; + if ( k === il ) k = 0; - if ( regex.global ) regex.lastIndex = 0; // see #17920 + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - if ( regex.test( file ) ) { + } - return loader; + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); } - } - return null; + // Loop bevelSegments, 1 for the front, 1 for the back - }; + for ( let b = 0; b < bevelSegments; b ++ ) { - } + //for ( b = bevelSegments; b > 0; b -- ) { - const DefaultLoadingManager = new LoadingManager(); + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - function Loader( manager ) { + // contract shape - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + for ( let i = 0, il = contour.length; i < il; i ++ ) { - this.crossOrigin = 'anonymous'; - this.withCredentials = false; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - } + v( vert.x, vert.y, - z ); - Object.assign( Loader.prototype, { + } - load: function ( /* url, onLoad, onProgress, onError */ ) {}, + // expand holes - loadAsync: function ( url, onProgress ) { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - const scope = this; + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - return new Promise( function ( resolve, reject ) { + for ( let i = 0, il = ahole.length; i < il; i ++ ) { - scope.load( url, resolve, onProgress, reject ); + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - } ); + v( vert.x, vert.y, - z ); - }, + } - parse: function ( /* data */ ) {}, + } - setCrossOrigin: function ( crossOrigin ) { + } - this.crossOrigin = crossOrigin; - return this; + const bs = bevelSize + bevelOffset; - }, + // Back facing vertices - setWithCredentials: function ( value ) { + for ( let i = 0; i < vlen; i ++ ) { - this.withCredentials = value; - return this; + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - }, + if ( ! extrudeByPath ) { - setPath: function ( path ) { + v( vert.x, vert.y, 0 ); - this.path = path; - return this; - - }, + } else { - setResourcePath: function ( resourcePath ) { + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - this.resourcePath = resourcePath; - return this; + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - }, + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - setRequestHeader: function ( requestHeader ) { + v( position2.x, position2.y, position2.z ); - this.requestHeader = requestHeader; - return this; + } - } + } - } ); + // Add stepped vertices... + // Including front facing vertices - const loading = {}; + for ( let s = 1; s <= steps; s ++ ) { - function FileLoader( manager ) { + for ( let i = 0; i < vlen; i ++ ) { - Loader.call( this, manager ); + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - } + if ( ! extrudeByPath ) { - FileLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + v( vert.x, vert.y, depth / steps * s ); - constructor: FileLoader, + } else { - load: function ( url, onLoad, onProgress, onError ) { + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - if ( url === undefined ) url = ''; + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - if ( this.path !== undefined ) url = this.path + url; + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - url = this.manager.resolveURL( url ); + v( position2.x, position2.y, position2.z ); - const scope = this; + } - const cached = Cache.get( url ); + } - if ( cached !== undefined ) { + } - scope.manager.itemStart( url ); - setTimeout( function () { + // Add bevel segments planes - if ( onLoad ) onLoad( cached ); + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( let b = bevelSegments - 1; b >= 0; b -- ) { - scope.manager.itemEnd( url ); + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - }, 0 ); + // contract shape - return cached; + for ( let i = 0, il = contour.length; i < il; i ++ ) { - } + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, depth + z ); - // Check if request is duplicate + } - if ( loading[ url ] !== undefined ) { + // expand holes - loading[ url ].push( { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - onLoad: onLoad, - onProgress: onProgress, - onError: onError + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - } ); + for ( let i = 0, il = ahole.length; i < il; i ++ ) { - return; + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - } + if ( ! extrudeByPath ) { - // Check for data: URI - const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - const dataUriRegexResult = url.match( dataUriRegex ); - let request; + v( vert.x, vert.y, depth + z ); - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { + } else { - const mimeType = dataUriRegexResult[ 1 ]; - const isBase64 = !! dataUriRegexResult[ 2 ]; + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - let data = dataUriRegexResult[ 3 ]; - data = decodeURIComponent( data ); + } - if ( isBase64 ) data = atob( data ); + } - try { + } - let response; - const responseType = ( this.responseType || '' ).toLowerCase(); + } - switch ( responseType ) { + /* Faces */ - case 'arraybuffer': - case 'blob': + // Top and bottom faces - const view = new Uint8Array( data.length ); + buildLidFaces(); - for ( let i = 0; i < data.length; i ++ ) { + // Sides faces - view[ i ] = data.charCodeAt( i ); + buildSideFaces(); - } - if ( responseType === 'blob' ) { + ///// Internal functions - response = new Blob( [ view.buffer ], { type: mimeType } ); + function buildLidFaces() { - } else { + const start = verticesArray.length / 3; - response = view.buffer; + if ( bevelEnabled ) { - } + let layer = 0; // steps + 1 + let offset = vlen * layer; - break; + // Bottom faces - case 'document': + for ( let i = 0; i < flen; i ++ ) { - const parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); + const face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - break; + } - case 'json': + layer = steps + bevelSegments * 2; + offset = vlen * layer; - response = JSON.parse( data ); + // Top faces - break; + for ( let i = 0; i < flen; i ++ ) { - default: // 'text' or other + const face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - response = data; + } - break; + } else { - } + // Bottom faces - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + for ( let i = 0; i < flen; i ++ ) { - if ( onLoad ) onLoad( response ); + const face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - scope.manager.itemEnd( url ); + } - }, 0 ); + // Top faces - } catch ( error ) { + for ( let i = 0; i < flen; i ++ ) { - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + const face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - if ( onError ) onError( error ); + } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } - }, 0 ); + scope.addGroup( start, verticesArray.length / 3 - start, 0 ); } - } else { - - // Initialise array for duplicate requests - - loading[ url ] = []; - - loading[ url ].push( { + // Create faces for the z-sides of the shape - onLoad: onLoad, - onProgress: onProgress, - onError: onError + function buildSideFaces() { - } ); + const start = verticesArray.length / 3; + let layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; - request = new XMLHttpRequest(); + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - request.open( 'GET', url, true ); + const ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); - request.addEventListener( 'load', function ( event ) { + //, true + layeroffset += ahole.length; - const response = this.response; + } - const callbacks = loading[ url ]; - delete loading[ url ]; + scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - if ( this.status === 200 || this.status === 0 ) { - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. + } - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); + function sidewalls( contour, layeroffset ) { - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); + let i = contour.length; - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + while ( -- i >= 0 ) { - const callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); + const j = i; + let k = i - 1; + if ( k < 0 ) k = contour.length - 1; - } + //console.log('b', i,j, i-1, k,vertices.length); - scope.manager.itemEnd( url ); + for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { - } else { + const slen1 = vlen * s; + const slen2 = vlen * ( s + 1 ); - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + const a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + f4( a, b, c, d ); } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - } - }, false ); + } - request.addEventListener( 'progress', function ( event ) { + function v( x, y, z ) { - const callbacks = loading[ url ]; + placeholder.push( x ); + placeholder.push( y ); + placeholder.push( z ); - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + } - const callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); - } + function f3( a, b, c ) { - }, false ); + addVertex( a ); + addVertex( b ); + addVertex( c ); - request.addEventListener( 'error', function ( event ) { + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - const callbacks = loading[ url ]; + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); - delete loading[ url ]; + } - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + function f4( a, b, c, d ) { - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + addVertex( a ); + addVertex( b ); + addVertex( d ); - } + addVertex( b ); + addVertex( c ); + addVertex( d ); - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - }, false ); + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - request.addEventListener( 'abort', function ( event ) { + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 3 ] ); - const callbacks = loading[ url ]; + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); + addUV( uvs[ 3 ] ); - delete loading[ url ]; + } - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + function addVertex( index ) { - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + verticesArray.push( placeholder[ index * 3 + 0 ] ); + verticesArray.push( placeholder[ index * 3 + 1 ] ); + verticesArray.push( placeholder[ index * 3 + 2 ] ); - } + } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - }, false ); + function addUV( vector2 ) { - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + uvArray.push( vector2.x ); + uvArray.push( vector2.y ); - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); + } - for ( const header in this.requestHeader ) { + } - request.setRequestHeader( header, this.requestHeader[ header ] ); + } - } + toJSON() { - request.send( null ); + const data = super.toJSON(); - } + const shapes = this.parameters.shapes; + const options = this.parameters.options; - scope.manager.itemStart( url ); + return toJSON$1( shapes, options, data ); - return request; + } - }, + static fromJSON( data, shapes ) { - setResponseType: function ( value ) { + const geometryShapes = []; - this.responseType = value; - return this; + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - }, + const shape = shapes[ data.shapes[ j ] ]; - setMimeType: function ( value ) { + geometryShapes.push( shape ); - this.mimeType = value; - return this; + } - } + const extrudePath = data.options.extrudePath; - } ); + if ( extrudePath !== undefined ) { - function AnimationLoader( manager ) { + data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - Loader.call( this, manager ); + } - } + return new ExtrudeGeometry( geometryShapes, data.options ); - AnimationLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + } - constructor: AnimationLoader, + } - load: function ( url, onLoad, onProgress, onError ) { + const WorldUVGenerator = { - const scope = this; + generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); - loader.load( url, function ( text ) { + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; - try { + return [ + new Vector2( a_x, a_y ), + new Vector2( b_x, b_y ), + new Vector2( c_x, c_y ) + ]; - onLoad( scope.parse( JSON.parse( text ) ) ); + }, - } catch ( e ) { + generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - if ( onError ) { + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const a_z = vertices[ indexA * 3 + 2 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const b_z = vertices[ indexB * 3 + 2 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; + const c_z = vertices[ indexC * 3 + 2 ]; + const d_x = vertices[ indexD * 3 ]; + const d_y = vertices[ indexD * 3 + 1 ]; + const d_z = vertices[ indexD * 3 + 2 ]; - onError( e ); + if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { - } else { + return [ + new Vector2( a_x, 1 - a_z ), + new Vector2( b_x, 1 - b_z ), + new Vector2( c_x, 1 - c_z ), + new Vector2( d_x, 1 - d_z ) + ]; - console.error( e ); + } else { - } + return [ + new Vector2( a_y, 1 - a_z ), + new Vector2( b_y, 1 - b_z ), + new Vector2( c_y, 1 - c_z ), + new Vector2( d_y, 1 - d_z ) + ]; - scope.manager.itemError( url ); + } - } + } - }, onProgress, onError ); + }; - }, + function toJSON$1( shapes, options, data ) { - parse: function ( json ) { + data.shapes = []; - const animations = []; + if ( Array.isArray( shapes ) ) { - for ( let i = 0; i < json.length; i ++ ) { + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - const clip = AnimationClip.parse( json[ i ] ); + const shape = shapes[ i ]; - animations.push( clip ); + data.shapes.push( shape.uuid ); } - return animations; + } else { + + data.shapes.push( shapes.uuid ); } - } ); + if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); - /** - * Abstract Base class to block based textures loader (dds, pvr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ + return data; - function CompressedTextureLoader( manager ) { + } - Loader.call( this, manager ); + class ShapeGeometry extends BufferGeometry { - } + constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) { - CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + super(); + this.type = 'ShapeGeometry'; - constructor: CompressedTextureLoader, + this.parameters = { + shapes: shapes, + curveSegments: curveSegments + }; - load: function ( url, onLoad, onProgress, onError ) { + // buffers - const scope = this; + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - const images = []; + // helper variables - const texture = new CompressedTexture(); + let groupStart = 0; + let groupCount = 0; - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); + // allow single and array values for "shapes" parameter - let loaded = 0; + if ( Array.isArray( shapes ) === false ) { - function loadTexture( i ) { + addShape( shapes ); - loader.load( url[ i ], function ( buffer ) { + } else { - const texDatas = scope.parse( buffer, true ); + for ( let i = 0; i < shapes.length; i ++ ) { - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; + addShape( shapes[ i ] ); - loaded += 1; + this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - if ( loaded === 6 ) { + groupStart += groupCount; + groupCount = 0; - if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; + } - texture.image = images; - texture.format = texDatas.format; - texture.needsUpdate = true; + } - if ( onLoad ) onLoad( texture ); + // build geometry - } + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - }, onProgress, onError ); - } + // helper functions + + function addShape( shape ) { + + const indexOffset = vertices.length / 3; + const points = shape.extractPoints( curveSegments ); - if ( Array.isArray( url ) ) { + let shapeVertices = points.shape; + const shapeHoles = points.holes; + + // check direction of vertices - for ( let i = 0, il = url.length; i < il; ++ i ) { + if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - loadTexture( i ); + shapeVertices = shapeVertices.reverse(); } - } else { + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - // compressed cubemap texture stored in a single DDS file + const shapeHole = shapeHoles[ i ]; - loader.load( url, function ( buffer ) { + if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - const texDatas = scope.parse( buffer, true ); + shapeHoles[ i ] = shapeHole.reverse(); - if ( texDatas.isCubemap ) { + } - const faces = texDatas.mipmaps.length / texDatas.mipmapCount; + } - for ( let f = 0; f < faces; f ++ ) { + const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - images[ f ] = { mipmaps: [] }; + // join vertices of inner and outer paths to a single array - for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; + const shapeHole = shapeHoles[ i ]; + shapeVertices = shapeVertices.concat( shapeHole ); - } + } - } + // vertices, normals, uvs - texture.image = images; + for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - } else { + const vertex = shapeVertices[ i ]; - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; + vertices.push( vertex.x, vertex.y, 0 ); + normals.push( 0, 0, 1 ); + uvs.push( vertex.x, vertex.y ); // world uvs - } + } - if ( texDatas.mipmapCount === 1 ) { + // incides - texture.minFilter = LinearFilter; + for ( let i = 0, l = faces.length; i < l; i ++ ) { - } + const face = faces[ i ]; - texture.format = texDatas.format; - texture.needsUpdate = true; + const a = face[ 0 ] + indexOffset; + const b = face[ 1 ] + indexOffset; + const c = face[ 2 ] + indexOffset; - if ( onLoad ) onLoad( texture ); + indices.push( a, b, c ); + groupCount += 3; - }, onProgress, onError ); + } } - return texture; - } - } ); + toJSON() { - function ImageLoader( manager ) { + const data = super.toJSON(); - Loader.call( this, manager ); + const shapes = this.parameters.shapes; - } + return toJSON( shapes, data ); - ImageLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + } - constructor: ImageLoader, + static fromJSON( data, shapes ) { - load: function ( url, onLoad, onProgress, onError ) { + const geometryShapes = []; - if ( this.path !== undefined ) url = this.path + url; + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - url = this.manager.resolveURL( url ); + const shape = shapes[ data.shapes[ j ] ]; - const scope = this; + geometryShapes.push( shape ); - const cached = Cache.get( url ); + } - if ( cached !== undefined ) { + return new ShapeGeometry( geometryShapes, data.curveSegments ); - scope.manager.itemStart( url ); + } - setTimeout( function () { + } - if ( onLoad ) onLoad( cached ); + function toJSON( shapes, data ) { - scope.manager.itemEnd( url ); + data.shapes = []; - }, 0 ); + if ( Array.isArray( shapes ) ) { - return cached; + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - } + const shape = shapes[ i ]; - const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + data.shapes.push( shape.uuid ); - function onImageLoad() { + } - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); + } else { - Cache.add( url, this ); + data.shapes.push( shapes.uuid ); - if ( onLoad ) onLoad( this ); + } - scope.manager.itemEnd( url ); + return data; - } + } - function onImageError( event ) { + class SphereGeometry extends BufferGeometry { - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); + constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { - if ( onError ) onError( event ); + super(); + this.type = 'SphereGeometry'; - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - } + widthSegments = Math.max( 3, Math.floor( widthSegments ) ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) ); - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); + const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); - if ( url.substr( 0, 5 ) !== 'data:' ) { + let index = 0; + const grid = []; - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + const vertex = new Vector3(); + const normal = new Vector3(); - } + // buffers - scope.manager.itemStart( url ); + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - image.src = url; + // generate vertices, normals and uvs - return image; + for ( let iy = 0; iy <= heightSegments; iy ++ ) { - } + const verticesRow = []; - } ); + const v = iy / heightSegments; - function CubeTextureLoader( manager ) { + // special case for the poles - Loader.call( this, manager ); + let uOffset = 0; - } + if ( iy == 0 && thetaStart == 0 ) { - CubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + uOffset = 0.5 / widthSegments; - constructor: CubeTextureLoader, + } else if ( iy == heightSegments && thetaEnd == Math.PI ) { - load: function ( urls, onLoad, onProgress, onError ) { + uOffset = - 0.5 / widthSegments; - const texture = new CubeTexture(); + } - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + for ( let ix = 0; ix <= widthSegments; ix ++ ) { - let loaded = 0; + const u = ix / widthSegments; - function loadTexture( i ) { + // vertex - loader.load( urls[ i ], function ( image ) { + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - texture.images[ i ] = image; + vertices.push( vertex.x, vertex.y, vertex.z ); - loaded ++; + // normal - if ( loaded === 6 ) { + normal.copy( vertex ).normalize(); + normals.push( normal.x, normal.y, normal.z ); - texture.needsUpdate = true; + // uv - if ( onLoad ) onLoad( texture ); + uvs.push( u + uOffset, 1 - v ); - } + verticesRow.push( index ++ ); - }, undefined, onError ); + } + + grid.push( verticesRow ); } - for ( let i = 0; i < urls.length; ++ i ) { + // indices - loadTexture( i ); + for ( let iy = 0; iy < heightSegments; iy ++ ) { - } + for ( let ix = 0; ix < widthSegments; ix ++ ) { - return texture; + const a = grid[ iy ][ ix + 1 ]; + const b = grid[ iy ][ ix ]; + const c = grid[ iy + 1 ][ ix ]; + const d = grid[ iy + 1 ][ ix + 1 ]; - } + if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); + if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); - } ); + } - /** - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ + } + + // build geometry - function DataTextureLoader( manager ) { + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - Loader.call( this, manager ); + } - } + static fromJSON( data ) { - DataTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); - constructor: DataTextureLoader, + } - load: function ( url, onLoad, onProgress, onError ) { + } - const scope = this; + /** + * parameters = { + * color: + * } + */ - const texture = new DataTexture(); + class ShadowMaterial extends Material { - const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setPath( this.path ); - loader.setWithCredentials( scope.withCredentials ); - loader.load( url, function ( buffer ) { + constructor( parameters ) { - const texData = scope.parse( buffer ); + super(); - if ( ! texData ) return; + this.type = 'ShadowMaterial'; - if ( texData.image !== undefined ) { + this.color = new Color( 0x000000 ); + this.transparent = true; - texture.image = texData.image; + this.setValues( parameters ); - } else if ( texData.data !== undefined ) { + } - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; + copy( source ) { - } + super.copy( source ); - texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; + this.color.copy( source.color ); - texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; - texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; + return this; - texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; + } - if ( texData.encoding !== undefined ) { + } - texture.encoding = texData.encoding; + ShadowMaterial.prototype.isShadowMaterial = true; - } + /** + * parameters = { + * color: , + * roughness: , + * metalness: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * roughnessMap: new THREE.Texture( ), + * + * metalnessMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * envMapIntensity: + * + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * flatShading: + * } + */ - if ( texData.flipY !== undefined ) { + class MeshStandardMaterial extends Material { - texture.flipY = texData.flipY; + constructor( parameters ) { - } + super(); - if ( texData.format !== undefined ) { + this.defines = { 'STANDARD': '' }; - texture.format = texData.format; + this.type = 'MeshStandardMaterial'; - } + this.color = new Color( 0xffffff ); // diffuse + this.roughness = 1.0; + this.metalness = 0.0; - if ( texData.type !== undefined ) { + this.map = null; - texture.type = texData.type; + this.lightMap = null; + this.lightMapIntensity = 1.0; - } + this.aoMap = null; + this.aoMapIntensity = 1.0; - if ( texData.mipmaps !== undefined ) { + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - texture.mipmaps = texData.mipmaps; - texture.minFilter = LinearMipmapLinearFilter; // presumably... + this.bumpMap = null; + this.bumpScale = 1; - } + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - if ( texData.mipmapCount === 1 ) { + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - texture.minFilter = LinearFilter; + this.roughnessMap = null; - } + this.metalnessMap = null; - texture.needsUpdate = true; + this.alphaMap = null; - if ( onLoad ) onLoad( texture, texData ); + this.envMap = null; + this.envMapIntensity = 1.0; - }, onProgress, onError ); + this.refractionRatio = 0.98; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - return texture; + this.flatShading = false; + + this.setValues( parameters ); } - } ); + copy( source ) { - function TextureLoader( manager ) { + super.copy( source ); - Loader.call( this, manager ); + this.defines = { 'STANDARD': '' }; - } + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; - TextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + this.map = source.map; - constructor: TextureLoader, + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - load: function ( url, onLoad, onProgress, onError ) { + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - const texture = new Texture(); + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - loader.load( url, function ( image ) { + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - texture.image = image; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; + this.roughnessMap = source.roughnessMap; - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; + this.metalnessMap = source.metalnessMap; - if ( onLoad !== undefined ) { + this.alphaMap = source.alphaMap; - onLoad( texture ); + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; - } + this.refractionRatio = source.refractionRatio; - }, onProgress, onError ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - return texture; + this.flatShading = source.flatShading; + + return this; } - } ); + } + + MeshStandardMaterial.prototype.isMeshStandardMaterial = true; /** - * Extensible curve object. - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() + * parameters = { + * clearcoat: , + * clearcoatMap: new THREE.Texture( ), + * clearcoatRoughness: , + * clearcoatRoughnessMap: new THREE.Texture( ), + * clearcoatNormalScale: , + * clearcoatNormalMap: new THREE.Texture( ), * - * This following curves inherit from THREE.Curve: + * ior: , + * reflectivity: , * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve + * sheen: , + * sheenColor: , + * sheenColorMap: new THREE.Texture( ), + * sheenRoughness: , + * sheenRoughnessMap: new THREE.Texture( ), * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 + * transmission: , + * transmissionMap: new THREE.Texture( ), * - * A series of curves can be represented as a THREE.CurvePath. + * thickness: , + * thicknessMap: new THREE.Texture( ), + * attenuationDistance: , + * attenuationColor: , * - **/ + * specularIntensity: , + * specularIntensityMap: new THREE.Texture( ), + * specularColor: , + * specularColorMap: new THREE.Texture( ) + * } + */ - function Curve() { + class MeshPhysicalMaterial extends MeshStandardMaterial { - this.type = 'Curve'; + constructor( parameters ) { - this.arcLengthDivisions = 200; + super(); - } + this.defines = { - Object.assign( Curve.prototype, { + 'STANDARD': '', + 'PHYSICAL': '' - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] + }; - getPoint: function ( /* t, optionalTarget */ ) { + this.type = 'MeshPhysicalMaterial'; - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; + this.clearcoatMap = null; + this.clearcoatRoughness = 0.0; + this.clearcoatRoughnessMap = null; + this.clearcoatNormalScale = new Vector2( 1, 1 ); + this.clearcoatNormalMap = null; - }, + this.ior = 1.5; - // Get point at relative position in curve according to arc length - // - u [0 .. 1] + Object.defineProperty( this, 'reflectivity', { + get: function () { - getPointAt: function ( u, optionalTarget ) { + return ( clamp$1( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); - const t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); + }, + set: function ( reflectivity ) { - }, + this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); - // Get sequence of points using getPoint( t ) + } + } ); - getPoints: function ( divisions = 5 ) { + this.sheenColor = new Color( 0x000000 ); + this.sheenColorMap = null; + this.sheenRoughness = 1.0; + this.sheenRoughnessMap = null; - const points = []; + this.transmissionMap = null; - for ( let d = 0; d <= divisions; d ++ ) { + this.thickness = 0.01; + this.thicknessMap = null; + this.attenuationDistance = 0.0; + this.attenuationColor = new Color( 1, 1, 1 ); - points.push( this.getPoint( d / divisions ) ); + this.specularIntensity = 1.0; + this.specularIntensityMap = null; + this.specularColor = new Color( 1, 1, 1 ); + this.specularColorMap = null; - } + this._sheen = 0.0; + this._clearcoat = 0; + this._transmission = 0; - return points; + this.setValues( parameters ); - }, + } - // Get sequence of points using getPointAt( u ) + get sheen() { - getSpacedPoints: function ( divisions = 5 ) { + return this._sheen; - const points = []; + } - for ( let d = 0; d <= divisions; d ++ ) { + set sheen( value ) { - points.push( this.getPointAt( d / divisions ) ); + if ( this._sheen > 0 !== value > 0 ) { + + this.version ++; } - return points; + this._sheen = value; - }, + } - // Get total curve arc length + get clearcoat() { - getLength: function () { + return this._clearcoat; - const lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; + } - }, + set clearcoat( value ) { - // Get list of cumulative segment lengths + if ( this._clearcoat > 0 !== value > 0 ) { - getLengths: function ( divisions ) { + this.version ++; - if ( divisions === undefined ) divisions = this.arcLengthDivisions; + } - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { + this._clearcoat = value; - return this.cacheArcLengths; + } - } + get transmission() { - this.needsUpdate = false; + return this._transmission; - const cache = []; - let current, last = this.getPoint( 0 ); - let sum = 0; + } - cache.push( 0 ); + set transmission( value ) { - for ( let p = 1; p <= divisions; p ++ ) { + if ( this._transmission > 0 !== value > 0 ) { - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + this.version ++; } - this.cacheArcLengths = cache; + this._transmission = value; - return cache; // { sums: cache, sum: sum }; Sum is in the last element. + } - }, + copy( source ) { - updateArcLengths: function () { + super.copy( source ); - this.needsUpdate = true; - this.getLengths(); + this.defines = { - }, + 'STANDARD': '', + 'PHYSICAL': '' - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + }; - getUtoTmapping: function ( u, distance ) { + this.clearcoat = source.clearcoat; + this.clearcoatMap = source.clearcoatMap; + this.clearcoatRoughness = source.clearcoatRoughness; + this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; + this.clearcoatNormalMap = source.clearcoatNormalMap; + this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - const arcLengths = this.getLengths(); + this.ior = source.ior; - let i = 0; - const il = arcLengths.length; + this.sheen = source.sheen; + this.sheenColor.copy( source.sheenColor ); + this.sheenColorMap = source.sheenColorMap; + this.sheenRoughness = source.sheenRoughness; + this.sheenRoughnessMap = source.sheenRoughnessMap; - let targetArcLength; // The targeted u distance value to get + this.transmission = source.transmission; + this.transmissionMap = source.transmissionMap; - if ( distance ) { + this.thickness = source.thickness; + this.thicknessMap = source.thicknessMap; + this.attenuationDistance = source.attenuationDistance; + this.attenuationColor.copy( source.attenuationColor ); - targetArcLength = distance; + this.specularIntensity = source.specularIntensity; + this.specularIntensityMap = source.specularIntensityMap; + this.specularColor.copy( source.specularColor ); + this.specularColorMap = source.specularColorMap; - } else { + return this; - targetArcLength = u * arcLengths[ il - 1 ]; + } - } + } - // binary search for the index with largest value smaller than target u distance + MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - let low = 0, high = il - 1, comparison; + /** + * parameters = { + * color: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.MultiplyOperation, + * reflectivity: , + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * flatShading: + * } + */ - while ( low <= high ) { + class MeshPhongMaterial extends Material { - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + constructor( parameters ) { - comparison = arcLengths[ i ] - targetArcLength; + super(); - if ( comparison < 0 ) { + this.type = 'MeshPhongMaterial'; - low = i + 1; + this.color = new Color( 0xffffff ); // diffuse + this.specular = new Color( 0x111111 ); + this.shininess = 30; - } else if ( comparison > 0 ) { + this.map = null; - high = i - 1; + this.lightMap = null; + this.lightMapIntensity = 1.0; - } else { + this.aoMap = null; + this.aoMapIntensity = 1.0; - high = i; - break; + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - // DONE + this.bumpMap = null; + this.bumpScale = 1; - } + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - i = high; + this.specularMap = null; - if ( arcLengths[ i ] === targetArcLength ) { + this.alphaMap = null; - return i / ( il - 1 ); + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - } + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - // we could get finer grain at lengths, or use simple interpolation between two points + this.flatShading = false; - const lengthBefore = arcLengths[ i ]; - const lengthAfter = arcLengths[ i + 1 ]; + this.setValues( parameters ); - const segmentLength = lengthAfter - lengthBefore; + } - // determine where we are between the 'before' and 'after' points + copy( source ) { - const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + super.copy( source ); - // add that fractional amount to t + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - const t = ( i + segmentFraction ) / ( il - 1 ); + this.map = source.map; - return t; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - }, + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - getTangent: function ( t, optionalTarget ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - const delta = 0.0001; - let t1 = t - delta; - let t2 = t + delta; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - // Capping in case of danger + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; + this.specularMap = source.specularMap; - const pt1 = this.getPoint( t1 ); - const pt2 = this.getPoint( t2 ); + this.alphaMap = source.alphaMap; - const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - tangent.copy( pt2 ).sub( pt1 ).normalize(); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - return tangent; + this.flatShading = source.flatShading; - }, + return this; - getTangentAt: function ( u, optionalTarget ) { + } - const t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); + } - }, + MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - computeFrenetFrames: function ( segments, closed ) { + /** + * parameters = { + * color: , + * + * map: new THREE.Texture( ), + * gradientMap: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * alphaMap: new THREE.Texture( ), + * + * wireframe: , + * wireframeLinewidth: , + * + * } + */ - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf + class MeshToonMaterial extends Material { - const normal = new Vector3(); + constructor( parameters ) { - const tangents = []; - const normals = []; - const binormals = []; + super(); - const vec = new Vector3(); - const mat = new Matrix4(); + this.defines = { 'TOON': '' }; - // compute the tangent vectors for each segment on the curve + this.type = 'MeshToonMaterial'; - for ( let i = 0; i <= segments; i ++ ) { + this.color = new Color( 0xffffff ); - const u = i / segments; + this.map = null; + this.gradientMap = null; - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); + this.lightMap = null; + this.lightMapIntensity = 1.0; - } + this.aoMap = null; + this.aoMapIntensity = 1.0; - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - let min = Number.MAX_VALUE; - const tx = Math.abs( tangents[ 0 ].x ); - const ty = Math.abs( tangents[ 0 ].y ); - const tz = Math.abs( tangents[ 0 ].z ); + this.bumpMap = null; + this.bumpScale = 1; - if ( tx <= min ) { + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - min = tx; - normal.set( 1, 0, 0 ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } + this.alphaMap = null; - if ( ty <= min ) { + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - min = ty; - normal.set( 0, 1, 0 ); + this.setValues( parameters ); - } + } - if ( tz <= min ) { + copy( source ) { - normal.set( 0, 0, 1 ); + super.copy( source ); - } + this.color.copy( source.color ); - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + this.map = source.map; + this.gradientMap = source.gradientMap; - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - // compute the slowly-varying normal and binormal vectors for each segment on the curve + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - for ( let i = 1; i <= segments; i ++ ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - normals[ i ] = normals[ i - 1 ].clone(); + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - binormals[ i ] = binormals[ i - 1 ].clone(); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + this.alphaMap = source.alphaMap; - if ( vec.length() > Number.EPSILON ) { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - vec.normalize(); + return this; - const theta = Math.acos( MathUtils.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + } - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + } - } + MeshToonMaterial.prototype.isMeshToonMaterial = true; - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + /** + * parameters = { + * opacity: , + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * wireframe: , + * wireframeLinewidth: + * + * flatShading: + * } + */ - } + class MeshNormalMaterial extends Material { - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + constructor( parameters ) { - if ( closed === true ) { + super(); - let theta = Math.acos( MathUtils.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; + this.type = 'MeshNormalMaterial'; - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { + this.bumpMap = null; + this.bumpScale = 1; - theta = - theta; + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - for ( let i = 1; i <= segments; i ++ ) { + this.wireframe = false; + this.wireframeLinewidth = 1; - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + this.fog = false; - } + this.flatShading = false; - } + this.setValues( parameters ); - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; + } - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.arcLengthDivisions = source.arcLengthDivisions; - - return this; - - }, - - toJSON: function () { + copy( source ) { - const data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; + super.copy( source ); - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - return data; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - }, + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - fromJSON: function ( json ) { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - this.arcLengthDivisions = json.arcLengthDivisions; + this.flatShading = source.flatShading; return this; } - } ); - - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - Curve.call( this ); - - this.type = 'EllipseCurve'; - - this.aX = aX || 0; - this.aY = aY || 0; - - this.xRadius = xRadius || 1; - this.yRadius = yRadius || 1; + } - this.aStartAngle = aStartAngle || 0; - this.aEndAngle = aEndAngle || 2 * Math.PI; + MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - this.aClockwise = aClockwise || false; + /** + * parameters = { + * color: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * } + */ - this.aRotation = aRotation || 0; + class MeshLambertMaterial extends Material { - } + constructor( parameters ) { - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; + super(); - EllipseCurve.prototype.isEllipseCurve = true; + this.type = 'MeshLambertMaterial'; - EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { + this.color = new Color( 0xffffff ); // diffuse - const point = optionalTarget || new Vector2(); + this.map = null; - const twoPi = Math.PI * 2; - let deltaAngle = this.aEndAngle - this.aStartAngle; - const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; + this.lightMap = null; + this.lightMapIntensity = 1.0; - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; + this.aoMap = null; + this.aoMapIntensity = 1.0; - if ( deltaAngle < Number.EPSILON ) { + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - if ( samePoints ) { + this.specularMap = null; - deltaAngle = 0; + this.alphaMap = null; - } else { + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - deltaAngle = twoPi; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - } + this.setValues( parameters ); } - if ( this.aClockwise === true && ! samePoints ) { + copy( source ) { - if ( deltaAngle === twoPi ) { + super.copy( source ); - deltaAngle = - twoPi; + this.color.copy( source.color ); - } else { + this.map = source.map; - deltaAngle = deltaAngle - twoPi; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - } + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - } + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - const angle = this.aStartAngle + t * deltaAngle; - let x = this.aX + this.xRadius * Math.cos( angle ); - let y = this.aY + this.yRadius * Math.sin( angle ); + this.specularMap = source.specularMap; - if ( this.aRotation !== 0 ) { + this.alphaMap = source.alphaMap; - const cos = Math.cos( this.aRotation ); - const sin = Math.sin( this.aRotation ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - const tx = x - this.aX; - const ty = y - this.aY; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; + return this; } - return point.set( x, y ); - - }; + } - EllipseCurve.prototype.copy = function ( source ) { + MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - Curve.prototype.copy.call( this, source ); + /** + * parameters = { + * color: , + * opacity: , + * + * matcap: new THREE.Texture( ), + * + * map: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * alphaMap: new THREE.Texture( ), + * + * flatShading: + * } + */ - this.aX = source.aX; - this.aY = source.aY; + class MeshMatcapMaterial extends Material { - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; + constructor( parameters ) { - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; + super(); - this.aClockwise = source.aClockwise; + this.defines = { 'MATCAP': '' }; - this.aRotation = source.aRotation; + this.type = 'MeshMatcapMaterial'; - return this; + this.color = new Color( 0xffffff ); // diffuse - }; + this.matcap = null; + this.map = null; - EllipseCurve.prototype.toJSON = function () { + this.bumpMap = null; + this.bumpScale = 1; - const data = Curve.prototype.toJSON.call( this ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - data.aX = this.aX; - data.aY = this.aY; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; + this.alphaMap = null; - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; + this.flatShading = false; - data.aClockwise = this.aClockwise; + this.setValues( parameters ); - data.aRotation = this.aRotation; + } - return data; - }; + copy( source ) { - EllipseCurve.prototype.fromJSON = function ( json ) { + super.copy( source ); - Curve.prototype.fromJSON.call( this, json ); + this.defines = { 'MATCAP': '' }; - this.aX = json.aX; - this.aY = json.aY; + this.color.copy( source.color ); - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; + this.matcap = source.matcap; - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; + this.map = source.map; - this.aClockwise = json.aClockwise; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - this.aRotation = json.aRotation; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - return this; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - }; + this.alphaMap = source.alphaMap; - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.flatShading = source.flatShading; - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + return this; - this.type = 'ArcCurve'; + } } - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; - - ArcCurve.prototype.isArcCurve = true; + MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; /** - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * parameters = { + * color: , + * opacity: , * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: + * } */ + class LineDashedMaterial extends LineBasicMaterial { - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM - - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ + constructor( parameters ) { - function CubicPoly() { + super(); - let c0 = 0, c1 = 0, c2 = 0, c3 = 0; + this.type = 'LineDashedMaterial'; - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; + this.setValues( parameters ); } - return { + copy( source ) { - initCatmullRom: function ( x0, x1, x2, x3, tension ) { + super.copy( source ); - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; - }, + return this; - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { + } - // compute tangents when parameterized in [t1,t2] - let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + } - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; + LineDashedMaterial.prototype.isLineDashedMaterial = true; - init( x1, x2, t1, t2 ); + const AnimationUtils = { - }, + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function ( array, from, to ) { - calc: function ( t ) { + if ( AnimationUtils.isTypedArray( array ) ) { - const t2 = t * t; - const t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; + // in ios9 array.subarray(from, undefined) will return empty array + // but array.subarray(from) or array.subarray(from, len) is correct + return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); } - }; + return array.slice( from, to ); - } + }, - // + // converts an array to a specific type + convertArray: function ( array, type, forceClone ) { - const tmp = new Vector3(); - const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; - function CatmullRomCurve3( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - Curve.call( this ); + return new type( array ); // create typed array - this.type = 'CatmullRomCurve3'; + } - this.points = points; - this.closed = closed; - this.curveType = curveType; - this.tension = tension; + return Array.prototype.slice.call( array ); // create Array - } + }, - CatmullRomCurve3.prototype = Object.create( Curve.prototype ); - CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; + isTypedArray: function ( object ) { - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); - CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) { + }, - const point = optionalTarget; + // returns an array by which times and values can be sorted + getKeyframeOrder: function ( times ) { - const points = this.points; - const l = points.length; + function compareTime( i, j ) { - const p = ( l - ( this.closed ? 0 : 1 ) ) * t; - let intPoint = Math.floor( p ); - let weight = p - intPoint; + return times[ i ] - times[ j ]; - if ( this.closed ) { + } - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; + const n = times.length; + const result = new Array( n ); + for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - } else if ( weight === 0 && intPoint === l - 1 ) { + result.sort( compareTime ); - intPoint = l - 2; - weight = 1; + return result; - } + }, - let p0, p3; // 4 points (p1 & p2 defined below) + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function ( values, stride, order ) { - if ( this.closed || intPoint > 0 ) { + const nValues = values.length; + const result = new values.constructor( nValues ); - p0 = points[ ( intPoint - 1 ) % l ]; + for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - } else { + const srcOffset = order[ i ] * stride; - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; + for ( let j = 0; j !== stride; ++ j ) { - } + result[ dstOffset ++ ] = values[ srcOffset + j ]; - const p1 = points[ intPoint % l ]; - const p2 = points[ ( intPoint + 1 ) % l ]; + } - if ( this.closed || intPoint + 2 < l ) { + } - p3 = points[ ( intPoint + 2 ) % l ]; + return result; - } else { + }, - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; + // function for parsing AOS keyframe formats + flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - } + let i = 1, key = jsonKeys[ 0 ]; - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - // init Centripetal / Chordal Catmull-Rom - const pow = this.curveType === 'chordal' ? 0.5 : 0.25; - let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + key = jsonKeys[ i ++ ]; - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; + } - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + if ( key === undefined ) return; // no data - } else if ( this.curveType === 'catmullrom' ) { + let value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); + if ( Array.isArray( value ) ) { - } + do { - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); + value = key[ valuePropertyName ]; - return point; + if ( value !== undefined ) { - }; + times.push( key.time ); + values.push.apply( values, value ); // push all elements - CatmullRomCurve3.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + key = jsonKeys[ i ++ ]; - this.points = []; + } while ( key !== undefined ); - for ( let i = 0, l = source.points.length; i < l; i ++ ) { + } else if ( value.toArray !== undefined ) { - const point = source.points[ i ]; + // ...assume THREE.Math-ish - this.points.push( point.clone() ); + do { - } + value = key[ valuePropertyName ]; - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; + if ( value !== undefined ) { - return this; + times.push( key.time ); + value.toArray( values, values.length ); - }; + } - CatmullRomCurve3.prototype.toJSON = function () { + key = jsonKeys[ i ++ ]; - const data = Curve.prototype.toJSON.call( this ); + } while ( key !== undefined ); - data.points = []; + } else { - for ( let i = 0, l = this.points.length; i < l; i ++ ) { + // otherwise push as-is - const point = this.points[ i ]; - data.points.push( point.toArray() ); + do { - } + value = key[ valuePropertyName ]; - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; + if ( value !== undefined ) { - return data; + times.push( key.time ); + values.push( value ); - }; + } - CatmullRomCurve3.prototype.fromJSON = function ( json ) { + key = jsonKeys[ i ++ ]; - Curve.prototype.fromJSON.call( this, json ); + } while ( key !== undefined ); - this.points = []; + } - for ( let i = 0, l = json.points.length; i < l; i ++ ) { + }, - const point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); + subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { - } + const clip = sourceClip.clone(); - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; + clip.name = name; - return this; + const tracks = []; - }; + for ( let i = 0; i < clip.tracks.length; ++ i ) { - /** - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ + const track = clip.tracks[ i ]; + const valueSize = track.getValueSize(); - function CatmullRom( t, p0, p1, p2, p3 ) { + const times = []; + const values = []; - const v0 = ( p2 - p0 ) * 0.5; - const v1 = ( p3 - p1 ) * 0.5; - const t2 = t * t; - const t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + for ( let j = 0; j < track.times.length; ++ j ) { - } + const frame = track.times[ j ] * fps; - // + if ( frame < startFrame || frame >= endFrame ) continue; - function QuadraticBezierP0( t, p ) { + times.push( track.times[ j ] ); - const k = 1 - t; - return k * k * p; + for ( let k = 0; k < valueSize; ++ k ) { - } + values.push( track.values[ j * valueSize + k ] ); - function QuadraticBezierP1( t, p ) { + } - return 2 * ( 1 - t ) * t * p; + } - } + if ( times.length === 0 ) continue; - function QuadraticBezierP2( t, p ) { + track.times = AnimationUtils.convertArray( times, track.times.constructor ); + track.values = AnimationUtils.convertArray( values, track.values.constructor ); - return t * t * p; + tracks.push( track ); - } + } - function QuadraticBezier( t, p0, p1, p2 ) { + clip.tracks = tracks; - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); + // find minimum .times value across all tracks in the trimmed clip - } + let minStartTime = Infinity; - // + for ( let i = 0; i < clip.tracks.length; ++ i ) { - function CubicBezierP0( t, p ) { + if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - const k = 1 - t; - return k * k * k * p; + minStartTime = clip.tracks[ i ].times[ 0 ]; - } + } - function CubicBezierP1( t, p ) { + } - const k = 1 - t; - return 3 * k * k * t * p; + // shift all tracks such that clip begins at t=0 - } + for ( let i = 0; i < clip.tracks.length; ++ i ) { - function CubicBezierP2( t, p ) { + clip.tracks[ i ].shift( - 1 * minStartTime ); - return 3 * ( 1 - t ) * t * t * p; + } - } + clip.resetDuration(); - function CubicBezierP3( t, p ) { + return clip; - return t * t * t * p; + }, - } + makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { - function CubicBezier( t, p0, p1, p2, p3 ) { + if ( fps <= 0 ) fps = 30; - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); + const numTracks = referenceClip.tracks.length; + const referenceTime = referenceFrame / fps; - } + // Make each track's values relative to the values at the reference frame + for ( let i = 0; i < numTracks; ++ i ) { + + const referenceTrack = referenceClip.tracks[ i ]; + const referenceTrackType = referenceTrack.ValueTypeName; - function CubicBezierCurve( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { + // Skip this track if it's non-numeric + if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - Curve.call( this ); + // Find the track in the target clip whose name and type matches the reference track + const targetTrack = targetClip.tracks.find( function ( track ) { - this.type = 'CubicBezierCurve'; + return track.name === referenceTrack.name + && track.ValueTypeName === referenceTrackType; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + } ); - } + if ( targetTrack === undefined ) continue; - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; + let referenceOffset = 0; + const referenceValueSize = referenceTrack.getValueSize(); - CubicBezierCurve.prototype.isCubicBezierCurve = true; + if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2() ) { + referenceOffset = referenceValueSize / 3; - const point = optionalTarget; + } - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + let targetOffset = 0; + const targetValueSize = targetTrack.getValueSize(); - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); + if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - return point; + targetOffset = targetValueSize / 3; - }; + } - CubicBezierCurve.prototype.copy = function ( source ) { + const lastIndex = referenceTrack.times.length - 1; + let referenceValue; - Curve.prototype.copy.call( this, source ); + // Find the value to subtract out of the track + if ( referenceTime <= referenceTrack.times[ 0 ] ) { - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + // Reference frame is earlier than the first keyframe, so just use the first keyframe + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - return this; + } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - }; + // Reference frame is after the last keyframe, so just use the last keyframe + const startIndex = lastIndex * referenceValueSize + referenceOffset; + const endIndex = startIndex + referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - CubicBezierCurve.prototype.toJSON = function () { + } else { - const data = Curve.prototype.toJSON.call( this ); + // Interpolate to the reference value + const interpolant = referenceTrack.createInterpolant(); + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + interpolant.evaluate( referenceTime ); + referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + } - return data; + // Conjugate the quaternion + if ( referenceTrackType === 'quaternion' ) { - }; + const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); + referenceQuat.toArray( referenceValue ); - CubicBezierCurve.prototype.fromJSON = function ( json ) { + } - Curve.prototype.fromJSON.call( this, json ); + // Subtract the reference value from all of the track values - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + const numTimes = targetTrack.times.length; + for ( let j = 0; j < numTimes; ++ j ) { - return this; + const valueStart = j * targetValueSize + targetOffset; - }; + if ( referenceTrackType === 'quaternion' ) { - function CubicBezierCurve3( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { + // Multiply the conjugate for quaternion track types + Quaternion.multiplyQuaternionsFlat( + targetTrack.values, + valueStart, + referenceValue, + 0, + targetTrack.values, + valueStart + ); - Curve.call( this ); + } else { - this.type = 'CubicBezierCurve3'; + const valueEnd = targetValueSize - targetOffset * 2; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + // Subtract each value for all other numeric track types + for ( let k = 0; k < valueEnd; ++ k ) { - } + targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - CubicBezierCurve3.prototype = Object.create( Curve.prototype ); - CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; + } - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; + } - CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) { + } - const point = optionalTarget; + } - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + targetClip.blendMode = AdditiveAnimationBlendMode; - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); + return targetClip; - return point; + } }; - CubicBezierCurve3.prototype.copy = function ( source ) { + /** + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + */ - Curve.prototype.copy.call( this, source ); + class Interpolant { - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - return this; + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; - }; + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; - CubicBezierCurve3.prototype.toJSON = function () { + this.settings = null; + this.DefaultSettings_ = {}; - const data = Curve.prototype.toJSON.call( this ); + } - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + evaluate( t ) { - return data; + const pp = this.parameterPositions; + let i1 = this._cachedIndex, + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; - }; + validate_interval: { - CubicBezierCurve3.prototype.fromJSON = function ( json ) { + seek: { - Curve.prototype.fromJSON.call( this, json ); + let right; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + linear_scan: { - return this; + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { - }; + for ( let giveUpAt = i1 + 2; ; ) { - function LineCurve( v1 = new Vector2(), v2 = new Vector2() ) { + if ( t1 === undefined ) { - Curve.call( this ); + if ( t < t0 ) break forward_scan; - this.type = 'LineCurve'; - - this.v1 = v1; - this.v2 = v2; - - } + // after end - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t, t0 ); - LineCurve.prototype.isLineCurve = true; + } - LineCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2() ) { + if ( i1 === giveUpAt ) break; // this loop - const point = optionalTarget; + t0 = t1; + t1 = pp[ ++ i1 ]; - if ( t === 1 ) { + if ( t < t1 ) { - point.copy( this.v2 ); + // we have arrived at the sought interval + break seek; - } else { + } - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + } - } + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; - return point; + } - }; + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { - // Line curve is linear, so we can overwrite default getPointAt + // looping? - LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { + const t1global = pp[ 1 ]; - return this.getPoint( u, optionalTarget ); + if ( t < t1global ) { - }; + i1 = 2; // + 1, using the scan for the details + t0 = t1global; - LineCurve.prototype.getTangent = function ( t, optionalTarget ) { + } - const tangent = optionalTarget || new Vector2(); + // linear reverse scan - tangent.copy( this.v2 ).sub( this.v1 ).normalize(); + for ( let giveUpAt = i1 - 2; ; ) { - return tangent; + if ( t0 === undefined ) { - }; + // before start - LineCurve.prototype.copy = function ( source ) { + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - Curve.prototype.copy.call( this, source ); + } - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + if ( i1 === giveUpAt ) break; // this loop - return this; + t1 = t0; + t0 = pp[ -- i1 - 1 ]; - }; + if ( t >= t0 ) { - LineCurve.prototype.toJSON = function () { + // we have arrived at the sought interval + break seek; - const data = Curve.prototype.toJSON.call( this ); + } - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + } - return data; + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; - }; + } - LineCurve.prototype.fromJSON = function ( json ) { + // the interval is valid - Curve.prototype.fromJSON.call( this, json ); + break validate_interval; - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + } // linear scan - return this; + // binary search - }; + while ( i1 < right ) { - function LineCurve3( v1 = new Vector3(), v2 = new Vector3() ) { + const mid = ( i1 + right ) >>> 1; - Curve.call( this ); + if ( t < pp[ mid ] ) { - this.type = 'LineCurve3'; + right = mid; - this.v1 = v1; - this.v2 = v2; + } else { - } + i1 = mid + 1; - LineCurve3.prototype = Object.create( Curve.prototype ); - LineCurve3.prototype.constructor = LineCurve3; + } - LineCurve3.prototype.isLineCurve3 = true; + } - LineCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) { + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; - const point = optionalTarget; + // check boundary cases, again - if ( t === 1 ) { + if ( t0 === undefined ) { - point.copy( this.v2 ); + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - } else { + } - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + if ( t1 === undefined ) { - } + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t0, t ); - return point; + } - }; + } // seek - // Line curve is linear, so we can overwrite default getPointAt + this._cachedIndex = i1; - LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { + this.intervalChanged_( i1, t0, t1 ); - return this.getPoint( u, optionalTarget ); + } // validate_interval - }; + return this.interpolate_( i1, t0, t, t1 ); - LineCurve3.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + getSettings_() { - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + return this.settings || this.DefaultSettings_; - return this; + } - }; + copySampleValue_( index ) { - LineCurve3.prototype.toJSON = function () { + // copies a sample value to the result buffer - const data = Curve.prototype.toJSON.call( this ); + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + for ( let i = 0; i !== stride; ++ i ) { - return data; + result[ i ] = values[ offset + i ]; - }; + } - LineCurve3.prototype.fromJSON = function ( json ) { + return result; - Curve.prototype.fromJSON.call( this, json ); + } - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + // Template methods for derived classes: - return this; + interpolate_( /* i1, t0, t, t1 */ ) { - }; + throw new Error( 'call to abstract method' ); + // implementations shall return this.resultBuffer - function QuadraticBezierCurve( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { + } - Curve.call( this ); + intervalChanged_( /* i1, t0, t1 */ ) { - this.type = 'QuadraticBezierCurve'; + // empty - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + } } - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; + // ALIAS DEFINITIONS - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - - QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2() ) { + Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; + Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; - const point = optionalTarget; - - const v0 = this.v0, v1 = this.v1, v2 = this.v2; + /** + * Fast and simple cubic spline interpolant. + * + * It was derived from a Hermitian construction setting the first derivative + * at each sample position to the linear slope between neighboring positions + * over their parameter interval. + */ - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); + class CubicInterpolant extends Interpolant { - return point; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - }; + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - QuadraticBezierCurve.prototype.copy = function ( source ) { + this._weightPrev = - 0; + this._offsetPrev = - 0; + this._weightNext = - 0; + this._offsetNext = - 0; - Curve.prototype.copy.call( this, source ); + this.DefaultSettings_ = { - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding - return this; + }; - }; + } - QuadraticBezierCurve.prototype.toJSON = function () { + intervalChanged_( i1, t0, t1 ) { - const data = Curve.prototype.toJSON.call( this ); + const pp = this.parameterPositions; + let iPrev = i1 - 2, + iNext = i1 + 1, - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + tPrev = pp[ iPrev ], + tNext = pp[ iNext ]; - return data; + if ( tPrev === undefined ) { - }; + switch ( this.getSettings_().endingStart ) { - QuadraticBezierCurve.prototype.fromJSON = function ( json ) { + case ZeroSlopeEnding: - Curve.prototype.fromJSON.call( this, json ); + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + break; - return this; + case WrapAroundEnding: - }; + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - function QuadraticBezierCurve3( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { + break; - Curve.call( this ); + default: // ZeroCurvatureEnding - this.type = 'QuadraticBezierCurve3'; + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + } - } + } - QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; + if ( tNext === undefined ) { - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; + switch ( this.getSettings_().endingEnd ) { - QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) { + case ZeroSlopeEnding: - const point = optionalTarget; + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; - const v0 = this.v0, v1 = this.v1, v2 = this.v2; + break; - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); + case WrapAroundEnding: - return point; + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[ 1 ] - pp[ 0 ]; - }; + break; - QuadraticBezierCurve3.prototype.copy = function ( source ) { + default: // ZeroCurvatureEnding - Curve.prototype.copy.call( this, source ); + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + } - return this; + } - }; + const halfDt = ( t1 - t0 ) * 0.5, + stride = this.valueSize; - QuadraticBezierCurve3.prototype.toJSON = function () { + this._weightPrev = halfDt / ( t0 - tPrev ); + this._weightNext = halfDt / ( tNext - t1 ); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; - const data = Curve.prototype.toJSON.call( this ); + } - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + interpolate_( i1, t0, t, t1 ) { - return data; + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - }; + o1 = i1 * stride, o0 = o1 - stride, + oP = this._offsetPrev, oN = this._offsetNext, + wP = this._weightPrev, wN = this._weightNext, - QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { + p = ( t - t0 ) / ( t1 - t0 ), + pp = p * p, + ppp = pp * p; - Curve.prototype.fromJSON.call( this, json ); + // evaluate polynomials - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + const sP = - wP * ppp + 2 * wP * pp - wP * p; + const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; + const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + const sN = wN * ppp - wN * pp; - return this; + // combine data linearly - }; + for ( let i = 0; i !== stride; ++ i ) { - function SplineCurve( points = [] ) { + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; - Curve.call( this ); + } - this.type = 'SplineCurve'; + return result; - this.points = points; + } } - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; + class LinearInterpolant extends Interpolant { - SplineCurve.prototype.isSplineCurve = true; - - SplineCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2() ) { - - const point = optionalTarget; - - const points = this.points; - const p = ( points.length - 1 ) * t; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - const intPoint = Math.floor( p ); - const weight = p - intPoint; + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - const p1 = points[ intPoint ]; - const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); + } - return point; + interpolate_( i1, t0, t, t1 ) { - }; + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - SplineCurve.prototype.copy = function ( source ) { + offset1 = i1 * stride, + offset0 = offset1 - stride, - Curve.prototype.copy.call( this, source ); + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; - this.points = []; + for ( let i = 0; i !== stride; ++ i ) { - for ( let i = 0, l = source.points.length; i < l; i ++ ) { + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; - const point = source.points[ i ]; + } - this.points.push( point.clone() ); + return result; } - return this; - - }; - - SplineCurve.prototype.toJSON = function () { + } - const data = Curve.prototype.toJSON.call( this ); + /** + * + * Interpolant that evaluates to the sample value at the position preceeding + * the parameter. + */ - data.points = []; + class DiscreteInterpolant extends Interpolant { - for ( let i = 0, l = this.points.length; i < l; i ++ ) { + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - const point = this.points[ i ]; - data.points.push( point.toArray() ); + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } - return data; - - }; + interpolate_( i1 /*, t0, t, t1 */ ) { - SplineCurve.prototype.fromJSON = function ( json ) { + return this.copySampleValue_( i1 - 1 ); - Curve.prototype.fromJSON.call( this, json ); + } - this.points = []; + } - for ( let i = 0, l = json.points.length; i < l; i ++ ) { + class KeyframeTrack { - const point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); + constructor( name, times, values, interpolation ) { - } + if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); + if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - return this; + this.name = name; - }; + this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); + this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); + this.setInterpolation( interpolation || this.DefaultInterpolation ); - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + } - function CurvePath() { + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): - Curve.call( this ); + static toJSON( track ) { - this.type = 'CurvePath'; + const trackType = track.constructor; - this.curves = []; - this.autoClose = false; // Automatically closes the path + let json; - } + // derived classes can define a static toJSON method + if ( trackType.toJSON !== this.toJSON ) { - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { + json = trackType.toJSON( track ); - constructor: CurvePath, + } else { - add: function ( curve ) { + // by default, we assume the data can be serialized as-is + json = { - this.curves.push( curve ); + 'name': track.name, + 'times': AnimationUtils.convertArray( track.times, Array ), + 'values': AnimationUtils.convertArray( track.values, Array ) - }, + }; - closePath: function () { + const interpolation = track.getInterpolation(); - // Add a line curve if start and end of lines are not connected - const startPoint = this.curves[ 0 ].getPoint( 0 ); - const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + if ( interpolation !== track.DefaultInterpolation ) { - if ( ! startPoint.equals( endPoint ) ) { + json.interpolation = interpolation; - this.curves.push( new LineCurve( endPoint, startPoint ) ); + } } - }, + json.type = track.ValueTypeName; // mandatory - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: + return json; - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') + } - getPoint: function ( t ) { + InterpolantFactoryMethodDiscrete( result ) { - const d = t * this.getLength(); - const curveLengths = this.getCurveLengths(); - let i = 0; + return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - // To think about boundaries points. + } - while ( i < curveLengths.length ) { + InterpolantFactoryMethodLinear( result ) { - if ( curveLengths[ i ] >= d ) { + return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - const diff = curveLengths[ i ] - d; - const curve = this.curves[ i ]; + } - const segmentLength = curve.getLength(); - const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + InterpolantFactoryMethodSmooth( result ) { - return curve.getPointAt( u ); + return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - } + } - i ++; + setInterpolation( interpolation ) { - } + let factoryMethod; - return null; + switch ( interpolation ) { - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - - points.push( points[ 0 ] ); - - } - - return points; + return this; - }, + } - copy: function ( source ) { + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale( timeScale ) { - Curve.prototype.copy.call( this, source ); + if ( timeScale !== 1.0 ) { - this.curves = []; + const times = this.times; - for ( let i = 0, l = source.curves.length; i < l; i ++ ) { + for ( let i = 0, n = times.length; i !== n; ++ i ) { - const curve = source.curves[ i ]; + times[ i ] *= timeScale; - this.curves.push( curve.clone() ); + } } - this.autoClose = source.autoClose; - return this; - }, + } - toJSON: function () { + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim( startTime, endTime ) { - const data = Curve.prototype.toJSON.call( this ); + const times = this.times, + nKeys = times.length; - data.autoClose = this.autoClose; - data.curves = []; + let from = 0, + to = nKeys - 1; - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { + while ( from !== nKeys && times[ from ] < startTime ) { - const curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); + ++ from; } - return data; + while ( to !== - 1 && times[ to ] > endTime ) { - }, + -- to; - fromJSON: function ( json ) { + } - Curve.prototype.fromJSON.call( this, json ); + ++ to; // inclusive -> exclusive bound - this.autoClose = json.autoClose; - this.curves = []; + if ( from !== 0 || to !== nKeys ) { - for ( let i = 0, l = json.curves.length; i < l; i ++ ) { + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) { - const curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); + to = Math.max( to, 1 ); + from = to - 1; + + } + + const stride = this.getValueSize(); + this.times = AnimationUtils.arraySlice( times, from, to ); + this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); } @@ -38860,2472 +37831,2505 @@ } - } ); + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate() { - function Path( points ) { + let valid = true; - CurvePath.call( this ); + const valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { - this.type = 'Path'; + console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); + valid = false; - this.currentPoint = new Vector2(); + } - if ( points ) { + const times = this.times, + values = this.values, - this.setFromPoints( points ); + nKeys = times.length; - } + if ( nKeys === 0 ) { - } + console.error( 'THREE.KeyframeTrack: Track is empty.', this ); + valid = false; - Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { + } - constructor: Path, + let prevTime = null; - setFromPoints: function ( points ) { + for ( let i = 0; i !== nKeys; i ++ ) { - this.moveTo( points[ 0 ].x, points[ 0 ].y ); + const currTime = times[ i ]; - for ( let i = 1, l = points.length; i < l; i ++ ) { + if ( typeof currTime === 'number' && isNaN( currTime ) ) { - this.lineTo( points[ i ].x, points[ i ].y ); + console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); + valid = false; + break; - } + } - return this; + if ( prevTime !== null && prevTime > currTime ) { - }, + console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); + valid = false; + break; - moveTo: function ( x, y ) { + } - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? + prevTime = currTime; - return this; + } - }, + if ( values !== undefined ) { - lineTo: function ( x, y ) { + if ( AnimationUtils.isTypedArray( values ) ) { - const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); + for ( let i = 0, n = values.length; i !== n; ++ i ) { - this.currentPoint.set( x, y ); + const value = values[ i ]; - return this; + if ( isNaN( value ) ) { - }, + console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); + valid = false; + break; - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + } - const curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); + } - this.curves.push( curve ); + } - this.currentPoint.set( aX, aY ); + } - return this; + return valid; - }, + } - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + optimize() { - const curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); + // times or values may be shared with other tracks, so overwriting is unsafe + const times = AnimationUtils.arraySlice( this.times ), + values = AnimationUtils.arraySlice( this.values ), + stride = this.getValueSize(), - this.curves.push( curve ); + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - this.currentPoint.set( aX, aY ); + lastIndex = times.length - 1; - return this; + let writeIndex = 1; - }, + for ( let i = 1; i < lastIndex; ++ i ) { - splineThru: function ( pts /*Array of Vector*/ ) { + let keep = false; - const npts = [ this.currentPoint.clone() ].concat( pts ); + const time = times[ i ]; + const timeNext = times[ i + 1 ]; - const curve = new SplineCurve( npts ); - this.curves.push( curve ); + // remove adjacent keyframes scheduled at the same time - this.currentPoint.copy( pts[ pts.length - 1 ] ); + if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { - return this; + if ( ! smoothInterpolation ) { - }, + // remove unnecessary keyframes same as their neighbors - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + const offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; + for ( let j = 0; j !== stride; ++ j ) { - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + const value = values[ offset + j ]; - return this; + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { - }, + keep = true; + break; - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + } - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + } - return this; + } else { - }, + keep = true; - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + } - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; + } - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + // in-place compaction - return this; + if ( keep ) { - }, + if ( i !== writeIndex ) { - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + times[ writeIndex ] = times[ i ]; - const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + const readOffset = i * stride, + writeOffset = writeIndex * stride; - if ( this.curves.length > 0 ) { + for ( let j = 0; j !== stride; ++ j ) { - // if a previous curve is present, attempt to join - const firstPoint = curve.getPoint( 0 ); + values[ writeOffset + j ] = values[ readOffset + j ]; - if ( ! firstPoint.equals( this.currentPoint ) ) { + } - this.lineTo( firstPoint.x, firstPoint.y ); + } + + ++ writeIndex; } } - this.curves.push( curve ); - - const lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); - - return this; - - }, - - copy: function ( source ) { + // flush last keyframe (compaction looks ahead) - CurvePath.prototype.copy.call( this, source ); + if ( lastIndex > 0 ) { - this.currentPoint.copy( source.currentPoint ); + times[ writeIndex ] = times[ lastIndex ]; - return this; + for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - }, + values[ writeOffset + j ] = values[ readOffset + j ]; - toJSON: function () { + } - const data = CurvePath.prototype.toJSON.call( this ); + ++ writeIndex; - data.currentPoint = this.currentPoint.toArray(); + } - return data; + if ( writeIndex !== times.length ) { - }, + this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - fromJSON: function ( json ) { + } else { - CurvePath.prototype.fromJSON.call( this, json ); + this.times = times; + this.values = values; - this.currentPoint.fromArray( json.currentPoint ); + } return this; } - } ); + clone() { - function Shape( points ) { + const times = AnimationUtils.arraySlice( this.times, 0 ); + const values = AnimationUtils.arraySlice( this.values, 0 ); - Path.call( this, points ); + const TypedKeyframeTrack = this.constructor; + const track = new TypedKeyframeTrack( this.name, times, values ); - this.uuid = MathUtils.generateUUID(); + // Interpolant argument to constructor is not saved, so copy the factory method directly. + track.createInterpolant = this.createInterpolant; - this.type = 'Shape'; + return track; - this.holes = []; + } } - Shape.prototype = Object.assign( Object.create( Path.prototype ), { - - constructor: Shape, - - getPointsHoles: function ( divisions ) { - - const holesPts = []; + KeyframeTrack.prototype.TimeBufferType = Float32Array; + KeyframeTrack.prototype.ValueBufferType = Float32Array; + KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + /** + * A Track of Boolean keyframe values. + */ + class BooleanKeyframeTrack extends KeyframeTrack {} - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; + BooleanKeyframeTrack.prototype.ValueBufferType = Array; + BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; + BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; + BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - } + /** + * A Track of keyframe values that represent color. + */ + class ColorKeyframeTrack extends KeyframeTrack {} - return holesPts; + ColorKeyframeTrack.prototype.ValueTypeName = 'color'; - }, + /** + * A Track of numeric keyframe values. + */ + class NumberKeyframeTrack extends KeyframeTrack {} - // get points of shape and holes (keypoints based on segments parameter) + NumberKeyframeTrack.prototype.ValueTypeName = 'number'; - extractPoints: function ( divisions ) { + /** + * Spherical linear unit quaternion interpolant. + */ - return { + class QuaternionLinearInterpolant extends Interpolant { - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - }; + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - }, + } - copy: function ( source ) { + interpolate_( i1, t0, t, t1 ) { - Path.prototype.copy.call( this, source ); + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - this.holes = []; + alpha = ( t - t0 ) / ( t1 - t0 ); - for ( let i = 0, l = source.holes.length; i < l; i ++ ) { + let offset = i1 * stride; - const hole = source.holes[ i ]; + for ( let end = offset + stride; offset !== end; offset += 4 ) { - this.holes.push( hole.clone() ); + Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } - return this; + return result; - }, + } - toJSON: function () { + } - const data = Path.prototype.toJSON.call( this ); + /** + * A Track of quaternion keyframe values. + */ + class QuaternionKeyframeTrack extends KeyframeTrack { - data.uuid = this.uuid; - data.holes = []; + InterpolantFactoryMethodLinear( result ) { - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - const hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); + } - } + } - return data; + QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; + // ValueBufferType is inherited + QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; + QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - }, + /** + * A Track that interpolates Strings + */ + class StringKeyframeTrack extends KeyframeTrack {} - fromJSON: function ( json ) { + StringKeyframeTrack.prototype.ValueTypeName = 'string'; + StringKeyframeTrack.prototype.ValueBufferType = Array; + StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; + StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; + StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - Path.prototype.fromJSON.call( this, json ); + /** + * A Track of vectored keyframe values. + */ + class VectorKeyframeTrack extends KeyframeTrack {} - this.uuid = json.uuid; - this.holes = []; + VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; - for ( let i = 0, l = json.holes.length; i < l; i ++ ) { + class AnimationClip { - const hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); + constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { - } + this.name = name; + this.tracks = tracks; + this.duration = duration; + this.blendMode = blendMode; - return this; + this.uuid = generateUUID(); - } + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { - } ); + this.resetDuration(); - function Light( color, intensity = 1 ) { + } - Object3D.call( this ); + } - this.type = 'Light'; - this.color = new Color( color ); - this.intensity = intensity; + static parse( json ) { - } + const tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { + for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - constructor: Light, + tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - isLight: true, + } - copy: function ( source ) { + const clip = new this( json.name, json.duration, tracks, json.blendMode ); + clip.uuid = json.uuid; - Object3D.prototype.copy.call( this, source ); + return clip; - this.color.copy( source.color ); - this.intensity = source.intensity; + } - return this; + static toJSON( clip ) { - }, + const tracks = [], + clipTracks = clip.tracks; - toJSON: function ( meta ) { + const json = { - const data = Object3D.prototype.toJSON.call( this, meta ); + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks, + 'uuid': clip.uuid, + 'blendMode': clip.blendMode - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + }; - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; + tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); + } - return data; + return json; } - } ); - - function HemisphereLight( skyColor, groundColor, intensity ) { + static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { - Light.call( this, skyColor, intensity ); + const numMorphTargets = morphTargetSequence.length; + const tracks = []; - this.type = 'HemisphereLight'; + for ( let i = 0; i < numMorphTargets; i ++ ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + let times = []; + let values = []; - this.groundColor = new Color( groundColor ); + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); - } + values.push( 0, 1, 0 ); - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { + const order = AnimationUtils.getKeyframeOrder( times ); + times = AnimationUtils.sortedArray( times, 1, order ); + values = AnimationUtils.sortedArray( values, 1, order ); - constructor: HemisphereLight, + // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + if ( ! noLoop && times[ 0 ] === 0 ) { - isHemisphereLight: true, + times.push( numMorphTargets ); + values.push( values[ 0 ] ); - copy: function ( source ) { + } - Light.prototype.copy.call( this, source ); + tracks.push( + new NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); - this.groundColor.copy( source.groundColor ); + } - return this; + return new this( name, - 1, tracks ); } - } ); + static findByName( objectOrClipArray, name ) { - function LightShadow( camera ) { + let clipArray = objectOrClipArray; - this.camera = camera; + if ( ! Array.isArray( objectOrClipArray ) ) { - this.bias = 0; - this.normalBias = 0; - this.radius = 1; + const o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; - this.mapSize = new Vector2( 512, 512 ); + } - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); + for ( let i = 0; i < clipArray.length; i ++ ) { - this.autoUpdate = true; - this.needsUpdate = false; + if ( clipArray[ i ].name === name ) { - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); + return clipArray[ i ]; - this._viewportCount = 1; + } - this._viewports = [ + } - new Vector4( 0, 0, 1, 1 ) + return null; - ]; + } - } + static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { - Object.assign( LightShadow.prototype, { + const animationToMorphTargets = {}; - _projScreenMatrix: new Matrix4(), + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + const pattern = /^([\w-]*?)([\d]+)$/; - _lightPositionWorld: new Vector3(), + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - _lookTarget: new Vector3(), + const morphTarget = morphTargets[ i ]; + const parts = morphTarget.name.match( pattern ); - getViewportCount: function () { + if ( parts && parts.length > 1 ) { - return this._viewportCount; + const name = parts[ 1 ]; - }, + let animationMorphTargets = animationToMorphTargets[ name ]; - getFrustum: function () { + if ( ! animationMorphTargets ) { - return this._frustum; + animationToMorphTargets[ name ] = animationMorphTargets = []; - }, + } - updateMatrices: function ( light ) { + animationMorphTargets.push( morphTarget ); - const shadowCamera = this.camera, - shadowMatrix = this.matrix, - projScreenMatrix = this._projScreenMatrix, - lookTarget = this._lookTarget, - lightPositionWorld = this._lightPositionWorld; + } - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( lightPositionWorld ); + } - lookTarget.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( lookTarget ); - shadowCamera.updateMatrixWorld(); + const clips = []; - projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); + for ( const name in animationToMorphTargets ) { - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); + clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + } - }, + return clips; - getViewport: function ( viewportIndex ) { + } - return this._viewports[ viewportIndex ]; + // parse the animation.hierarchy format + static parseAnimation( animation, bones ) { - }, + if ( ! animation ) { - getFrameExtents: function () { + console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); + return null; - return this._frameExtents; + } - }, + const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - copy: function ( source ) { + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { - this.camera = source.camera.clone(); + const times = []; + const values = []; - this.bias = source.bias; - this.radius = source.radius; + AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - this.mapSize.copy( source.mapSize ); + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { - return this; + destTracks.push( new trackType( trackName, times, values ) ); - }, + } - clone: function () { + } - return new this.constructor().copy( this ); + }; - }, + const tracks = []; - toJSON: function () { + const clipName = animation.name || 'default'; + const fps = animation.fps || 30; + const blendMode = animation.blendMode; - const object = {}; + // automatic length determination in AnimationClip. + let duration = animation.length || - 1; - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); + const hierarchyTracks = animation.hierarchy || []; - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; + for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - return object; + const animationKeys = hierarchyTracks[ h ].keys; - } + // skip empty tracks + if ( ! animationKeys || animationKeys.length === 0 ) continue; - } ); + // process morph targets + if ( animationKeys[ 0 ].morphTargets ) { - function SpotLightShadow() { + // figure out all morph targets used in this track + const morphTargetNames = {}; - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); + let k; - this.focus = 1; + for ( k = 0; k < animationKeys.length; k ++ ) { - } + if ( animationKeys[ k ].morphTargets ) { - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - constructor: SpotLightShadow, + morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - isSpotLightShadow: true, + } - updateMatrices: function ( light ) { + } - const camera = this.camera; + } - const fov = MathUtils.RAD2DEG * 2 * light.angle * this.focus; - const aspect = this.mapSize.width / this.mapSize.height; - const far = light.distance || camera.far; + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( const morphTargetName in morphTargetNames ) { - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { + const times = []; + const values = []; - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); + for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - } + const animationKey = animationKeys[ k ]; - LightShadow.prototype.updateMatrices.call( this, light ); + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - } + } - } ); + tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { + } - Light.call( this, color, intensity ); + duration = morphTargetNames.length * ( fps || 1.0 ); - this.type = 'SpotLight'; + } else { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + // ...assume skeletal animation - this.target = new Object3D(); + const boneName = '.bones[' + bones[ h ].name + ']'; - Object.defineProperty( this, 'power', { - get: function () { + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; + addNonemptyTrack( + QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); - }, - set: function ( power ) { + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; + } } - } ); - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + if ( tracks.length === 0 ) { + + return null; - this.shadow = new SpotLightShadow(); + } - } + const clip = new this( clipName, duration, tracks, blendMode ); - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { + return clip; - constructor: SpotLight, + } - isSpotLight: true, + resetDuration() { - copy: function ( source ) { + const tracks = this.tracks; + let duration = 0; - Light.prototype.copy.call( this, source ); + for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; + const track = this.tracks[ i ]; - this.target = source.target.clone(); + duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - this.shadow = source.shadow.clone(); + } + + this.duration = duration; return this; } - } ); + trim() { - function PointLightShadow() { + for ( let i = 0; i < this.tracks.length; i ++ ) { - LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + this.tracks[ i ].trim( 0, this.duration ); - this._frameExtents = new Vector2( 4, 2 ); + } - this._viewportCount = 6; + return this; - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction - - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; + } - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; + validate() { - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; + let valid = true; - } + for ( let i = 0; i < this.tracks.length; i ++ ) { - PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + valid = valid && this.tracks[ i ].validate(); - constructor: PointLightShadow, + } - isPointLightShadow: true, + return valid; - updateMatrices: function ( light, viewportIndex = 0 ) { + } - const camera = this.camera, - shadowMatrix = this.matrix, - lightPositionWorld = this._lightPositionWorld, - lookTarget = this._lookTarget, - projScreenMatrix = this._projScreenMatrix; + optimize() { - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( lightPositionWorld ); + for ( let i = 0; i < this.tracks.length; i ++ ) { - lookTarget.copy( camera.position ); - lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( lookTarget ); - camera.updateMatrixWorld(); + this.tracks[ i ].optimize(); - shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z ); + } - projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); + return this; } - } ); - - function PointLight( color, intensity, distance, decay ) { + clone() { - Light.call( this, color, intensity ); + const tracks = []; - this.type = 'PointLight'; + for ( let i = 0; i < this.tracks.length; i ++ ) { - Object.defineProperty( this, 'power', { - get: function () { + tracks.push( this.tracks[ i ].clone() ); - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; + } - }, - set: function ( power ) { + return new this.constructor( this.name, this.duration, tracks, this.blendMode ); - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); + } - } - } ); + toJSON() { - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + return this.constructor.toJSON( this ); - this.shadow = new PointLightShadow(); + } } - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: PointLight, + function getTrackTypeForValueTypeName( typeName ) { - isPointLight: true, + switch ( typeName.toLowerCase() ) { - copy: function ( source ) { + case 'scalar': + case 'double': + case 'float': + case 'number': + case 'integer': - Light.prototype.copy.call( this, source ); + return NumberKeyframeTrack; - this.distance = source.distance; - this.decay = source.decay; + case 'vector': + case 'vector2': + case 'vector3': + case 'vector4': - this.shadow = source.shadow.clone(); + return VectorKeyframeTrack; - return this; + case 'color': - } + return ColorKeyframeTrack; - } ); + case 'quaternion': - function OrthographicCamera( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { + return QuaternionKeyframeTrack; - Camera$1.call( this ); + case 'bool': + case 'boolean': - this.type = 'OrthographicCamera'; + return BooleanKeyframeTrack; - this.zoom = 1; - this.view = null; + case 'string': - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; + return StringKeyframeTrack; - this.near = near; - this.far = far; + } - this.updateProjectionMatrix(); + throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); } - OrthographicCamera.prototype = Object.assign( Object.create( Camera$1.prototype ), { + function parseKeyframeTrack( json ) { - constructor: OrthographicCamera, + if ( json.type === undefined ) { - isOrthographicCamera: true, + throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - copy: function ( source, recursive ) { + } - Camera$1.prototype.copy.call( this, source, recursive ); + const trackType = getTrackTypeForValueTypeName( json.type ); - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; + if ( json.times === undefined ) { - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); + const times = [], values = []; - return this; + AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - }, + json.times = times; + json.values = values; - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { + } - if ( this.view === null ) { + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; + return trackType.parse( json ); - } + } else { - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; + // by default, we assume a constructor compatible with the base + return new trackType( json.name, json.times, json.values, json.interpolation ); - this.updateProjectionMatrix(); + } - }, + } - clearViewOffset: function () { + const Cache = { - if ( this.view !== null ) { + enabled: false, - this.view.enabled = false; + files: {}, - } + add: function ( key, file ) { - this.updateProjectionMatrix(); + if ( this.enabled === false ) return; - }, + // console.log( 'THREE.Cache', 'Adding key:', key ); - updateProjectionMatrix: function () { + this.files[ key ] = file; - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; + }, - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; + get: function ( key ) { - if ( this.view !== null && this.view.enabled ) { + if ( this.enabled === false ) return; - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; + // console.log( 'THREE.Cache', 'Checking key:', key ); - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; + return this.files[ key ]; - } + }, - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); + remove: function ( key ) { - this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); + delete this.files[ key ]; }, - toJSON: function ( meta ) { + clear: function () { - const data = Object3D.prototype.toJSON.call( this, meta ); + this.files = {}; - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; + } - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); + }; - return data; + class LoadingManager { - } + constructor( onLoad, onProgress, onError ) { - } ); + const scope = this; - function DirectionalLightShadow() { + let isLoading = false; + let itemsLoaded = 0; + let itemsTotal = 0; + let urlModifier = undefined; + const handlers = []; - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + // Refer to #5689 for the reason why we don't set .onStart + // in the constructor - } + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + this.itemStart = function ( url ) { - constructor: DirectionalLightShadow, + itemsTotal ++; - isDirectionalLightShadow: true, + if ( isLoading === false ) { - updateMatrices: function ( light ) { + if ( scope.onStart !== undefined ) { - LightShadow.prototype.updateMatrices.call( this, light ); + scope.onStart( url, itemsLoaded, itemsTotal ); - } + } - } ); + } - function DirectionalLight( color, intensity ) { + isLoading = true; - Light.call( this, color, intensity ); + }; - this.type = 'DirectionalLight'; + this.itemEnd = function ( url ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + itemsLoaded ++; - this.target = new Object3D(); + if ( scope.onProgress !== undefined ) { - this.shadow = new DirectionalLightShadow(); + scope.onProgress( url, itemsLoaded, itemsTotal ); - } + } - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { + if ( itemsLoaded === itemsTotal ) { - constructor: DirectionalLight, + isLoading = false; - isDirectionalLight: true, + if ( scope.onLoad !== undefined ) { - copy: function ( source ) { + scope.onLoad(); - Light.prototype.copy.call( this, source ); + } - this.target = source.target.clone(); + } - this.shadow = source.shadow.clone(); + }; - return this; + this.itemError = function ( url ) { - } + if ( scope.onError !== undefined ) { - } ); + scope.onError( url ); - function AmbientLight( color, intensity ) { + } - Light.call( this, color, intensity ); + }; - this.type = 'AmbientLight'; + this.resolveURL = function ( url ) { - } + if ( urlModifier ) { - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { + return urlModifier( url ); - constructor: AmbientLight, + } - isAmbientLight: true + return url; - } ); + }; - function RectAreaLight( color, intensity, width, height ) { + this.setURLModifier = function ( transform ) { - Light.call( this, color, intensity ); + urlModifier = transform; - this.type = 'RectAreaLight'; + return this; - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; + }; - } + this.addHandler = function ( regex, loader ) { - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { + handlers.push( regex, loader ); - constructor: RectAreaLight, + return this; - isRectAreaLight: true, + }; - copy: function ( source ) { + this.removeHandler = function ( regex ) { - Light.prototype.copy.call( this, source ); + const index = handlers.indexOf( regex ); - this.width = source.width; - this.height = source.height; + if ( index !== - 1 ) { - return this; + handlers.splice( index, 2 ); - }, + } - toJSON: function ( meta ) { + return this; - const data = Light.prototype.toJSON.call( this, meta ); + }; - data.object.width = this.width; - data.object.height = this.height; + this.getHandler = function ( file ) { - return data; + for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - } + const regex = handlers[ i ]; + const loader = handlers[ i + 1 ]; - } ); + if ( regex.global ) regex.lastIndex = 0; // see #17920 - /** - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ + if ( regex.test( file ) ) { - // 3-band SH defined by 9 coefficients + return loader; - class SphericalHarmonics3 { + } - constructor() { + } - Object.defineProperty( this, 'isSphericalHarmonics3', { value: true } ); + return null; - this.coefficients = []; + }; - for ( let i = 0; i < 9; i ++ ) { + } - this.coefficients.push( new Vector3() ); + } - } + const DefaultLoadingManager = new LoadingManager(); + + class Loader { + + constructor( manager ) { + + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + + this.crossOrigin = 'anonymous'; + this.withCredentials = false; + this.path = ''; + this.resourcePath = ''; + this.requestHeader = {}; } - set( coefficients ) { + load( /* url, onLoad, onProgress, onError */ ) {} - for ( let i = 0; i < 9; i ++ ) { + loadAsync( url, onProgress ) { - this.coefficients[ i ].copy( coefficients[ i ] ); + const scope = this; - } + return new Promise( function ( resolve, reject ) { - return this; + scope.load( url, resolve, onProgress, reject ); + + } ); } - zero() { + parse( /* data */ ) {} - for ( let i = 0; i < 9; i ++ ) { + setCrossOrigin( crossOrigin ) { - this.coefficients[ i ].set( 0, 0, 0 ); + this.crossOrigin = crossOrigin; + return this; - } + } + setWithCredentials( value ) { + + this.withCredentials = value; return this; } - // get the radiance in the direction of the normal - // target is a Vector3 - getAt( normal, target ) { + setPath( path ) { - // normal is assumed to be unit length + this.path = path; + return this; - const x = normal.x, y = normal.y, z = normal.z; + } - const coeff = this.coefficients; + setResourcePath( resourcePath ) { - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); + this.resourcePath = resourcePath; + return this; - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); + } - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); + setRequestHeader( requestHeader ) { - return target; + this.requestHeader = requestHeader; + return this; } - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt( normal, target ) { + } - // normal is assumed to be unit length + const loading = {}; - const x = normal.x, y = normal.y, z = normal.z; + class FileLoader extends Loader { - const coeff = this.coefficients; + constructor( manager ) { - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 + super( manager ); - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); + } - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 + load( url, onLoad, onProgress, onError ) { - return target; + if ( url === undefined ) url = ''; - } + if ( this.path !== undefined ) url = this.path + url; - add( sh ) { + url = this.manager.resolveURL( url ); - for ( let i = 0; i < 9; i ++ ) { + const cached = Cache.get( url ); - this.coefficients[ i ].add( sh.coefficients[ i ] ); + if ( cached !== undefined ) { - } + this.manager.itemStart( url ); - return this; + setTimeout( () => { - } + if ( onLoad ) onLoad( cached ); - addScaledSH( sh, s ) { + this.manager.itemEnd( url ); - for ( let i = 0; i < 9; i ++ ) { + }, 0 ); - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); + return cached; } - return this; + // Check if request is duplicate - } + if ( loading[ url ] !== undefined ) { - scale( s ) { + loading[ url ].push( { - for ( let i = 0; i < 9; i ++ ) { + onLoad: onLoad, + onProgress: onProgress, + onError: onError - this.coefficients[ i ].multiplyScalar( s ); + } ); + + return; } - return this; + // Initialise array for duplicate requests + loading[ url ] = []; - } + loading[ url ].push( { + onLoad: onLoad, + onProgress: onProgress, + onError: onError, + } ); - lerp( sh, alpha ) { + // create request + const req = new Request( url, { + headers: new Headers( this.requestHeader ), + credentials: this.withCredentials ? 'include' : 'same-origin', + // An abort controller could be added within a future PR + } ); - for ( let i = 0; i < 9; i ++ ) { + // start the fetch + fetch( req ) + .then( response => { - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); + if ( response.status === 200 || response.status === 0 ) { - } + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. - return this; + if ( response.status === 0 ) { - } + console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - equals( sh ) { + } - for ( let i = 0; i < 9; i ++ ) { + const callbacks = loading[ url ]; + const reader = response.body.getReader(); + const contentLength = response.headers.get( 'Content-Length' ); + const total = contentLength ? parseInt( contentLength ) : 0; + const lengthComputable = total !== 0; + let loaded = 0; - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { + // periodically read data into the new stream tracking while download progress + return new ReadableStream( { + start( controller ) { - return false; + readData(); - } + function readData() { - } + reader.read().then( ( { done, value } ) => { - return true; + if ( done ) { - } + controller.close(); - copy( sh ) { + } else { - return this.set( sh.coefficients ); + loaded += value.byteLength; - } + const event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } ); + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - clone() { + const callback = callbacks[ i ]; + if ( callback.onProgress ) callback.onProgress( event ); - return new this.constructor().copy( this ); + } - } + controller.enqueue( value ); + readData(); - fromArray( array, offset = 0 ) { + } - const coefficients = this.coefficients; + } ); - for ( let i = 0; i < 9; i ++ ) { + } - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); + } - } + } ); - return this; + } else { - } + throw Error( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}` ); - toArray( array = [], offset = 0 ) { + } - const coefficients = this.coefficients; + } ) + .then( stream => { - for ( let i = 0; i < 9; i ++ ) { + const response = new Response( stream ); - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); + switch ( this.responseType ) { - } + case 'arraybuffer': - return array; + return response.arrayBuffer(); - } + case 'blob': - // evaluate the basis functions - // shBasis is an Array[ 9 ] - static getBasisAt( normal, shBasis ) { + return response.blob(); - // normal is assumed to be unit length + case 'document': - const x = normal.x, y = normal.y, z = normal.z; + return response.text() + .then( text => { - // band 0 - shBasis[ 0 ] = 0.282095; + const parser = new DOMParser(); + return parser.parseFromString( text, this.mimeType ); - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; + } ); - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); + case 'json': - } + return response.json(); - } + default: - function LightProbe( sh, intensity ) { + return response.text(); - Light.call( this, undefined, intensity ); + } - this.type = 'LightProbe'; + } ) + .then( data => { - this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3(); + // Add to cache only on HTTP success, so that we do not cache + // error response bodies as proper responses to requests. + Cache.add( url, data ); - } + const callbacks = loading[ url ]; + delete loading[ url ]; - LightProbe.prototype = Object.assign( Object.create( Light.prototype ), { + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - constructor: LightProbe, + const callback = callbacks[ i ]; + if ( callback.onLoad ) callback.onLoad( data ); - isLightProbe: true, + } - copy: function ( source ) { + this.manager.itemEnd( url ); - Light.prototype.copy.call( this, source ); + } ) + .catch( err => { - this.sh.copy( source.sh ); + // Abort errors and other errors are handled the same - return this; + const callbacks = loading[ url ]; + delete loading[ url ]; - }, + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - fromJSON: function ( json ) { + const callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( err ); - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); + } - return this; + this.manager.itemError( url ); + this.manager.itemEnd( url ); - }, + } ); - toJSON: function ( meta ) { + this.manager.itemStart( url ); - const data = Light.prototype.toJSON.call( this, meta ); + } - data.object.sh = this.sh.toArray(); + setResponseType( value ) { - return data; + this.responseType = value; + return this; } - } ); - - function MaterialLoader( manager ) { + setMimeType( value ) { - Loader.call( this, manager ); + this.mimeType = value; + return this; - this.textures = {}; + } } - MaterialLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: MaterialLoader, + class ImageLoader extends Loader { - load: function ( url, onLoad, onProgress, onError ) { + constructor( manager ) { - const scope = this; + super( manager ); - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); - loader.load( url, function ( text ) { + } - try { + load( url, onLoad, onProgress, onError ) { - onLoad( scope.parse( JSON.parse( text ) ) ); + if ( this.path !== undefined ) url = this.path + url; - } catch ( e ) { + url = this.manager.resolveURL( url ); - if ( onError ) { + const scope = this; - onError( e ); + const cached = Cache.get( url ); - } else { + if ( cached !== undefined ) { - console.error( e ); + scope.manager.itemStart( url ); - } + setTimeout( function () { - scope.manager.itemError( url ); + if ( onLoad ) onLoad( cached ); - } + scope.manager.itemEnd( url ); - }, onProgress, onError ); + }, 0 ); - }, + return cached; - parse: function ( json ) { + } - const textures = this.textures; + const image = createElementNS( 'img' ); - function getTexture( name ) { + function onImageLoad() { - if ( textures[ name ] === undefined ) { + removeEventListeners(); - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + Cache.add( url, this ); - } + if ( onLoad ) onLoad( this ); - return textures[ name ]; + scope.manager.itemEnd( url ); } - const material = new Materials[ json.type ](); - - if ( json.uuid !== undefined ) material.uuid = json.uuid; - if ( json.name !== undefined ) material.name = json.name; - if ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color ); - if ( json.roughness !== undefined ) material.roughness = json.roughness; - if ( json.metalness !== undefined ) material.metalness = json.metalness; - if ( json.sheen !== undefined ) material.sheen = new Color().setHex( json.sheen ); - if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; - if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; - if ( json.fog !== undefined ) material.fog = json.fog; - if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.combine !== undefined ) material.combine = json.combine; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; - if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; - if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; - if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; + function onImageError( event ) { - if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite; - if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask; - if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc; - if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef; - if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask; - if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail; - if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail; - if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass; + removeEventListeners(); - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; - if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; + if ( onError ) onError( event ); - if ( json.rotation !== undefined ) material.rotation = json.rotation; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; - if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; - if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; - if ( json.scale !== undefined ) material.scale = json.scale; + } - if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; - if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; - if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; + function removeEventListeners() { - if ( json.skinning !== undefined ) material.skinning = json.skinning; - if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; - if ( json.morphNormals !== undefined ) material.morphNormals = json.morphNormals; - if ( json.dithering !== undefined ) material.dithering = json.dithering; + image.removeEventListener( 'load', onImageLoad, false ); + image.removeEventListener( 'error', onImageError, false ); - if ( json.vertexTangents !== undefined ) material.vertexTangents = json.vertexTangents; + } - if ( json.visible !== undefined ) material.visible = json.visible; + image.addEventListener( 'load', onImageLoad, false ); + image.addEventListener( 'error', onImageError, false ); - if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; + if ( url.substr( 0, 5 ) !== 'data:' ) { - if ( json.userData !== undefined ) material.userData = json.userData; + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - if ( json.vertexColors !== undefined ) { + } - if ( typeof json.vertexColors === 'number' ) { + scope.manager.itemStart( url ); - material.vertexColors = ( json.vertexColors > 0 ) ? true : false; + image.src = url; - } else { + return image; - material.vertexColors = json.vertexColors; + } - } + } - } + class CubeTextureLoader extends Loader { - // Shader Material + constructor( manager ) { - if ( json.uniforms !== undefined ) { + super( manager ); - for ( const name in json.uniforms ) { + } - const uniform = json.uniforms[ name ]; + load( urls, onLoad, onProgress, onError ) { - material.uniforms[ name ] = {}; + const texture = new CubeTexture(); - switch ( uniform.type ) { + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - case 't': - material.uniforms[ name ].value = getTexture( uniform.value ); - break; + let loaded = 0; - case 'c': - material.uniforms[ name ].value = new Color().setHex( uniform.value ); - break; + function loadTexture( i ) { - case 'v2': - material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); - break; + loader.load( urls[ i ], function ( image ) { - case 'v3': - material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); - break; + texture.images[ i ] = image; - case 'v4': - material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); - break; + loaded ++; - case 'm3': - material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); - break; + if ( loaded === 6 ) { - case 'm4': - material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); - break; + texture.needsUpdate = true; - default: - material.uniforms[ name ].value = uniform.value; + if ( onLoad ) onLoad( texture ); } - } + }, undefined, onError ); } - if ( json.defines !== undefined ) material.defines = json.defines; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + for ( let i = 0; i < urls.length; ++ i ) { - if ( json.extensions !== undefined ) { + loadTexture( i ); - for ( const key in json.extensions ) { + } - material.extensions[ key ] = json.extensions[ key ]; + return texture; - } + } - } + } - // Deprecated + class TextureLoader extends Loader { - if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading + constructor( manager ) { - // for PointsMaterial + super( manager ); - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + } - // maps + load( url, onLoad, onProgress, onError ) { - if ( json.map !== undefined ) material.map = getTexture( json.map ); - if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); - - if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap ); - - if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; + const texture = new Texture(); - if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); - if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; - if ( json.normalScale !== undefined ) { + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - let normalScale = json.normalScale; + loader.load( url, function ( image ) { - if ( Array.isArray( normalScale ) === false ) { + texture.image = image; + texture.needsUpdate = true; - // Blender exporter used to export a scalar. See #7459 + if ( onLoad !== undefined ) { - normalScale = [ normalScale, normalScale ]; + onLoad( texture ); } - material.normalScale = new Vector2().fromArray( normalScale ); + }, onProgress, onError ); - } + return texture; - if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; + } - if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); - if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); + } - if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); - if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; + class Light extends Object3D { - if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); + constructor( color, intensity = 1 ) { - if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); - if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; + super(); - if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; + this.type = 'Light'; - if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; + this.color = new Color( color ); + this.intensity = intensity; - if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; + } - if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); + dispose() { - if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap ); - if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); - if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); - if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); + // Empty here in base class; some subclasses override. - if ( json.transmission !== undefined ) material.transmission = json.transmission; - if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap ); + } - return material; + copy( source ) { - }, + super.copy( source ); - setTextures: function ( value ) { + this.color.copy( source.color ); + this.intensity = source.intensity; - this.textures = value; return this; } - } ); - - const LoaderUtils = { + toJSON( meta ) { - decodeText: function ( array ) { + const data = super.toJSON( meta ); - if ( typeof TextDecoder !== 'undefined' ) { + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - return new TextDecoder().decode( array ); + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - } + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. + if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - let s = ''; + return data; - for ( let i = 0, il = array.length; i < il; i ++ ) { + } - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); + } - } + Light.prototype.isLight = true; - try { + class HemisphereLight extends Light { - // merges multi-byte utf-8 characters. + constructor( skyColor, groundColor, intensity ) { - return decodeURIComponent( escape( s ) ); + super( skyColor, intensity ); - } catch ( e ) { // see #16358 + this.type = 'HemisphereLight'; - return s; + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - } + this.groundColor = new Color( groundColor ); - }, + } - extractUrlBase: function ( url ) { + copy( source ) { - const index = url.lastIndexOf( '/' ); + Light.prototype.copy.call( this, source ); - if ( index === - 1 ) return './'; + this.groundColor.copy( source.groundColor ); - return url.substr( 0, index + 1 ); + return this; } - }; - - function InstancedBufferGeometry() { - - BufferGeometry.call( this ); - - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; - } - InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { - - constructor: InstancedBufferGeometry, + HemisphereLight.prototype.isHemisphereLight = true; - isInstancedBufferGeometry: true, + const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); + const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); + const _lookTarget$1 = /*@__PURE__*/ new Vector3(); - copy: function ( source ) { + class LightShadow { - BufferGeometry.prototype.copy.call( this, source ); - - this.instanceCount = source.instanceCount; + constructor( camera ) { - return this; + this.camera = camera; - }, + this.bias = 0; + this.normalBias = 0; + this.radius = 1; + this.blurSamples = 8; - clone: function () { + this.mapSize = new Vector2( 512, 512 ); - return new this.constructor().copy( this ); + this.map = null; + this.mapPass = null; + this.matrix = new Matrix4(); - }, + this.autoUpdate = true; + this.needsUpdate = false; - toJSON: function () { + this._frustum = new Frustum(); + this._frameExtents = new Vector2( 1, 1 ); - const data = BufferGeometry.prototype.toJSON.call( this ); + this._viewportCount = 1; - data.instanceCount = this.instanceCount; + this._viewports = [ - data.isInstancedBufferGeometry = true; + new Vector4( 0, 0, 1, 1 ) - return data; + ]; } - } ); - - function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) { + getViewportCount() { - if ( typeof ( normalized ) === 'number' ) { + return this._viewportCount; - meshPerAttribute = normalized; + } - normalized = false; + getFrustum() { - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); + return this._frustum; } - BufferAttribute.call( this, array, itemSize, normalized ); + updateMatrices( light ) { - this.meshPerAttribute = meshPerAttribute || 1; + const shadowCamera = this.camera; + const shadowMatrix = this.matrix; - } + _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld$1 ); - InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { + _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget$1 ); + shadowCamera.updateMatrixWorld(); - constructor: InstancedBufferAttribute, + _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); - isInstancedBufferAttribute: true, + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); - copy: function ( source ) { + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - BufferAttribute.prototype.copy.call( this, source ); + } - this.meshPerAttribute = source.meshPerAttribute; + getViewport( viewportIndex ) { - return this; + return this._viewports[ viewportIndex ]; - }, + } - toJSON: function () { + getFrameExtents() { - const data = BufferAttribute.prototype.toJSON.call( this ); + return this._frameExtents; - data.meshPerAttribute = this.meshPerAttribute; + } - data.isInstancedBufferAttribute = true; + dispose() { - return data; + if ( this.map ) { - } + this.map.dispose(); - } ); + } - function BufferGeometryLoader( manager ) { + if ( this.mapPass ) { - Loader.call( this, manager ); + this.mapPass.dispose(); - } + } - BufferGeometryLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + } - constructor: BufferGeometryLoader, + copy( source ) { - load: function ( url, onLoad, onProgress, onError ) { + this.camera = source.camera.clone(); - const scope = this; + this.bias = source.bias; + this.radius = source.radius; - const loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); - loader.load( url, function ( text ) { + this.mapSize.copy( source.mapSize ); - try { + return this; - onLoad( scope.parse( JSON.parse( text ) ) ); + } - } catch ( e ) { + clone() { - if ( onError ) { + return new this.constructor().copy( this ); - onError( e ); + } - } else { + toJSON() { - console.error( e ); + const object = {}; - } + if ( this.bias !== 0 ) object.bias = this.bias; + if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; + if ( this.radius !== 1 ) object.radius = this.radius; + if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - scope.manager.itemError( url ); + object.camera = this.camera.toJSON( false ).object; + delete object.camera.matrix; - } + return object; - }, onProgress, onError ); + } - }, + } + + class SpotLightShadow extends LightShadow { - parse: function ( json ) { + constructor() { - const interleavedBufferMap = {}; - const arrayBufferMap = {}; + super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - function getInterleavedBuffer( json, uuid ) { + this.focus = 1; - if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; + } - const interleavedBuffers = json.interleavedBuffers; - const interleavedBuffer = interleavedBuffers[ uuid ]; + updateMatrices( light ) { - const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); + const camera = this.camera; - const array = getTypedArray( interleavedBuffer.type, buffer ); - const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); - ib.uuid = interleavedBuffer.uuid; + const fov = RAD2DEG$1 * 2 * light.angle * this.focus; + const aspect = this.mapSize.width / this.mapSize.height; + const far = light.distance || camera.far; - interleavedBufferMap[ uuid ] = ib; + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - return ib; + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); } - function getArrayBuffer( json, uuid ) { + super.updateMatrices( light ); - if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; + } - const arrayBuffers = json.arrayBuffers; - const arrayBuffer = arrayBuffers[ uuid ]; + copy( source ) { - const ab = new Uint32Array( arrayBuffer ).buffer; + super.copy( source ); - arrayBufferMap[ uuid ] = ab; + this.focus = source.focus; - return ab; + return this; - } + } - const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); + } - const index = json.data.index; + SpotLightShadow.prototype.isSpotLightShadow = true; - if ( index !== undefined ) { + class SpotLight extends Light { - const typedArray = getTypedArray( index.type, index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); + constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1 ) { - } + super( color, intensity ); - const attributes = json.data.attributes; + this.type = 'SpotLight'; - for ( const key in attributes ) { + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - const attribute = attributes[ key ]; - let bufferAttribute; + this.target = new Object3D(); - if ( attribute.isInterleavedBufferAttribute ) { + this.distance = distance; + this.angle = angle; + this.penumbra = penumbra; + this.decay = decay; // for physically correct lights, should be 2. - const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); - bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); + this.shadow = new SpotLightShadow(); - } else { + } - const typedArray = getTypedArray( attribute.type, attribute.array ); - const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; - bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); + get power() { - } + // compute the light's luminous power (in lumens) from its intensity (in candela) + // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) + return this.intensity * Math.PI; - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - geometry.setAttribute( key, bufferAttribute ); + } - } + set power( power ) { - const morphAttributes = json.data.morphAttributes; + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / Math.PI; - if ( morphAttributes ) { + } - for ( const key in morphAttributes ) { + dispose() { - const attributeArray = morphAttributes[ key ]; + this.shadow.dispose(); - const array = []; + } - for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { + copy( source ) { - const attribute = attributeArray[ i ]; - let bufferAttribute; + super.copy( source ); - if ( attribute.isInterleavedBufferAttribute ) { + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; - const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); - bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); + this.target = source.target.clone(); - } else { + this.shadow = source.shadow.clone(); - const typedArray = getTypedArray( attribute.type, attribute.array ); - bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); + return this; - } + } - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - array.push( bufferAttribute ); + } - } + SpotLight.prototype.isSpotLight = true; - geometry.morphAttributes[ key ] = array; + const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); + const _lightPositionWorld = /*@__PURE__*/ new Vector3(); + const _lookTarget = /*@__PURE__*/ new Vector3(); - } + class PointLightShadow extends LightShadow { - } + constructor() { - const morphTargetsRelative = json.data.morphTargetsRelative; + super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - if ( morphTargetsRelative ) { + this._frameExtents = new Vector2( 4, 2 ); - geometry.morphTargetsRelative = true; + this._viewportCount = 6; - } + this._viewports = [ + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction + + // positive X + new Vector4( 2, 1, 1, 1 ), + // negative X + new Vector4( 0, 1, 1, 1 ), + // positive Z + new Vector4( 3, 1, 1, 1 ), + // negative Z + new Vector4( 1, 1, 1, 1 ), + // positive Y + new Vector4( 3, 0, 1, 1 ), + // negative Y + new Vector4( 1, 0, 1, 1 ) + ]; - const groups = json.data.groups || json.data.drawcalls || json.data.offsets; + this._cubeDirections = [ + new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), + new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) + ]; - if ( groups !== undefined ) { + this._cubeUps = [ + new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), + new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) + ]; - for ( let i = 0, n = groups.length; i !== n; ++ i ) { + } - const group = groups[ i ]; + updateMatrices( light, viewportIndex = 0 ) { - geometry.addGroup( group.start, group.count, group.materialIndex ); + const camera = this.camera; + const shadowMatrix = this.matrix; - } + const far = light.distance || camera.far; - } + if ( far !== camera.far ) { - const boundingSphere = json.data.boundingSphere; + camera.far = far; + camera.updateProjectionMatrix(); - if ( boundingSphere !== undefined ) { + } - const center = new Vector3(); + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + camera.position.copy( _lightPositionWorld ); - if ( boundingSphere.center !== undefined ) { + _lookTarget.copy( camera.position ); + _lookTarget.add( this._cubeDirections[ viewportIndex ] ); + camera.up.copy( this._cubeUps[ viewportIndex ] ); + camera.lookAt( _lookTarget ); + camera.updateMatrixWorld(); - center.fromArray( boundingSphere.center ); + shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); - } + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix ); - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); + } - } + } - if ( json.name ) geometry.name = json.name; - if ( json.userData ) geometry.userData = json.userData; + PointLightShadow.prototype.isPointLightShadow = true; - return geometry; + class PointLight extends Light { - } + constructor( color, intensity, distance = 0, decay = 1 ) { - } ); + super( color, intensity ); - function ImageBitmapLoader( manager ) { + this.type = 'PointLight'; - if ( typeof createImageBitmap === 'undefined' ) { + this.distance = distance; + this.decay = decay; // for physically correct lights, should be 2. - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); + this.shadow = new PointLightShadow(); } - if ( typeof fetch === 'undefined' ) { + get power() { - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); + // compute the light's luminous power (in lumens) from its intensity (in candela) + // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) + return this.intensity * 4 * Math.PI; } - Loader.call( this, manager ); + set power( power ) { - this.options = { premultiplyAlpha: 'none' }; + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / ( 4 * Math.PI ); - } - - ImageBitmapLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + } - constructor: ImageBitmapLoader, + dispose() { - isImageBitmapLoader: true, + this.shadow.dispose(); - setOptions: function setOptions( options ) { + } - this.options = options; + copy( source ) { - return this; + super.copy( source ); - }, + this.distance = source.distance; + this.decay = source.decay; - load: function ( url, onLoad, onProgress, onError ) { + this.shadow = source.shadow.clone(); - if ( url === undefined ) url = ''; + return this; - if ( this.path !== undefined ) url = this.path + url; + } - url = this.manager.resolveURL( url ); + } - const scope = this; + PointLight.prototype.isPointLight = true; - const cached = Cache.get( url ); + class DirectionalLightShadow extends LightShadow { - if ( cached !== undefined ) { + constructor() { - scope.manager.itemStart( url ); + super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - setTimeout( function () { + } - if ( onLoad ) onLoad( cached ); + } - scope.manager.itemEnd( url ); + DirectionalLightShadow.prototype.isDirectionalLightShadow = true; - }, 0 ); + class DirectionalLight extends Light { - return cached; + constructor( color, intensity ) { - } + super( color, intensity ); - const fetchOptions = {}; - fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; + this.type = 'DirectionalLight'; - fetch( url, fetchOptions ).then( function ( res ) { + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - return res.blob(); + this.target = new Object3D(); - } ).then( function ( blob ) { + this.shadow = new DirectionalLightShadow(); - return createImageBitmap( blob, scope.options ); + } - } ).then( function ( imageBitmap ) { + dispose() { - Cache.add( url, imageBitmap ); + this.shadow.dispose(); - if ( onLoad ) onLoad( imageBitmap ); + } - scope.manager.itemEnd( url ); + copy( source ) { - } ).catch( function ( e ) { + super.copy( source ); - if ( onError ) onError( e ); + this.target = source.target.clone(); + this.shadow = source.shadow.clone(); - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + return this; - } ); + } - scope.manager.itemStart( url ); + } - } + DirectionalLight.prototype.isDirectionalLight = true; - } ); + class AmbientLight extends Light { - function ShapePath() { + constructor( color, intensity ) { - this.type = 'ShapePath'; + super( color, intensity ); - this.color = new Color(); + this.type = 'AmbientLight'; - this.subPaths = []; - this.currentPath = null; + } } - Object.assign( ShapePath.prototype, { + AmbientLight.prototype.isAmbientLight = true; - moveTo: function ( x, y ) { + class RectAreaLight extends Light { - this.currentPath = new Path(); - this.subPaths.push( this.currentPath ); - this.currentPath.moveTo( x, y ); + constructor( color, intensity, width = 10, height = 10 ) { - return this; + super( color, intensity ); - }, + this.type = 'RectAreaLight'; - lineTo: function ( x, y ) { + this.width = width; + this.height = height; - this.currentPath.lineTo( x, y ); + } - return this; + get power() { - }, + // compute the light's luminous power (in lumens) from its intensity (in nits) + return this.intensity * this.width * this.height * Math.PI; - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + } - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); + set power( power ) { - return this; + // set the light's intensity (in nits) from the desired luminous power (in lumens) + this.intensity = power / ( this.width * this.height * Math.PI ); - }, + } + + copy( source ) { - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + super.copy( source ); - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); + this.width = source.width; + this.height = source.height; return this; - }, + } - splineThru: function ( pts ) { + toJSON( meta ) { - this.currentPath.splineThru( pts ); + const data = super.toJSON( meta ); - return this; + data.object.width = this.width; + data.object.height = this.height; - }, + return data; + + } - toShapes: function ( isCCW, noHoles ) { + } - function toShapesNoHoles( inSubpaths ) { + RectAreaLight.prototype.isRectAreaLight = true; - const shapes = []; + /** + * Primary reference: + * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * + * Secondary reference: + * https://www.ppsloan.org/publications/StupidSH36.pdf + */ - for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) { + // 3-band SH defined by 9 coefficients - const tmpPath = inSubpaths[ i ]; + class SphericalHarmonics3 { - const tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; + constructor() { - shapes.push( tmpShape ); + this.coefficients = []; - } + for ( let i = 0; i < 9; i ++ ) { - return shapes; + this.coefficients.push( new Vector3() ); } - function isPointInsidePolygon( inPt, inPolygon ) { + } - const polyLen = inPolygon.length; + set( coefficients ) { - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - let inside = false; - for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + for ( let i = 0; i < 9; i ++ ) { - let edgeLowPt = inPolygon[ p ]; - let edgeHighPt = inPolygon[ q ]; + this.coefficients[ i ].copy( coefficients[ i ] ); - let edgeDx = edgeHighPt.x - edgeLowPt.x; - let edgeDy = edgeHighPt.y - edgeLowPt.y; + } - if ( Math.abs( edgeDy ) > Number.EPSILON ) { + return this; - // not parallel - if ( edgeDy < 0 ) { + } - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + zero() { - } + for ( let i = 0; i < 9; i ++ ) { - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + this.coefficients[ i ].set( 0, 0, 0 ); - if ( inPt.y === edgeLowPt.y ) { + } - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! + return this; - } else { + } - const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt + // get the radiance in the direction of the normal + // target is a Vector3 + getAt( normal, target ) { - } + // normal is assumed to be unit length - } else { + const x = normal.x, y = normal.y, z = normal.z; - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; + const coeff = this.coefficients; - } + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - } + // band 1 + target.addScaledVector( coeff[ 1 ], 0.488603 * y ); + target.addScaledVector( coeff[ 2 ], 0.488603 * z ); + target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - return inside; + // band 2 + target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); + target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); + target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); + target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); + target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - } + return target; - const isClockWise = ShapeUtils.isClockWise; + } - const subPaths = this.subPaths; - if ( subPaths.length === 0 ) return []; + // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal + // target is a Vector3 + // https://graphics.stanford.edu/papers/envmap/envmap.pdf + getIrradianceAt( normal, target ) { - if ( noHoles === true ) return toShapesNoHoles( subPaths ); + // normal is assumed to be unit length + const x = normal.x, y = normal.y, z = normal.z; - let solid, tmpPath, tmpShape; - const shapes = []; + const coeff = this.coefficients; - if ( subPaths.length === 1 ) { + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; + // band 1 + target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 + target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); + target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - } + // band 2 + target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 + target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); + target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 + target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); + target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; + return target; - // console.log("Holes first", holesFirst); + } - const betterShapeHoles = []; - const newShapes = []; - let newShapeHoles = []; - let mainIdx = 0; - let tmpPoints; + add( sh ) { - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; + for ( let i = 0; i < 9; i ++ ) { - for ( let i = 0, l = subPaths.length; i < l; i ++ ) { + this.coefficients[ i ].add( sh.coefficients[ i ] ); - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; + } - if ( solid ) { + return this; - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + } - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; + addScaledSH( sh, s ) { - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; + for ( let i = 0; i < 9; i ++ ) { - //console.log('cw', i); + this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - } else { + } - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + return this; - //console.log('ccw', i); + } - } + scale( s ) { - } + for ( let i = 0; i < 9; i ++ ) { - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + this.coefficients[ i ].multiplyScalar( s ); + } - if ( newShapes.length > 1 ) { + return this; - let ambiguous = false; - const toChange = []; + } - for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + lerp( sh, alpha ) { - betterShapeHoles[ sIdx ] = []; + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + } - const sho = newShapeHoles[ sIdx ]; + return this; - for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) { + } - const ho = sho[ hIdx ]; - let hole_unassigned = true; + equals( sh ) { - for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + for ( let i = 0; i < 9; i ++ ) { - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { + return false; - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); + } - } else { + } - ambiguous = true; + return true; - } + } - } + copy( sh ) { - } + return this.set( sh.coefficients ); - if ( hole_unassigned ) { + } - betterShapeHoles[ sIdx ].push( ho ); + clone() { - } + return new this.constructor().copy( this ); - } + } - } - // console.log("ambiguous: ", ambiguous); + fromArray( array, offset = 0 ) { - if ( toChange.length > 0 ) { + const coefficients = this.coefficients; - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + for ( let i = 0; i < 9; i ++ ) { - } + coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); } - let tmpHoles; + return this; - for ( let i = 0, il = newShapes.length; i < il; i ++ ) { + } - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; + toArray( array = [], offset = 0 ) { - for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + const coefficients = this.coefficients; - tmpShape.holes.push( tmpHoles[ j ].h ); + for ( let i = 0; i < 9; i ++ ) { - } + coefficients[ i ].toArray( array, offset + ( i * 3 ) ); } - //console.log("shape", shapes); - - return shapes; + return array; } - } ); + // evaluate the basis functions + // shBasis is an Array[ 9 ] + static getBasisAt( normal, shBasis ) { - class Font { + // normal is assumed to be unit length - constructor( data ) { + const x = normal.x, y = normal.y, z = normal.z; - Object.defineProperty( this, 'isFont', { value: true } ); + // band 0 + shBasis[ 0 ] = 0.282095; - this.type = 'Font'; + // band 1 + shBasis[ 1 ] = 0.488603 * y; + shBasis[ 2 ] = 0.488603 * z; + shBasis[ 3 ] = 0.488603 * x; - this.data = data; + // band 2 + shBasis[ 4 ] = 1.092548 * x * y; + shBasis[ 5 ] = 1.092548 * y * z; + shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); + shBasis[ 7 ] = 1.092548 * x * z; + shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); } - generateShapes( text, size = 100 ) { + } - const shapes = []; - const paths = createPaths( text, size, this.data ); + SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; - for ( let p = 0, pl = paths.length; p < pl; p ++ ) { + class LightProbe extends Light { - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { - } + super( undefined, intensity ); - return shapes; + this.sh = sh; } - } + copy( source ) { + + super.copy( source ); - function createPaths( text, size, data ) { + this.sh.copy( source.sh ); - const chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988 - const scale = size / data.resolution; - const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; + return this; - const paths = []; + } - let offsetX = 0, offsetY = 0; + fromJSON( json ) { - for ( let i = 0; i < chars.length; i ++ ) { + this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); + this.sh.fromArray( json.sh ); - const char = chars[ i ]; + return this; - if ( char === '\n' ) { + } - offsetX = 0; - offsetY -= line_height; + toJSON( meta ) { - } else { + const data = super.toJSON( meta ); - const ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); + data.object.sh = this.sh.toArray(); - } + return data; } - return paths; - } - function createPath( char, scale, offsetX, offsetY, data ) { + LightProbe.prototype.isLightProbe = true; - const glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; + class LoaderUtils { - if ( ! glyph ) { + static decodeText( array ) { - console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); + if ( typeof TextDecoder !== 'undefined' ) { - return; + return new TextDecoder().decode( array ); - } + } - const path = new ShapePath(); + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. + + let s = ''; - let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; + for ( let i = 0, il = array.length; i < il; i ++ ) { - if ( glyph.o ) { + // Implicitly assumes little-endian. + s += String.fromCharCode( array[ i ] ); - const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + } - for ( let i = 0, l = outline.length; i < l; ) { + try { - const action = outline[ i ++ ]; + // merges multi-byte utf-8 characters. - switch ( action ) { + return decodeURIComponent( escape( s ) ); - case 'm': // moveTo + } catch ( e ) { // see #16358 - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; + return s; - path.moveTo( x, y ); + } - break; + } - case 'l': // lineTo + static extractUrlBase( url ) { - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; + const index = url.lastIndexOf( '/' ); - path.lineTo( x, y ); + if ( index === - 1 ) return './'; - break; + return url.substr( 0, index + 1 ); - case 'q': // quadraticCurveTo + } - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; + static resolveURL( url, path ) { - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + // Invalid URL + if ( typeof url !== 'string' || url === '' ) return ''; - break; + // Host Relative URL + if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - case 'b': // bezierCurveTo + path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; + } - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) return url; - break; + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) return url; - } + // Blob URL + if ( /^blob:.*$/i.test( url ) ) return url; - } + // Relative URL + return path + url; } - return { offsetX: glyph.ha * scale, path: path }; - } - function FontLoader( manager ) { + class InstancedBufferGeometry extends BufferGeometry { - Loader.call( this, manager ); + constructor() { - } + super(); - FontLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + this.type = 'InstancedBufferGeometry'; + this.instanceCount = Infinity; - constructor: FontLoader, + } - load: function ( url, onLoad, onProgress, onError ) { + copy( source ) { + + super.copy( source ); + + this.instanceCount = source.instanceCount; + + return this; + + } + + clone() { + + return new this.constructor().copy( this ); + + } + + toJSON() { + + const data = super.toJSON( this ); + + data.instanceCount = this.instanceCount; + + data.isInstancedBufferGeometry = true; + + return data; + + } + + } + + InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; + + class ImageBitmapLoader extends Loader { + + constructor( manager ) { + + super( manager ); + + if ( typeof createImageBitmap === 'undefined' ) { + + console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); + + } + + if ( typeof fetch === 'undefined' ) { + + console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); + + } + + this.options = { premultiplyAlpha: 'none' }; + + } + + setOptions( options ) { + + this.options = options; + + return this; + + } + + load( url, onLoad, onProgress, onError ) { + + if ( url === undefined ) url = ''; + + if ( this.path !== undefined ) url = this.path + url; + + url = this.manager.resolveURL( url ); const scope = this; - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); - loader.load( url, function ( text ) { + const cached = Cache.get( url ); - let json; + if ( cached !== undefined ) { - try { + scope.manager.itemStart( url ); - json = JSON.parse( text ); + setTimeout( function () { - } catch ( e ) { + if ( onLoad ) onLoad( cached ); + + scope.manager.itemEnd( url ); - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); + }, 0 ); - } + return cached; - const font = scope.parse( json ); + } - if ( onLoad ) onLoad( font ); + const fetchOptions = {}; + fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; + fetchOptions.headers = this.requestHeader; - }, onProgress, onError ); + fetch( url, fetchOptions ).then( function ( res ) { - }, + return res.blob(); + + } ).then( function ( blob ) { - parse: function ( json ) { + return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); - return new Font( json ); + } ).then( function ( imageBitmap ) { + + Cache.add( url, imageBitmap ); + + if ( onLoad ) onLoad( imageBitmap ); + + scope.manager.itemEnd( url ); + + } ).catch( function ( e ) { + + if ( onError ) onError( e ); + + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); + + } ); + + scope.manager.itemStart( url ); } - } ); + } + + ImageBitmapLoader.prototype.isImageBitmapLoader = true; let _context; @@ -41351,25 +40355,23 @@ }; - function AudioLoader( manager ) { - - Loader.call( this, manager ); + class AudioLoader extends Loader { - } + constructor( manager ) { - AudioLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + super( manager ); - constructor: AudioLoader, + } - load: function ( url, onLoad, onProgress, onError ) { + load( url, onLoad, onProgress, onError ) { const scope = this; - const loader = new FileLoader( scope.manager ); + const loader = new FileLoader( this.manager ); loader.setResponseType( 'arraybuffer' ); - loader.setPath( scope.path ); - loader.setRequestHeader( scope.requestHeader ); - loader.setWithCredentials( scope.withCredentials ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( buffer ) { try { @@ -41405,183 +40407,122 @@ } - } ); - - function HemisphereLightProbe( skyColor, groundColor, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - const color1 = new Color().set( skyColor ); - const color2 = new Color().set( groundColor ); - - const sky = new Vector3( color1.r, color1.g, color1.b ); - const ground = new Vector3( color2.r, color2.g, color2.b ); - - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - const c0 = Math.sqrt( Math.PI ); - const c1 = c0 * Math.sqrt( 0.75 ); - - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - } - HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { + class HemisphereLightProbe extends LightProbe { - constructor: HemisphereLightProbe, + constructor( skyColor, groundColor, intensity = 1 ) { - isHemisphereLightProbe: true, + super( undefined, intensity ); - copy: function ( source ) { // modifying colors not currently supported + const color1 = new Color().set( skyColor ); + const color2 = new Color().set( groundColor ); - LightProbe.prototype.copy.call( this, source ); + const sky = new Vector3( color1.r, color1.g, color1.b ); + const ground = new Vector3( color2.r, color2.g, color2.b ); - return this; - - }, - - toJSON: function ( meta ) { - - const data = LightProbe.prototype.toJSON.call( this, meta ); + // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); + const c0 = Math.sqrt( Math.PI ); + const c1 = c0 * Math.sqrt( 0.75 ); - // data.sh = this.sh.toArray(); // todo - - return data; + this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); + this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); } - } ); - - function AmbientLightProbe( color, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - const color1 = new Color().set( color ); - - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); - } - AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: AmbientLightProbe, + HemisphereLightProbe.prototype.isHemisphereLightProbe = true; - isAmbientLightProbe: true, + class AmbientLightProbe extends LightProbe { - copy: function ( source ) { // modifying color not currently supported + constructor( color, intensity = 1 ) { - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, + super( undefined, intensity ); - toJSON: function ( meta ) { + const color1 = new Color().set( color ); - const data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; + // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); + this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); } - } ); - - const _eyeRight = new Matrix4(); - const _eyeLeft = new Matrix4(); + } - function StereoCamera() { + AmbientLightProbe.prototype.isAmbientLightProbe = true; - this.type = 'StereoCamera'; + class Clock { - this.aspect = 1; + constructor( autoStart = true ) { - this.eyeSep = 0.064; + this.autoStart = autoStart; - this.cameraL = new PerspectiveCamera(); - this.cameraL.layers.enable( 1 ); - this.cameraL.matrixAutoUpdate = false; + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; - this.cameraR = new PerspectiveCamera(); - this.cameraR.layers.enable( 2 ); - this.cameraR.matrixAutoUpdate = false; + this.running = false; - this._cache = { - focus: null, - fov: null, - aspect: null, - near: null, - far: null, - zoom: null, - eyeSep: null - }; + } - } + start() { - Object.assign( StereoCamera.prototype, { + this.startTime = now(); - update: function ( camera ) { + this.oldTime = this.startTime; + this.elapsedTime = 0; + this.running = true; - const cache = this._cache; + } - const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || - cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || - cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; + stop() { - if ( needsUpdate ) { + this.getElapsedTime(); + this.running = false; + this.autoStart = false; - cache.focus = camera.focus; - cache.fov = camera.fov; - cache.aspect = camera.aspect * this.aspect; - cache.near = camera.near; - cache.far = camera.far; - cache.zoom = camera.zoom; - cache.eyeSep = this.eyeSep; + } - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ + getElapsedTime() { - const projectionMatrix = camera.projectionMatrix.clone(); - const eyeSepHalf = cache.eyeSep / 2; - const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; - const ymax = ( cache.near * Math.tan( MathUtils.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; - let xmin, xmax; + this.getDelta(); + return this.elapsedTime; - // translate xOffset + } - _eyeLeft.elements[ 12 ] = - eyeSepHalf; - _eyeRight.elements[ 12 ] = eyeSepHalf; + getDelta() { - // for left eye + let diff = 0; - xmin = - ymax * cache.aspect + eyeSepOnProjection; - xmax = ymax * cache.aspect + eyeSepOnProjection; + if ( this.autoStart && ! this.running ) { - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + this.start(); + return 0; - this.cameraL.projectionMatrix.copy( projectionMatrix ); + } - // for right eye + if ( this.running ) { - xmin = - ymax * cache.aspect - eyeSepOnProjection; - xmax = ymax * cache.aspect - eyeSepOnProjection; + const newTime = now(); - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + diff = ( newTime - this.oldTime ) / 1000; + this.oldTime = newTime; - this.cameraR.projectionMatrix.copy( projectionMatrix ); + this.elapsedTime += diff; } - this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); - this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); + return diff; } - } ); + } + + function now() { + + return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 + + } class Audio extends Object3D { @@ -41971,82 +40912,82 @@ } - function PropertyMixer( binding, typeName, valueSize ) { + class PropertyMixer { - this.binding = binding; - this.valueSize = valueSize; + constructor( binding, typeName, valueSize ) { - let mixFunction, - mixFunctionAdditive, - setIdentity; + this.binding = binding; + this.valueSize = valueSize; - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results + let mixFunction, + mixFunctionAdditive, + setIdentity; - switch ( typeName ) { + // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property + // + // 'add' is used for additive cumulative results + // + // 'work' is optional and is only present for quaternion types. It is used + // to store intermediate quaternion multiplication results - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; + switch ( typeName ) { - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; + case 'quaternion': + mixFunction = this._slerp; + mixFunctionAdditive = this._slerpAdditive; + setIdentity = this._setAdditiveIdentityQuaternion; - case 'string': - case 'bool': - mixFunction = this._select; + this.buffer = new Float64Array( valueSize * 6 ); + this._workIndex = 5; + break; - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; + case 'string': + case 'bool': + mixFunction = this._select; - setIdentity = this._setAdditiveIdentityOther; + // Use the regular mix function and for additive on these types, + // additive is not relevant for non-numeric types + mixFunctionAdditive = this._select; - this.buffer = new Array( valueSize * 5 ); - break; + setIdentity = this._setAdditiveIdentityOther; - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; + this.buffer = new Array( valueSize * 5 ); + break; - this.buffer = new Float64Array( valueSize * 5 ); + default: + mixFunction = this._lerp; + mixFunctionAdditive = this._lerpAdditive; + setIdentity = this._setAdditiveIdentityNumeric; - } + this.buffer = new Float64Array( valueSize * 5 ); - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; + } - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + this._mixBufferRegion = mixFunction; + this._mixBufferRegionAdditive = mixFunctionAdditive; + this._setIdentity = setIdentity; + this._origIndex = 3; + this._addIndex = 4; - this.useCount = 0; - this.referenceCount = 0; + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - } + this.useCount = 0; + this.referenceCount = 0; - Object.assign( PropertyMixer.prototype, { + } // accumulate data in the 'incoming' region into 'accu' - accumulate: function ( accuIndex, weight ) { + accumulate( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn't have made the call in the first place @@ -42081,10 +41022,10 @@ this.cumulativeWeight = currentWeight; - }, + } // accumulate data in the 'incoming' region into 'add' - accumulateAdditive: function ( weight ) { + accumulateAdditive( weight ) { const buffer = this.buffer, stride = this.valueSize, @@ -42103,10 +41044,10 @@ this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); this.cumulativeWeightAdditive += weight; - }, + } // apply the state of 'accu' to the binding when accus differ - apply: function ( accuIndex ) { + apply( accuIndex ) { const stride = this.valueSize, buffer = this.buffer, @@ -42152,10 +41093,10 @@ } - }, + } // remember the state of the bound property and copy it to both accus - saveOriginalState: function () { + saveOriginalState() { const binding = this.binding; @@ -42179,17 +41120,17 @@ this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; - }, + } // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState: function () { + restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); - }, + } - _setAdditiveIdentityNumeric: function () { + _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; @@ -42200,16 +41141,16 @@ } - }, + } - _setAdditiveIdentityQuaternion: function () { + _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; - }, + } - _setAdditiveIdentityOther: function () { + _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; @@ -42220,12 +41161,12 @@ } - }, + } // mix functions - _select: function ( buffer, dstOffset, srcOffset, t, stride ) { + _select( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { @@ -42237,15 +41178,15 @@ } - }, + } - _slerp: function ( buffer, dstOffset, srcOffset, t ) { + _slerp( buffer, dstOffset, srcOffset, t ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); - }, + } - _slerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { + _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { const workOffset = this._workIndex * stride; @@ -42255,9 +41196,9 @@ // Slerp to the intermediate result Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - }, + } - _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { + _lerp( buffer, dstOffset, srcOffset, t, stride ) { const s = 1 - t; @@ -42269,9 +41210,9 @@ } - }, + } - _lerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { + _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { for ( let i = 0; i !== stride; ++ i ) { @@ -42283,7 +41224,7 @@ } - } ); + } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; @@ -42321,18 +41262,18 @@ const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; - function Composite( targetGroup, path, optionalParsedPath ) { + class Composite { - const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); + constructor( targetGroup, path, optionalParsedPath ) { - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); + const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - } + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_( path, parsedPath ); - Object.assign( Composite.prototype, { + } - getValue: function ( array, offset ) { + getValue( array, offset ) { this.bind(); // bind all binding @@ -42342,9 +41283,9 @@ // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); - }, + } - setValue: function ( array, offset ) { + setValue( array, offset ) { const bindings = this._bindings; @@ -42354,9 +41295,9 @@ } - }, + } - bind: function () { + bind() { const bindings = this._bindings; @@ -42366,9 +41307,9 @@ } - }, + } - unbind: function () { + unbind() { const bindings = this._bindings; @@ -42380,25 +41321,32 @@ } - } ); + } + // Note: This class uses a State pattern on a per-method basis: + // 'bind' sets 'this.getValue' / 'setValue' and shadows the + // prototype version of these methods with one that represents + // the bound state. When the property is not found, the methods + // become no-ops. + class PropertyBinding { - function PropertyBinding( rootNode, path, parsedPath ) { + constructor( rootNode, path, parsedPath ) { - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); + this.path = path; + this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; + this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; - this.rootNode = rootNode; + this.rootNode = rootNode; - } + // initial state of these methods that calls 'bind' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; - Object.assign( PropertyBinding, { + } - Composite: Composite, - create: function ( root, path, parsedPath ) { + static create( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { @@ -42410,7 +41358,7 @@ } - }, + } /** * Replaces spaces with underscores and removes unsupported characters from @@ -42419,13 +41367,13 @@ * @param {string} name Node name to be sanitized. * @return {string} */ - sanitizeNodeName: function ( name ) { + static sanitizeNodeName( name ) { return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - }, + } - parseTrackName: function ( trackName ) { + static parseTrackName( trackName ) { const matches = _trackRe.exec( trackName ); @@ -42471,9 +41419,9 @@ return results; - }, + } - findNode: function ( root, nodeName ) { + static findNode( root, nodeName ) { if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { @@ -42533,204 +41481,166 @@ } - } ); - - Object.assign( PropertyBinding.prototype, { // prototype, continued - // these are used to "bind" a nonexistent property - _getValue_unavailable: function () {}, - _setValue_unavailable: function () {}, - - BindingType: { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }, + _getValue_unavailable() {} + _setValue_unavailable() {} - Versioning: { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }, - - GetterByBindingType: [ - - function getValue_direct( buffer, offset ) { + // Getters - buffer[ offset ] = this.node[ this.propertyName ]; - - }, + _getValue_direct( buffer, offset ) { - function getValue_array( buffer, offset ) { + buffer[ offset ] = this.targetObject[ this.propertyName ]; - const source = this.resolvedProperty; - - for ( let i = 0, n = source.length; i !== n; ++ i ) { - - buffer[ offset ++ ] = source[ i ]; - - } - - }, - - function getValue_arrayElement( buffer, offset ) { + } - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; + _getValue_array( buffer, offset ) { - }, + const source = this.resolvedProperty; - function getValue_toArray( buffer, offset ) { + for ( let i = 0, n = source.length; i !== n; ++ i ) { - this.resolvedProperty.toArray( buffer, offset ); + buffer[ offset ++ ] = source[ i ]; } - ], + } - SetterByBindingTypeAndVersioning: [ + _getValue_arrayElement( buffer, offset ) { - [ - // Direct + buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - function setValue_direct( buffer, offset ) { + } - this.targetObject[ this.propertyName ] = buffer[ offset ]; + _getValue_toArray( buffer, offset ) { - }, + this.resolvedProperty.toArray( buffer, offset ); - function setValue_direct_setNeedsUpdate( buffer, offset ) { + } - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; + // Direct - }, + _setValue_direct( buffer, offset ) { - function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { + this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + } - } + _setValue_direct_setNeedsUpdate( buffer, offset ) { - ], [ + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - // EntireArray + } - function setValue_array( buffer, offset ) { + _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - const dest = this.resolvedProperty; + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + } - dest[ i ] = buffer[ offset ++ ]; + // EntireArray - } + _setValue_array( buffer, offset ) { - }, + const dest = this.resolvedProperty; - function setValue_array_setNeedsUpdate( buffer, offset ) { + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - const dest = this.resolvedProperty; + dest[ i ] = buffer[ offset ++ ]; - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + } - dest[ i ] = buffer[ offset ++ ]; + } - } + _setValue_array_setNeedsUpdate( buffer, offset ) { - this.targetObject.needsUpdate = true; + const dest = this.resolvedProperty; - }, + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { + dest[ i ] = buffer[ offset ++ ]; - const dest = this.resolvedProperty; + } - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + this.targetObject.needsUpdate = true; - dest[ i ] = buffer[ offset ++ ]; + } - } + _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - this.targetObject.matrixWorldNeedsUpdate = true; + const dest = this.resolvedProperty; - } + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - ], [ + dest[ i ] = buffer[ offset ++ ]; - // ArrayElement + } - function setValue_arrayElement( buffer, offset ) { + this.targetObject.matrixWorldNeedsUpdate = true; - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + } - }, + // ArrayElement - function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { + _setValue_arrayElement( buffer, offset ) { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - }, + } - function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { + _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - } + } - ], [ + _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - // HasToFromArray + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - function setValue_fromArray( buffer, offset ) { + } - this.resolvedProperty.fromArray( buffer, offset ); + // HasToFromArray - }, + _setValue_fromArray( buffer, offset ) { - function setValue_fromArray_setNeedsUpdate( buffer, offset ) { + this.resolvedProperty.fromArray( buffer, offset ); - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; + } - }, + _setValue_fromArray_setNeedsUpdate( buffer, offset ) { - function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.needsUpdate = true; - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; + } - } + _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - ] + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.matrixWorldNeedsUpdate = true; - ], + } - getValue: function getValue_unbound( targetArray, offset ) { + _getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - - }, + } - setValue: function getValue_unbound( sourceArray, offset ) { + _setValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); - }, + } // create getter / setter pair for a property in the scene graph - bind: function () { + bind() { let targetObject = this.node; const parsedPath = this.parsedPath; @@ -42944,9 +41854,9 @@ this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - }, + } - unbind: function () { + unbind() { this.node = null; @@ -42957,399 +41867,65 @@ } - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( PropertyBinding.prototype, { - - // initial state of these methods that calls 'bind' - _getValue_unbound: PropertyBinding.prototype.getValue, - _setValue_unbound: PropertyBinding.prototype.setValue, - - } ); - - /** - * - * A group of objects that receives a shared animation state. - * - * Usage: - * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. - * - * - Instead pass this object as 'root'. - * - * - You can also add and remove objects later when the mixer - * is running. - * - * Note: - * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. - * - * Limitation: - * - * - The animated properties must be compatible among the - * all objects in the group. - * - * - A single property can either be controlled through a - * target group or directly, but not both. - */ - - function AnimationObjectGroup() { - - this.uuid = MathUtils.generateUUID(); - - // cached objects followed by the active ones - this._objects = Array.prototype.slice.call( arguments ); - - this.nCachedObjects_ = 0; // threshold - // note: read by PropertyBinding.Composite - - const indices = {}; - this._indicesByUUID = indices; // for bookkeeping - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - indices[ arguments[ i ].uuid ] = i; - - } - - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays - - const scope = this; - - this.stats = { - - objects: { - get total() { - - return scope._objects.length; - - }, - get inUse() { - - return this.total - scope.nCachedObjects_; - - } - }, - get bindingsPerObject() { - - return scope._bindings.length; - - } - - }; - } - Object.assign( AnimationObjectGroup.prototype, { - - isAnimationObjectGroup: true, - - add: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - nBindings = bindings.length; - - let knownObject = undefined, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid; - let index = indicesByUUID[ uuid ]; - - if ( index === undefined ) { - - // unknown object -> add it to the ACTIVE region - - index = nObjects ++; - indicesByUUID[ uuid ] = index; - objects.push( object ); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); - - } - - } else if ( index < nCachedObjects ) { - - knownObject = objects[ index ]; - - // move existing object to the ACTIVE region - - const firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ]; - - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - indicesByUUID[ uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ]; - - let binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = lastCached; - - if ( binding === undefined ) { - - // since we do not bother to create new bindings - // for objects that are cached, the binding may - // or may not exist - - binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); - - } - - bindingsForPath[ firstActiveIndex ] = binding; - - } - - } else if ( objects[ index ] !== knownObject ) { - - console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + - 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); - - } // else the object is already where we want it to be - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - remove: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - let nCachedObjects = this.nCachedObjects_; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined && index >= nCachedObjects ) { - - // move existing object into the CACHED region - - const lastCachedIndex = nCachedObjects ++, - firstActiveObject = objects[ lastCachedIndex ]; - - indicesByUUID[ firstActiveObject.uuid ] = index; - objects[ index ] = firstActiveObject; - - indicesByUUID[ uuid ] = lastCachedIndex; - objects[ lastCachedIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - firstActive = bindingsForPath[ lastCachedIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = firstActive; - bindingsForPath[ lastCachedIndex ] = binding; - - } - - } - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // remove & forget - uncache: function () { - - const objects = this._objects, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - let nCachedObjects = this.nCachedObjects_, - nObjects = objects.length; - - for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - - const object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined ) { - - delete indicesByUUID[ uuid ]; - - if ( index < nCachedObjects ) { - - // object is cached, shrink the CACHED region - - const firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ], - lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - // last cached object takes this object's place - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - // last object goes to the activated slot and pop - indicesByUUID[ lastObject.uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - last = bindingsForPath[ lastIndex ]; - - bindingsForPath[ index ] = lastCached; - bindingsForPath[ firstActiveIndex ] = last; - bindingsForPath.pop(); - - } - - } else { - - // object is active, just swap with the last and pop - - const lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - if ( lastIndex > 0 ) { - - indicesByUUID[ lastObject.uuid ] = index; - - } - - objects[ index ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( let j = 0, m = nBindings; j !== m; ++ j ) { - - const bindingsForPath = bindings[ j ]; - - bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; - bindingsForPath.pop(); - - } - - } // cached or active - - } // if object is known - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // Internal interface used by befriended PropertyBinding.Composite: - - subscribe_: function ( path, parsedPath ) { - - // returns an array of bindings for the given path that is changed - // according to the contained objects in the group + PropertyBinding.Composite = Composite; - const indicesByPath = this._bindingsIndicesByPath; - let index = indicesByPath[ path ]; - const bindings = this._bindings; - - if ( index !== undefined ) return bindings[ index ]; - - const paths = this._paths, - parsedPaths = this._parsedPaths, - objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - bindingsForPath = new Array( nObjects ); - - index = bindings.length; - - indicesByPath[ path ] = index; - - paths.push( path ); - parsedPaths.push( parsedPath ); - bindings.push( bindingsForPath ); - - for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { - - const object = objects[ i ]; - bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); + PropertyBinding.prototype.BindingType = { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 + }; - } + PropertyBinding.prototype.Versioning = { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 + }; - return bindingsForPath; + PropertyBinding.prototype.GetterByBindingType = [ - }, + PropertyBinding.prototype._getValue_direct, + PropertyBinding.prototype._getValue_array, + PropertyBinding.prototype._getValue_arrayElement, + PropertyBinding.prototype._getValue_toArray, - unsubscribe_: function ( path ) { + ]; - // tells the group to forget about a property path and no longer - // update the array previously obtained with 'subscribe_' + PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ - const indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ]; + [ + // Direct + PropertyBinding.prototype._setValue_direct, + PropertyBinding.prototype._setValue_direct_setNeedsUpdate, + PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, - if ( index !== undefined ) { + ], [ - const paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - lastBindingsIndex = bindings.length - 1, - lastBindings = bindings[ lastBindingsIndex ], - lastBindingsPath = path[ lastBindingsIndex ]; + // EntireArray - indicesByPath[ lastBindingsPath ] = index; + PropertyBinding.prototype._setValue_array, + PropertyBinding.prototype._setValue_array_setNeedsUpdate, + PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, - bindings[ index ] = lastBindings; - bindings.pop(); + ], [ - parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; - parsedPaths.pop(); + // ArrayElement + PropertyBinding.prototype._setValue_arrayElement, + PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, + PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, - paths[ index ] = paths[ lastBindingsIndex ]; - paths.pop(); + ], [ - } + // HasToFromArray + PropertyBinding.prototype._setValue_fromArray, + PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, + PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, - } + ] - } ); + ]; class AnimationAction { @@ -44045,23 +42621,21 @@ } - function AnimationMixer( root ) { - - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - - this.time = 0; + class AnimationMixer extends EventDispatcher { - this.timeScale = 1.0; + constructor( root ) { - } + super(); - AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + this._root = root; + this._initMemoryManager(); + this._accuIndex = 0; + this.time = 0; + this.timeScale = 1.0; - constructor: AnimationMixer, + } - _bindAction: function ( action, prototypeAction ) { + _bindAction( action, prototypeAction ) { const root = action._localRoot || this._root, tracks = action._clip.tracks, @@ -44128,9 +42702,9 @@ } - }, + } - _activateAction: function ( action ) { + _activateAction( action ) { if ( ! this._isActiveAction( action ) ) { @@ -44170,9 +42744,9 @@ } - }, + } - _deactivateAction: function ( action ) { + _deactivateAction( action ) { if ( this._isActiveAction( action ) ) { @@ -44196,11 +42770,11 @@ } - }, + } // Memory manager - _initMemoryManager: function () { + _initMemoryManager() { this._actions = []; // 'nActiveActions' followed by inactive ones this._nActiveActions = 0; @@ -44265,18 +42839,18 @@ }; - }, + } // Memory management for AnimationAction objects - _isActiveAction: function ( action ) { + _isActiveAction( action ) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; - }, + } - _addInactiveAction: function ( action, clipUuid, rootUuid ) { + _addInactiveAction( action, clipUuid, rootUuid ) { const actions = this._actions, actionsByClip = this._actionsByClip; @@ -44310,9 +42884,9 @@ actionsForClip.actionByRoot[ rootUuid ] = action; - }, + } - _removeInactiveAction: function ( action ) { + _removeInactiveAction( action ) { const actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], @@ -44355,9 +42929,9 @@ this._removeInactiveBindingsForAction( action ); - }, + } - _removeInactiveBindingsForAction: function ( action ) { + _removeInactiveBindingsForAction( action ) { const bindings = action._propertyBindings; @@ -44373,9 +42947,9 @@ } - }, + } - _lendAction: function ( action ) { + _lendAction( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] @@ -44396,9 +42970,9 @@ firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; - }, + } - _takeBackAction: function ( action ) { + _takeBackAction( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] @@ -44419,11 +42993,11 @@ lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; - }, + } // Memory management for PropertyMixer objects - _addInactiveBinding: function ( binding, rootUuid, trackName ) { + _addInactiveBinding( binding, rootUuid, trackName ) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; @@ -44442,9 +43016,9 @@ binding._cacheIndex = bindings.length; bindings.push( binding ); - }, + } - _removeInactiveBinding: function ( binding ) { + _removeInactiveBinding( binding ) { const bindings = this._bindings, propBinding = binding.binding, @@ -44468,9 +43042,9 @@ } - }, + } - _lendBinding: function ( binding ) { + _lendBinding( binding ) { const bindings = this._bindings, prevIndex = binding._cacheIndex, @@ -44485,9 +43059,9 @@ firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; - }, + } - _takeBackBinding: function ( binding ) { + _takeBackBinding( binding ) { const bindings = this._bindings, prevIndex = binding._cacheIndex, @@ -44502,12 +43076,12 @@ lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; - }, + } // Memory management of Interpolants for weight and time scale - _lendControlInterpolant: function () { + _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++; @@ -44527,9 +43101,9 @@ return interpolant; - }, + } - _takeBackControlInterpolant: function ( interpolant ) { + _takeBackControlInterpolant( interpolant ) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, @@ -44544,14 +43118,12 @@ lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; - }, - - _controlInterpolantsResultBuffer: new Float32Array( 1 ), + } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) - clipAction: function ( clip, optionalRoot, blendMode ) { + clipAction( clip, optionalRoot, blendMode ) { const root = optionalRoot || this._root, rootUuid = root.uuid; @@ -44610,10 +43182,10 @@ return newAction; - }, + } // get an existing action - existingAction: function ( clip, optionalRoot ) { + existingAction( clip, optionalRoot ) { const root = optionalRoot || this._root, rootUuid = root.uuid, @@ -44633,10 +43205,10 @@ return null; - }, + } // deactivates all previously scheduled actions - stopAllAction: function () { + stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; @@ -44649,10 +43221,10 @@ return this; - }, + } // advance the time and update apply the animation - update: function ( deltaTime ) { + update( deltaTime ) { deltaTime *= this.timeScale; @@ -44687,10 +43259,10 @@ return this; - }, + } // Allows you to seek to a specific time in an animation. - setTime: function ( timeInSeconds ) { + setTime( timeInSeconds ) { this.time = 0; // Zero out time attribute for AnimationMixer object; for ( let i = 0; i < this._actions.length; i ++ ) { @@ -44701,17 +43273,17 @@ return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - }, + } // return this mixer's root target object - getRoot: function () { + getRoot() { return this._root; - }, + } // free all resources specific to a particular clip - uncacheClip: function ( clip ) { + uncacheClip( clip ) { const actions = this._actions, clipUuid = clip.uuid, @@ -44750,10 +43322,10 @@ } - }, + } // free all resources specific to a particular root target object - uncacheRoot: function ( root ) { + uncacheRoot( root ) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; @@ -44787,10 +43359,10 @@ } - }, + } // remove a targeted clip from the cache - uncacheAction: function ( clip, optionalRoot ) { + uncacheAction( clip, optionalRoot ) { const action = this.existingAction( clip, optionalRoot ); @@ -44803,68 +43375,43 @@ } - } ); - - class Uniform { - - constructor( value ) { - - if ( typeof value === 'string' ) { - - console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); - value = arguments[ 1 ]; - - } - - this.value = value; - - } - - clone() { - - return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); - - } - } - function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { + AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array( 1 ); - InterleavedBuffer.call( this, array, stride ); + class InstancedInterleavedBuffer extends InterleavedBuffer { - this.meshPerAttribute = meshPerAttribute || 1; - - } + constructor( array, stride, meshPerAttribute = 1 ) { - InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { + super( array, stride ); - constructor: InstancedInterleavedBuffer, + this.meshPerAttribute = meshPerAttribute; - isInstancedInterleavedBuffer: true, + } - copy: function ( source ) { + copy( source ) { - InterleavedBuffer.prototype.copy.call( this, source ); + super.copy( source ); this.meshPerAttribute = source.meshPerAttribute; return this; - }, + } - clone: function ( data ) { + clone( data ) { - const ib = InterleavedBuffer.prototype.clone.call( this, data ); + const ib = super.clone( data ); ib.meshPerAttribute = this.meshPerAttribute; return ib; - }, + } - toJSON: function ( data ) { + toJSON( data ) { - const json = InterleavedBuffer.prototype.toJSON.call( this, data ); + const json = super.toJSON( data ); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; @@ -44873,97 +43420,85 @@ } - } ); - - function GLBufferAttribute( buffer, type, itemSize, elementSize, count ) { - - this.buffer = buffer; - this.type = type; - this.itemSize = itemSize; - this.elementSize = elementSize; - this.count = count; - - this.version = 0; - } - Object.defineProperty( GLBufferAttribute.prototype, 'needsUpdate', { + InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; - set: function ( value ) { + class Raycaster { - if ( value === true ) this.version ++; + constructor( origin, direction, near = 0, far = Infinity ) { - } + this.ray = new Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) - } ); + this.near = near; + this.far = far; + this.camera = null; + this.layers = new Layers(); + + this.params = { + Mesh: {}, + Line: { threshold: 1 }, + LOD: {}, + Points: { threshold: 1 }, + Sprite: {} + }; - Object.assign( GLBufferAttribute.prototype, { + } - isGLBufferAttribute: true, + set( origin, direction ) { - setBuffer: function ( buffer ) { + // direction is assumed to be normalized (for accurate distance calculations) - this.buffer = buffer; + this.ray.set( origin, direction ); - return this; + } - }, + setFromCamera( coords, camera ) { - setType: function ( type, elementSize ) { + if ( camera && camera.isPerspectiveCamera ) { - this.type = type; - this.elementSize = elementSize; + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); + this.camera = camera; - return this; + } else if ( camera && camera.isOrthographicCamera ) { - }, + this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + this.camera = camera; - setItemSize: function ( itemSize ) { + } else { - this.itemSize = itemSize; + console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); - return this; + } - }, + } - setCount: function ( count ) { + intersectObject( object, recursive = true, intersects = [] ) { - this.count = count; + intersectObject( object, this, intersects, recursive ); - return this; + intersects.sort( ascSort ); - }, + return intersects; - } ); + } - function Raycaster( origin, direction, near, far ) { + intersectObjects( objects, recursive = true, intersects = [] ) { - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) + for ( let i = 0, l = objects.length; i < l; i ++ ) { - this.near = near || 0; - this.far = far || Infinity; - this.camera = null; - this.layers = new Layers(); + intersectObject( objects[ i ], this, intersects, recursive ); - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; + } - Object.defineProperties( this.params, { - PointCloud: { - get: function () { + intersects.sort( ascSort ); - console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); - return this.Points; + return intersects; - } - } - } ); + } } @@ -44995,335 +43530,159 @@ } - Object.assign( Raycaster.prototype, { - - set: function ( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) - - this.ray.set( origin, direction ); - - }, - - setFromCamera: function ( coords, camera ) { - - if ( camera && camera.isPerspectiveCamera ) { - - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; - - } else if ( camera && camera.isOrthographicCamera ) { - - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; - - } else { - - console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); - - } - - }, - - intersectObject: function ( object, recursive, optionalTarget ) { - - const intersects = optionalTarget || []; - - intersectObject( object, this, intersects, recursive ); - - intersects.sort( ascSort ); - - return intersects; - - }, - - intersectObjects: function ( objects, recursive, optionalTarget ) { - - const intersects = optionalTarget || []; - - if ( Array.isArray( objects ) === false ) { - - console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); - return intersects; - - } - - for ( let i = 0, l = objects.length; i < l; i ++ ) { - - intersectObject( objects[ i ], this, intersects, recursive ); - - } - - intersects.sort( ascSort ); - - return intersects; - - } - - } ); - - const _vector$8 = /*@__PURE__*/ new Vector2(); - - class Box2 { + const _vector$2 = /*@__PURE__*/ new Vector3(); + const _boneMatrix = /*@__PURE__*/ new Matrix4(); + const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); - constructor( min, max ) { - Object.defineProperty( this, 'isBox2', { value: true } ); + class SkeletonHelper extends LineSegments { - this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); + constructor( object ) { - } + const bones = getBoneList( object ); - set( min, max ) { + const geometry = new BufferGeometry(); - this.min.copy( min ); - this.max.copy( max ); + const vertices = []; + const colors = []; - return this; + const color1 = new Color( 0, 0, 1 ); + const color2 = new Color( 0, 1, 0 ); - } + for ( let i = 0; i < bones.length; i ++ ) { - setFromPoints( points ) { + const bone = bones[ i ]; - this.makeEmpty(); + if ( bone.parent && bone.parent.isBone ) { - for ( let i = 0, il = points.length; i < il; i ++ ) { + vertices.push( 0, 0, 0 ); + vertices.push( 0, 0, 0 ); + colors.push( color1.r, color1.g, color1.b ); + colors.push( color2.r, color2.g, color2.b ); - this.expandByPoint( points[ i ] ); + } } - return this; - - } - - setFromCenterAndSize( center, size ) { - - const halfSize = _vector$8.copy( size ).multiplyScalar( 0.5 ); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - } - - clone() { - - return new this.constructor().copy( this ); - - } - - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - return this; + const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); - } + super( geometry, material ); - makeEmpty() { + this.type = 'SkeletonHelper'; + this.isSkeletonHelper = true; - this.min.x = this.min.y = + Infinity; - this.max.x = this.max.y = - Infinity; + this.root = object; + this.bones = bones; - return this; + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; } - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); + updateMatrixWorld( force ) { - } + const bones = this.bones; - getCenter( target ) { + const geometry = this.geometry; + const position = geometry.getAttribute( 'position' ); - if ( target === undefined ) { + _matrixWorldInv.copy( this.root.matrixWorld ).invert(); - console.warn( 'THREE.Box2: .getCenter() target is now required' ); - target = new Vector2(); + for ( let i = 0, j = 0; i < bones.length; i ++ ) { - } + const bone = bones[ i ]; - return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + if ( bone.parent && bone.parent.isBone ) { - } + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); - getSize( target ) { + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); - if ( target === undefined ) { + j += 2; - console.warn( 'THREE.Box2: .getSize() target is now required' ); - target = new Vector2(); + } } - return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); - - } - - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - } - - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); + geometry.getAttribute( 'position' ).needsUpdate = true; - return this; + super.updateMatrixWorld( force ); } - expandByScalar( scalar ) { + } - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - return this; + function getBoneList( object ) { - } + const boneList = []; - containsPoint( point ) { + if ( object && object.isBone ) { - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y ? false : true; + boneList.push( object ); } - containsBox( box ) { + for ( let i = 0; i < object.children.length; i ++ ) { - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y; + boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } - getParameter( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { + return boneList; - console.warn( 'THREE.Box2: .getParameter() target is now required' ); - target = new Vector2(); - - } + } - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ) - ); + class GridHelper extends LineSegments { - } + constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { - intersectsBox( box ) { + color1 = new Color( color1 ); + color2 = new Color( color2 ); - // using 4 splitting planes to rule out intersections + const center = divisions / 2; + const step = size / divisions; + const halfSize = size / 2; - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y ? false : true; + const vertices = [], colors = []; - } + for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { - clampPoint( point, target ) { + vertices.push( - halfSize, 0, k, halfSize, 0, k ); + vertices.push( k, 0, - halfSize, k, 0, halfSize ); - if ( target === undefined ) { + const color = i === center ? color1 : color2; - console.warn( 'THREE.Box2: .clampPoint() target is now required' ); - target = new Vector2(); + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; } - return target.copy( point ).clamp( this.min, this.max ); - - } - - distanceToPoint( point ) { - - const clampedPoint = _vector$8.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); - - } - - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - return this; - - } - - union( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - } + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - translate( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - } + const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - equals( box ) { + super( geometry, material ); - return box.min.equals( this.min ) && box.max.equals( this.max ); + this.type = 'GridHelper'; } } - function ImmediateRenderObject( material ) { - - Object3D.call( this ); - - this.material = material; - this.render = function ( /* renderCallback */ ) {}; - - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; - - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; - - this.count = 0; - - } - - ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); - ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; - - ImmediateRenderObject.prototype.isImmediateRenderObject = true; - - const backgroundMaterial = new MeshBasicMaterial( { - side: BackSide, - depthWrite: false, - depthTest: false, - } ); - new Mesh( new BoxGeometry(), backgroundMaterial ); + const _floatView = new Float32Array( 1 ); + new Int32Array( _floatView.buffer ); // @@ -45341,589 +43700,532 @@ // - Object.assign( Path.prototype, { + Path.prototype.fromPoints = function ( points ) { - fromPoints: function ( points ) { + console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); + return this.setFromPoints( points ); - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); + }; - } + GridHelper.prototype.setColors = function () { - } ); + console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - // + }; - function Spline( points ) { + SkeletonHelper.prototype.update = function () { - console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' ); + console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; + }; - } + // - Spline.prototype = Object.create( CatmullRomCurve3.prototype ); + Loader.prototype.extractUrlBase = function ( url ) { - Object.assign( Spline.prototype, { + console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); + return LoaderUtils.extractUrlBase( url ); - initFromArray: function ( /* a */ ) { + }; - console.error( 'THREE.Spline: .initFromArray() has been removed.' ); + Loader.Handlers = { - }, - getControlPointsArray: function ( /* optionalTarget */ ) { + add: function ( /* regex, loader */ ) { - console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' ); + console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); }, - reparametrizeByArcLength: function ( /* samplingCoef */ ) { - console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' ); + get: function ( /* file */ ) { + + console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); } - } ); + }; // - Object.assign( Loader.prototype, { + Box3.prototype.center = function ( optionalTarget ) { + + console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); + return this.getCenter( optionalTarget ); - extractUrlBase: function ( url ) { + }; - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); + Box3.prototype.empty = function () { - } + console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); - } ); + }; - Loader.Handlers = { + Box3.prototype.isIntersectionBox = function ( box ) { - add: function ( /* regex, loader */ ) { + console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); + }; - }, + Box3.prototype.isIntersectionSphere = function ( sphere ) { - get: function ( /* file */ ) { + console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); + }; - } + Box3.prototype.size = function ( optionalTarget ) { + + console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); + return this.getSize( optionalTarget ); }; // - Object.assign( Box2.prototype, { + Sphere.prototype.empty = function () { - center: function ( optionalTarget ) { + console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); - console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + }; - }, - empty: function () { + // - console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + Frustum.prototype.setFromMatrix = function ( m ) { - }, - isIntersectionBox: function ( box ) { + console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); + return this.setFromProjectionMatrix( m ); - console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + }; - }, - size: function ( optionalTarget ) { + // - console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); + Matrix3.prototype.flattenToArrayOffset = function ( array, offset ) { - } - } ); + console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); + return this.toArray( array, offset ); - Object.assign( Box3.prototype, { + }; - center: function ( optionalTarget ) { + Matrix3.prototype.multiplyVector3 = function ( vector ) { - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); - }, - empty: function () { + }; - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + Matrix3.prototype.multiplyVector3Array = function ( /* a */ ) { - }, - isIntersectionBox: function ( box ) { + console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + }; - }, - isIntersectionSphere: function ( sphere ) { + Matrix3.prototype.applyToBufferAttribute = function ( attribute ) { - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); + console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); + return attribute.applyMatrix3( this ); - }, - size: function ( optionalTarget ) { + }; - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); + Matrix3.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - } - } ); + console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); - Object.assign( Sphere.prototype, { + }; - empty: function () { + Matrix3.prototype.getInverse = function ( matrix ) { - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); + return this.copy( matrix ).invert(); - }, + }; - } ); + // - Frustum.prototype.setFromMatrix = function ( m ) { + Matrix4.prototype.extractPosition = function ( m ) { - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); }; - Object.assign( MathUtils, { + Matrix4.prototype.flattenToArrayOffset = function ( array, offset ) { - random16: function () { + console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); + return this.toArray( array, offset ); - console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' ); - return Math.random(); - - }, + }; - nearestPowerOfTwo: function ( value ) { + Matrix4.prototype.getPosition = function () { - console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' ); - return MathUtils.floorPowerOfTwo( value ); + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + return new Vector3().setFromMatrixColumn( this, 3 ); - }, + }; - nextPowerOfTwo: function ( value ) { + Matrix4.prototype.setRotationFromQuaternion = function ( q ) { - console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' ); - return MathUtils.ceilPowerOfTwo( value ); + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + return this.makeRotationFromQuaternion( q ); - } + }; - } ); + Matrix4.prototype.multiplyToArray = function () { - Object.assign( Matrix3.prototype, { + console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - flattenToArrayOffset: function ( array, offset ) { + }; - console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); + Matrix4.prototype.multiplyVector3 = function ( vector ) { - }, - multiplyVector3: function ( vector ) { + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); + }; - }, - multiplyVector3Array: function ( /* a */ ) { + Matrix4.prototype.multiplyVector4 = function ( vector ) { - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - }, - applyToBufferAttribute: function ( attribute ) { + }; - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); + Matrix4.prototype.multiplyVector3Array = function ( /* a */ ) { - }, - applyToVector3Array: function ( /* array, offset, length */ ) { + console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); + }; - }, - getInverse: function ( matrix ) { + Matrix4.prototype.rotateAxis = function ( v ) { - console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + v.transformDirection( this ); - } + }; - } ); + Matrix4.prototype.crossVector = function ( vector ) { - Object.assign( Matrix4.prototype, { + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - extractPosition: function ( m ) { + }; - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); + Matrix4.prototype.translate = function () { - }, - flattenToArrayOffset: function ( array, offset ) { + console.error( 'THREE.Matrix4: .translate() has been removed.' ); - console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); + }; - }, - getPosition: function () { + Matrix4.prototype.rotateX = function () { - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - }, - setRotationFromQuaternion: function ( q ) { + }; - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); + Matrix4.prototype.rotateY = function () { - }, - multiplyToArray: function () { + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); + }; - }, - multiplyVector3: function ( vector ) { + Matrix4.prototype.rotateZ = function () { - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - }, - multiplyVector4: function ( vector ) { + }; - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + Matrix4.prototype.rotateByAxis = function () { - }, - multiplyVector3Array: function ( /* a */ ) { + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); + }; - }, - rotateAxis: function ( v ) { + Matrix4.prototype.applyToBufferAttribute = function ( attribute ) { - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); + console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); + return attribute.applyMatrix4( this ); - }, - crossVector: function ( vector ) { + }; - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + Matrix4.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - }, - translate: function () { + console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - console.error( 'THREE.Matrix4: .translate() has been removed.' ); + }; - }, - rotateX: function () { + Matrix4.prototype.makeFrustum = function ( left, right, bottom, top, near, far ) { - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); + return this.makePerspective( left, right, top, bottom, near, far ); - }, - rotateY: function () { + }; - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + Matrix4.prototype.getInverse = function ( matrix ) { - }, - rotateZ: function () { + console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); + return this.copy( matrix ).invert(); - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + }; - }, - rotateByAxis: function () { + // - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + Plane.prototype.isIntersectionLine = function ( line ) { - }, - applyToBufferAttribute: function ( attribute ) { + console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); + return this.intersectsLine( line ); - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); + }; - }, - applyToVector3Array: function ( /* array, offset, length */ ) { + // - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); + Quaternion.prototype.multiplyVector3 = function ( vector ) { - }, - makeFrustum: function ( left, right, bottom, top, near, far ) { + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); + }; - }, - getInverse: function ( matrix ) { + Quaternion.prototype.inverse = function ( ) { - console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); + console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); + return this.invert(); - } + }; - } ); + // - Plane.prototype.isIntersectionLine = function ( line ) { + Ray.prototype.isIntersectionBox = function ( box ) { - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); + console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); }; - Object.assign( Quaternion.prototype, { + Ray.prototype.isIntersectionPlane = function ( plane ) { - multiplyVector3: function ( vector ) { + console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); + return this.intersectsPlane( plane ); - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); + }; - }, - inverse: function ( ) { + Ray.prototype.isIntersectionSphere = function ( sphere ) { - console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); - return this.invert(); + console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); - } + }; - } ); + // - Object.assign( Ray.prototype, { + Triangle.prototype.area = function () { - isIntersectionBox: function ( box ) { + console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); + return this.getArea(); - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + }; - }, - isIntersectionPlane: function ( plane ) { + Triangle.prototype.barycoordFromPoint = function ( point, target ) { - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); + console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); + return this.getBarycoord( point, target ); - }, - isIntersectionSphere: function ( sphere ) { + }; - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); + Triangle.prototype.midpoint = function ( target ) { - } + console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); + return this.getMidpoint( target ); - } ); + }; - Object.assign( Triangle.prototype, { + Triangle.prototypenormal = function ( target ) { - area: function () { + console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); + return this.getNormal( target ); - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); + }; - }, - barycoordFromPoint: function ( point, target ) { + Triangle.prototype.plane = function ( target ) { - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); + console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); + return this.getPlane( target ); - }, - midpoint: function ( target ) { + }; - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); + Triangle.barycoordFromPoint = function ( point, a, b, c, target ) { - }, - normal: function ( target ) { + console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); + return Triangle.getBarycoord( point, a, b, c, target ); - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); + }; - }, - plane: function ( target ) { + Triangle.normal = function ( a, b, c, target ) { - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); + console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); + return Triangle.getNormal( a, b, c, target ); - } + }; - } ); + // - Object.assign( Triangle, { + Shape.prototype.extractAllPoints = function ( divisions ) { - barycoordFromPoint: function ( point, a, b, c, target ) { + console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); + return this.extractPoints( divisions ); - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); + }; - }, - normal: function ( a, b, c, target ) { + Shape.prototype.extrude = function ( options ) { - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); + console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); + return new ExtrudeGeometry( this, options ); - } + }; - } ); + Shape.prototype.makeGeometry = function ( options ) { + + console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); + return new ShapeGeometry( this, options ); - Object.assign( Shape.prototype, { + }; - extractAllPoints: function ( divisions ) { + // - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); + Vector2.prototype.fromAttribute = function ( attribute, index, offset ) { - }, - extrude: function ( options ) { + console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); + }; - }, - makeGeometry: function ( options ) { + Vector2.prototype.distanceToManhattan = function ( v ) { - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); + console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); + return this.manhattanDistanceTo( v ); - } + }; - } ); + Vector2.prototype.lengthManhattan = function () { - Object.assign( Vector2.prototype, { + console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - fromAttribute: function ( attribute, index, offset ) { + }; - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + // - }, - distanceToManhattan: function ( v ) { + Vector3.prototype.setEulerFromRotationMatrix = function () { - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - }, - lengthManhattan: function () { + }; - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + Vector3.prototype.setEulerFromQuaternion = function () { - } + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - } ); + }; - Object.assign( Vector3.prototype, { + Vector3.prototype.getPositionFromMatrix = function ( m ) { - setEulerFromRotationMatrix: function () { + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + return this.setFromMatrixPosition( m ); - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + }; - }, - setEulerFromQuaternion: function () { + Vector3.prototype.getScaleFromMatrix = function ( m ) { - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + return this.setFromMatrixScale( m ); - }, - getPositionFromMatrix: function ( m ) { + }; - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); + Vector3.prototype.getColumnFromMatrix = function ( index, matrix ) { - }, - getScaleFromMatrix: function ( m ) { + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + return this.setFromMatrixColumn( matrix, index ); - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); + }; - }, - getColumnFromMatrix: function ( index, matrix ) { + Vector3.prototype.applyProjection = function ( m ) { - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); + console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); + return this.applyMatrix4( m ); - }, - applyProjection: function ( m ) { + }; - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); + Vector3.prototype.fromAttribute = function ( attribute, index, offset ) { - }, - fromAttribute: function ( attribute, index, offset ) { + console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + }; - }, - distanceToManhattan: function ( v ) { + Vector3.prototype.distanceToManhattan = function ( v ) { - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); + return this.manhattanDistanceTo( v ); - }, - lengthManhattan: function () { + }; - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + Vector3.prototype.lengthManhattan = function () { - } + console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - } ); + }; - Object.assign( Vector4.prototype, { + // - fromAttribute: function ( attribute, index, offset ) { + Vector4.prototype.fromAttribute = function ( attribute, index, offset ) { - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - }, - lengthManhattan: function () { + }; - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + Vector4.prototype.lengthManhattan = function () { - } + console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - } ); + }; // - Object.assign( Object3D.prototype, { + Object3D.prototype.getChildByName = function ( name ) { + + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); - getChildByName: function ( name ) { + }; - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); + Object3D.prototype.renderDepth = function () { - }, - renderDepth: function () { + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + }; - }, - translate: function ( distance, axis ) { + Object3D.prototype.translate = function ( distance, axis ) { - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); - }, - getWorldRotation: function () { + }; - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); + Object3D.prototype.getWorldRotation = function () { - }, - applyMatrix: function ( matrix ) { + console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); + + }; - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); + Object3D.prototype.applyMatrix = function ( matrix ) { - } + console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); + return this.applyMatrix4( matrix ); - } ); + }; Object.defineProperties( Object3D.prototype, { @@ -45956,15 +44258,11 @@ } ); - Object.assign( Mesh.prototype, { + Mesh.prototype.setDrawMode = function () { - setDrawMode: function () { + console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - }, - - } ); + }; Object.defineProperties( Mesh.prototype, { @@ -45984,57 +44282,12 @@ } ); - Object.defineProperties( LOD.prototype, { - - objects: { - get: function () { - - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; - - } - } - - } ); - - Object.defineProperty( Skeleton.prototype, 'useVertexTexture', { - - get: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - } - - } ); - SkinnedMesh.prototype.initBones = function () { console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); }; - Object.defineProperty( Curve.prototype, '__arcLengthDivisions', { - - get: function () { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - return this.arcLengthDivisions; - - }, - set: function ( value ) { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - this.arcLengthDivisions = value; - - } - - } ); - // PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { @@ -46182,96 +44435,100 @@ } ); - Object.assign( BufferAttribute.prototype, { - setDynamic: function ( value ) { + BufferAttribute.prototype.setDynamic = function ( value ) { - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; + console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); + this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); + return this; - }, - copyIndicesArray: function ( /* indices */ ) { + }; - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); + BufferAttribute.prototype.copyIndicesArray = function ( /* indices */ ) { - }, - setArray: function ( /* array */ ) { + console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); + }, - } - } ); + BufferAttribute.prototype.setArray = function ( /* array */ ) { - Object.assign( BufferGeometry.prototype, { + console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - addIndex: function ( index ) { + }; - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); + // - }, - addAttribute: function ( name, attribute ) { + BufferGeometry.prototype.addIndex = function ( index ) { - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { + }; - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + BufferGeometry.prototype.addAttribute = function ( name, attribute ) { - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - } + if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - if ( name === 'index' ) { + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); + return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - return this; + } - } + if ( name === 'index' ) { - return this.setAttribute( name, attribute ); + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); - }, - addDrawCall: function ( start, count, indexOffset ) { + return this; + + } - if ( indexOffset !== undefined ) { + return this.setAttribute( name, attribute ); - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + }; - } + BufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset ) { - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); + if ( indexOffset !== undefined ) { - }, - clearDrawCalls: function () { + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); + } - }, - computeOffsets: function () { + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); + }; - }, - removeAttribute: function ( name ) { + BufferGeometry.prototype.clearDrawCalls = function () { - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); - return this.deleteAttribute( name ); + }; - }, - applyMatrix: function ( matrix ) { + BufferGeometry.prototype.computeOffsets = function () { - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - } + }; - } ); + BufferGeometry.prototype.removeAttribute = function ( name ) { + + console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); + + return this.deleteAttribute( name ); + + }; + + BufferGeometry.prototype.applyMatrix = function ( matrix ) { + + console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); + return this.applyMatrix4( matrix ); + + }; Object.defineProperties( BufferGeometry.prototype, { @@ -46294,135 +44551,47 @@ } ); - Object.defineProperties( InstancedBufferGeometry.prototype, { + InterleavedBuffer.prototype.setDynamic = function ( value ) { - maxInstancedCount: { - get: function () { + console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); + this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); + return this; - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - return this.instanceCount; + }; - }, - set: function ( value ) { + InterleavedBuffer.prototype.setArray = function ( /* array */ ) { - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - this.instanceCount = value; + console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - } - } + }; - } ); + // - Object.defineProperties( Raycaster.prototype, { + ExtrudeGeometry.prototype.getArrays = function () { - linePrecision: { - get: function () { + console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - return this.params.Line.threshold; + }; - }, - set: function ( value ) { + ExtrudeGeometry.prototype.addShapeList = function () { - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - this.params.Line.threshold = value; + console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); - } - } + }; - } ); + ExtrudeGeometry.prototype.addShape = function () { - Object.defineProperties( InterleavedBuffer.prototype, { + console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); - dynamic: { - get: function () { + }; - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; + // - }, - set: function ( value ) { + Scene.prototype.dispose = function () { - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - this.setUsage( value ); + console.error( 'THREE.Scene: .dispose() has been removed.' ); - } - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - // - - Object.assign( ExtrudeGeometry.prototype, { - - getArrays: function () { - - console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); - - }, - - addShapeList: function () { - - console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); - - }, - - addShape: function () { - - console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); - - } - - } ); - - // - - Object.assign( Scene.prototype, { - - dispose: function () { - - console.error( 'THREE.Scene: .dispose() has been removed.' ); - - } - - } ); - - // - - Object.defineProperties( Uniform.prototype, { - - dynamic: { - set: function () { - - console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); - - } - }, - onUpdate: { - value: function () { - - console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); - return this; - - } - } - - } ); + }; // @@ -46490,44 +44659,20 @@ this.stencilFuncMask = value; } - } - - } ); - - Object.defineProperties( MeshPhongMaterial.prototype, { + }, - metal: { + vertexTangents: { get: function () { - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); - return false; + console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); }, set: function () { - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); - - } - } - - } ); - - Object.defineProperties( MeshPhysicalMaterial.prototype, { - - transparency: { - get: function () { - - console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' ); - return this.transmission; - - }, - set: function ( value ) { - - console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' ); - this.transmission = value; + console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); } - } + }, } ); @@ -46552,152 +44697,172 @@ // - Object.assign( WebGLRenderer.prototype, { + WebGLRenderer.prototype.clearTarget = function ( renderTarget, color, depth, stencil ) { - clearTarget: function ( renderTarget, color, depth, stencil ) { + console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + }; - }, - animate: function ( callback ) { + WebGLRenderer.prototype.animate = function ( callback ) { - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); + console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); + this.setAnimationLoop( callback ); - }, - getCurrentRenderTarget: function () { + }; - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); + WebGLRenderer.prototype.getCurrentRenderTarget = function () { - }, - getMaxAnisotropy: function () { + console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); + return this.getRenderTarget(); - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); + }; - }, - getPrecision: function () { + WebGLRenderer.prototype.getMaxAnisotropy = function () { - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; + console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); + return this.capabilities.getMaxAnisotropy(); - }, - resetGLState: function () { + }; - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); + WebGLRenderer.prototype.getPrecision = function () { - }, - supportsFloatTextures: function () { + console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); + return this.capabilities.precision; - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); + }; - }, - supportsHalfFloatTextures: function () { + WebGLRenderer.prototype.resetGLState = function () { - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); + console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); + return this.state.reset(); - }, - supportsStandardDerivatives: function () { + }; - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); + WebGLRenderer.prototype.supportsFloatTextures = function () { - }, - supportsCompressedTextureS3TC: function () { + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return this.extensions.get( 'OES_texture_float' ); - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); + }; - }, - supportsCompressedTexturePVRTC: function () { + WebGLRenderer.prototype.supportsHalfFloatTextures = function () { - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return this.extensions.get( 'OES_texture_half_float' ); - }, - supportsBlendMinMax: function () { + }; - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); + WebGLRenderer.prototype.supportsStandardDerivatives = function () { - }, - supportsVertexTextures: function () { + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return this.extensions.get( 'OES_standard_derivatives' ); - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; + }; - }, - supportsInstancedArrays: function () { + WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - }, - enableScissorTest: function ( boolean ) { + }; - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); + WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { - }, - initMaterial: function () { + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + }; - }, - addPrePlugin: function () { + WebGLRenderer.prototype.supportsBlendMinMax = function () { - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return this.extensions.get( 'EXT_blend_minmax' ); - }, - addPostPlugin: function () { + }; - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + WebGLRenderer.prototype.supportsVertexTextures = function () { - }, - updateShadowMap: function () { + console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); + return this.capabilities.vertexTextures; - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + }; - }, - setFaceCulling: function () { + WebGLRenderer.prototype.supportsInstancedArrays = function () { - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return this.extensions.get( 'ANGLE_instanced_arrays' ); - }, - allocTextureUnit: function () { + }; - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); + WebGLRenderer.prototype.enableScissorTest = function ( boolean ) { - }, - setTexture: function () { + console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); + this.setScissorTest( boolean ); + + }; - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); + WebGLRenderer.prototype.initMaterial = function () { - }, - setTexture2D: function () { + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); + }; - }, - setTextureCube: function () { + WebGLRenderer.prototype.addPrePlugin = function () { - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - }, - getActiveMipMapLevel: function () { + }; - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); + WebGLRenderer.prototype.addPostPlugin = function () { - } + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - } ); + }; + + WebGLRenderer.prototype.updateShadowMap = function () { + + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + + }; + + WebGLRenderer.prototype.setFaceCulling = function () { + + console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); + + }; + + WebGLRenderer.prototype.allocTextureUnit = function () { + + console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); + + }; + + WebGLRenderer.prototype.setTexture = function () { + + console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); + + }; + + WebGLRenderer.prototype.setTexture2D = function () { + + console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); + + }; + + WebGLRenderer.prototype.setTextureCube = function () { + + console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); + + }; + + WebGLRenderer.prototype.getActiveMipMapLevel = function () { + + console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); + return this.getActiveMipmapLevel(); + + }; Object.defineProperties( WebGLRenderer.prototype, { @@ -46992,32 +45157,19 @@ // - Object.defineProperties( Audio.prototype, { - - load: { - value: function ( file ) { - - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - const scope = this; - const audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { - - scope.setBuffer( buffer ); + Audio.prototype.load = function ( file ) { - } ); - return this; - - } - }, - startTime: { - set: function () { + console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); + const scope = this; + const audioLoader = new AudioLoader(); + audioLoader.load( file, function ( buffer ) { - console.warn( 'THREE.Audio: .startTime is now .play( delay ).' ); + scope.setBuffer( buffer ); - } - } + } ); + return this; - } ); + }; // @@ -51420,2342 +49572,717 @@ } } - /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Ported from Webkit - * http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h - */ - var unitbezier = UnitBezier; + class ComponentService { + constructor(container, navigator) { + this._components = {}; + for (const componentName in ComponentService.registeredComponents) { + if (!ComponentService.registeredComponents.hasOwnProperty(componentName)) { + continue; + } + const component = ComponentService.registeredComponents[componentName]; + this._components[componentName] = { + active: false, + component: new component(componentName, container, navigator), + }; + } + this._coverComponent = new ComponentService.registeredCoverComponent("cover", container, navigator); + this._coverComponent.activate(); + this._coverActivated = true; + } + static register(component) { + if (ComponentService.registeredComponents[component.componentName] === undefined) { + ComponentService.registeredComponents[component.componentName] = component; + } + } + static registerCover(coverComponent) { + ComponentService.registeredCoverComponent = coverComponent; + } + get coverActivated() { + return this._coverActivated; + } + activateCover() { + if (this._coverActivated) { + return; + } + this._coverActivated = true; + for (const componentName in this._components) { + if (!this._components.hasOwnProperty(componentName)) { + continue; + } + const component = this._components[componentName]; + if (component.active) { + component.component.deactivate(); + } + } + } + deactivateCover() { + if (!this._coverActivated) { + return; + } + this._coverActivated = false; + for (const componentName in this._components) { + if (!this._components.hasOwnProperty(componentName)) { + continue; + } + const component = this._components[componentName]; + if (component.active) { + component.component.activate(); + } + } + } + activate(name) { + this._checkName(name); + this._components[name].active = true; + if (!this._coverActivated) { + this.get(name).activate(); + } + } + configure(name, conf) { + this._checkName(name); + this.get(name).configure(conf); + } + deactivate(name) { + this._checkName(name); + this._components[name].active = false; + if (!this._coverActivated) { + this.get(name).deactivate(); + } + } + get(name) { + return this._components[name].component; + } + getCover() { + return this._coverComponent; + } + remove() { + this._coverComponent.deactivate(); + for (const componentName in this._components) { + if (!this._components.hasOwnProperty(componentName)) { + continue; + } + this._components[componentName].component.deactivate(); + } + } + _checkName(name) { + if (!(name in this._components)) { + throw new ArgumentMapillaryError(`Component does not exist: ${name}`); + } + } + } + ComponentService.registeredComponents = {}; - function UnitBezier(p1x, p1y, p2x, p2y) { - // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). - this.cx = 3.0 * p1x; - this.bx = 3.0 * (p2x - p1x) - this.cx; - this.ax = 1.0 - this.cx - this.bx; + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - this.cy = 3.0 * p1y; - this.by = 3.0 * (p2y - p1y) - this.cy; - this.ay = 1.0 - this.cy - this.by; + function getAugmentedNamespace(n) { + if (n.__esModule) return n; + var a = Object.defineProperty({}, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; + } - this.p1x = p1x; - this.p1y = p2y; - this.p2x = p2x; - this.p2y = p2y; + function commonjsRequire (path) { + throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.'); } - UnitBezier.prototype.sampleCurveX = function(t) { - // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. - return ((this.ax * t + this.bx) * t + this.cx) * t; - }; + var nativeIsArray = Array.isArray; + var toString$2 = Object.prototype.toString; - UnitBezier.prototype.sampleCurveY = function(t) { - return ((this.ay * t + this.by) * t + this.cy) * t; - }; + var xIsArray = nativeIsArray || isArray$3; - UnitBezier.prototype.sampleCurveDerivativeX = function(t) { - return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx; - }; + function isArray$3(obj) { + return toString$2.call(obj) === "[object Array]" + } + + var version$5 = "2"; + + var version$4 = version$5; + + VirtualPatch.NONE = 0; + VirtualPatch.VTEXT = 1; + VirtualPatch.VNODE = 2; + VirtualPatch.WIDGET = 3; + VirtualPatch.PROPS = 4; + VirtualPatch.ORDER = 5; + VirtualPatch.INSERT = 6; + VirtualPatch.REMOVE = 7; + VirtualPatch.THUNK = 8; + + var vpatch = VirtualPatch; + + function VirtualPatch(type, vNode, patch) { + this.type = Number(type); + this.vNode = vNode; + this.patch = patch; + } + + VirtualPatch.prototype.version = version$4; + VirtualPatch.prototype.type = "VirtualPatch"; + + var version$3 = version$5; + + var isVnode = isVirtualNode; + + function isVirtualNode(x) { + return x && x.type === "VirtualNode" && x.version === version$3 + } + + var version$2 = version$5; + + var isVtext = isVirtualText; + + function isVirtualText(x) { + return x && x.type === "VirtualText" && x.version === version$2 + } + + var isWidget_1 = isWidget$7; + + function isWidget$7(w) { + return w && w.type === "Widget" + } + + var isThunk_1 = isThunk$3; + + function isThunk$3(t) { + return t && t.type === "Thunk" + } + + var isVNode$4 = isVnode; + var isVText$3 = isVtext; + var isWidget$6 = isWidget_1; + var isThunk$2 = isThunk_1; + + var handleThunk_1 = handleThunk$2; + + function handleThunk$2(a, b) { + var renderedA = a; + var renderedB = b; - UnitBezier.prototype.solveCurveX = function(x, epsilon) { - if (typeof epsilon === 'undefined') epsilon = 1e-6; + if (isThunk$2(b)) { + renderedB = renderThunk(b, a); + } - var t0, t1, t2, x2, i; + if (isThunk$2(a)) { + renderedA = renderThunk(a, null); + } - // First try a few iterations of Newton's method -- normally very fast. - for (t2 = x, i = 0; i < 8; i++) { + return { + a: renderedA, + b: renderedB + } + } - x2 = this.sampleCurveX(t2) - x; - if (Math.abs(x2) < epsilon) return t2; + function renderThunk(thunk, previous) { + var renderedThunk = thunk.vnode; - var d2 = this.sampleCurveDerivativeX(t2); - if (Math.abs(d2) < 1e-6) break; + if (!renderedThunk) { + renderedThunk = thunk.vnode = thunk.render(previous); + } - t2 = t2 - x2 / d2; + if (!(isVNode$4(renderedThunk) || + isVText$3(renderedThunk) || + isWidget$6(renderedThunk))) { + throw new Error("thunk did not return a valid node"); } - // Fall back to the bisection method for reliability. - t0 = 0.0; - t1 = 1.0; - t2 = x; + return renderedThunk + } + + var isObject$2 = function isObject(x) { + return typeof x === 'object' && x !== null; + }; + + var isVhook = isHook$3; + + function isHook$3(hook) { + return hook && + (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") || + typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook")) + } + + var isObject$1 = isObject$2; + var isHook$2 = isVhook; - if (t2 < t0) return t0; - if (t2 > t1) return t1; + var diffProps_1 = diffProps$1; - while (t0 < t1) { + function diffProps$1(a, b) { + var diff; + + for (var aKey in a) { + if (!(aKey in b)) { + diff = diff || {}; + diff[aKey] = undefined; + } - x2 = this.sampleCurveX(t2); - if (Math.abs(x2 - x) < epsilon) return t2; + var aValue = a[aKey]; + var bValue = b[aKey]; - if (x > x2) { - t0 = t2; + if (aValue === bValue) { + continue + } else if (isObject$1(aValue) && isObject$1(bValue)) { + if (getPrototype$1(bValue) !== getPrototype$1(aValue)) { + diff = diff || {}; + diff[aKey] = bValue; + } else if (isHook$2(bValue)) { + diff = diff || {}; + diff[aKey] = bValue; + } else { + var objectDiff = diffProps$1(aValue, bValue); + if (objectDiff) { + diff = diff || {}; + diff[aKey] = objectDiff; + } + } } else { - t1 = t2; + diff = diff || {}; + diff[aKey] = bValue; } + } - t2 = (t1 - t0) * 0.5 + t0; + for (var bKey in b) { + if (!(bKey in a)) { + diff = diff || {}; + diff[bKey] = b[bKey]; + } } - // Failure. - return t2; - }; + return diff + } - UnitBezier.prototype.solve = function(x, epsilon) { - return this.sampleCurveY(this.solveCurveX(x, epsilon)); - }; + function getPrototype$1(value) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(value) + } else if (value.__proto__) { + return value.__proto__ + } else if (value.constructor) { + return value.constructor.prototype + } + } - /** - * Enumeration for transition mode - * @enum {number} - * @readonly - * @description Modes for specifying how transitions - * between images are performed. - */ - exports.TransitionMode = void 0; - (function (TransitionMode) { - /** - * Default transitions. - * - * @description The viewer dynamically determines - * whether transitions should be performed with or - * without motion and blending for each transition - * based on the underlying data. - */ - TransitionMode[TransitionMode["Default"] = 0] = "Default"; - /** - * Instantaneous transitions. - * - * @description All transitions are performed - * without motion or blending. - */ - TransitionMode[TransitionMode["Instantaneous"] = 1] = "Instantaneous"; - })(exports.TransitionMode || (exports.TransitionMode = {})); + var isArray$2 = xIsArray; - /** - * @class Camera - * - * @classdesc Holds information about a camera. - */ - class Camera { - /** - * Create a new camera instance. - * @param {Transform} [transform] - Optional transform instance. - */ - constructor(transform) { - if (transform != null) { - this._position = new Vector3().fromArray(transform.unprojectSfM([0, 0], 0)); - this._lookat = new Vector3().fromArray(transform.unprojectSfM([0, 0], 10)); - this._up = transform.upVector(); - this._focal = this._getFocal(transform); + var VPatch$1 = vpatch; + var isVNode$3 = isVnode; + var isVText$2 = isVtext; + var isWidget$5 = isWidget_1; + var isThunk$1 = isThunk_1; + var handleThunk$1 = handleThunk_1; + + var diffProps = diffProps_1; + + var diff_1$1 = diff$2; + + function diff$2(a, b) { + var patch = { a: a }; + walk(a, b, patch, 0); + return patch + } + + function walk(a, b, patch, index) { + if (a === b) { + return + } + + var apply = patch[index]; + var applyClear = false; + + if (isThunk$1(a) || isThunk$1(b)) { + thunks(a, b, patch, index); + } else if (b == null) { + + // If a is a widget we will add a remove patch for it + // Otherwise any child widgets/hooks must be destroyed. + // This prevents adding two remove patches for a widget. + if (!isWidget$5(a)) { + clearState(a, patch, index); + apply = patch[index]; } - else { - this._position = new Vector3(0, 0, 0); - this._lookat = new Vector3(1, 0, 0); - this._up = new Vector3(0, 0, 1); - this._focal = 1; + + apply = appendPatch(apply, new VPatch$1(VPatch$1.REMOVE, a, b)); + } else if (isVNode$3(b)) { + if (isVNode$3(a)) { + if (a.tagName === b.tagName && + a.namespace === b.namespace && + a.key === b.key) { + var propsPatch = diffProps(a.properties, b.properties); + if (propsPatch) { + apply = appendPatch(apply, + new VPatch$1(VPatch$1.PROPS, a, propsPatch)); + } + apply = diffChildren(a, b, patch, apply, index); + } else { + apply = appendPatch(apply, new VPatch$1(VPatch$1.VNODE, a, b)); + applyClear = true; + } + } else { + apply = appendPatch(apply, new VPatch$1(VPatch$1.VNODE, a, b)); + applyClear = true; + } + } else if (isVText$2(b)) { + if (!isVText$2(a)) { + apply = appendPatch(apply, new VPatch$1(VPatch$1.VTEXT, a, b)); + applyClear = true; + } else if (a.text !== b.text) { + apply = appendPatch(apply, new VPatch$1(VPatch$1.VTEXT, a, b)); + } + } else if (isWidget$5(b)) { + if (!isWidget$5(a)) { + applyClear = true; } + + apply = appendPatch(apply, new VPatch$1(VPatch$1.WIDGET, a, b)); } - /** - * Get position. - * @returns {THREE.Vector3} The position vector. - */ - get position() { - return this._position; + + if (apply) { + patch[index] = apply; } - /** - * Get lookat. - * @returns {THREE.Vector3} The lookat vector. - */ - get lookat() { - return this._lookat; - } - /** - * Get up. - * @returns {THREE.Vector3} The up vector. - */ - get up() { - return this._up; - } - /** - * Get focal. - * @returns {number} The focal length. - */ - get focal() { - return this._focal; - } - /** - * Set focal. - */ - set focal(value) { - this._focal = value; + + if (applyClear) { + clearState(a, patch, index); } - /** - * Update this camera to the linearly interpolated value of two other cameras. - * - * @param {Camera} a - First camera. - * @param {Camera} b - Second camera. - * @param {number} alpha - Interpolation value on the interval [0, 1]. - */ - lerpCameras(a, b, alpha) { - this._position.subVectors(b.position, a.position).multiplyScalar(alpha).add(a.position); - this._lookat.subVectors(b.lookat, a.lookat).multiplyScalar(alpha).add(a.lookat); - this._up.subVectors(b.up, a.up).multiplyScalar(alpha).add(a.up); - this._focal = (1 - alpha) * a.focal + alpha * b.focal; + } + + function diffChildren(a, b, patch, apply, index) { + var aChildren = a.children; + var orderedSet = reorder(aChildren, b.children); + var bChildren = orderedSet.children; + + var aLen = aChildren.length; + var bLen = bChildren.length; + var len = aLen > bLen ? aLen : bLen; + + for (var i = 0; i < len; i++) { + var leftNode = aChildren[i]; + var rightNode = bChildren[i]; + index += 1; + + if (!leftNode) { + if (rightNode) { + // Excess nodes in b need to be added + apply = appendPatch(apply, + new VPatch$1(VPatch$1.INSERT, null, rightNode)); + } + } else { + walk(leftNode, rightNode, patch, index); + } + + if (isVNode$3(leftNode) && leftNode.count) { + index += leftNode.count; + } } - /** - * Copy the properties of another camera to this camera. - * - * @param {Camera} other - Another camera. - */ - copy(other) { - this._position.copy(other.position); - this._lookat.copy(other.lookat); - this._up.copy(other.up); - this._focal = other.focal; + + if (orderedSet.moves) { + // Reorder nodes last + apply = appendPatch(apply, new VPatch$1( + VPatch$1.ORDER, + a, + orderedSet.moves + )); } - /** - * Clone this camera. - * - * @returns {Camera} A camera with cloned properties equal to this camera. - */ - clone() { - let camera = new Camera(); - camera.position.copy(this._position); - camera.lookat.copy(this._lookat); - camera.up.copy(this._up); - camera.focal = this._focal; - return camera; + + return apply + } + + function clearState(vNode, patch, index) { + // TODO: Make this a single walk, not two + unhook(vNode, patch, index); + destroyWidgets(vNode, patch, index); + } + + // Patch records for all destroyed widgets must be added because we need + // a DOM node reference for the destroy function + function destroyWidgets(vNode, patch, index) { + if (isWidget$5(vNode)) { + if (typeof vNode.destroy === "function") { + patch[index] = appendPatch( + patch[index], + new VPatch$1(VPatch$1.REMOVE, vNode, null) + ); + } + } else if (isVNode$3(vNode) && (vNode.hasWidgets || vNode.hasThunks)) { + var children = vNode.children; + var len = children.length; + for (var i = 0; i < len; i++) { + var child = children[i]; + index += 1; + + destroyWidgets(child, patch, index); + + if (isVNode$3(child) && child.count) { + index += child.count; + } + } + } else if (isThunk$1(vNode)) { + thunks(vNode, null, patch, index); } - /** - * Determine the distance between this camera and another camera. - * - * @param {Camera} other - Another camera. - * @returns {number} The distance between the cameras. - */ - diff(other) { - let pd = this._position.distanceToSquared(other.position); - let ld = this._lookat.distanceToSquared(other.lookat); - let ud = this._up.distanceToSquared(other.up); - let fd = 100 * Math.abs(this._focal - other.focal); - return Math.max(pd, ld, ud, fd); + } + + // Create a sub-patch for thunks + function thunks(a, b, patch, index) { + var nodes = handleThunk$1(a, b); + var thunkPatch = diff$2(nodes.a, nodes.b); + if (hasPatches(thunkPatch)) { + patch[index] = new VPatch$1(VPatch$1.THUNK, null, thunkPatch); } - /** - * Get the focal length based on the transform. - * - * @description Returns the focal length corresponding - * to a 90 degree field of view for spherical - * transforms. - * - * Returns the transform focal length for other - * projection types. - * - * @returns {number} Focal length. - */ - _getFocal(transform) { - if (!isSpherical(transform.cameraType)) { - return transform.focal; + } + + function hasPatches(patch) { + for (var index in patch) { + if (index !== "a") { + return true } - return 0.5 / Math.tan(Math.PI / 2); } + + return false } - const EPSILON = 1e-8; - /** - * @class Transform - * - * @classdesc Class used for calculating coordinate transformations - * and projections. - */ - class Transform { - /** - * Create a new transform instance. - * @param {number} orientation - Image orientation. - * @param {number} width - Image height. - * @param {number} height - Image width. - * @param {number} focal - Focal length. - * @param {number} scale - Atomic scale. - * @param {Array} rotation - Rotation vector in three dimensions. - * @param {Array} translation - Translation vector in three dimensions. - * @param {HTMLImageElement} image - Image for fallback size calculations. - */ - constructor(orientation, width, height, scale, rotation, translation, image, textureScale, cameraParameters, cameraType) { - this._orientation = this._getValue(orientation, 1); - let imageWidth = image != null ? image.width : 4; - let imageHeight = image != null ? image.height : 3; - let keepOrientation = this._orientation < 5; - this._width = this._getValue(width, keepOrientation ? imageWidth : imageHeight); - this._height = this._getValue(height, keepOrientation ? imageHeight : imageWidth); - this._basicAspect = keepOrientation ? - this._width / this._height : - this._height / this._width; - this._basicWidth = keepOrientation ? width : height; - this._basicHeight = keepOrientation ? height : width; - const parameters = this._getCameraParameters(cameraParameters, cameraType); - const focal = parameters[0]; - const ck1 = parameters[1]; - const ck2 = parameters[2]; - this._focal = this._getValue(focal, 1); - this._scale = this._getValue(scale, 0); - this._worldToCamera = this.createWorldToCamera(rotation, translation); - this._worldToCameraInverse = new Matrix4() - .copy(this._worldToCamera) - .invert(); - this._scaledWorldToCamera = - this._createScaledWorldToCamera(this._worldToCamera, this._scale); - this._scaledWorldToCameraInverse = new Matrix4() - .copy(this._scaledWorldToCamera) - .invert(); - this._basicWorldToCamera = this._createBasicWorldToCamera(this._worldToCamera, orientation); - this._textureScale = !!textureScale ? textureScale : [1, 1]; - this._ck1 = !!ck1 ? ck1 : 0; - this._ck2 = !!ck2 ? ck2 : 0; - this._cameraType = !!cameraType ? - cameraType : - "perspective"; - this._radialPeak = this._getRadialPeak(this._ck1, this._ck2); - } - get ck1() { - return this._ck1; + // Execute hooks when two nodes are identical + function unhook(vNode, patch, index) { + if (isVNode$3(vNode)) { + if (vNode.hooks) { + patch[index] = appendPatch( + patch[index], + new VPatch$1( + VPatch$1.PROPS, + vNode, + undefinedKeys(vNode.hooks) + ) + ); + } + + if (vNode.descendantHooks || vNode.hasThunks) { + var children = vNode.children; + var len = children.length; + for (var i = 0; i < len; i++) { + var child = children[i]; + index += 1; + + unhook(child, patch, index); + + if (isVNode$3(child) && child.count) { + index += child.count; + } + } + } + } else if (isThunk$1(vNode)) { + thunks(vNode, null, patch, index); } - get ck2() { - return this._ck2; + } + + function undefinedKeys(obj) { + var result = {}; + + for (var key in obj) { + result[key] = undefined; } - get cameraType() { - return this._cameraType; + + return result + } + + // List diff, naive left to right reordering + function reorder(aChildren, bChildren) { + // O(M) time, O(M) memory + var bChildIndex = keyIndex(bChildren); + var bKeys = bChildIndex.keys; + var bFree = bChildIndex.free; + + if (bFree.length === bChildren.length) { + return { + children: bChildren, + moves: null + } } - /** - * Get basic aspect. - * @returns {number} The orientation adjusted aspect ratio. - */ - get basicAspect() { - return this._basicAspect; + + // O(N) time, O(N) memory + var aChildIndex = keyIndex(aChildren); + var aKeys = aChildIndex.keys; + var aFree = aChildIndex.free; + + if (aFree.length === aChildren.length) { + return { + children: bChildren, + moves: null + } } - /** - * Get basic height. - * - * @description Does not fall back to image image height but - * uses original value from API so can be faulty. - * - * @returns {number} The height of the basic version image - * (adjusted for orientation). - */ - get basicHeight() { - return this._basicHeight; + + // O(MAX(N, M)) memory + var newChildren = []; + + var freeIndex = 0; + var freeCount = bFree.length; + var deletedItems = 0; + + // Iterate through a and match a node in b + // O(N) time, + for (var i = 0 ; i < aChildren.length; i++) { + var aItem = aChildren[i]; + var itemIndex; + + if (aItem.key) { + if (bKeys.hasOwnProperty(aItem.key)) { + // Match up the old keys + itemIndex = bKeys[aItem.key]; + newChildren.push(bChildren[itemIndex]); + + } else { + // Remove old keyed items + itemIndex = i - deletedItems++; + newChildren.push(null); + } + } else { + // Match the item in a with the next free item in b + if (freeIndex < freeCount) { + itemIndex = bFree[freeIndex++]; + newChildren.push(bChildren[itemIndex]); + } else { + // There are no free items in b to match with + // the free items in a, so the extra free nodes + // are deleted. + itemIndex = i - deletedItems++; + newChildren.push(null); + } + } } - get basicRt() { - return this._basicWorldToCamera; + + var lastFreeIndex = freeIndex >= bFree.length ? + bChildren.length : + bFree[freeIndex]; + + // Iterate through b and append any new keys + // O(M) time + for (var j = 0; j < bChildren.length; j++) { + var newItem = bChildren[j]; + + if (newItem.key) { + if (!aKeys.hasOwnProperty(newItem.key)) { + // Add any new keyed items + // We are adding new items to the end and then sorting them + // in place. In future we should insert new items in place. + newChildren.push(newItem); + } + } else if (j >= lastFreeIndex) { + // Add any leftover non-keyed items + newChildren.push(newItem); + } } - /** - * Get basic width. - * - * @description Does not fall back to image image width but - * uses original value from API so can be faulty. - * - * @returns {number} The width of the basic version image - * (adjusted for orientation). - */ - get basicWidth() { - return this._basicWidth; + + var simulate = newChildren.slice(); + var simulateIndex = 0; + var removes = []; + var inserts = []; + var simulateItem; + + for (var k = 0; k < bChildren.length;) { + var wantedItem = bChildren[k]; + simulateItem = simulate[simulateIndex]; + + // remove items + while (simulateItem === null && simulate.length) { + removes.push(remove(simulate, simulateIndex, null)); + simulateItem = simulate[simulateIndex]; + } + + if (!simulateItem || simulateItem.key !== wantedItem.key) { + // if we need a key in this position... + if (wantedItem.key) { + if (simulateItem && simulateItem.key) { + // if an insert doesn't put this key in place, it needs to move + if (bKeys[simulateItem.key] !== k + 1) { + removes.push(remove(simulate, simulateIndex, simulateItem.key)); + simulateItem = simulate[simulateIndex]; + // if the remove didn't put the wanted item in place, we need to insert it + if (!simulateItem || simulateItem.key !== wantedItem.key) { + inserts.push({key: wantedItem.key, to: k}); + } + // items are matching, so skip ahead + else { + simulateIndex++; + } + } + else { + inserts.push({key: wantedItem.key, to: k}); + } + } + else { + inserts.push({key: wantedItem.key, to: k}); + } + k++; + } + // a key in simulate has no matching wanted key, remove it + else if (simulateItem && simulateItem.key) { + removes.push(remove(simulate, simulateIndex, simulateItem.key)); + } + } + else { + simulateIndex++; + k++; + } } - /** - * Get focal. - * @returns {number} The image focal length. - */ - get focal() { - return this._focal; + + // remove all the remaining nodes from simulate + while(simulateIndex < simulate.length) { + simulateItem = simulate[simulateIndex]; + removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key)); } - /** - * Get height. - * - * @description Falls back to the image image height if - * the API data is faulty. - * - * @returns {number} The orientation adjusted image height. - */ - get height() { - return this._height; + + // If the only moves we have are deletes then we can just + // let the delete patch remove these items. + if (removes.length === deletedItems && !inserts.length) { + return { + children: newChildren, + moves: null + } } - /** - * Get orientation. - * @returns {number} The image orientation. - */ - get orientation() { - return this._orientation; + + return { + children: newChildren, + moves: { + removes: removes, + inserts: inserts + } } - /** - * Get rt. - * @returns {THREE.Matrix4} The extrinsic camera matrix. - */ - get rt() { - return this._worldToCamera; + } + + function remove(arr, index, key) { + arr.splice(index, 1); + + return { + from: index, + key: key } - /** - * Get srt. - * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. - */ - get srt() { - return this._scaledWorldToCamera; + } + + function keyIndex(children) { + var keys = {}; + var free = []; + var length = children.length; + + for (var i = 0; i < length; i++) { + var child = children[i]; + + if (child.key) { + keys[child.key] = i; + } else { + free.push(i); + } } - /** - * Get srtInverse. - * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. - */ - get srtInverse() { - return this._scaledWorldToCameraInverse; - } - /** - * Get scale. - * @returns {number} The image atomic reconstruction scale. - */ - get scale() { - return this._scale; - } - /** - * Get has valid scale. - * @returns {boolean} Value indicating if the scale of the transform is valid. - */ - get hasValidScale() { - return this._scale > 1e-2 && this._scale < 50; - } - /** - * Get radial peak. - * @returns {number} Value indicating the radius where the radial - * undistortion function peaks. - */ - get radialPeak() { - return this._radialPeak; - } - /** - * Get width. - * - * @description Falls back to the image image width if - * the API data is faulty. - * - * @returns {number} The orientation adjusted image width. - */ - get width() { - return this._width; - } - /** - * Calculate the up vector for the image transform. - * - * @returns {THREE.Vector3} Normalized and orientation adjusted up vector. - */ - upVector() { - let rte = this._worldToCamera.elements; - switch (this._orientation) { - case 1: - return new Vector3(-rte[1], -rte[5], -rte[9]); - case 3: - return new Vector3(rte[1], rte[5], rte[9]); - case 6: - return new Vector3(-rte[0], -rte[4], -rte[8]); - case 8: - return new Vector3(rte[0], rte[4], rte[8]); - default: - return new Vector3(-rte[1], -rte[5], -rte[9]); - } - } - /** - * Calculate projector matrix for projecting 3D points to texture map - * coordinates (u and v). - * - * @returns {THREE.Matrix4} Projection matrix for 3D point to texture - * map coordinate calculations. - */ - projectorMatrix() { - let projector = this._normalizedToTextureMatrix(); - let f = this._focal; - let projection = new Matrix4().set(f, 0, 0, 0, 0, f, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0); - projector.multiply(projection); - projector.multiply(this._worldToCamera); - return projector; - } - /** - * Project 3D world coordinates to basic coordinates. - * - * @param {Array} point3d - 3D world coordinates. - * @return {Array} 2D basic coordinates. - */ - projectBasic(point3d) { - let sfm = this.projectSfM(point3d); - return this._sfmToBasic(sfm); - } - /** - * Unproject basic coordinates to 3D world coordinates. - * - * @param {Array} basic - 2D basic coordinates. - * @param {Array} distance - Distance to unproject from camera center. - * @param {boolean} [depth] - Treat the distance value as depth from camera center. - * Only applicable for perspective images. Will be - * ignored for spherical. - * @returns {Array} Unprojected 3D world coordinates. - */ - unprojectBasic(basic, distance, depth) { - let sfm = this._basicToSfm(basic); - return this.unprojectSfM(sfm, distance, depth); - } - /** - * Project 3D world coordinates to SfM coordinates. - * - * @param {Array} point3d - 3D world coordinates. - * @return {Array} 2D SfM coordinates. - */ - projectSfM(point3d) { - let v = new Vector4(point3d[0], point3d[1], point3d[2], 1); - v.applyMatrix4(this._worldToCamera); - return this._bearingToSfm([v.x, v.y, v.z]); - } - /** - * Unproject SfM coordinates to a 3D world coordinates. - * - * @param {Array} sfm - 2D SfM coordinates. - * @param {Array} distance - Distance to unproject - * from camera center. - * @param {boolean} [depth] - Treat the distance value as - * depth from camera center. Only applicable for perspective - * images. Will be ignored for spherical. - * @returns {Array} Unprojected 3D world coordinates. - */ - unprojectSfM(sfm, distance, depth) { - const bearing = this._sfmToBearing(sfm); - const unprojectedCamera = depth && !isSpherical(this._cameraType) ? - new Vector4(distance * bearing[0] / bearing[2], distance * bearing[1] / bearing[2], distance, 1) : - new Vector4(distance * bearing[0], distance * bearing[1], distance * bearing[2], 1); - const unprojectedWorld = unprojectedCamera - .applyMatrix4(this._worldToCameraInverse); - return [ - unprojectedWorld.x / unprojectedWorld.w, - unprojectedWorld.y / unprojectedWorld.w, - unprojectedWorld.z / unprojectedWorld.w, - ]; - } - /** - * Transform SfM coordinates to bearing vector (3D cartesian - * coordinates on the unit sphere). - * - * @param {Array} sfm - 2D SfM coordinates. - * @returns {Array} Bearing vector (3D cartesian coordinates - * on the unit sphere). - */ - _sfmToBearing(sfm) { - if (isSpherical(this._cameraType)) { - let lng = sfm[0] * 2 * Math.PI; - let lat = -sfm[1] * 2 * Math.PI; - let x = Math.cos(lat) * Math.sin(lng); - let y = -Math.sin(lat); - let z = Math.cos(lat) * Math.cos(lng); - return [x, y, z]; - } - else if (isFisheye(this._cameraType)) { - let [dxn, dyn] = [sfm[0] / this._focal, sfm[1] / this._focal]; - const dTheta = Math.sqrt(dxn * dxn + dyn * dyn); - let d = this._distortionFromDistortedRadius(dTheta, this._ck1, this._ck2, this._radialPeak); - let theta = dTheta / d; - let z = Math.cos(theta); - let r = Math.sin(theta); - const denomTheta = dTheta > EPSILON ? 1 / dTheta : 1; - let x = r * dxn * denomTheta; - let y = r * dyn * denomTheta; - return [x, y, z]; - } - else { - let [dxn, dyn] = [sfm[0] / this._focal, sfm[1] / this._focal]; - const dr = Math.sqrt(dxn * dxn + dyn * dyn); - let d = this._distortionFromDistortedRadius(dr, this._ck1, this._ck2, this._radialPeak); - const xn = dxn / d; - const yn = dyn / d; - let v = new Vector3(xn, yn, 1); - v.normalize(); - return [v.x, v.y, v.z]; - } - } - /** Compute distortion given the distorted radius. - * - * Solves for d in the equation - * y = d(x, k1, k2) * x - * given the distorted radius, y. - */ - _distortionFromDistortedRadius(distortedRadius, k1, k2, radialPeak) { - let d = 1.0; - for (let i = 0; i < 10; i++) { - let radius = distortedRadius / d; - if (radius > radialPeak) { - radius = radialPeak; - } - d = 1 + k1 * Math.pow(radius, 2) + k2 * Math.pow(radius, 4); - } - return d; - } - /** - * Transform bearing vector (3D cartesian coordiantes on the unit sphere) to - * SfM coordinates. - * - * @param {Array} bearing - Bearing vector (3D cartesian coordinates on the - * unit sphere). - * @returns {Array} 2D SfM coordinates. - */ - _bearingToSfm(bearing) { - if (isSpherical(this._cameraType)) { - let x = bearing[0]; - let y = bearing[1]; - let z = bearing[2]; - let lng = Math.atan2(x, z); - let lat = Math.atan2(-y, Math.sqrt(x * x + z * z)); - return [lng / (2 * Math.PI), -lat / (2 * Math.PI)]; - } - else if (isFisheye(this._cameraType)) { - if (bearing[2] > 0) { - const [x, y, z] = bearing; - const r = Math.sqrt(x * x + y * y); - let theta = Math.atan2(r, z); - if (theta > this._radialPeak) { - theta = this._radialPeak; - } - const distortion = 1.0 + Math.pow(theta, 2) * (this._ck1 + Math.pow(theta, 2) * this._ck2); - const s = this._focal * distortion * theta / r; - return [s * x, s * y]; - } - else { - return [ - bearing[0] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, - bearing[1] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, - ]; - } - } - else { - if (bearing[2] > 0) { - let [xn, yn] = [bearing[0] / bearing[2], bearing[1] / bearing[2]]; - let r2 = xn * xn + yn * yn; - const rp2 = Math.pow(this._radialPeak, 2); - if (r2 > rp2) { - r2 = rp2; - } - const d = 1 + this._ck1 * r2 + this._ck2 * Math.pow(r2, 2); - return [ - this._focal * d * xn, - this._focal * d * yn, - ]; - } - else { - return [ - bearing[0] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, - bearing[1] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, - ]; - } - } - } - /** - * Convert basic coordinates to SfM coordinates. - * - * @param {Array} basic - 2D basic coordinates. - * @returns {Array} 2D SfM coordinates. - */ - _basicToSfm(basic) { - let rotatedX; - let rotatedY; - switch (this._orientation) { - case 1: - rotatedX = basic[0]; - rotatedY = basic[1]; - break; - case 3: - rotatedX = 1 - basic[0]; - rotatedY = 1 - basic[1]; - break; - case 6: - rotatedX = basic[1]; - rotatedY = 1 - basic[0]; - break; - case 8: - rotatedX = 1 - basic[1]; - rotatedY = basic[0]; - break; - default: - rotatedX = basic[0]; - rotatedY = basic[1]; - break; - } - let w = this._width; - let h = this._height; - let s = Math.max(w, h); - let sfmX = rotatedX * w / s - w / s / 2; - let sfmY = rotatedY * h / s - h / s / 2; - return [sfmX, sfmY]; - } - /** - * Convert SfM coordinates to basic coordinates. - * - * @param {Array} sfm - 2D SfM coordinates. - * @returns {Array} 2D basic coordinates. - */ - _sfmToBasic(sfm) { - let w = this._width; - let h = this._height; - let s = Math.max(w, h); - let rotatedX = (sfm[0] + w / s / 2) / w * s; - let rotatedY = (sfm[1] + h / s / 2) / h * s; - let basicX; - let basicY; - switch (this._orientation) { - case 1: - basicX = rotatedX; - basicY = rotatedY; - break; - case 3: - basicX = 1 - rotatedX; - basicY = 1 - rotatedY; - break; - case 6: - basicX = 1 - rotatedY; - basicY = rotatedX; - break; - case 8: - basicX = rotatedY; - basicY = 1 - rotatedX; - break; - default: - basicX = rotatedX; - basicY = rotatedY; - break; - } - return [basicX, basicY]; - } - /** - * Checks a value and returns it if it exists and is larger than 0. - * Fallbacks if it is null. - * - * @param {number} value - Value to check. - * @param {number} fallback - Value to fall back to. - * @returns {number} The value or its fallback value if it is not defined or negative. - */ - _getValue(value, fallback) { - return value != null && value > 0 ? value : fallback; - } - _getCameraParameters(value, cameraType) { - if (isSpherical(cameraType)) { - return []; - } - if (!value || value.length === 0) { - return [1, 0, 0]; - } - const padding = 3 - value.length; - if (padding <= 0) { - return value; - } - return value - .concat(new Array(padding) - .fill(0)); - } - /** - * Creates the extrinsic camera matrix [ R | t ]. - * - * @param {Array} rotation - Rotation vector in angle axis representation. - * @param {Array} translation - Translation vector. - * @returns {THREE.Matrix4} Extrisic camera matrix. - */ - createWorldToCamera(rotation, translation) { - const axis = new Vector3(rotation[0], rotation[1], rotation[2]); - const angle = axis.length(); - if (angle > 0) { - axis.normalize(); - } - const worldToCamera = new Matrix4(); - worldToCamera.makeRotationAxis(axis, angle); - worldToCamera.setPosition(new Vector3(translation[0], translation[1], translation[2])); - return worldToCamera; - } - /** - * Calculates the scaled extrinsic camera matrix scale * [ R | t ]. - * - * @param {THREE.Matrix4} worldToCamera - Extrisic camera matrix. - * @param {number} scale - Scale factor. - * @returns {THREE.Matrix4} Scaled extrisic camera matrix. - */ - _createScaledWorldToCamera(worldToCamera, scale) { - const scaledWorldToCamera = worldToCamera.clone(); - const elements = scaledWorldToCamera.elements; - elements[12] = scale * elements[12]; - elements[13] = scale * elements[13]; - elements[14] = scale * elements[14]; - scaledWorldToCamera.scale(new Vector3(scale, scale, scale)); - return scaledWorldToCamera; - } - _createBasicWorldToCamera(rt, orientation) { - const axis = new Vector3(0, 0, 1); - let angle = 0; - switch (orientation) { - case 3: - angle = Math.PI; - break; - case 6: - angle = Math.PI / 2; - break; - case 8: - angle = 3 * Math.PI / 2; - break; - } - return new Matrix4() - .makeRotationAxis(axis, angle) - .multiply(rt); - } - _getRadialPeak(k1, k2) { - const a = 5 * k2; - const b = 3 * k1; - const c = 1; - const d = Math.pow(b, 2) - 4 * a * c; - if (d < 0) { - return undefined; - } - const root1 = (-b - Math.sqrt(d)) / 2 / a; - const root2 = (-b + Math.sqrt(d)) / 2 / a; - const minRoot = Math.min(root1, root2); - const maxRoot = Math.max(root1, root2); - return minRoot > 0 ? - Math.sqrt(minRoot) : - maxRoot > 0 ? - Math.sqrt(maxRoot) : - undefined; - } - /** - * Calculate a transformation matrix from normalized coordinates for - * texture map coordinates. - * - * @returns {THREE.Matrix4} Normalized coordinates to texture map - * coordinates transformation matrix. - */ - _normalizedToTextureMatrix() { - const size = Math.max(this._width, this._height); - const scaleX = this._orientation < 5 ? this._textureScale[0] : this._textureScale[1]; - const scaleY = this._orientation < 5 ? this._textureScale[1] : this._textureScale[0]; - const w = size / this._width * scaleX; - const h = size / this._height * scaleY; - switch (this._orientation) { - case 1: - return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); - case 3: - return new Matrix4().set(-w, 0, 0, 0.5, 0, h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); - case 6: - return new Matrix4().set(0, -h, 0, 0.5, -w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); - case 8: - return new Matrix4().set(0, h, 0, 0.5, w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); - default: - return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); - } - } - } - - class StateBase { - constructor(state) { - this._spatial = new Spatial(); - this._referenceThreshold = 0.01; - this._transitionMode = state.transitionMode; - this._reference = state.reference; - this._alpha = state.alpha; - this._camera = state.camera.clone(); - this._zoom = state.zoom; - this._currentIndex = state.currentIndex; - this._trajectory = state.trajectory.slice(); - this._trajectoryTransforms = []; - this._trajectoryCameras = []; - for (let image of this._trajectory) { - let translation = this._imageToTranslation(image, this._reference); - let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, undefined, image.cameraParameters, image.cameraType); - this._trajectoryTransforms.push(transform); - this._trajectoryCameras.push(new Camera(transform)); - } - this._currentImage = this._trajectory.length > 0 ? - this._trajectory[this._currentIndex] : - null; - this._previousImage = this._trajectory.length > 1 && this.currentIndex > 0 ? - this._trajectory[this._currentIndex - 1] : - null; - this._currentCamera = this._trajectoryCameras.length > 0 ? - this._trajectoryCameras[this._currentIndex].clone() : - new Camera(); - this._previousCamera = this._trajectoryCameras.length > 1 && this.currentIndex > 0 ? - this._trajectoryCameras[this._currentIndex - 1].clone() : - this._currentCamera.clone(); - } - get reference() { - return this._reference; - } - get alpha() { - return this._getAlpha(); - } - get camera() { - return this._camera; - } - get zoom() { - return this._zoom; - } - get trajectory() { - return this._trajectory; - } - get currentIndex() { - return this._currentIndex; - } - get currentImage() { - return this._currentImage; - } - get previousImage() { - return this._previousImage; - } - get currentCamera() { - return this._currentCamera; - } - get currentTransform() { - return this._trajectoryTransforms.length > 0 ? - this._trajectoryTransforms[this.currentIndex] : null; - } - get previousTransform() { - return this._trajectoryTransforms.length > 1 && this.currentIndex > 0 ? - this._trajectoryTransforms[this.currentIndex - 1] : null; - } - get motionless() { - return this._motionless; - } - get transitionMode() { - return this._transitionMode; - } - move(delta) { } - moveTo(position) { } - rotate(delta) { } - rotateUnbounded(delta) { } - rotateWithoutInertia(delta) { } - rotateBasic(basicRotation) { } - rotateBasicUnbounded(basicRotation) { } - rotateBasicWithoutInertia(basicRotation) { } - rotateToBasic(basic) { } - setSpeed(speed) { } - zoomIn(delta, reference) { } - update(fps) { } - setCenter(center) { } - setZoom(zoom) { } - dolly(delta) { } - orbit(rotation) { } - setViewMatrix(matrix) { } - truck(direction) { } - append(images) { - if (images.length < 1) { - throw Error("Trajectory can not be empty"); - } - if (this._currentIndex < 0) { - this.set(images); - } - else { - this._trajectory = this._trajectory.concat(images); - this._appendToTrajectories(images); - } - } - prepend(images) { - if (images.length < 1) { - throw Error("Trajectory can not be empty"); - } - this._trajectory = images.slice().concat(this._trajectory); - this._currentIndex += images.length; - this._setCurrentImage(); - let referenceReset = this._setReference(this._currentImage); - if (referenceReset) { - this._setTrajectories(); - } - else { - this._prependToTrajectories(images); - } - this._setCurrentCamera(); - } - remove(n) { - if (n < 0) { - throw Error("n must be a positive integer"); - } - if (this._currentIndex - 1 < n) { - throw Error("Current and previous images can not be removed"); - } - for (let i = 0; i < n; i++) { - this._trajectory.shift(); - this._trajectoryTransforms.shift(); - this._trajectoryCameras.shift(); - this._currentIndex--; - } - this._setCurrentImage(); - } - clearPrior() { - if (this._currentIndex > 0) { - this.remove(this._currentIndex - 1); - } - } - clear() { - this.cut(); - if (this._currentIndex > 0) { - this.remove(this._currentIndex - 1); - } - } - cut() { - while (this._trajectory.length - 1 > this._currentIndex) { - this._trajectory.pop(); - this._trajectoryTransforms.pop(); - this._trajectoryCameras.pop(); - } - } - set(images) { - this._setTrajectory(images); - this._setCurrentImage(); - this._setReference(this._currentImage); - this._setTrajectories(); - this._setCurrentCamera(); - } - getCenter() { - return this._currentImage != null ? - this.currentTransform.projectBasic(this._camera.lookat.toArray()) : - [0.5, 0.5]; - } - setTransitionMode(mode) { - this._transitionMode = mode; - } - _getAlpha() { return 1; } - _setCurrent() { - this._setCurrentImage(); - let referenceReset = this._setReference(this._currentImage); - if (referenceReset) { - this._setTrajectories(); - } - this._setCurrentCamera(); - } - _setCurrentCamera() { - this._currentCamera = this._trajectoryCameras[this._currentIndex].clone(); - this._previousCamera = this._currentIndex > 0 ? - this._trajectoryCameras[this._currentIndex - 1].clone() : - this._currentCamera.clone(); - } - _motionlessTransition() { - let imagesSet = this._currentImage != null && this._previousImage != null; - return imagesSet && (this._transitionMode === exports.TransitionMode.Instantaneous || !(this._currentImage.merged && - this._previousImage.merged && - this._withinOriginalDistance() && - this._sameConnectedComponent())); - } - _setReference(image) { - // do not reset reference if image is within threshold distance - if (Math.abs(image.lngLat.lat - this.reference.lat) < this._referenceThreshold && - Math.abs(image.lngLat.lng - this.reference.lng) < this._referenceThreshold) { - return false; - } - // do not reset reference if previous image exist and transition is with motion - if (this._previousImage != null && !this._motionlessTransition()) { - return false; - } - this._reference.lat = image.lngLat.lat; - this._reference.lng = image.lngLat.lng; - this._reference.alt = image.computedAltitude; - return true; - } - _setCurrentImage() { - this._currentImage = this._trajectory.length > 0 ? - this._trajectory[this._currentIndex] : - null; - this._previousImage = this._currentIndex > 0 ? - this._trajectory[this._currentIndex - 1] : - null; - } - _setTrajectory(images) { - if (images.length < 1) { - throw new ArgumentMapillaryError("Trajectory can not be empty"); - } - if (this._currentImage != null) { - this._trajectory = [this._currentImage].concat(images); - this._currentIndex = 1; - } - else { - this._trajectory = images.slice(); - this._currentIndex = 0; - } - } - _setTrajectories() { - this._trajectoryTransforms.length = 0; - this._trajectoryCameras.length = 0; - this._appendToTrajectories(this._trajectory); - } - _appendToTrajectories(images) { - for (let image of images) { - if (!image.assetsCached) { - throw new ArgumentMapillaryError("Assets must be cached when image is added to trajectory"); - } - let translation = this._imageToTranslation(image, this.reference); - let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, undefined, image.cameraParameters, image.cameraType); - this._trajectoryTransforms.push(transform); - this._trajectoryCameras.push(new Camera(transform)); - } - } - _prependToTrajectories(images) { - for (let image of images.reverse()) { - if (!image.assetsCached) { - throw new ArgumentMapillaryError("Assets must be cached when added to trajectory"); - } - let translation = this._imageToTranslation(image, this.reference); - let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, undefined, image.cameraParameters, image.cameraType); - this._trajectoryTransforms.unshift(transform); - this._trajectoryCameras.unshift(new Camera(transform)); - } - } - _imageToTranslation(image, reference) { - return computeTranslation({ alt: image.computedAltitude, lat: image.lngLat.lat, lng: image.lngLat.lng }, image.rotation, reference); - } - _sameConnectedComponent() { - let current = this._currentImage; - let previous = this._previousImage; - return !!current && !!previous && - current.mergeId === previous.mergeId; - } - _withinOriginalDistance() { - let current = this._currentImage; - let previous = this._previousImage; - if (!current || !previous) { - return true; - } - // 50 km/h moves 28m in 2s - let distance = this._spatial.distanceFromLngLat(current.originalLngLat.lng, current.originalLngLat.lat, previous.originalLngLat.lng, previous.originalLngLat.lat); - return distance < 25; - } - } - - class EulerRotationDelta { - constructor(phi, theta) { - this._phi = phi; - this._theta = theta; - } - get phi() { - return this._phi; - } - set phi(value) { - this._phi = value; - } - get theta() { - return this._theta; - } - set theta(value) { - this._theta = value; - } - get isZero() { - return this._phi === 0 && this._theta === 0; - } - copy(delta) { - this._phi = delta.phi; - this._theta = delta.theta; - } - lerp(other, alpha) { - this._phi = (1 - alpha) * this._phi + alpha * other.phi; - this._theta = (1 - alpha) * this._theta + alpha * other.theta; - } - multiply(value) { - this._phi *= value; - this._theta *= value; - } - threshold(value) { - this._phi = Math.abs(this._phi) > value ? this._phi : 0; - this._theta = Math.abs(this._theta) > value ? this._theta : 0; - } - lengthSquared() { - return this._phi * this._phi + this._theta * this._theta; - } - reset() { - this._phi = 0; - this._theta = 0; - } - } - - class InteractiveStateBase extends StateBase { - constructor(state) { - super(state); - this._animationSpeed = 1 / 40; - this._rotationDelta = new EulerRotationDelta(0, 0); - this._requestedRotationDelta = null; - this._basicRotation = [0, 0]; - this._requestedBasicRotation = null; - this._requestedBasicRotationUnbounded = null; - this._rotationAcceleration = 0.86; - this._rotationIncreaseAlpha = 0.97; - this._rotationDecreaseAlpha = 0.9; - this._rotationThreshold = 1e-3; - this._unboundedRotationAlpha = 0.8; - this._desiredZoom = state.zoom; - this._minZoom = 0; - this._maxZoom = 3; - this._lookatDepth = 10; - this._desiredLookat = null; - this._desiredCenter = null; - } - rotate(rotationDelta) { - if (this._currentImage == null) { - return; - } - if (rotationDelta.phi === 0 && rotationDelta.theta === 0) { - return; - } - this._desiredZoom = this._zoom; - this._desiredLookat = null; - this._requestedBasicRotation = null; - if (this._requestedRotationDelta != null) { - this._requestedRotationDelta.phi = this._requestedRotationDelta.phi + rotationDelta.phi; - this._requestedRotationDelta.theta = this._requestedRotationDelta.theta + rotationDelta.theta; - } - else { - this._requestedRotationDelta = new EulerRotationDelta(rotationDelta.phi, rotationDelta.theta); - } - } - rotateUnbounded(delta) { - if (this._currentImage == null) { - return; - } - this._requestedBasicRotation = null; - this._requestedRotationDelta = null; - this._applyRotation(delta, this._currentCamera); - this._applyRotation(delta, this._previousCamera); - if (!this._desiredLookat) { - return; - } - const q = new Quaternion().setFromUnitVectors(this._currentCamera.up, new Vector3(0, 0, 1)); - const qInverse = q.clone().invert(); - const offset = new Vector3() - .copy(this._desiredLookat) - .sub(this._camera.position) - .applyQuaternion(q); - const length = offset.length(); - let phi = Math.atan2(offset.y, offset.x); - phi += delta.phi; - let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); - theta += delta.theta; - theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); - offset.x = Math.sin(theta) * Math.cos(phi); - offset.y = Math.sin(theta) * Math.sin(phi); - offset.z = Math.cos(theta); - offset.applyQuaternion(qInverse); - this._desiredLookat - .copy(this._camera.position) - .add(offset.multiplyScalar(length)); - } - rotateWithoutInertia(rotationDelta) { - if (this._currentImage == null) { - return; - } - this._desiredZoom = this._zoom; - this._desiredLookat = null; - this._requestedBasicRotation = null; - this._requestedRotationDelta = null; - const threshold = Math.PI / (10 * Math.pow(2, this._zoom)); - const delta = { - phi: this._spatial.clamp(rotationDelta.phi, -threshold, threshold), - theta: this._spatial.clamp(rotationDelta.theta, -threshold, threshold), - }; - this._applyRotation(delta, this._currentCamera); - this._applyRotation(delta, this._previousCamera); - } - rotateBasic(basicRotation) { - if (this._currentImage == null) { - return; - } - this._desiredZoom = this._zoom; - this._desiredLookat = null; - this._requestedRotationDelta = null; - if (this._requestedBasicRotation != null) { - this._requestedBasicRotation[0] += basicRotation[0]; - this._requestedBasicRotation[1] += basicRotation[1]; - let threshold = 0.05 / Math.pow(2, this._zoom); - this._requestedBasicRotation[0] = - this._spatial.clamp(this._requestedBasicRotation[0], -threshold, threshold); - this._requestedBasicRotation[1] = - this._spatial.clamp(this._requestedBasicRotation[1], -threshold, threshold); - } - else { - this._requestedBasicRotation = basicRotation.slice(); - } - } - rotateBasicUnbounded(basicRotation) { - if (this._currentImage == null) { - return; - } - if (this._requestedBasicRotationUnbounded != null) { - this._requestedBasicRotationUnbounded[0] += basicRotation[0]; - this._requestedBasicRotationUnbounded[1] += basicRotation[1]; - } - else { - this._requestedBasicRotationUnbounded = basicRotation.slice(); - } - } - rotateBasicWithoutInertia(basic) { - if (this._currentImage == null) { - return; - } - this._desiredZoom = this._zoom; - this._desiredLookat = null; - this._requestedRotationDelta = null; - this._requestedBasicRotation = null; - const threshold = 0.05 / Math.pow(2, this._zoom); - const basicRotation = basic.slice(); - basicRotation[0] = this._spatial.clamp(basicRotation[0], -threshold, threshold); - basicRotation[1] = this._spatial.clamp(basicRotation[1], -threshold, threshold); - this._applyRotationBasic(basicRotation); - } - rotateToBasic(basic) { - if (this._currentImage == null) { - return; - } - this._desiredZoom = this._zoom; - this._desiredLookat = null; - basic[0] = this._spatial.clamp(basic[0], 0, 1); - basic[1] = this._spatial.clamp(basic[1], 0, 1); - let lookat = this.currentTransform.unprojectBasic(basic, this._lookatDepth); - this._currentCamera.lookat.fromArray(lookat); - } - zoomIn(delta, reference) { - if (this._currentImage == null) { - return; - } - this._desiredZoom = Math.max(this._minZoom, Math.min(this._maxZoom, this._desiredZoom + delta)); - let currentCenter = this.currentTransform.projectBasic(this._currentCamera.lookat.toArray()); - let currentCenterX = currentCenter[0]; - let currentCenterY = currentCenter[1]; - let zoom0 = Math.pow(2, this._zoom); - let zoom1 = Math.pow(2, this._desiredZoom); - let refX = reference[0]; - let refY = reference[1]; - if (isSpherical(this.currentTransform.cameraType)) { - if (refX - currentCenterX > 0.5) { - refX = refX - 1; - } - else if (currentCenterX - refX > 0.5) { - refX = 1 + refX; - } - } - let newCenterX = refX - zoom0 / zoom1 * (refX - currentCenterX); - let newCenterY = refY - zoom0 / zoom1 * (refY - currentCenterY); - if (isSpherical(this._currentImage.cameraType)) { - newCenterX = this._spatial - .wrap(newCenterX + this._basicRotation[0], 0, 1); - newCenterY = this._spatial - .clamp(newCenterY + this._basicRotation[1], 0.05, 0.95); - } - else { - newCenterX = this._spatial.clamp(newCenterX, 0, 1); - newCenterY = this._spatial.clamp(newCenterY, 0, 1); - } - this._desiredLookat = new Vector3() - .fromArray(this.currentTransform.unprojectBasic([newCenterX, newCenterY], this._lookatDepth)); - } - setCenter(center) { - this._desiredLookat = null; - this._requestedRotationDelta = null; - this._requestedBasicRotation = null; - this._desiredZoom = this._zoom; - let clamped = [ - this._spatial.clamp(center[0], 0, 1), - this._spatial.clamp(center[1], 0, 1), - ]; - if (this._currentImage == null) { - this._desiredCenter = clamped; - return; - } - this._desiredCenter = null; - let currentLookat = new Vector3() - .fromArray(this.currentTransform.unprojectBasic(clamped, this._lookatDepth)); - let previousTransform = this.previousTransform != null ? - this.previousTransform : - this.currentTransform; - let previousLookat = new Vector3() - .fromArray(previousTransform.unprojectBasic(clamped, this._lookatDepth)); - this._currentCamera.lookat.copy(currentLookat); - this._previousCamera.lookat.copy(previousLookat); - } - setZoom(zoom) { - this._desiredLookat = null; - this._requestedRotationDelta = null; - this._requestedBasicRotation = null; - this._zoom = this._spatial.clamp(zoom, this._minZoom, this._maxZoom); - this._desiredZoom = this._zoom; - } - _applyRotation(delta, camera) { - if (camera == null) { - return; - } - let q = new Quaternion().setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); - let qInverse = q.clone().invert(); - let offset = new Vector3(); - offset.copy(camera.lookat).sub(camera.position); - offset.applyQuaternion(q); - let length = offset.length(); - let phi = Math.atan2(offset.y, offset.x); - phi += delta.phi; - let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); - theta += delta.theta; - theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); - offset.x = Math.sin(theta) * Math.cos(phi); - offset.y = Math.sin(theta) * Math.sin(phi); - offset.z = Math.cos(theta); - offset.applyQuaternion(qInverse); - camera.lookat.copy(camera.position).add(offset.multiplyScalar(length)); - } - _applyRotationBasic(basicRotation) { - let currentImage = this._currentImage; - let previousImage = this._previousImage != null ? - this.previousImage : - this.currentImage; - let currentCamera = this._currentCamera; - let previousCamera = this._previousCamera; - let currentTransform = this.currentTransform; - let previousTransform = this.previousTransform != null ? - this.previousTransform : - this.currentTransform; - let currentBasic = currentTransform.projectBasic(currentCamera.lookat.toArray()); - let previousBasic = previousTransform.projectBasic(previousCamera.lookat.toArray()); - if (isSpherical(currentImage.cameraType)) { - currentBasic[0] = this._spatial.wrap(currentBasic[0] + basicRotation[0], 0, 1); - currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0.05, 0.95); - } - else { - currentBasic[0] = this._spatial.clamp(currentBasic[0] + basicRotation[0], 0, 1); - currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); - } - if (isSpherical(previousImage.cameraType)) { - previousBasic[0] = this._spatial.wrap(previousBasic[0] + basicRotation[0], 0, 1); - previousBasic[1] = this._spatial.clamp(previousBasic[1] + basicRotation[1], 0.05, 0.95); - } - else { - previousBasic[0] = this._spatial.clamp(previousBasic[0] + basicRotation[0], 0, 1); - previousBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); - } - let currentLookat = currentTransform.unprojectBasic(currentBasic, this._lookatDepth); - currentCamera.lookat.fromArray(currentLookat); - let previousLookat = previousTransform.unprojectBasic(previousBasic, this._lookatDepth); - previousCamera.lookat.fromArray(previousLookat); - } - _updateZoom(animationSpeed) { - let diff = this._desiredZoom - this._zoom; - let sign = diff > 0 ? 1 : diff < 0 ? -1 : 0; - if (diff === 0) { - return; - } - else if (Math.abs(diff) < 2e-3) { - this._zoom = this._desiredZoom; - if (this._desiredLookat != null) { - this._desiredLookat = null; - } - } - else { - this._zoom += sign * Math.max(Math.abs(5 * animationSpeed * diff), 2e-3); - } - } - _updateLookat(animationSpeed) { - if (this._desiredLookat === null) { - return; - } - let diff = this._desiredLookat.distanceToSquared(this._currentCamera.lookat); - if (Math.abs(diff) < 1e-6) { - this._currentCamera.lookat.copy(this._desiredLookat); - this._desiredLookat = null; - } - else { - this._currentCamera.lookat.lerp(this._desiredLookat, 5 * animationSpeed); - } - } - _updateRotation() { - if (this._requestedRotationDelta != null) { - let length = this._rotationDelta.lengthSquared(); - let requestedLength = this._requestedRotationDelta.lengthSquared(); - if (requestedLength > length) { - this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationIncreaseAlpha); - } - else { - this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationDecreaseAlpha); - } - this._requestedRotationDelta = null; - return; - } - if (this._rotationDelta.isZero) { - return; - } - const alpha = isSpherical(this.currentImage.cameraType) ? - 1 : this._alpha; - this._rotationDelta.multiply(this._rotationAcceleration * alpha); - this._rotationDelta.threshold(this._rotationThreshold); - } - _updateRotationBasic() { - if (this._requestedBasicRotation != null) { - let x = this._basicRotation[0]; - let y = this._basicRotation[1]; - let reqX = this._requestedBasicRotation[0]; - let reqY = this._requestedBasicRotation[1]; - if (Math.abs(reqX) > Math.abs(x)) { - this._basicRotation[0] = (1 - this._rotationIncreaseAlpha) * x + this._rotationIncreaseAlpha * reqX; - } - else { - this._basicRotation[0] = (1 - this._rotationDecreaseAlpha) * x + this._rotationDecreaseAlpha * reqX; - } - if (Math.abs(reqY) > Math.abs(y)) { - this._basicRotation[1] = (1 - this._rotationIncreaseAlpha) * y + this._rotationIncreaseAlpha * reqY; - } - else { - this._basicRotation[1] = (1 - this._rotationDecreaseAlpha) * y + this._rotationDecreaseAlpha * reqY; - } - this._requestedBasicRotation = null; - return; - } - if (this._requestedBasicRotationUnbounded != null) { - let reqX = this._requestedBasicRotationUnbounded[0]; - let reqY = this._requestedBasicRotationUnbounded[1]; - if (Math.abs(reqX) > 0) { - this._basicRotation[0] = (1 - this._unboundedRotationAlpha) * this._basicRotation[0] + this._unboundedRotationAlpha * reqX; - } - if (Math.abs(reqY) > 0) { - this._basicRotation[1] = (1 - this._unboundedRotationAlpha) * this._basicRotation[1] + this._unboundedRotationAlpha * reqY; - } - if (this._desiredLookat != null) { - let desiredBasicLookat = this.currentTransform.projectBasic(this._desiredLookat.toArray()); - desiredBasicLookat[0] += reqX; - desiredBasicLookat[1] += reqY; - this._desiredLookat = new Vector3() - .fromArray(this.currentTransform.unprojectBasic(desiredBasicLookat, this._lookatDepth)); - } - this._requestedBasicRotationUnbounded = null; - } - if (this._basicRotation[0] === 0 && this._basicRotation[1] === 0) { - return; - } - this._basicRotation[0] = this._rotationAcceleration * this._basicRotation[0]; - this._basicRotation[1] = this._rotationAcceleration * this._basicRotation[1]; - if (Math.abs(this._basicRotation[0]) < this._rotationThreshold / Math.pow(2, this._zoom) && - Math.abs(this._basicRotation[1]) < this._rotationThreshold / Math.pow(2, this._zoom)) { - this._basicRotation = [0, 0]; - } - } - _clearRotation() { - if (isSpherical(this._currentImage.cameraType)) { - return; - } - if (this._requestedRotationDelta != null) { - this._requestedRotationDelta = null; - } - if (!this._rotationDelta.isZero) { - this._rotationDelta.reset(); - } - if (this._requestedBasicRotation != null) { - this._requestedBasicRotation = null; - } - if (this._basicRotation[0] > 0 || this._basicRotation[1] > 0) { - this._basicRotation = [0, 0]; - } - } - _setDesiredCenter() { - if (this._desiredCenter == null) { - return; - } - let lookatDirection = new Vector3() - .fromArray(this.currentTransform.unprojectBasic(this._desiredCenter, this._lookatDepth)) - .sub(this._currentCamera.position); - this._currentCamera.lookat.copy(this._currentCamera.position.clone().add(lookatDirection)); - this._previousCamera.lookat.copy(this._previousCamera.position.clone().add(lookatDirection)); - this._desiredCenter = null; - } - _setDesiredZoom() { - this._desiredZoom = - isSpherical(this._currentImage.cameraType) || - this._previousImage == null ? - this._zoom : 0; - } - } - - class TraversingState extends InteractiveStateBase { - constructor(state) { - super(state); - this._adjustCameras(); - this._motionless = this._motionlessTransition(); - this._baseAlpha = this._alpha; - this._speedCoefficient = 1; - this._unitBezier = - new TraversingState._interpolator(0.74, 0.67, 0.38, 0.96); - this._useBezier = false; - } - static register(interpolator) { - TraversingState._interpolator = interpolator; - } - append(images) { - let emptyTrajectory = this._trajectory.length === 0; - if (emptyTrajectory) { - this._resetTransition(); - } - super.append(images); - if (emptyTrajectory) { - this._setDesiredCenter(); - this._setDesiredZoom(); - } - } - prepend(images) { - let emptyTrajectory = this._trajectory.length === 0; - if (emptyTrajectory) { - this._resetTransition(); - } - super.prepend(images); - if (emptyTrajectory) { - this._setDesiredCenter(); - this._setDesiredZoom(); - } - } - set(images) { - super.set(images); - this._desiredLookat = null; - this._resetTransition(); - this._clearRotation(); - this._setDesiredCenter(); - this._setDesiredZoom(); - if (this._trajectory.length < 3) { - this._useBezier = true; - } - } - setSpeed(speed) { - this._speedCoefficient = this._spatial.clamp(speed, 0, 10); - } - update(fps) { - if (this._alpha === 1 && this._currentIndex + this._alpha < this._trajectory.length) { - this._currentIndex += 1; - this._useBezier = this._trajectory.length < 3 && - this._currentIndex + 1 === this._trajectory.length; - this._setCurrent(); - this._resetTransition(); - this._clearRotation(); - this._desiredZoom = - isSpherical(this._currentImage.cameraType) ? - this._zoom : 0; - this._desiredLookat = null; - } - let animationSpeed = this._animationSpeed * (60 / fps); - this._baseAlpha = Math.min(1, this._baseAlpha + this._speedCoefficient * animationSpeed); - if (this._useBezier) { - this._alpha = this._unitBezier.solve(this._baseAlpha); - } - else { - this._alpha = this._baseAlpha; - } - this._updateRotation(); - if (!this._rotationDelta.isZero) { - this._applyRotation(this._rotationDelta, this._previousCamera); - this._applyRotation(this._rotationDelta, this._currentCamera); - } - this._updateRotationBasic(); - if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { - this._applyRotationBasic(this._basicRotation); - } - this._updateZoom(animationSpeed); - this._updateLookat(animationSpeed); - this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); - } - _getAlpha() { - return this._motionless ? Math.ceil(this._alpha) : this._alpha; - } - _setCurrentCamera() { - super._setCurrentCamera(); - this._adjustCameras(); - } - _adjustCameras() { - if (this._previousImage == null) { - return; - } - let lookat = this._camera.lookat.clone().sub(this._camera.position); - this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); - if (isSpherical(this._currentImage.cameraType)) { - this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); - } - } - _resetTransition() { - this._alpha = 0; - this._baseAlpha = 0; - this._motionless = this._motionlessTransition(); - } - } - - class ComponentService { - constructor(container, navigator) { - this._components = {}; - for (const componentName in ComponentService.registeredComponents) { - if (!ComponentService.registeredComponents.hasOwnProperty(componentName)) { - continue; - } - const component = ComponentService.registeredComponents[componentName]; - this._components[componentName] = { - active: false, - component: new component(componentName, container, navigator), - }; - } - this._coverComponent = new ComponentService.registeredCoverComponent("cover", container, navigator); - this._coverComponent.activate(); - this._coverActivated = true; - } - static register(component) { - if (ComponentService.registeredComponents[component.componentName] === undefined) { - ComponentService.registeredComponents[component.componentName] = component; - } - } - static registerCover(coverComponent) { - ComponentService.registeredCoverComponent = coverComponent; - } - get coverActivated() { - return this._coverActivated; - } - activateCover() { - if (this._coverActivated) { - return; - } - this._coverActivated = true; - for (const componentName in this._components) { - if (!this._components.hasOwnProperty(componentName)) { - continue; - } - const component = this._components[componentName]; - if (component.active) { - component.component.deactivate(); - } - } - } - deactivateCover() { - if (!this._coverActivated) { - return; - } - this._coverActivated = false; - for (const componentName in this._components) { - if (!this._components.hasOwnProperty(componentName)) { - continue; - } - const component = this._components[componentName]; - if (component.active) { - component.component.activate(); - } - } - } - activate(name) { - this._checkName(name); - this._components[name].active = true; - if (!this._coverActivated) { - this.get(name).activate(); - } - } - configure(name, conf) { - this._checkName(name); - this.get(name).configure(conf); - } - deactivate(name) { - this._checkName(name); - this._components[name].active = false; - if (!this._coverActivated) { - this.get(name).deactivate(); - } - } - get(name) { - return this._components[name].component; - } - getCover() { - return this._coverComponent; - } - remove() { - this._coverComponent.deactivate(); - for (const componentName in this._components) { - if (!this._components.hasOwnProperty(componentName)) { - continue; - } - this._components[componentName].component.deactivate(); - } - } - _checkName(name) { - if (!(name in this._components)) { - throw new ArgumentMapillaryError(`Component does not exist: ${name}`); - } - } - } - ComponentService.registeredComponents = {}; - - var nativeIsArray = Array.isArray; - var toString$2 = Object.prototype.toString; - - var xIsArray = nativeIsArray || isArray; - - function isArray(obj) { - return toString$2.call(obj) === "[object Array]" - } - - var version = "2"; - - VirtualPatch.NONE = 0; - VirtualPatch.VTEXT = 1; - VirtualPatch.VNODE = 2; - VirtualPatch.WIDGET = 3; - VirtualPatch.PROPS = 4; - VirtualPatch.ORDER = 5; - VirtualPatch.INSERT = 6; - VirtualPatch.REMOVE = 7; - VirtualPatch.THUNK = 8; - - var vpatch = VirtualPatch; - - function VirtualPatch(type, vNode, patch) { - this.type = Number(type); - this.vNode = vNode; - this.patch = patch; - } - - VirtualPatch.prototype.version = version; - VirtualPatch.prototype.type = "VirtualPatch"; - - var isVnode = isVirtualNode; - - function isVirtualNode(x) { - return x && x.type === "VirtualNode" && x.version === version - } - - var isVtext = isVirtualText; - - function isVirtualText(x) { - return x && x.type === "VirtualText" && x.version === version - } - - var isWidget_1 = isWidget; - - function isWidget(w) { - return w && w.type === "Widget" - } - - var isThunk_1 = isThunk; - - function isThunk(t) { - return t && t.type === "Thunk" - } - - var handleThunk_1 = handleThunk; - - function handleThunk(a, b) { - var renderedA = a; - var renderedB = b; - - if (isThunk_1(b)) { - renderedB = renderThunk(b, a); - } - - if (isThunk_1(a)) { - renderedA = renderThunk(a, null); - } - - return { - a: renderedA, - b: renderedB - } - } - - function renderThunk(thunk, previous) { - var renderedThunk = thunk.vnode; - - if (!renderedThunk) { - renderedThunk = thunk.vnode = thunk.render(previous); - } - - if (!(isVnode(renderedThunk) || - isVtext(renderedThunk) || - isWidget_1(renderedThunk))) { - throw new Error("thunk did not return a valid node"); - } - - return renderedThunk - } - - var isObject = function isObject(x) { - return typeof x === 'object' && x !== null; - }; - - var isVhook = isHook; - - function isHook(hook) { - return hook && - (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") || - typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook")) - } - - var diffProps_1 = diffProps; - - function diffProps(a, b) { - var diff; - - for (var aKey in a) { - if (!(aKey in b)) { - diff = diff || {}; - diff[aKey] = undefined; - } - - var aValue = a[aKey]; - var bValue = b[aKey]; - - if (aValue === bValue) { - continue - } else if (isObject(aValue) && isObject(bValue)) { - if (getPrototype$1(bValue) !== getPrototype$1(aValue)) { - diff = diff || {}; - diff[aKey] = bValue; - } else if (isVhook(bValue)) { - diff = diff || {}; - diff[aKey] = bValue; - } else { - var objectDiff = diffProps(aValue, bValue); - if (objectDiff) { - diff = diff || {}; - diff[aKey] = objectDiff; - } - } - } else { - diff = diff || {}; - diff[aKey] = bValue; - } - } - - for (var bKey in b) { - if (!(bKey in a)) { - diff = diff || {}; - diff[bKey] = b[bKey]; - } - } - - return diff - } - - function getPrototype$1(value) { - if (Object.getPrototypeOf) { - return Object.getPrototypeOf(value) - } else if (value.__proto__) { - return value.__proto__ - } else if (value.constructor) { - return value.constructor.prototype - } - } - - var diff_1$1 = diff; - - function diff(a, b) { - var patch = { a: a }; - walk(a, b, patch, 0); - return patch - } - - function walk(a, b, patch, index) { - if (a === b) { - return - } - - var apply = patch[index]; - var applyClear = false; - - if (isThunk_1(a) || isThunk_1(b)) { - thunks(a, b, patch, index); - } else if (b == null) { - - // If a is a widget we will add a remove patch for it - // Otherwise any child widgets/hooks must be destroyed. - // This prevents adding two remove patches for a widget. - if (!isWidget_1(a)) { - clearState(a, patch, index); - apply = patch[index]; - } - - apply = appendPatch(apply, new vpatch(vpatch.REMOVE, a, b)); - } else if (isVnode(b)) { - if (isVnode(a)) { - if (a.tagName === b.tagName && - a.namespace === b.namespace && - a.key === b.key) { - var propsPatch = diffProps_1(a.properties, b.properties); - if (propsPatch) { - apply = appendPatch(apply, - new vpatch(vpatch.PROPS, a, propsPatch)); - } - apply = diffChildren(a, b, patch, apply, index); - } else { - apply = appendPatch(apply, new vpatch(vpatch.VNODE, a, b)); - applyClear = true; - } - } else { - apply = appendPatch(apply, new vpatch(vpatch.VNODE, a, b)); - applyClear = true; - } - } else if (isVtext(b)) { - if (!isVtext(a)) { - apply = appendPatch(apply, new vpatch(vpatch.VTEXT, a, b)); - applyClear = true; - } else if (a.text !== b.text) { - apply = appendPatch(apply, new vpatch(vpatch.VTEXT, a, b)); - } - } else if (isWidget_1(b)) { - if (!isWidget_1(a)) { - applyClear = true; - } - - apply = appendPatch(apply, new vpatch(vpatch.WIDGET, a, b)); - } - - if (apply) { - patch[index] = apply; - } - - if (applyClear) { - clearState(a, patch, index); - } - } - - function diffChildren(a, b, patch, apply, index) { - var aChildren = a.children; - var orderedSet = reorder(aChildren, b.children); - var bChildren = orderedSet.children; - - var aLen = aChildren.length; - var bLen = bChildren.length; - var len = aLen > bLen ? aLen : bLen; - - for (var i = 0; i < len; i++) { - var leftNode = aChildren[i]; - var rightNode = bChildren[i]; - index += 1; - - if (!leftNode) { - if (rightNode) { - // Excess nodes in b need to be added - apply = appendPatch(apply, - new vpatch(vpatch.INSERT, null, rightNode)); - } - } else { - walk(leftNode, rightNode, patch, index); - } - - if (isVnode(leftNode) && leftNode.count) { - index += leftNode.count; - } - } - - if (orderedSet.moves) { - // Reorder nodes last - apply = appendPatch(apply, new vpatch( - vpatch.ORDER, - a, - orderedSet.moves - )); - } - - return apply - } - - function clearState(vNode, patch, index) { - // TODO: Make this a single walk, not two - unhook(vNode, patch, index); - destroyWidgets(vNode, patch, index); - } - - // Patch records for all destroyed widgets must be added because we need - // a DOM node reference for the destroy function - function destroyWidgets(vNode, patch, index) { - if (isWidget_1(vNode)) { - if (typeof vNode.destroy === "function") { - patch[index] = appendPatch( - patch[index], - new vpatch(vpatch.REMOVE, vNode, null) - ); - } - } else if (isVnode(vNode) && (vNode.hasWidgets || vNode.hasThunks)) { - var children = vNode.children; - var len = children.length; - for (var i = 0; i < len; i++) { - var child = children[i]; - index += 1; - - destroyWidgets(child, patch, index); - - if (isVnode(child) && child.count) { - index += child.count; - } - } - } else if (isThunk_1(vNode)) { - thunks(vNode, null, patch, index); - } - } - - // Create a sub-patch for thunks - function thunks(a, b, patch, index) { - var nodes = handleThunk_1(a, b); - var thunkPatch = diff(nodes.a, nodes.b); - if (hasPatches(thunkPatch)) { - patch[index] = new vpatch(vpatch.THUNK, null, thunkPatch); - } - } - - function hasPatches(patch) { - for (var index in patch) { - if (index !== "a") { - return true - } - } - - return false - } - - // Execute hooks when two nodes are identical - function unhook(vNode, patch, index) { - if (isVnode(vNode)) { - if (vNode.hooks) { - patch[index] = appendPatch( - patch[index], - new vpatch( - vpatch.PROPS, - vNode, - undefinedKeys(vNode.hooks) - ) - ); - } - - if (vNode.descendantHooks || vNode.hasThunks) { - var children = vNode.children; - var len = children.length; - for (var i = 0; i < len; i++) { - var child = children[i]; - index += 1; - - unhook(child, patch, index); - - if (isVnode(child) && child.count) { - index += child.count; - } - } - } - } else if (isThunk_1(vNode)) { - thunks(vNode, null, patch, index); - } - } - - function undefinedKeys(obj) { - var result = {}; - - for (var key in obj) { - result[key] = undefined; - } - - return result - } - - // List diff, naive left to right reordering - function reorder(aChildren, bChildren) { - // O(M) time, O(M) memory - var bChildIndex = keyIndex(bChildren); - var bKeys = bChildIndex.keys; - var bFree = bChildIndex.free; - - if (bFree.length === bChildren.length) { - return { - children: bChildren, - moves: null - } - } - - // O(N) time, O(N) memory - var aChildIndex = keyIndex(aChildren); - var aKeys = aChildIndex.keys; - var aFree = aChildIndex.free; - - if (aFree.length === aChildren.length) { - return { - children: bChildren, - moves: null - } - } - - // O(MAX(N, M)) memory - var newChildren = []; - - var freeIndex = 0; - var freeCount = bFree.length; - var deletedItems = 0; - - // Iterate through a and match a node in b - // O(N) time, - for (var i = 0 ; i < aChildren.length; i++) { - var aItem = aChildren[i]; - var itemIndex; - - if (aItem.key) { - if (bKeys.hasOwnProperty(aItem.key)) { - // Match up the old keys - itemIndex = bKeys[aItem.key]; - newChildren.push(bChildren[itemIndex]); - - } else { - // Remove old keyed items - itemIndex = i - deletedItems++; - newChildren.push(null); - } - } else { - // Match the item in a with the next free item in b - if (freeIndex < freeCount) { - itemIndex = bFree[freeIndex++]; - newChildren.push(bChildren[itemIndex]); - } else { - // There are no free items in b to match with - // the free items in a, so the extra free nodes - // are deleted. - itemIndex = i - deletedItems++; - newChildren.push(null); - } - } - } - - var lastFreeIndex = freeIndex >= bFree.length ? - bChildren.length : - bFree[freeIndex]; - - // Iterate through b and append any new keys - // O(M) time - for (var j = 0; j < bChildren.length; j++) { - var newItem = bChildren[j]; - - if (newItem.key) { - if (!aKeys.hasOwnProperty(newItem.key)) { - // Add any new keyed items - // We are adding new items to the end and then sorting them - // in place. In future we should insert new items in place. - newChildren.push(newItem); - } - } else if (j >= lastFreeIndex) { - // Add any leftover non-keyed items - newChildren.push(newItem); - } - } - - var simulate = newChildren.slice(); - var simulateIndex = 0; - var removes = []; - var inserts = []; - var simulateItem; - - for (var k = 0; k < bChildren.length;) { - var wantedItem = bChildren[k]; - simulateItem = simulate[simulateIndex]; - - // remove items - while (simulateItem === null && simulate.length) { - removes.push(remove(simulate, simulateIndex, null)); - simulateItem = simulate[simulateIndex]; - } - - if (!simulateItem || simulateItem.key !== wantedItem.key) { - // if we need a key in this position... - if (wantedItem.key) { - if (simulateItem && simulateItem.key) { - // if an insert doesn't put this key in place, it needs to move - if (bKeys[simulateItem.key] !== k + 1) { - removes.push(remove(simulate, simulateIndex, simulateItem.key)); - simulateItem = simulate[simulateIndex]; - // if the remove didn't put the wanted item in place, we need to insert it - if (!simulateItem || simulateItem.key !== wantedItem.key) { - inserts.push({key: wantedItem.key, to: k}); - } - // items are matching, so skip ahead - else { - simulateIndex++; - } - } - else { - inserts.push({key: wantedItem.key, to: k}); - } - } - else { - inserts.push({key: wantedItem.key, to: k}); - } - k++; - } - // a key in simulate has no matching wanted key, remove it - else if (simulateItem && simulateItem.key) { - removes.push(remove(simulate, simulateIndex, simulateItem.key)); - } - } - else { - simulateIndex++; - k++; - } - } - - // remove all the remaining nodes from simulate - while(simulateIndex < simulate.length) { - simulateItem = simulate[simulateIndex]; - removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key)); - } - - // If the only moves we have are deletes then we can just - // let the delete patch remove these items. - if (removes.length === deletedItems && !inserts.length) { - return { - children: newChildren, - moves: null - } - } - - return { - children: newChildren, - moves: { - removes: removes, - inserts: inserts - } - } - } - - function remove(arr, index, key) { - arr.splice(index, 1); - - return { - from: index, - key: key - } - } - - function keyIndex(children) { - var keys = {}; - var free = []; - var length = children.length; - - for (var i = 0; i < length; i++) { - var child = children[i]; - - if (child.key) { - keys[child.key] = i; - } else { - free.push(i); - } - } - - return { - keys: keys, // A hash of key name to index - free: free // An array of unkeyed item indices + + return { + keys: keys, // A hash of key name to index + free: free // An array of unkeyed item indices } } function appendPatch(apply, patch) { if (apply) { - if (xIsArray(apply)) { + if (isArray$2(apply)) { apply.push(patch); } else { apply = [apply, patch]; @@ -53767,37 +50294,13 @@ } } - var diff_1 = diff_1$1; - - var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - function getAugmentedNamespace(n) { - if (n.__esModule) return n; - var a = Object.defineProperty({}, '__esModule', {value: true}); - Object.keys(n).forEach(function (k) { - var d = Object.getOwnPropertyDescriptor(n, k); - Object.defineProperty(a, k, d.get ? d : { - enumerable: true, - get: function () { - return n[k]; - } - }); - }); - return a; - } - - function createCommonjsModule(fn) { - var module = { exports: {} }; - return fn(module, module.exports), module.exports; - } + var diff$1 = diff_1$1; - function commonjsRequire (target) { - throw new Error('Could not dynamically require "' + target + '". Please configure the dynamicRequireTargets option of @rollup/plugin-commonjs appropriately for this require call to behave properly.'); - } + var diff_1 = diff$1; var slice = Array.prototype.slice; - var domWalk = iterativelyWalk; + var domWalk$2 = iterativelyWalk; function iterativelyWalk(nodes, cb) { if (!('length' in nodes)) { @@ -53820,11 +50323,11 @@ } } - var domComment = Comment; + var domComment = Comment$1; - function Comment(data, owner) { - if (!(this instanceof Comment)) { - return new Comment(data, owner) + function Comment$1(data, owner) { + if (!(this instanceof Comment$1)) { + return new Comment$1(data, owner) } this.data = data; @@ -53833,18 +50336,18 @@ this.ownerDocument = owner || null; } - Comment.prototype.nodeType = 8; - Comment.prototype.nodeName = "#comment"; + Comment$1.prototype.nodeType = 8; + Comment$1.prototype.nodeName = "#comment"; - Comment.prototype.toString = function _Comment_toString() { + Comment$1.prototype.toString = function _Comment_toString() { return "[object Comment]" }; - var domText = DOMText; + var domText = DOMText$1; - function DOMText(value, owner) { - if (!(this instanceof DOMText)) { - return new DOMText(value) + function DOMText$1(value, owner) { + if (!(this instanceof DOMText$1)) { + return new DOMText$1(value) } this.data = value || ""; @@ -53852,15 +50355,15 @@ this.ownerDocument = owner || null; } - DOMText.prototype.type = "DOMTextNode"; - DOMText.prototype.nodeType = 3; - DOMText.prototype.nodeName = "#text"; + DOMText$1.prototype.type = "DOMTextNode"; + DOMText$1.prototype.nodeType = 3; + DOMText$1.prototype.nodeName = "#text"; - DOMText.prototype.toString = function _Text_toString() { + DOMText$1.prototype.toString = function _Text_toString() { return this.data }; - DOMText.prototype.replaceData = function replaceData(index, length, value) { + DOMText$1.prototype.replaceData = function replaceData(index, length, value) { var current = this.data; var left = current.substring(0, index); var right = current.substring(index + length, current.length); @@ -53868,9 +50371,9 @@ this.length = this.data.length; }; - var dispatchEvent_1 = dispatchEvent; + var dispatchEvent_1 = dispatchEvent$2; - function dispatchEvent(ev) { + function dispatchEvent$2(ev) { var elem = this; var type = ev.type; @@ -53900,9 +50403,9 @@ } } - var addEventListener_1 = addEventListener; + var addEventListener_1 = addEventListener$2; - function addEventListener(type, listener) { + function addEventListener$2(type, listener) { var elem = this; if (!elem.listeners) { @@ -53918,9 +50421,9 @@ } } - var removeEventListener_1 = removeEventListener; + var removeEventListener_1 = removeEventListener$2; - function removeEventListener(type, listener) { + function removeEventListener$2(type, listener) { var elem = this; if (!elem.listeners) { @@ -53938,11 +50441,11 @@ } } - var serialize = serializeNode; + var serialize = serializeNode$1; var voidElements = ["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"]; - function serializeNode(node) { + function serializeNode$1(node) { switch (node.nodeType) { case 3: return escapeText(node.data) @@ -53970,7 +50473,7 @@ strings.push(">"); if (elem.childNodes.length) { - strings.push.apply(strings, elem.childNodes.map(serializeNode)); + strings.push.apply(strings, elem.childNodes.map(serializeNode$1)); } else if (elem.textContent || elem.innerText) { strings.push(escapeText(elem.textContent || elem.innerText)); } else if (elem.innerHTML) { @@ -54078,13 +50581,19 @@ return escapeText(str).replace(/"/g, """) } + var domWalk$1 = domWalk$2; + var dispatchEvent$1 = dispatchEvent_1; + var addEventListener$1 = addEventListener_1; + var removeEventListener$1 = removeEventListener_1; + var serializeNode = serialize; + var htmlns = "http://www.w3.org/1999/xhtml"; - var domElement = DOMElement; + var domElement = DOMElement$2; - function DOMElement(tagName, owner, namespace) { - if (!(this instanceof DOMElement)) { - return new DOMElement(tagName) + function DOMElement$2(tagName, owner, namespace) { + if (!(this instanceof DOMElement$2)) { + return new DOMElement$2(tagName) } var ns = namespace === undefined ? htmlns : (namespace || null); @@ -54105,10 +50614,10 @@ } } - DOMElement.prototype.type = "DOMElement"; - DOMElement.prototype.nodeType = 1; + DOMElement$2.prototype.type = "DOMElement"; + DOMElement$2.prototype.nodeType = 1; - DOMElement.prototype.appendChild = function _Element_appendChild(child) { + DOMElement$2.prototype.appendChild = function _Element_appendChild(child) { if (child.parentNode) { child.parentNode.removeChild(child); } @@ -54119,7 +50628,7 @@ return child }; - DOMElement.prototype.replaceChild = + DOMElement$2.prototype.replaceChild = function _Element_replaceChild(elem, needle) { // TODO: Throw NotFoundError if needle.parentNode !== this @@ -54136,7 +50645,7 @@ return needle }; - DOMElement.prototype.removeChild = function _Element_removeChild(elem) { + DOMElement$2.prototype.removeChild = function _Element_removeChild(elem) { // TODO: Throw NotFoundError if elem.parentNode !== this var index = this.childNodes.indexOf(elem); @@ -54146,7 +50655,7 @@ return elem }; - DOMElement.prototype.insertBefore = + DOMElement$2.prototype.insertBefore = function _Element_insertBefore(elem, needle) { // TODO: Throw NotFoundError if referenceElement is a dom node // and parentNode !== this @@ -54169,7 +50678,7 @@ return elem }; - DOMElement.prototype.setAttributeNS = + DOMElement$2.prototype.setAttributeNS = function _Element_setAttributeNS(namespace, name, value) { var prefix = null; var localName = name; @@ -54187,7 +50696,7 @@ } }; - DOMElement.prototype.getAttributeNS = + DOMElement$2.prototype.getAttributeNS = function _Element_getAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; var value = attributes && attributes[name] && attributes[name].value; @@ -54200,7 +50709,7 @@ return value }; - DOMElement.prototype.removeAttributeNS = + DOMElement$2.prototype.removeAttributeNS = function _Element_removeAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; if (attributes) { @@ -54208,46 +50717,46 @@ } }; - DOMElement.prototype.hasAttributeNS = + DOMElement$2.prototype.hasAttributeNS = function _Element_hasAttributeNS(namespace, name) { var attributes = this._attributes[namespace]; return !!attributes && name in attributes; }; - DOMElement.prototype.setAttribute = function _Element_setAttribute(name, value) { + DOMElement$2.prototype.setAttribute = function _Element_setAttribute(name, value) { return this.setAttributeNS(null, name, value) }; - DOMElement.prototype.getAttribute = function _Element_getAttribute(name) { + DOMElement$2.prototype.getAttribute = function _Element_getAttribute(name) { return this.getAttributeNS(null, name) }; - DOMElement.prototype.removeAttribute = function _Element_removeAttribute(name) { + DOMElement$2.prototype.removeAttribute = function _Element_removeAttribute(name) { return this.removeAttributeNS(null, name) }; - DOMElement.prototype.hasAttribute = function _Element_hasAttribute(name) { + DOMElement$2.prototype.hasAttribute = function _Element_hasAttribute(name) { return this.hasAttributeNS(null, name) }; - DOMElement.prototype.removeEventListener = removeEventListener_1; - DOMElement.prototype.addEventListener = addEventListener_1; - DOMElement.prototype.dispatchEvent = dispatchEvent_1; + DOMElement$2.prototype.removeEventListener = removeEventListener$1; + DOMElement$2.prototype.addEventListener = addEventListener$1; + DOMElement$2.prototype.dispatchEvent = dispatchEvent$1; // Un-implemented - DOMElement.prototype.focus = function _Element_focus() { + DOMElement$2.prototype.focus = function _Element_focus() { return void 0 }; - DOMElement.prototype.toString = function _Element_toString() { - return serialize(this) + DOMElement$2.prototype.toString = function _Element_toString() { + return serializeNode(this) }; - DOMElement.prototype.getElementsByClassName = function _Element_getElementsByClassName(classNames) { + DOMElement$2.prototype.getElementsByClassName = function _Element_getElementsByClassName(classNames) { var classes = classNames.split(" "); var elems = []; - domWalk(this, function (node) { + domWalk$1(this, function (node) { if (node.nodeType === 1) { var nodeClassName = node.className || ""; var nodeClasses = nodeClassName.split(" "); @@ -54263,11 +50772,11 @@ return elems }; - DOMElement.prototype.getElementsByTagName = function _Element_getElementsByTagName(tagName) { + DOMElement$2.prototype.getElementsByTagName = function _Element_getElementsByTagName(tagName) { tagName = tagName.toLowerCase(); var elems = []; - domWalk(this.childNodes, function (node) { + domWalk$1(this.childNodes, function (node) { if (node.nodeType === 1 && (tagName === '*' || node.tagName.toLowerCase() === tagName)) { elems.push(node); } @@ -54276,17 +50785,19 @@ return elems }; - DOMElement.prototype.contains = function _Element_contains(element) { - return domWalk(this, function (node) { + DOMElement$2.prototype.contains = function _Element_contains(element) { + return domWalk$1(this, function (node) { return element === node }) || false }; - var domFragment = DocumentFragment; + var DOMElement$1 = domElement; - function DocumentFragment(owner) { - if (!(this instanceof DocumentFragment)) { - return new DocumentFragment() + var domFragment = DocumentFragment$1; + + function DocumentFragment$1(owner) { + if (!(this instanceof DocumentFragment$1)) { + return new DocumentFragment$1() } this.childNodes = []; @@ -54294,40 +50805,51 @@ this.ownerDocument = owner || null; } - DocumentFragment.prototype.type = "DocumentFragment"; - DocumentFragment.prototype.nodeType = 11; - DocumentFragment.prototype.nodeName = "#document-fragment"; + DocumentFragment$1.prototype.type = "DocumentFragment"; + DocumentFragment$1.prototype.nodeType = 11; + DocumentFragment$1.prototype.nodeName = "#document-fragment"; - DocumentFragment.prototype.appendChild = domElement.prototype.appendChild; - DocumentFragment.prototype.replaceChild = domElement.prototype.replaceChild; - DocumentFragment.prototype.removeChild = domElement.prototype.removeChild; + DocumentFragment$1.prototype.appendChild = DOMElement$1.prototype.appendChild; + DocumentFragment$1.prototype.replaceChild = DOMElement$1.prototype.replaceChild; + DocumentFragment$1.prototype.removeChild = DOMElement$1.prototype.removeChild; - DocumentFragment.prototype.toString = + DocumentFragment$1.prototype.toString = function _DocumentFragment_toString() { return this.childNodes.map(function (node) { return String(node) }).join("") }; - var event = Event; + var event = Event$1; - function Event(family) {} + function Event$1(family) {} - Event.prototype.initEvent = function _Event_initEvent(type, bubbles, cancelable) { + Event$1.prototype.initEvent = function _Event_initEvent(type, bubbles, cancelable) { this.type = type; this.bubbles = bubbles; this.cancelable = cancelable; }; - Event.prototype.preventDefault = function _Event_preventDefault() { + Event$1.prototype.preventDefault = function _Event_preventDefault() { }; - var document$1 = Document; + var domWalk = domWalk$2; + + var Comment = domComment; + var DOMText = domText; + var DOMElement = domElement; + var DocumentFragment = domFragment; + var Event = event; + var dispatchEvent = dispatchEvent_1; + var addEventListener = addEventListener_1; + var removeEventListener = removeEventListener_1; - function Document() { - if (!(this instanceof Document)) { - return new Document(); + var document$3 = Document$1; + + function Document$1() { + if (!(this instanceof Document$1)) { + return new Document$1(); } this.head = this.createElement("head"); @@ -54339,30 +50861,30 @@ this.nodeType = 9; } - var proto = Document.prototype; + var proto = Document$1.prototype; proto.createTextNode = function createTextNode(value) { - return new domText(value, this) + return new DOMText(value, this) }; proto.createElementNS = function createElementNS(namespace, tagName) { var ns = namespace === null ? null : String(namespace); - return new domElement(tagName, this, ns) + return new DOMElement(tagName, this, ns) }; proto.createElement = function createElement(tagName) { - return new domElement(tagName, this) + return new DOMElement(tagName, this) }; proto.createDocumentFragment = function createDocumentFragment() { - return new domFragment(this) + return new DocumentFragment(this) }; proto.createEvent = function createEvent(family) { - return new event(family) + return new Event(family) }; proto.createComment = function createComment(data) { - return new domComment(data, this) + return new Comment(data, this) }; proto.getElementById = function getElementById(id) { @@ -54377,19 +50899,21 @@ return result || null }; - proto.getElementsByClassName = domElement.prototype.getElementsByClassName; - proto.getElementsByTagName = domElement.prototype.getElementsByTagName; - proto.contains = domElement.prototype.contains; + proto.getElementsByClassName = DOMElement.prototype.getElementsByClassName; + proto.getElementsByTagName = DOMElement.prototype.getElementsByTagName; + proto.contains = DOMElement.prototype.contains; + + proto.removeEventListener = removeEventListener; + proto.addEventListener = addEventListener; + proto.dispatchEvent = dispatchEvent; - proto.removeEventListener = removeEventListener_1; - proto.addEventListener = addEventListener_1; - proto.dispatchEvent = dispatchEvent_1; + var Document = document$3; - var minDocument = new document$1(); + var minDocument = new Document(); var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof window !== 'undefined' ? window : {}; - + var minDoc = minDocument; var doccy; @@ -54399,21 +50923,24 @@ doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4']; if (!doccy) { - doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDocument; + doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc; } } var document_1 = doccy; - var applyProperties_1 = applyProperties; + var isObject = isObject$2; + var isHook$1 = isVhook; + + var applyProperties_1 = applyProperties$2; - function applyProperties(node, props, previous) { + function applyProperties$2(node, props, previous) { for (var propName in props) { var propValue = props[propName]; if (propValue === undefined) { removeProperty(node, propName, propValue, previous); - } else if (isVhook(propValue)) { + } else if (isHook$1(propValue)) { removeProperty(node, propName, propValue, previous); if (propValue.hook) { propValue.hook(node, @@ -54434,7 +50961,7 @@ if (previous) { var previousValue = previous[propName]; - if (!isVhook(previousValue)) { + if (!isHook$1(previousValue)) { if (propName === "attributes") { for (var attrName in previousValue) { node.removeAttribute(attrName); @@ -54500,19 +51027,28 @@ } } - var createElement_1$1 = createElement; + var document$2 = document_1; + + var applyProperties$1 = applyProperties_1; + + var isVNode$2 = isVnode; + var isVText$1 = isVtext; + var isWidget$4 = isWidget_1; + var handleThunk = handleThunk_1; - function createElement(vnode, opts) { - var doc = opts ? opts.document || document_1 : document_1; + var createElement_1$1 = createElement$1; + + function createElement$1(vnode, opts) { + var doc = opts ? opts.document || document$2 : document$2; var warn = opts ? opts.warn : null; - vnode = handleThunk_1(vnode).a; + vnode = handleThunk(vnode).a; - if (isWidget_1(vnode)) { + if (isWidget$4(vnode)) { return vnode.init() - } else if (isVtext(vnode)) { + } else if (isVText$1(vnode)) { return doc.createTextNode(vnode.text) - } else if (!isVnode(vnode)) { + } else if (!isVNode$2(vnode)) { if (warn) { warn("Item is not a valid virtual dom node", vnode); } @@ -54524,12 +51060,12 @@ doc.createElementNS(vnode.namespace, vnode.tagName); var props = vnode.properties; - applyProperties_1(node, props); + applyProperties$1(node, props); var children = vnode.children; for (var i = 0; i < children.length; i++) { - var childNode = createElement(children[i], opts); + var childNode = createElement$1(children[i], opts); if (childNode) { node.appendChild(childNode); } @@ -54546,9 +51082,9 @@ var noChild = {}; - var domIndex_1 = domIndex; + var domIndex_1 = domIndex$1; - function domIndex(rootNode, tree, indices, nodes) { + function domIndex$1(rootNode, tree, indices, nodes) { if (!indices || indices.length === 0) { return {} } else { @@ -54624,10 +51160,12 @@ return a > b ? 1 : -1 } - var updateWidget_1 = updateWidget; + var isWidget$3 = isWidget_1; + + var updateWidget_1 = updateWidget$1; - function updateWidget(a, b) { - if (isWidget_1(a) && isWidget_1(b)) { + function updateWidget$1(a, b) { + if (isWidget$3(a) && isWidget$3(b)) { if ("name" in a && "name" in b) { return a.id === b.id } else { @@ -54638,31 +51176,38 @@ return false } - var patchOp = applyPatch$1; + var applyProperties = applyProperties_1; + + var isWidget$2 = isWidget_1; + var VPatch = vpatch; + + var updateWidget = updateWidget_1; - function applyPatch$1(vpatch$1, domNode, renderOptions) { - var type = vpatch$1.type; - var vNode = vpatch$1.vNode; - var patch = vpatch$1.patch; + var patchOp$1 = applyPatch$1; + + function applyPatch$1(vpatch, domNode, renderOptions) { + var type = vpatch.type; + var vNode = vpatch.vNode; + var patch = vpatch.patch; switch (type) { - case vpatch.REMOVE: + case VPatch.REMOVE: return removeNode$1(domNode, vNode) - case vpatch.INSERT: + case VPatch.INSERT: return insertNode$1(domNode, patch, renderOptions) - case vpatch.VTEXT: + case VPatch.VTEXT: return stringPatch(domNode, vNode, patch, renderOptions) - case vpatch.WIDGET: + case VPatch.WIDGET: return widgetPatch(domNode, vNode, patch, renderOptions) - case vpatch.VNODE: + case VPatch.VNODE: return vNodePatch(domNode, vNode, patch, renderOptions) - case vpatch.ORDER: + case VPatch.ORDER: reorderChildren(domNode, patch); return domNode - case vpatch.PROPS: - applyProperties_1(domNode, patch, vNode.properties); + case VPatch.PROPS: + applyProperties(domNode, patch, vNode.properties); return domNode - case vpatch.THUNK: + case VPatch.THUNK: return replaceRoot(domNode, renderOptions.patch(domNode, patch, renderOptions)) default: @@ -54711,7 +51256,7 @@ } function widgetPatch(domNode, leftVNode, widget, renderOptions) { - var updating = updateWidget_1(leftVNode, widget); + var updating = updateWidget(leftVNode, widget); var newNode; if (updating) { @@ -54745,7 +51290,7 @@ } function destroyWidget(domNode, w) { - if (typeof w.destroy === "function" && isWidget_1(w)) { + if (typeof w.destroy === "function" && isWidget$2(w)) { w.destroy(domNode); } } @@ -54783,14 +51328,20 @@ return newRoot; } - var patch_1$1 = patch; + var document$1 = document_1; + var isArray$1 = xIsArray; + + var render = createElement_1$1; + var domIndex = domIndex_1; + var patchOp = patchOp$1; + var patch_1$1 = patch$2; - function patch(rootNode, patches, renderOptions) { + function patch$2(rootNode, patches, renderOptions) { renderOptions = renderOptions || {}; - renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch + renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch$2 ? renderOptions.patch : patchRecursive; - renderOptions.render = renderOptions.render || createElement_1$1; + renderOptions.render = renderOptions.render || render; return renderOptions.patch(rootNode, patches, renderOptions) } @@ -54802,10 +51353,10 @@ return rootNode } - var index = domIndex_1(rootNode, patches.a, indices); + var index = domIndex(rootNode, patches.a, indices); var ownerDocument = rootNode.ownerDocument; - if (!renderOptions.document && ownerDocument !== document_1) { + if (!renderOptions.document && ownerDocument !== document$1) { renderOptions.document = ownerDocument; } @@ -54827,7 +51378,7 @@ var newNode; - if (xIsArray(patchList)) { + if (isArray$1(patchList)) { for (var i = 0; i < patchList.length; i++) { newNode = patchOp(patchList[i], domNode, renderOptions); @@ -54858,7 +51409,15 @@ return indices } - var patch_1 = patch_1$1; + var patch$1 = patch_1$1; + + var patch_1 = patch$1; + + var version$1 = version$5; + var isVNode$1 = isVnode; + var isWidget$1 = isWidget_1; + var isThunk = isThunk_1; + var isVHook = isVhook; var vnode = VirtualNode; @@ -54882,7 +51441,7 @@ for (var propName in properties) { if (properties.hasOwnProperty(propName)) { var property = properties[propName]; - if (isVhook(property) && property.unhook) { + if (isVHook(property) && property.unhook) { if (!hooks) { hooks = {}; } @@ -54894,7 +51453,7 @@ for (var i = 0; i < count; i++) { var child = children[i]; - if (isVnode(child)) { + if (isVNode$1(child)) { descendants += child.count || 0; if (!hasWidgets && child.hasWidgets) { @@ -54908,11 +51467,11 @@ if (!descendantHooks && (child.hooks || child.descendantHooks)) { descendantHooks = true; } - } else if (!hasWidgets && isWidget_1(child)) { + } else if (!hasWidgets && isWidget$1(child)) { if (typeof child.destroy === "function") { hasWidgets = true; } - } else if (!hasThunks && isThunk_1(child)) { + } else if (!hasThunks && isThunk(child)) { hasThunks = true; } } @@ -54924,9 +51483,11 @@ this.descendantHooks = descendantHooks; } - VirtualNode.prototype.version = version; + VirtualNode.prototype.version = version$1; VirtualNode.prototype.type = "VirtualNode"; + var version = version$5; + var vtext = VirtualText; function VirtualText(text) { @@ -54942,6 +51503,7 @@ * Available under the MIT License * ECMAScript compliant, uniform cross-browser split method */ + /** * Splits a string into an array of strings using a regex or string separator. Matches of the * separator are not included in the result array. However, if `separator` is a regex that contains @@ -55042,19 +51604,21 @@ return self; })(); + var split = browserSplit; + var classIdSplit = /([\.#]?[a-zA-Z0-9\u007F-\uFFFF_:-]+)/; var notClassId = /^\.|#/; - var parseTag_1 = parseTag; + var parseTag_1 = parseTag$1; - function parseTag(tag, props) { + function parseTag$1(tag, props) { if (!tag) { return 'DIV'; } var noId = !(props.hasOwnProperty('id')); - var tagParts = browserSplit(tag, classIdSplit); + var tagParts = split(tag, classIdSplit); var tagName = null; if (notClassId.test(tagParts[1])) { @@ -55093,7 +51657,7 @@ return props.namespace ? tagName : tagName.toUpperCase(); } - var softSetHook = SoftSetHook; + var softSetHook$1 = SoftSetHook; function SoftSetHook(value) { if (!(this instanceof SoftSetHook)) { @@ -55115,9 +51679,9 @@ window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : {}; - var individual = Individual; + var individual = Individual$1; - function Individual(key, value) { + function Individual$1(key, value) { if (key in root) { return root[key]; } @@ -55127,13 +51691,15 @@ return value; } + var Individual = individual; + var oneVersion = OneVersion; function OneVersion(moduleName, version, defaultValue) { var key = '__INDIVIDUAL_ONE_VERSION_' + moduleName; var enforceKey = key + '_ENFORCE_SINGLETON'; - var versionValue = individual(enforceKey, version); + var versionValue = Individual(enforceKey, version); if (versionValue !== version) { throw new Error('Can only have one copy of ' + @@ -55143,17 +51709,19 @@ 'This means you cannot install version ' + version); } - return individual(key, defaultValue); + return Individual(key, defaultValue); } + var OneVersionConstraint = oneVersion; + var MY_VERSION = '7'; - oneVersion('ev-store', MY_VERSION); + OneVersionConstraint('ev-store', MY_VERSION); var hashKey = '__EV_STORE_KEY@' + MY_VERSION; - var evStore = EvStore; + var evStore = EvStore$1; - function EvStore(elem) { + function EvStore$1(elem) { var hash = elem[hashKey]; if (!hash) { @@ -55163,7 +51731,9 @@ return hash; } - var evHook = EvHook; + var EvStore = evStore; + + var evHook$1 = EvHook; function EvHook(value) { if (!(this instanceof EvHook)) { @@ -55174,22 +51744,36 @@ } EvHook.prototype.hook = function (node, propertyName) { - var es = evStore(node); + var es = EvStore(node); var propName = propertyName.substr(3); es[propName] = this.value; }; EvHook.prototype.unhook = function(node, propertyName) { - var es = evStore(node); + var es = EvStore(node); var propName = propertyName.substr(3); es[propName] = undefined; }; - var virtualHyperscript = h; + var isArray = xIsArray; + + var VNode$1 = vnode; + var VText$1 = vtext; + var isVNode = isVnode; + var isVText = isVtext; + var isWidget = isWidget_1; + var isHook = isVhook; + var isVThunk = isThunk_1; + + var parseTag = parseTag_1; + var softSetHook = softSetHook$1; + var evHook = evHook$1; - function h(tagName, properties, children) { + var virtualHyperscript = h$2; + + function h$2(tagName, properties, children) { var childNodes = []; var tag, props, key, namespace; @@ -55199,7 +51783,7 @@ } props = props || properties || {}; - tag = parseTag_1(tagName, props); + tag = parseTag(tagName, props); // support keys if (props.hasOwnProperty('key')) { @@ -55218,7 +51802,7 @@ !namespace && props.hasOwnProperty('value') && props.value !== undefined && - !isVhook(props.value) + !isHook(props.value) ) { props.value = softSetHook(props.value); } @@ -55230,17 +51814,17 @@ } - return new vnode(tag, props, childNodes, key, namespace); + return new VNode$1(tag, props, childNodes, key, namespace); } function addChild(c, childNodes, tag, props) { if (typeof c === 'string') { - childNodes.push(new vtext(c)); + childNodes.push(new VText$1(c)); } else if (typeof c === 'number') { - childNodes.push(new vtext(String(c))); + childNodes.push(new VText$1(String(c))); } else if (isChild(c)) { childNodes.push(c); - } else if (xIsArray(c)) { + } else if (isArray(c)) { for (var i = 0; i < c.length; i++) { addChild(c[i], childNodes, tag, props); } @@ -55262,7 +51846,7 @@ if (props.hasOwnProperty(propName)) { var value = props[propName]; - if (isVhook(value)) { + if (isHook(value)) { continue; } @@ -55275,11 +51859,11 @@ } function isChild(x) { - return isVnode(x) || isVtext(x) || isWidget_1(x) || isThunk_1(x); + return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x); } function isChildren(x) { - return typeof x === 'string' || xIsArray(x) || isChild(x); + return typeof x === 'string' || isArray(x) || isChild(x); } function UnexpectedVirtualElement(data) { @@ -55307,31 +51891,42 @@ } } - var h_1 = virtualHyperscript; + var h$1 = virtualHyperscript; + + var h_1 = h$1; + + var createElement = createElement_1$1; + + var createElement_1 = createElement; - var createElement_1 = createElement_1$1; + var diff = diff_1; + var patch = patch_1; + var h = h_1; + var create = createElement_1; + var VNode = vnode; + var VText = vtext; var virtualDom = { - diff: diff_1, - patch: patch_1, - h: h_1, - create: createElement_1, - VNode: vnode, - VText: vtext + diff: diff, + patch: patch, + h: h, + create: create, + VNode: VNode, + VText: VText }; class EventEmitter { constructor() { this._events = {}; } /** - * Subscribe to an event by its name. - * @param {string} type - The name of the event - * to subscribe to. - * @param {(event: T) => void} handler - The - * handler called when the event occurs. + * @ignore */ - on(type, handler) { - this._events[type] = this._events[type] || []; - this._events[type].push(handler); + fire(type, event) { + if (!this._listens(type)) { + return; + } + for (const handler of this._events[type]) { + handler(event); + } } /** * Unsubscribe from an event by its name. @@ -55356,15 +51951,15 @@ } } /** - * @ignore + * Subscribe to an event by its name. + * @param {string} type - The name of the event + * to subscribe to. + * @param {(event: T) => void} handler - The + * handler called when the event occurs. */ - fire(type, event) { - if (!this._listens(type)) { - return; - } - for (const handler of this._events[type]) { - handler(event); - } + on(type, handler) { + this._events[type] = this._events[type] || []; + this._events[type].push(handler); } _listens(eventType) { return eventType in this._events; @@ -55592,7 +52187,9 @@ this._navigator.api.getImages$([id]) .subscribe((items) => { for (const item of items) { - if (item.node_id !== id) { + const imageId = typeof id === "number" ? + id.toString() : id; + if (item.node_id !== imageId) { continue; } this._navigator.api.data @@ -56151,7 +52748,6 @@ this._svgNamespace = "http://www.w3.org/2000/svg"; this._distinctThreshold = Math.PI / 360; this._animationSpeed = 0.075; - this._unitBezier = new unitbezier(0.74, 0.67, 0.38, 0.96); } _activate() { const subs = this._subscriptions; @@ -56202,7 +52798,7 @@ const smoothImageFov$ = imageFovOperation$.pipe(scan((state, operation) => { return operation(state); }, { alpha: 0, curr: [0, 0, 0], prev: [0, 0, 0] }), map((state) => { - const alpha = this._unitBezier.solve(state.alpha); + const alpha = MathUtils.smootherstep(state.alpha, 0, 1); const curr = state.curr; const prev = state.prev; return [ @@ -56212,7 +52808,7 @@ })); subs.push(imageFov$.pipe(map((nbf) => { return (state) => { - const a = this._unitBezier.solve(state.alpha); + const a = MathUtils.smootherstep(state.alpha, 0, 1); const c = state.curr; const p = state.prev; const prev = [ @@ -61825,123 +58421,709 @@ void main() if (index === -1) { return { index: null, max: null }; } - return { index: index, max: sequence.imageIds.length - 1 }; - })); - })); - const earth$ = this._navigator.stateService.state$.pipe(map((state) => { - return state === State.Earth; - }), distinctUntilChanged()); - subs.push(combineLatest(edgeStatus$, this._configuration$, this._containerWidth$, this._sequenceDOMRenderer.changed$.pipe(startWith(this._sequenceDOMRenderer)), this._navigator.playService.speed$, position$, earth$).pipe(map(([edgeStatus, configuration, containerWidth, , speed, position, earth]) => { - const vNode = this._sequenceDOMRenderer - .render(edgeStatus, configuration, containerWidth, speed, position.index, position.max, !earth, this, this._navigator); - return { name: this._name, vNode: vNode }; - })) - .subscribe(this._container.domRenderer.render$)); - subs.push(this._sequenceDOMRenderer.speed$ - .subscribe((speed) => { - this._navigator.playService.setSpeed(speed); - })); - subs.push(this._configuration$.pipe(map((configuration) => { - return configuration.direction; - }), distinctUntilChanged()) - .subscribe((direction) => { - this._navigator.playService.setDirection(direction); - })); - subs.push(combineLatest(this._container.renderService.size$, this._configuration$.pipe(distinctUntilChanged((value1, value2) => { - return value1[0] === value2[0] && value1[1] === value2[1]; - }, (configuration) => { - return [configuration.minWidth, configuration.maxWidth]; - }))).pipe(map(([size, configuration]) => { - return this._sequenceDOMRenderer.getContainerWidth(size, configuration); - })) - .subscribe(this._containerWidth$)); - subs.push(this._configuration$.pipe(map((configuration) => { - return configuration.playing; - }), distinctUntilChanged()) - .subscribe((playing) => { - if (playing) { - this._navigator.playService.play(); + return { index: index, max: sequence.imageIds.length - 1 }; + })); + })); + const earth$ = this._navigator.stateService.state$.pipe(map((state) => { + return state === State.Earth; + }), distinctUntilChanged()); + subs.push(combineLatest(edgeStatus$, this._configuration$, this._containerWidth$, this._sequenceDOMRenderer.changed$.pipe(startWith(this._sequenceDOMRenderer)), this._navigator.playService.speed$, position$, earth$).pipe(map(([edgeStatus, configuration, containerWidth, , speed, position, earth]) => { + const vNode = this._sequenceDOMRenderer + .render(edgeStatus, configuration, containerWidth, speed, position.index, position.max, !earth, this, this._navigator); + return { name: this._name, vNode: vNode }; + })) + .subscribe(this._container.domRenderer.render$)); + subs.push(this._sequenceDOMRenderer.speed$ + .subscribe((speed) => { + this._navigator.playService.setSpeed(speed); + })); + subs.push(this._configuration$.pipe(map((configuration) => { + return configuration.direction; + }), distinctUntilChanged()) + .subscribe((direction) => { + this._navigator.playService.setDirection(direction); + })); + subs.push(combineLatest(this._container.renderService.size$, this._configuration$.pipe(distinctUntilChanged((value1, value2) => { + return value1[0] === value2[0] && value1[1] === value2[1]; + }, (configuration) => { + return [configuration.minWidth, configuration.maxWidth]; + }))).pipe(map(([size, configuration]) => { + return this._sequenceDOMRenderer.getContainerWidth(size, configuration); + })) + .subscribe(this._containerWidth$)); + subs.push(this._configuration$.pipe(map((configuration) => { + return configuration.playing; + }), distinctUntilChanged()) + .subscribe((playing) => { + if (playing) { + this._navigator.playService.play(); + } + else { + this._navigator.playService.stop(); + } + })); + subs.push(this._sequenceDOMRenderer.mouseEnterDirection$.pipe(switchMap((direction) => { + const edgeTo$ = edgeStatus$.pipe(map((edgeStatus) => { + for (let edge of edgeStatus.edges) { + if (edge.data.direction === direction) { + return edge.target; + } + } + return null; + }), takeUntil(this._sequenceDOMRenderer.mouseLeaveDirection$)); + return concat(edgeTo$, of(null)); + }), distinctUntilChanged()) + .subscribe(this._hoveredIdSubject$)); + subs.push(this._hoveredId$ + .subscribe((id) => { + const type = "hover"; + const event = { + id, + target: this, + type, + }; + this.fire(type, event); + })); + } + _deactivate() { + this._subscriptions.unsubscribe(); + this._sequenceDOMRenderer.deactivate(); + } + _getDefaultConfiguration() { + return { + direction: exports.NavigationDirection.Next, + maxWidth: 108, + minWidth: 70, + playing: false, + visible: true, + }; + } + } + /** @inheritdoc */ + SequenceComponent.componentName = "sequence"; + + /** + * Enumeration for slider mode. + * + * @enum {number} + * @readonly + * + * @description Modes for specifying how transitions + * between images are performed in slider mode. Only + * applicable when the slider component determines + * that transitions with motion is possilble. When it + * is not, the stationary mode will be applied. + */ + exports.SliderConfigurationMode = void 0; + (function (SliderConfigurationMode) { + /** + * Transitions with motion. + * + * @description The slider component moves the + * camera between the image origins. + * + * In this mode it is not possible to zoom or pan. + * + * The slider component falls back to stationary + * mode when it determines that the pair of images + * does not have a strong enough relation. + */ + SliderConfigurationMode[SliderConfigurationMode["Motion"] = 0] = "Motion"; + /** + * Stationary transitions. + * + * @description The camera is stationary. + * + * In this mode it is possible to zoom and pan. + */ + SliderConfigurationMode[SliderConfigurationMode["Stationary"] = 1] = "Stationary"; + })(exports.SliderConfigurationMode || (exports.SliderConfigurationMode = {})); + + const EPSILON = 1e-8; + /** + * @class Transform + * + * @classdesc Class used for calculating coordinate transformations + * and projections. + */ + class Transform { + /** + * Create a new transform instance. + * @param {number} orientation - Image orientation. + * @param {number} width - Image height. + * @param {number} height - Image width. + * @param {number} focal - Focal length. + * @param {number} scale - Atomic scale. + * @param {Array} rotation - Rotation vector in three dimensions. + * @param {Array} translation - Translation vector in three dimensions. + * @param {HTMLImageElement} image - Image for fallback size calculations. + */ + constructor(orientation, width, height, scale, rotation, translation, image, textureScale, cameraParameters, cameraType) { + this._orientation = this._getValue(orientation, 1); + let imageWidth = image != null ? image.width : 4; + let imageHeight = image != null ? image.height : 3; + let keepOrientation = this._orientation < 5; + this._width = this._getValue(width, keepOrientation ? imageWidth : imageHeight); + this._height = this._getValue(height, keepOrientation ? imageHeight : imageWidth); + this._basicAspect = keepOrientation ? + this._width / this._height : + this._height / this._width; + this._basicWidth = keepOrientation ? width : height; + this._basicHeight = keepOrientation ? height : width; + const parameters = this._getCameraParameters(cameraParameters, cameraType); + const focal = parameters[0]; + const ck1 = parameters[1]; + const ck2 = parameters[2]; + this._focal = this._getValue(focal, 1); + this._scale = this._getValue(scale, 0); + this._worldToCamera = this.createWorldToCamera(rotation, translation); + this._worldToCameraInverse = new Matrix4() + .copy(this._worldToCamera) + .invert(); + this._scaledWorldToCamera = + this._createScaledWorldToCamera(this._worldToCamera, this._scale); + this._scaledWorldToCameraInverse = new Matrix4() + .copy(this._scaledWorldToCamera) + .invert(); + this._basicWorldToCamera = this._createBasicWorldToCamera(this._worldToCamera, orientation); + this._textureScale = !!textureScale ? textureScale : [1, 1]; + this._ck1 = !!ck1 ? ck1 : 0; + this._ck2 = !!ck2 ? ck2 : 0; + this._cameraType = !!cameraType ? + cameraType : + "perspective"; + this._radialPeak = this._getRadialPeak(this._ck1, this._ck2); + } + get ck1() { + return this._ck1; + } + get ck2() { + return this._ck2; + } + get cameraType() { + return this._cameraType; + } + /** + * Get basic aspect. + * @returns {number} The orientation adjusted aspect ratio. + */ + get basicAspect() { + return this._basicAspect; + } + /** + * Get basic height. + * + * @description Does not fall back to image image height but + * uses original value from API so can be faulty. + * + * @returns {number} The height of the basic version image + * (adjusted for orientation). + */ + get basicHeight() { + return this._basicHeight; + } + get basicRt() { + return this._basicWorldToCamera; + } + /** + * Get basic width. + * + * @description Does not fall back to image image width but + * uses original value from API so can be faulty. + * + * @returns {number} The width of the basic version image + * (adjusted for orientation). + */ + get basicWidth() { + return this._basicWidth; + } + /** + * Get focal. + * @returns {number} The image focal length. + */ + get focal() { + return this._focal; + } + /** + * Get height. + * + * @description Falls back to the image image height if + * the API data is faulty. + * + * @returns {number} The orientation adjusted image height. + */ + get height() { + return this._height; + } + /** + * Get orientation. + * @returns {number} The image orientation. + */ + get orientation() { + return this._orientation; + } + /** + * Get rt. + * @returns {THREE.Matrix4} The extrinsic camera matrix. + */ + get rt() { + return this._worldToCamera; + } + /** + * Get srt. + * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. + */ + get srt() { + return this._scaledWorldToCamera; + } + /** + * Get srtInverse. + * @returns {THREE.Matrix4} The scaled extrinsic camera matrix. + */ + get srtInverse() { + return this._scaledWorldToCameraInverse; + } + /** + * Get scale. + * @returns {number} The image atomic reconstruction scale. + */ + get scale() { + return this._scale; + } + /** + * Get has valid scale. + * @returns {boolean} Value indicating if the scale of the transform is valid. + */ + get hasValidScale() { + return this._scale > 1e-2 && this._scale < 50; + } + /** + * Get radial peak. + * @returns {number} Value indicating the radius where the radial + * undistortion function peaks. + */ + get radialPeak() { + return this._radialPeak; + } + /** + * Get width. + * + * @description Falls back to the image image width if + * the API data is faulty. + * + * @returns {number} The orientation adjusted image width. + */ + get width() { + return this._width; + } + /** + * Calculate the up vector for the image transform. + * + * @returns {THREE.Vector3} Normalized and orientation adjusted up vector. + */ + upVector() { + let rte = this._worldToCamera.elements; + switch (this._orientation) { + case 1: + return new Vector3(-rte[1], -rte[5], -rte[9]); + case 3: + return new Vector3(rte[1], rte[5], rte[9]); + case 6: + return new Vector3(-rte[0], -rte[4], -rte[8]); + case 8: + return new Vector3(rte[0], rte[4], rte[8]); + default: + return new Vector3(-rte[1], -rte[5], -rte[9]); + } + } + /** + * Calculate projector matrix for projecting 3D points to texture map + * coordinates (u and v). + * + * @returns {THREE.Matrix4} Projection matrix for 3D point to texture + * map coordinate calculations. + */ + projectorMatrix() { + let projector = this._normalizedToTextureMatrix(); + let f = this._focal; + let projection = new Matrix4().set(f, 0, 0, 0, 0, f, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0); + projector.multiply(projection); + projector.multiply(this._worldToCamera); + return projector; + } + /** + * Project 3D world coordinates to basic coordinates. + * + * @param {Array} point3d - 3D world coordinates. + * @return {Array} 2D basic coordinates. + */ + projectBasic(point3d) { + let sfm = this.projectSfM(point3d); + return this._sfmToBasic(sfm); + } + /** + * Unproject basic coordinates to 3D world coordinates. + * + * @param {Array} basic - 2D basic coordinates. + * @param {Array} distance - Distance to unproject from camera center. + * @param {boolean} [depth] - Treat the distance value as depth from camera center. + * Only applicable for perspective images. Will be + * ignored for spherical. + * @returns {Array} Unprojected 3D world coordinates. + */ + unprojectBasic(basic, distance, depth) { + let sfm = this._basicToSfm(basic); + return this.unprojectSfM(sfm, distance, depth); + } + /** + * Project 3D world coordinates to SfM coordinates. + * + * @param {Array} point3d - 3D world coordinates. + * @return {Array} 2D SfM coordinates. + */ + projectSfM(point3d) { + let v = new Vector4(point3d[0], point3d[1], point3d[2], 1); + v.applyMatrix4(this._worldToCamera); + return this._bearingToSfm([v.x, v.y, v.z]); + } + /** + * Unproject SfM coordinates to a 3D world coordinates. + * + * @param {Array} sfm - 2D SfM coordinates. + * @param {Array} distance - Distance to unproject + * from camera center. + * @param {boolean} [depth] - Treat the distance value as + * depth from camera center. Only applicable for perspective + * images. Will be ignored for spherical. + * @returns {Array} Unprojected 3D world coordinates. + */ + unprojectSfM(sfm, distance, depth) { + const bearing = this._sfmToBearing(sfm); + const unprojectedCamera = depth && !isSpherical(this._cameraType) ? + new Vector4(distance * bearing[0] / bearing[2], distance * bearing[1] / bearing[2], distance, 1) : + new Vector4(distance * bearing[0], distance * bearing[1], distance * bearing[2], 1); + const unprojectedWorld = unprojectedCamera + .applyMatrix4(this._worldToCameraInverse); + return [ + unprojectedWorld.x / unprojectedWorld.w, + unprojectedWorld.y / unprojectedWorld.w, + unprojectedWorld.z / unprojectedWorld.w, + ]; + } + /** + * Transform SfM coordinates to bearing vector (3D cartesian + * coordinates on the unit sphere). + * + * @param {Array} sfm - 2D SfM coordinates. + * @returns {Array} Bearing vector (3D cartesian coordinates + * on the unit sphere). + */ + _sfmToBearing(sfm) { + if (isSpherical(this._cameraType)) { + let lng = sfm[0] * 2 * Math.PI; + let lat = -sfm[1] * 2 * Math.PI; + let x = Math.cos(lat) * Math.sin(lng); + let y = -Math.sin(lat); + let z = Math.cos(lat) * Math.cos(lng); + return [x, y, z]; + } + else if (isFisheye(this._cameraType)) { + let [dxn, dyn] = [sfm[0] / this._focal, sfm[1] / this._focal]; + const dTheta = Math.sqrt(dxn * dxn + dyn * dyn); + let d = this._distortionFromDistortedRadius(dTheta, this._ck1, this._ck2, this._radialPeak); + let theta = dTheta / d; + let z = Math.cos(theta); + let r = Math.sin(theta); + const denomTheta = dTheta > EPSILON ? 1 / dTheta : 1; + let x = r * dxn * denomTheta; + let y = r * dyn * denomTheta; + return [x, y, z]; + } + else { + let [dxn, dyn] = [sfm[0] / this._focal, sfm[1] / this._focal]; + const dr = Math.sqrt(dxn * dxn + dyn * dyn); + let d = this._distortionFromDistortedRadius(dr, this._ck1, this._ck2, this._radialPeak); + const xn = dxn / d; + const yn = dyn / d; + let v = new Vector3(xn, yn, 1); + v.normalize(); + return [v.x, v.y, v.z]; + } + } + /** Compute distortion given the distorted radius. + * + * Solves for d in the equation + * y = d(x, k1, k2) * x + * given the distorted radius, y. + */ + _distortionFromDistortedRadius(distortedRadius, k1, k2, radialPeak) { + let d = 1.0; + for (let i = 0; i < 10; i++) { + let radius = distortedRadius / d; + if (radius > radialPeak) { + radius = radialPeak; + } + d = 1 + k1 * Math.pow(radius, 2) + k2 * Math.pow(radius, 4); + } + return d; + } + /** + * Transform bearing vector (3D cartesian coordiantes on the unit sphere) to + * SfM coordinates. + * + * @param {Array} bearing - Bearing vector (3D cartesian coordinates on the + * unit sphere). + * @returns {Array} 2D SfM coordinates. + */ + _bearingToSfm(bearing) { + if (isSpherical(this._cameraType)) { + let x = bearing[0]; + let y = bearing[1]; + let z = bearing[2]; + let lng = Math.atan2(x, z); + let lat = Math.atan2(-y, Math.sqrt(x * x + z * z)); + return [lng / (2 * Math.PI), -lat / (2 * Math.PI)]; + } + else if (isFisheye(this._cameraType)) { + if (bearing[2] > 0) { + const [x, y, z] = bearing; + const r = Math.sqrt(x * x + y * y); + let theta = Math.atan2(r, z); + if (theta > this._radialPeak) { + theta = this._radialPeak; + } + const distortion = 1.0 + Math.pow(theta, 2) * (this._ck1 + Math.pow(theta, 2) * this._ck2); + const s = this._focal * distortion * theta / r; + return [s * x, s * y]; } else { - this._navigator.playService.stop(); + return [ + bearing[0] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + bearing[1] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + ]; } - })); - subs.push(this._sequenceDOMRenderer.mouseEnterDirection$.pipe(switchMap((direction) => { - const edgeTo$ = edgeStatus$.pipe(map((edgeStatus) => { - for (let edge of edgeStatus.edges) { - if (edge.data.direction === direction) { - return edge.target; - } + } + else { + if (bearing[2] > 0) { + let [xn, yn] = [bearing[0] / bearing[2], bearing[1] / bearing[2]]; + let r2 = xn * xn + yn * yn; + const rp2 = Math.pow(this._radialPeak, 2); + if (r2 > rp2) { + r2 = rp2; } - return null; - }), takeUntil(this._sequenceDOMRenderer.mouseLeaveDirection$)); - return concat(edgeTo$, of(null)); - }), distinctUntilChanged()) - .subscribe(this._hoveredIdSubject$)); - subs.push(this._hoveredId$ - .subscribe((id) => { - const type = "hover"; - const event = { - id, - target: this, - type, - }; - this.fire(type, event); - })); - } - _deactivate() { - this._subscriptions.unsubscribe(); - this._sequenceDOMRenderer.deactivate(); + const d = 1 + this._ck1 * r2 + this._ck2 * Math.pow(r2, 2); + return [ + this._focal * d * xn, + this._focal * d * yn, + ]; + } + else { + return [ + bearing[0] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + bearing[1] < 0 ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + ]; + } + } } - _getDefaultConfiguration() { - return { - direction: exports.NavigationDirection.Next, - maxWidth: 108, - minWidth: 70, - playing: false, - visible: true, - }; + /** + * Convert basic coordinates to SfM coordinates. + * + * @param {Array} basic - 2D basic coordinates. + * @returns {Array} 2D SfM coordinates. + */ + _basicToSfm(basic) { + let rotatedX; + let rotatedY; + switch (this._orientation) { + case 1: + rotatedX = basic[0]; + rotatedY = basic[1]; + break; + case 3: + rotatedX = 1 - basic[0]; + rotatedY = 1 - basic[1]; + break; + case 6: + rotatedX = basic[1]; + rotatedY = 1 - basic[0]; + break; + case 8: + rotatedX = 1 - basic[1]; + rotatedY = basic[0]; + break; + default: + rotatedX = basic[0]; + rotatedY = basic[1]; + break; + } + let w = this._width; + let h = this._height; + let s = Math.max(w, h); + let sfmX = rotatedX * w / s - w / s / 2; + let sfmY = rotatedY * h / s - h / s / 2; + return [sfmX, sfmY]; } - } - /** @inheritdoc */ - SequenceComponent.componentName = "sequence"; - - /** - * Enumeration for slider mode. - * - * @enum {number} - * @readonly - * - * @description Modes for specifying how transitions - * between images are performed in slider mode. Only - * applicable when the slider component determines - * that transitions with motion is possilble. When it - * is not, the stationary mode will be applied. - */ - exports.SliderConfigurationMode = void 0; - (function (SliderConfigurationMode) { /** - * Transitions with motion. + * Convert SfM coordinates to basic coordinates. * - * @description The slider component moves the - * camera between the image origins. + * @param {Array} sfm - 2D SfM coordinates. + * @returns {Array} 2D basic coordinates. + */ + _sfmToBasic(sfm) { + let w = this._width; + let h = this._height; + let s = Math.max(w, h); + let rotatedX = (sfm[0] + w / s / 2) / w * s; + let rotatedY = (sfm[1] + h / s / 2) / h * s; + let basicX; + let basicY; + switch (this._orientation) { + case 1: + basicX = rotatedX; + basicY = rotatedY; + break; + case 3: + basicX = 1 - rotatedX; + basicY = 1 - rotatedY; + break; + case 6: + basicX = 1 - rotatedY; + basicY = rotatedX; + break; + case 8: + basicX = rotatedY; + basicY = 1 - rotatedX; + break; + default: + basicX = rotatedX; + basicY = rotatedY; + break; + } + return [basicX, basicY]; + } + /** + * Checks a value and returns it if it exists and is larger than 0. + * Fallbacks if it is null. * - * In this mode it is not possible to zoom or pan. + * @param {number} value - Value to check. + * @param {number} fallback - Value to fall back to. + * @returns {number} The value or its fallback value if it is not defined or negative. + */ + _getValue(value, fallback) { + return value != null && value > 0 ? value : fallback; + } + _getCameraParameters(value, cameraType) { + if (isSpherical(cameraType)) { + return []; + } + if (!value || value.length === 0) { + return [1, 0, 0]; + } + const padding = 3 - value.length; + if (padding <= 0) { + return value; + } + return value + .concat(new Array(padding) + .fill(0)); + } + /** + * Creates the extrinsic camera matrix [ R | t ]. * - * The slider component falls back to stationary - * mode when it determines that the pair of images - * does not have a strong enough relation. + * @param {Array} rotation - Rotation vector in angle axis representation. + * @param {Array} translation - Translation vector. + * @returns {THREE.Matrix4} Extrisic camera matrix. */ - SliderConfigurationMode[SliderConfigurationMode["Motion"] = 0] = "Motion"; + createWorldToCamera(rotation, translation) { + const axis = new Vector3(rotation[0], rotation[1], rotation[2]); + const angle = axis.length(); + if (angle > 0) { + axis.normalize(); + } + const worldToCamera = new Matrix4(); + worldToCamera.makeRotationAxis(axis, angle); + worldToCamera.setPosition(new Vector3(translation[0], translation[1], translation[2])); + return worldToCamera; + } /** - * Stationary transitions. + * Calculates the scaled extrinsic camera matrix scale * [ R | t ]. * - * @description The camera is stationary. + * @param {THREE.Matrix4} worldToCamera - Extrisic camera matrix. + * @param {number} scale - Scale factor. + * @returns {THREE.Matrix4} Scaled extrisic camera matrix. + */ + _createScaledWorldToCamera(worldToCamera, scale) { + const scaledWorldToCamera = worldToCamera.clone(); + const elements = scaledWorldToCamera.elements; + elements[12] = scale * elements[12]; + elements[13] = scale * elements[13]; + elements[14] = scale * elements[14]; + scaledWorldToCamera.scale(new Vector3(scale, scale, scale)); + return scaledWorldToCamera; + } + _createBasicWorldToCamera(rt, orientation) { + const axis = new Vector3(0, 0, 1); + let angle = 0; + switch (orientation) { + case 3: + angle = Math.PI; + break; + case 6: + angle = Math.PI / 2; + break; + case 8: + angle = 3 * Math.PI / 2; + break; + } + return new Matrix4() + .makeRotationAxis(axis, angle) + .multiply(rt); + } + _getRadialPeak(k1, k2) { + const a = 5 * k2; + const b = 3 * k1; + const c = 1; + const d = Math.pow(b, 2) - 4 * a * c; + if (d < 0) { + return undefined; + } + const root1 = (-b - Math.sqrt(d)) / 2 / a; + const root2 = (-b + Math.sqrt(d)) / 2 / a; + const minRoot = Math.min(root1, root2); + const maxRoot = Math.max(root1, root2); + return minRoot > 0 ? + Math.sqrt(minRoot) : + maxRoot > 0 ? + Math.sqrt(maxRoot) : + undefined; + } + /** + * Calculate a transformation matrix from normalized coordinates for + * texture map coordinates. * - * In this mode it is possible to zoom and pan. + * @returns {THREE.Matrix4} Normalized coordinates to texture map + * coordinates transformation matrix. */ - SliderConfigurationMode[SliderConfigurationMode["Stationary"] = 1] = "Stationary"; - })(exports.SliderConfigurationMode || (exports.SliderConfigurationMode = {})); + _normalizedToTextureMatrix() { + const size = Math.max(this._width, this._height); + const scaleX = this._orientation < 5 ? this._textureScale[0] : this._textureScale[1]; + const scaleY = this._orientation < 5 ? this._textureScale[1] : this._textureScale[0]; + const w = size / this._width * scaleX; + const h = size / this._height * scaleY; + switch (this._orientation) { + case 1: + return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); + case 3: + return new Matrix4().set(-w, 0, 0, 0.5, 0, h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); + case 6: + return new Matrix4().set(0, -h, 0, 0.5, -w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); + case 8: + return new Matrix4().set(0, h, 0, 0.5, w, 0, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); + default: + return new Matrix4().set(w, 0, 0, 0.5, 0, -h, 0, 0.5, 0, 0, 1, 0, 0, 0, 0, 1); + } + } + } class SliderGLRenderer { constructor() { @@ -63121,13 +60303,10 @@ void main() constructor(parameters) { super(); this._originalSize = parameters.originalSize; - const cluster = parameters.cluster; - const scale = parameters.scale; - const translation = parameters.translation; + const { cluster, color, scale, translation } = parameters; this._makeAttributes(cluster); this.material.size = scale * this._originalSize; - this.material.vertexColors = true; - this.material.needsUpdate = true; + this.setColor(color); this.matrixAutoUpdate = false; this.position.fromArray(translation); this.updateMatrix(); @@ -63137,6 +60316,11 @@ void main() this.geometry.dispose(); this.material.dispose(); } + setColor(color) { + this.material.vertexColors = color == null; + this.material.color = new Color(color); + this.material.needsUpdate = true; + } resize(scale) { this.material.size = scale * this._originalSize; this.material.needsUpdate = true; @@ -63589,12 +60773,11 @@ void main() return positions; } _makeDiags(size, transform, origin) { - const depth = size; const [originX, originY, originZ] = origin; const cameraCenter = [0, 0, 0]; const positions = []; for (const vertex2d of [[0, 0], [1, 0], [1, 1], [0, 1]]) { - const corner = transform.unprojectBasic(vertex2d, depth, true); + const corner = transform.unprojectBasic(vertex2d, size); corner[0] -= originX; corner[1] -= originY; corner[2] -= originZ; @@ -63607,11 +60790,10 @@ void main() vertices2d.push(...this._subsample([0, 1], [0, 0], samples)); vertices2d.push(...this._subsample([0, 0], [1, 0], samples)); vertices2d.push(...this._subsample([1, 0], [1, 1], samples)); - const depth = size; const [originX, originY, originZ] = origin; const positions = []; for (const vertex2d of vertices2d) { - const position = transform.unprojectBasic(vertex2d, depth, true); + const position = transform.unprojectBasic(vertex2d, size); position[0] -= originX; position[1] -= originY; position[2] -= originZ; @@ -63641,6 +60823,12 @@ void main() } } + function resetEnu(reference, prevEnu, prevReference) { + const [prevX, prevY, prevZ] = prevEnu; + const [lng, lat, alt] = enuToGeodetic(prevX, prevY, prevZ, prevReference.lng, prevReference.lat, prevReference.alt); + return geodeticToEnu(lng, lat, alt, reference.lng, reference.lat, reference.alt); + } + class SpatialCell { constructor(id, _scene, _intersection) { this.id = id; @@ -63766,6 +60954,24 @@ void main() hasImage(key) { return this.keys.indexOf(key) !== -1; } + resetReference(reference, prevReference) { + const frames = this._cameraFrames; + for (const frameId in frames) { + if (!frames.hasOwnProperty(frameId)) { + continue; + } + const frame = frames[frameId]; + frame.position.fromArray(resetEnu(reference, frame.position.toArray(), prevReference)); + } + const lines = this._positionLines; + for (const lineId in lines) { + if (!lines.hasOwnProperty(lineId)) { + continue; + } + const line = lines[lineId]; + line.position.fromArray(resetEnu(reference, line.position.toArray(), prevReference)); + } + } visualize(props) { var _a, _b; const id = props.id; @@ -63857,6 +61063,23 @@ void main() return state === State.Custom || state === State.Earth; } + exports.PointVisualizationMode = void 0; + (function (PointVisualizationMode) { + /** + * Points are hidden. + */ + PointVisualizationMode[PointVisualizationMode["Hidden"] = 0] = "Hidden"; + /** + * Visualize points with original colors. + */ + PointVisualizationMode[PointVisualizationMode["Original"] = 1] = "Original"; + /** + * Paint all points belonging to a specific + * cluster with the same random color. + */ + PointVisualizationMode[PointVisualizationMode["Cluster"] = 2] = "Cluster"; + })(exports.PointVisualizationMode || (exports.PointVisualizationMode = {})); + const NO_CLUSTER_ID = "NO_CLUSTER_ID"; const NO_MERGE_ID = "NO_MERGE_ID"; const NO_SEQUENCE_ID = "NO_SEQUENCE_ID"; @@ -63881,7 +61104,10 @@ void main() exports.CameraVisualizationMode.Homogeneous; this._cameraSize = configuration.cameraSize; this._pointSize = configuration.pointSize; - this._pointsVisible = configuration.pointsVisible; + this._pointVisualizationMode = + !!configuration.pointVisualizationMode ? + configuration.pointVisualizationMode : + exports.PointVisualizationMode.Original; this._positionMode = configuration.originalPositionMode; this._cellsVisible = configuration.cellsVisible; this._hoveredId = null; @@ -63904,14 +61130,18 @@ void main() cellIds: [], }; const visible = this._getClusterVisible(clusterId); - this._clusters[clusterId].points.visible = visible; - this._clusters[clusterId].points.add(new ClusterPoints({ + const cluster = this._clusters[clusterId]; + const color = this._pointVisualizationMode === exports.PointVisualizationMode.Cluster ? this._assets.getColor(clusterId) : null; + const points = new ClusterPoints({ cluster: reconstruction, + color, originalSize: this._originalPointSize, scale: this._pointSize, translation, - })); - this._scene.add(this._clusters[clusterId].points); + }); + cluster.points.visible = visible; + cluster.points.add(points); + this._scene.add(cluster.points); } if (this._clusters[clusterId].cellIds.indexOf(cellId) === -1) { this._clusters[clusterId].cellIds.push(cellId); @@ -63995,6 +61225,36 @@ void main() return cellId in this._images && this._images[cellId].hasImage(imageId); } + render(camera, renderer) { + renderer.render(this._scene, camera); + this._needsRender = false; + } + resetReference(reference, prevReference) { + const clusters = this._clusters; + for (const clusterId in clusters) { + if (!clusters.hasOwnProperty(clusterId)) { + continue; + } + const cluster = clusters[clusterId]; + cluster.points.position.fromArray(resetEnu(reference, cluster.points.position.toArray(), prevReference)); + } + const cells = this._cells; + for (const cellId in cells) { + if (!cells.hasOwnProperty(cellId)) { + continue; + } + const cell = cells[cellId]; + cell.position.fromArray(resetEnu(reference, cell.position.toArray(), prevReference)); + } + const images = this._images; + for (const cellId in images) { + if (!images.hasOwnProperty(cellId)) { + continue; + } + const spatialCell = images[cellId]; + spatialCell.resetReference(reference, prevReference); + } + } setCameraSize(cameraSize) { if (Math.abs(cameraSize - this._cameraSize) < 1e-3) { return; @@ -64023,7 +61283,7 @@ void main() clusterVisibles[clusterId] || (clusterVisibles[clusterId] = imageCV[clusterId]); } } - const pointsVisible = this._pointsVisible; + const pointsVisible = this._pointVisualizationMode !== exports.PointVisualizationMode.Hidden; for (const clusterId in clusterVisibles) { if (!clusterVisibles.hasOwnProperty(clusterId)) { continue; @@ -64074,17 +61334,23 @@ void main() this._pointSize = pointSize; this._needsRender = true; } - setPointVisibility(visible) { - if (visible === this._pointsVisible) { + setPointVisualizationMode(mode) { + if (mode === this._pointVisualizationMode) { return; } + this._pointVisualizationMode = mode; for (const clusterId in this._clusters) { if (!this._clusters.hasOwnProperty(clusterId)) { continue; } - this._clusters[clusterId].points.visible = visible; + const cluster = this._clusters[clusterId]; + cluster.points.visible = this._getClusterVisible(clusterId); + for (const points of cluster.points.children) { + const color = mode === exports.PointVisualizationMode.Cluster ? + this._assets.getColor(clusterId) : null; + points.setColor(color); + } } - this._pointsVisible = visible; this._needsRender = true; } setPositionMode(mode) { @@ -64142,10 +61408,6 @@ void main() this._cameraVisualizationMode = mode; this._needsRender = true; } - render(camera, renderer) { - renderer.render(this._scene, camera); - this._needsRender = false; - } uncache(keepCellIds) { for (const cellId of Object.keys(this._cellClusters)) { if (!!keepCellIds && keepCellIds.indexOf(cellId) !== -1) { @@ -64174,7 +61436,7 @@ void main() this._needsRender = true; } _getClusterVisible(clusterId) { - if (!this._pointsVisible) { + if (this._pointVisualizationMode === exports.PointVisualizationMode.Hidden) { return false; } let visible = false; @@ -64563,8 +61825,9 @@ void main() this._navigator.cacheService.configure({ cellDepth: 3 }); const subs = this._subscriptions; subs.push(this._navigator.stateService.reference$ - .subscribe(() => { - this._scene.uncache(); + .pipe(pairwise()) + .subscribe(([prevReference, reference]) => { + this._scene.resetReference(reference, prevReference); })); subs.push(this._navigator.graphService.filter$ .subscribe(imageFilter => { this._scene.setFilter(imageFilter); })); @@ -64659,15 +61922,19 @@ void main() this._scene.addCluster(reconstruction, this._computeTranslation(reconstruction, reference), cellId); })); subs.push(this._configuration$.pipe(map((c) => { + var _a; c.cameraSize = this._spatial.clamp(c.cameraSize, 0.01, 1); c.pointSize = this._spatial.clamp(c.pointSize, 0.01, 1); + const pointVisualizationMode = c.pointsVisible ? + (_a = c.pointVisualizationMode) !== null && _a !== void 0 ? _a : exports.PointVisualizationMode.Original : + exports.PointVisualizationMode.Hidden; return { cameraSize: c.cameraSize, cameraVisualizationMode: c.cameraVisualizationMode, cellsVisible: c.cellsVisible, originalPositionMode: c.originalPositionMode, pointSize: c.pointSize, - pointsVisible: c.pointsVisible, + pointVisualizationMode, }; }), distinctUntilChanged((c1, c2) => { return c1.cameraSize === c2.cameraSize && @@ -64675,15 +61942,16 @@ void main() c1.cellsVisible === c2.cellsVisible && c1.originalPositionMode === c2.originalPositionMode && c1.pointSize === c2.pointSize && - c1.pointsVisible === c2.pointsVisible; + c1.pointVisualizationMode === c2.pointVisualizationMode; })) .subscribe((c) => { this._scene.setCameraSize(c.cameraSize); - this._scene.setPointSize(c.pointSize); - this._scene.setPointVisibility(c.pointsVisible); - this._scene.setCellVisibility(c.cellsVisible); const cvm = c.cameraVisualizationMode; this._scene.setCameraVisualizationMode(cvm); + this._scene.setCellVisibility(c.cellsVisible); + this._scene.setPointSize(c.pointSize); + const pvm = c.pointVisualizationMode; + this._scene.setPointVisualizationMode(pvm); const opm = c.originalPositionMode; this._scene.setPositionMode(opm); })); @@ -64812,6 +62080,7 @@ void main() originalPositionMode: exports.OriginalPositionMode.Hidden, pointSize: 0.1, pointsVisible: true, + pointVisualizationMode: exports.PointVisualizationMode.Original, cellsVisible: false, }; } @@ -65168,8 +62437,10 @@ void main() } } - var earcut_1 = earcut; - var _default$2 = earcut; + var earcut$2 = {exports: {}}; + + earcut$2.exports = earcut; + earcut$2.exports.default = earcut; function earcut(data, holeIndices, dim) { @@ -65445,7 +62716,7 @@ void main() // process holes from left to right for (i = 0; i < queue.length; i++) { - eliminateHole(queue[i], outerNode); + outerNode = eliminateHole(queue[i], outerNode); outerNode = filterPoints(outerNode, outerNode.next); } @@ -65458,14 +62729,19 @@ void main() // find a bridge between vertices that connects hole with an outer ring and and link it function eliminateHole(hole, outerNode) { - outerNode = findHoleBridge(hole, outerNode); - if (outerNode) { - var b = splitPolygon(outerNode, hole); - - // filter collinear points around the cuts - filterPoints(outerNode, outerNode.next); - filterPoints(b, b.next); + var bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; } + + var bridgeReverse = splitPolygon(bridge, hole); + + // filter collinear points around the cuts + var filteredBridge = filterPoints(bridge, bridge.next); + filterPoints(bridgeReverse, bridgeReverse.next); + + // Check if input node was removed by the filtering + return outerNode === bridge ? filteredBridge : outerNode; } // David Eberly's algorithm for finding a bridge between hole and outer polygon @@ -65845,7 +63121,10 @@ void main() } return result; }; - earcut_1.default = _default$2; + + var earcut$1 = earcut$2.exports; + + var polylabel$2 = {exports: {}}; class TinyQueue$1 { constructor(data = [], compare = defaultCompare$1) { @@ -65933,12 +63212,12 @@ void main() var require$$0 = /*@__PURE__*/getAugmentedNamespace(tinyqueue$1); - var Queue = require$$0; + var Queue$1 = require$$0; - if (Queue.default) Queue = Queue.default; // temporary webpack fix + if (Queue$1.default) Queue$1 = Queue$1.default; // temporary webpack fix - var polylabel_1 = polylabel; - var _default$1 = polylabel; + polylabel$2.exports = polylabel; + polylabel$2.exports.default = polylabel; function polylabel(polygon, precision, debug) { precision = precision || 1.0; @@ -65965,7 +63244,7 @@ void main() } // a priority queue of cells in order of their "potential" (max distance to polygon) - var cellQueue = new Queue(undefined, compareMax); + var cellQueue = new Queue$1(undefined, compareMax); // cover polygon with initial cells for (var x = minX; x < maxX; x += cellSize) { @@ -66095,7 +63374,8 @@ void main() return dx * dx + dy * dy; } - polylabel_1.default = _default$1; + + var polylabel$1 = polylabel$2.exports; function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } @@ -67892,8 +65172,10 @@ void main() return contours; } - var tinyqueue = TinyQueue; - var _default = TinyQueue; + var tinyqueue = {exports: {}}; + + tinyqueue.exports = TinyQueue; + tinyqueue.exports.default = TinyQueue; function TinyQueue(data, compare) { if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare); @@ -67978,7 +65260,8 @@ void main() data[pos] = item; } }; - tinyqueue.default = _default; + + var Queue = tinyqueue.exports; const max = Math.max; const min = Math.min; @@ -68025,7 +65308,7 @@ void main() function fillQueue(subject, clipping, sbbox, cbbox, operation) { - const eventQueue = new tinyqueue(null, compareEvents); + const eventQueue = new Queue(null, compareEvents); let polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk; for (i = 0, ii = subject.length; i < ii; i++) { @@ -68166,7 +65449,7 @@ void main() * @ignore */ _getPoleOfInaccessibility2d(points2d) { - let pole2d = polylabel_1([points2d], 3e-2); + let pole2d = polylabel$1([points2d], 3e-2); return pole2d; } _project(points2d, transform) { @@ -68214,8 +65497,8 @@ void main() for (let hole3d of holes3d != null ? holes3d : []) { points = points.concat(hole3d.slice(0, -1)); } - let flattened = earcut_1.flatten(data); - let indices = earcut_1(flattened.vertices, flattened.holes, flattened.dimensions); + let flattened = earcut$1.flatten(data); + let indices = earcut$1(flattened.vertices, flattened.holes, flattened.dimensions); let triangles = []; for (let i = 0; i < indices.length; ++i) { let point = points[indices[i]]; @@ -72369,7 +69652,7 @@ void main() } NavigationFallbackComponent.componentName = "navigationfallback"; - /*! pako 2.0.3 https://github.com/nodeca/pako @license (MIT AND Zlib) */ + /*! pako 2.0.4 https://github.com/nodeca/pako @license (MIT AND Zlib) */ // (C) 1995-2013 Jean-loup Gailly and Mark Adler // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin // @@ -72398,19 +69681,19 @@ void main() //const Z_FILTERED = 1; //const Z_HUFFMAN_ONLY = 2; //const Z_RLE = 3; - const Z_FIXED = 4; + const Z_FIXED$1 = 4; //const Z_DEFAULT_STRATEGY = 0; /* Possible values of the data_type field (though see inflate()) */ const Z_BINARY = 0; const Z_TEXT = 1; //const Z_ASCII = 1; // = Z_TEXT - const Z_UNKNOWN = 2; + const Z_UNKNOWN$1 = 2; /*============================================================================*/ - function zero(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } } + function zero$1(buf) { let len = buf.length; while (--len >= 0) { buf[len] = 0; } } // From zutil.h @@ -72419,8 +69702,8 @@ void main() const DYN_TREES = 2; /* The three kinds of block type */ - const MIN_MATCH = 3; - const MAX_MATCH = 258; + const MIN_MATCH$1 = 3; + const MAX_MATCH$1 = 258; /* The minimum and maximum match lengths */ // From deflate.h @@ -72428,25 +69711,25 @@ void main() * Internal compression state. */ - const LENGTH_CODES = 29; + const LENGTH_CODES$1 = 29; /* number of length codes, not counting the special END_BLOCK code */ - const LITERALS = 256; + const LITERALS$1 = 256; /* number of literal bytes 0..255 */ - const L_CODES = LITERALS + 1 + LENGTH_CODES; + const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; /* number of Literal or Length codes, including the END_BLOCK code */ - const D_CODES = 30; + const D_CODES$1 = 30; /* number of distance codes */ - const BL_CODES = 19; + const BL_CODES$1 = 19; /* number of codes used to transfer the bit lengths */ - const HEAP_SIZE = 2 * L_CODES + 1; + const HEAP_SIZE$1 = 2 * L_CODES$1 + 1; /* maximum heap size */ - const MAX_BITS = 15; + const MAX_BITS$1 = 15; /* All codes must not exceed MAX_BITS bits */ const Buf_size = 16; @@ -72499,37 +69782,37 @@ void main() const DIST_CODE_LEN = 512; /* see definition of array dist_code below */ // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 - const static_ltree = new Array((L_CODES + 2) * 2); - zero(static_ltree); + const static_ltree = new Array((L_CODES$1 + 2) * 2); + zero$1(static_ltree); /* The static literal tree. Since the bit lengths are imposed, there is no * need for the L_CODES extra codes used during heap construction. However * The codes 286 and 287 are needed to build a canonical tree (see _tr_init * below). */ - const static_dtree = new Array(D_CODES * 2); - zero(static_dtree); + const static_dtree = new Array(D_CODES$1 * 2); + zero$1(static_dtree); /* The static distance tree. (Actually a trivial tree since all codes use * 5 bits.) */ const _dist_code = new Array(DIST_CODE_LEN); - zero(_dist_code); + zero$1(_dist_code); /* Distance codes. The first 256 values correspond to the distances * 3 .. 258, the last 256 values correspond to the top 8 bits of * the 15 bit distances. */ - const _length_code = new Array(MAX_MATCH - MIN_MATCH + 1); - zero(_length_code); + const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1); + zero$1(_length_code); /* length code for each normalized match length (0 == MIN_MATCH) */ - const base_length = new Array(LENGTH_CODES); - zero(base_length); + const base_length = new Array(LENGTH_CODES$1); + zero$1(base_length); /* First normalized length for each code (0 = MIN_MATCH) */ - const base_dist = new Array(D_CODES); - zero(base_dist); + const base_dist = new Array(D_CODES$1); + zero$1(base_dist); /* First normalized distance for each code (0 = distance of 1) */ @@ -72664,7 +69947,7 @@ void main() let f; /* frequency */ let overflow = 0; /* number of elements with bit length too large */ - for (bits = 0; bits <= MAX_BITS; bits++) { + for (bits = 0; bits <= MAX_BITS$1; bits++) { s.bl_count[bits] = 0; } @@ -72673,7 +69956,7 @@ void main() */ tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */ - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { + for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) { n = s.heap[h]; bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; if (bits > max_length) { @@ -72748,7 +70031,7 @@ void main() // int max_code; /* largest code with non zero frequency */ // ushf *bl_count; /* number of codes at each bit length */ { - const next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */ + const next_code = new Array(MAX_BITS$1 + 1); /* next code value for each bit length */ let code = 0; /* running code value */ let bits; /* bit index */ let n; /* code index */ @@ -72756,7 +70039,7 @@ void main() /* The distribution counts are first used to generate the code values * without bit reversal. */ - for (bits = 1; bits <= MAX_BITS; bits++) { + for (bits = 1; bits <= MAX_BITS$1; bits++) { next_code[bits] = code = (code + bl_count[bits - 1]) << 1; } /* Check that the bit counts in bl_count are consistent. The last code @@ -72788,7 +70071,7 @@ void main() let length; /* length value */ let code; /* code value */ let dist; /* distance index */ - const bl_count = new Array(MAX_BITS + 1); + const bl_count = new Array(MAX_BITS$1 + 1); /* number of codes at each bit length for an optimal tree */ // do check in _tr_init() @@ -72805,7 +70088,7 @@ void main() /* Initialize the mapping length (0..255) -> length code (0..28) */ length = 0; - for (code = 0; code < LENGTH_CODES - 1; code++) { + for (code = 0; code < LENGTH_CODES$1 - 1; code++) { base_length[code] = length; for (n = 0; n < (1 << extra_lbits[code]); n++) { _length_code[length++] = code; @@ -72828,7 +70111,7 @@ void main() } //Assert (dist == 256, "tr_static_init: dist != 256"); dist >>= 7; /* from now on, all distances are divided by 128 */ - for (; code < D_CODES; code++) { + for (; code < D_CODES$1; code++) { base_dist[code] = dist << 7; for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { _dist_code[256 + dist++] = code; @@ -72837,7 +70120,7 @@ void main() //Assert (dist == 256, "tr_static_init: 256+dist != 512"); /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) { + for (bits = 0; bits <= MAX_BITS$1; bits++) { bl_count[bits] = 0; } @@ -72866,18 +70149,18 @@ void main() * tree construction to get a canonical Huffman tree (longest code * all ones) */ - gen_codes(static_ltree, L_CODES + 1, bl_count); + gen_codes(static_ltree, L_CODES$1 + 1, bl_count); /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { + for (n = 0; n < D_CODES$1; n++) { static_dtree[n * 2 + 1]/*.Len*/ = 5; static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5); } // Now data ready and we can init static trees - static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); - static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); - static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); + static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1); + static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1); + static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS); //static_init_done = true; }; @@ -72891,9 +70174,9 @@ void main() let n; /* iterates over tree elements */ /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } - for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } - for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < L_CODES$1; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < D_CODES$1; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; } + for (n = 0; n < BL_CODES$1; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; } s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1; s.opt_len = s.static_len = 0; @@ -73013,7 +70296,7 @@ void main() } else { /* Here, lc is the match length - MIN_MATCH */ code = _length_code[lc]; - send_code(s, code + LITERALS + 1, ltree); /* send the length code */ + send_code(s, code + LITERALS$1 + 1, ltree); /* send the length code */ extra = extra_lbits[code]; if (extra !== 0) { lc -= base_length[code]; @@ -73067,7 +70350,7 @@ void main() * heap[0] is not used. */ s.heap_len = 0; - s.heap_max = HEAP_SIZE; + s.heap_max = HEAP_SIZE$1; for (n = 0; n < elems; n++) { if (tree[n * 2]/*.Freq*/ !== 0) { @@ -73301,7 +70584,7 @@ void main() * requires that at least 4 bit length codes be sent. (appnote.txt says * 3 but the actual value used is 4.) */ - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) { if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) { break; } @@ -73380,7 +70663,7 @@ void main() s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) { return Z_TEXT; } - for (n = 32; n < LITERALS; n++) { + for (n = 32; n < LITERALS$1; n++) { if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) { return Z_TEXT; } @@ -73398,7 +70681,7 @@ void main() /* =========================================================================== * Initialize the tree data structures for a new zlib stream. */ - const _tr_init = (s) => + const _tr_init$1 = (s) => { if (!static_init_done) { @@ -73421,7 +70704,7 @@ void main() /* =========================================================================== * Send a stored block */ - const _tr_stored_block = (s, buf, stored_len, last) => + const _tr_stored_block$1 = (s, buf, stored_len, last) => //DeflateState *s; //charf *buf; /* input block */ //ulg stored_len; /* length of input block */ @@ -73436,7 +70719,7 @@ void main() * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. */ - const _tr_align = (s) => { + const _tr_align$1 = (s) => { send_bits(s, STATIC_TREES << 1, 3); send_code(s, END_BLOCK, static_ltree); bi_flush(s); @@ -73447,7 +70730,7 @@ void main() * Determine the best encoding for the current block: dynamic trees, static * trees or store, and output the encoded block to the zip file. */ - const _tr_flush_block = (s, buf, stored_len, last) => + const _tr_flush_block$1 = (s, buf, stored_len, last) => //DeflateState *s; //charf *buf; /* input block, or NULL if too old */ //ulg stored_len; /* length of input block */ @@ -73460,7 +70743,7 @@ void main() if (s.level > 0) { /* Check if the file is binary or text */ - if (s.strm.data_type === Z_UNKNOWN) { + if (s.strm.data_type === Z_UNKNOWN$1) { s.strm.data_type = detect_data_type(s); } @@ -73505,9 +70788,9 @@ void main() * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to * transform a block into a stored block. */ - _tr_stored_block(s, buf, stored_len, last); + _tr_stored_block$1(s, buf, stored_len, last); - } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) { + } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) { send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); compress_block(s, static_ltree, static_dtree); @@ -73534,7 +70817,7 @@ void main() * Save the match info and tally the frequency counts. Return true if * the current block must be flushed. */ - const _tr_tally = (s, dist, lc) => + const _tr_tally$1 = (s, dist, lc) => // deflate_state *s; // unsigned dist; /* distance of matched string */ // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ @@ -73558,7 +70841,7 @@ void main() // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++; + s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2]/*.Freq*/++; s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++; } @@ -73592,11 +70875,11 @@ void main() */ }; - var _tr_init_1 = _tr_init; - var _tr_stored_block_1 = _tr_stored_block; - var _tr_flush_block_1 = _tr_flush_block; - var _tr_tally_1 = _tr_tally; - var _tr_align_1 = _tr_align; + var _tr_init_1 = _tr_init$1; + var _tr_stored_block_1 = _tr_stored_block$1; + var _tr_flush_block_1 = _tr_flush_block$1; + var _tr_tally_1 = _tr_tally$1; + var _tr_align_1 = _tr_align$1; var trees = { _tr_init: _tr_init_1, @@ -73764,7 +71047,7 @@ void main() // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. - var constants = { + var constants$2 = { /* Allowed flush values; see deflate() and inflate() below for details */ Z_NO_FLUSH: 0, @@ -73831,7 +71114,7 @@ void main() // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. - const { _tr_init: _tr_init$1, _tr_stored_block: _tr_stored_block$1, _tr_flush_block: _tr_flush_block$1, _tr_tally: _tr_tally$1, _tr_align: _tr_align$1 } = trees; + const { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align } = trees; @@ -73840,42 +71123,42 @@ void main() /* ===========================================================================*/ const { - Z_NO_FLUSH, Z_PARTIAL_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_BLOCK, - Z_OK, Z_STREAM_END, Z_STREAM_ERROR, Z_DATA_ERROR, Z_BUF_ERROR, - Z_DEFAULT_COMPRESSION, - Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED: Z_FIXED$1, Z_DEFAULT_STRATEGY, - Z_UNKNOWN: Z_UNKNOWN$1, - Z_DEFLATED - } = constants; + Z_NO_FLUSH: Z_NO_FLUSH$2, Z_PARTIAL_FLUSH, Z_FULL_FLUSH: Z_FULL_FLUSH$1, Z_FINISH: Z_FINISH$3, Z_BLOCK: Z_BLOCK$1, + Z_OK: Z_OK$3, Z_STREAM_END: Z_STREAM_END$3, Z_STREAM_ERROR: Z_STREAM_ERROR$2, Z_DATA_ERROR: Z_DATA_ERROR$2, Z_BUF_ERROR: Z_BUF_ERROR$1, + Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1, + Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED, Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1, + Z_UNKNOWN, + Z_DEFLATED: Z_DEFLATED$2 + } = constants$2; /*============================================================================*/ const MAX_MEM_LEVEL = 9; /* Maximum value for memLevel in deflateInit2 */ - const MAX_WBITS = 15; + const MAX_WBITS$1 = 15; /* 32K LZ77 window */ const DEF_MEM_LEVEL = 8; - const LENGTH_CODES$1 = 29; + const LENGTH_CODES = 29; /* number of length codes, not counting the special END_BLOCK code */ - const LITERALS$1 = 256; + const LITERALS = 256; /* number of literal bytes 0..255 */ - const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; + const L_CODES = LITERALS + 1 + LENGTH_CODES; /* number of Literal or Length codes, including the END_BLOCK code */ - const D_CODES$1 = 30; + const D_CODES = 30; /* number of distance codes */ - const BL_CODES$1 = 19; + const BL_CODES = 19; /* number of codes used to transfer the bit lengths */ - const HEAP_SIZE$1 = 2 * L_CODES$1 + 1; + const HEAP_SIZE = 2 * L_CODES + 1; /* maximum heap size */ - const MAX_BITS$1 = 15; + const MAX_BITS = 15; /* All codes must not exceed MAX_BITS bits */ - const MIN_MATCH$1 = 3; - const MAX_MATCH$1 = 258; - const MIN_LOOKAHEAD = (MAX_MATCH$1 + MIN_MATCH$1 + 1); + const MIN_MATCH = 3; + const MAX_MATCH = 258; + const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); const PRESET_DICT = 0x20; @@ -73903,7 +71186,7 @@ void main() return ((f) << 1) - ((f) > 4 ? 9 : 0); }; - const zero$1 = (buf) => { + const zero = (buf) => { let len = buf.length; while (--len >= 0) { buf[len] = 0; } }; @@ -73944,7 +71227,7 @@ void main() const flush_block_only = (s, last) => { - _tr_flush_block$1(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); + _tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); s.block_start = s.strstart; flush_pending(s.strm); }; @@ -74031,7 +71314,7 @@ void main() * we prevent matches with the string of window index 0. */ - const strend = s.strstart + MAX_MATCH$1; + const strend = s.strstart + MAX_MATCH; let scan_end1 = _win[scan + best_len - 1]; let scan_end = _win[scan + best_len]; @@ -74094,8 +71377,8 @@ void main() // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - len = MAX_MATCH$1 - (strend - scan); - scan = strend - MAX_MATCH$1; + len = MAX_MATCH - (strend - scan); + scan = strend - MAX_MATCH; if (len > best_len) { s.match_start = cur_match; @@ -74209,7 +71492,7 @@ void main() s.lookahead += n; /* Initialize the hash value now that we have some input: */ - if (s.lookahead + s.insert >= MIN_MATCH$1) { + if (s.lookahead + s.insert >= MIN_MATCH) { str = s.strstart - s.insert; s.ins_h = s.window[str]; @@ -74220,13 +71503,13 @@ void main() //#endif while (s.insert) { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH$1 - 1]); + s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); s.prev[str & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = str; str++; s.insert--; - if (s.lookahead + s.insert < MIN_MATCH$1) { + if (s.lookahead + s.insert < MIN_MATCH) { break; } } @@ -74308,7 +71591,7 @@ void main() // } fill_window(s); - if (s.lookahead === 0 && flush === Z_NO_FLUSH) { + if (s.lookahead === 0 && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } @@ -74354,7 +71637,7 @@ void main() s.insert = 0; - if (flush === Z_FINISH) { + if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { @@ -74396,7 +71679,7 @@ void main() */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { @@ -74408,9 +71691,9 @@ void main() * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH$1) { + if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ @@ -74427,24 +71710,24 @@ void main() s.match_length = longest_match(s, hash_head); /* longest_match() sets match_start */ } - if (s.match_length >= MIN_MATCH$1) { + if (s.match_length >= MIN_MATCH) { // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only /*** _tr_tally_dist(s, s.strstart - s.match_start, s.match_length - MIN_MATCH, bflush); ***/ - bflush = _tr_tally$1(s, s.strstart - s.match_start, s.match_length - MIN_MATCH$1); + bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; /* Insert new strings in the hash table only if the match length * is not too large. This saves time but degrades compression. */ - if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH$1) { + if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { s.match_length--; /* string at strstart already in table */ do { s.strstart++; /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ @@ -74472,7 +71755,7 @@ void main() /* No match, output a literal byte */ //Tracevv((stderr,"%c", s.window[s.strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = _tr_tally$1(s, 0, s.window[s.strstart]); + bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; @@ -74486,8 +71769,8 @@ void main() /***/ } } - s.insert = ((s.strstart < (MIN_MATCH$1 - 1)) ? s.strstart : MIN_MATCH$1 - 1); - if (flush === Z_FINISH) { + s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1); + if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { @@ -74528,7 +71811,7 @@ void main() */ if (s.lookahead < MIN_LOOKAHEAD) { fill_window(s); - if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) { + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ @@ -74538,9 +71821,9 @@ void main() * dictionary, and set hash_head to the head of the hash chain: */ hash_head = 0/*NIL*/; - if (s.lookahead >= MIN_MATCH$1) { + if (s.lookahead >= MIN_MATCH) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ @@ -74550,7 +71833,7 @@ void main() */ s.prev_length = s.match_length; s.prev_match = s.match_start; - s.match_length = MIN_MATCH$1 - 1; + s.match_length = MIN_MATCH - 1; if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { @@ -74562,26 +71845,26 @@ void main() /* longest_match() sets match_start */ if (s.match_length <= 5 && - (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH$1 && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { + (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { /* If prev_match is also MIN_MATCH, match_start is garbage * but we will ignore the current match anyway. */ - s.match_length = MIN_MATCH$1 - 1; + s.match_length = MIN_MATCH - 1; } } /* If there was a match at the previous step and the current * match is not better, output the previous match: */ - if (s.prev_length >= MIN_MATCH$1 && s.match_length <= s.prev_length) { - max_insert = s.strstart + s.lookahead - MIN_MATCH$1; + if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { + max_insert = s.strstart + s.lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ //check_match(s, s.strstart-1, s.prev_match, s.prev_length); /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH, bflush);***/ - bflush = _tr_tally$1(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH$1); + bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH); /* Insert in hash table all strings up to the end of the match. * strstart-1 and strstart are already inserted. If there is not * enough lookahead, the last two strings are not inserted in @@ -74592,14 +71875,14 @@ void main() do { if (++s.strstart <= max_insert) { /*** INSERT_STRING(s, s.strstart, hash_head); ***/ - s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH$1 - 1]); + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; s.head[s.ins_h] = s.strstart; /***/ } } while (--s.prev_length !== 0); s.match_available = 0; - s.match_length = MIN_MATCH$1 - 1; + s.match_length = MIN_MATCH - 1; s.strstart++; if (bflush) { @@ -74618,7 +71901,7 @@ void main() */ //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = _tr_tally$1(s, 0, s.window[s.strstart - 1]); + bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); if (bflush) { /*** FLUSH_BLOCK_ONLY(s, 0) ***/ @@ -74643,12 +71926,12 @@ void main() if (s.match_available) { //Tracevv((stderr,"%c", s->window[s->strstart-1])); /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ - bflush = _tr_tally$1(s, 0, s.window[s.strstart - 1]); + bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); s.match_available = 0; } - s.insert = s.strstart < MIN_MATCH$1 - 1 ? s.strstart : MIN_MATCH$1 - 1; - if (flush === Z_FINISH) { + s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; + if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { @@ -74688,9 +71971,9 @@ void main() * at the end of the input file. We need MAX_MATCH bytes * for the longest run, plus one for the unrolled loop. */ - if (s.lookahead <= MAX_MATCH$1) { + if (s.lookahead <= MAX_MATCH) { fill_window(s); - if (s.lookahead <= MAX_MATCH$1 && flush === Z_NO_FLUSH) { + if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } if (s.lookahead === 0) { break; } /* flush the current block */ @@ -74698,11 +71981,11 @@ void main() /* See how many times the previous byte repeats */ s.match_length = 0; - if (s.lookahead >= MIN_MATCH$1 && s.strstart > 0) { + if (s.lookahead >= MIN_MATCH && s.strstart > 0) { scan = s.strstart - 1; prev = _win[scan]; if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) { - strend = s.strstart + MAX_MATCH$1; + strend = s.strstart + MAX_MATCH; do { /*jshint noempty:false*/ } while (prev === _win[++scan] && prev === _win[++scan] && @@ -74710,7 +71993,7 @@ void main() prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend); - s.match_length = MAX_MATCH$1 - (strend - scan); + s.match_length = MAX_MATCH - (strend - scan); if (s.match_length > s.lookahead) { s.match_length = s.lookahead; } @@ -74719,11 +72002,11 @@ void main() } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (s.match_length >= MIN_MATCH$1) { + if (s.match_length >= MIN_MATCH) { //check_match(s, s.strstart, s.strstart - 1, s.match_length); /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ - bflush = _tr_tally$1(s, 1, s.match_length - MIN_MATCH$1); + bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH); s.lookahead -= s.match_length; s.strstart += s.match_length; @@ -74732,7 +72015,7 @@ void main() /* No match, output a literal byte */ //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = _tr_tally$1(s, 0, s.window[s.strstart]); + bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; @@ -74747,7 +72030,7 @@ void main() } } s.insert = 0; - if (flush === Z_FINISH) { + if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { @@ -74780,7 +72063,7 @@ void main() if (s.lookahead === 0) { fill_window(s); if (s.lookahead === 0) { - if (flush === Z_NO_FLUSH) { + if (flush === Z_NO_FLUSH$2) { return BS_NEED_MORE; } break; /* flush the current block */ @@ -74791,7 +72074,7 @@ void main() s.match_length = 0; //Tracevv((stderr,"%c", s->window[s->strstart])); /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ - bflush = _tr_tally$1(s, 0, s.window[s.strstart]); + bflush = _tr_tally(s, 0, s.window[s.strstart]); s.lookahead--; s.strstart++; if (bflush) { @@ -74804,7 +72087,7 @@ void main() } } s.insert = 0; - if (flush === Z_FINISH) { + if (flush === Z_FINISH$3) { /*** FLUSH_BLOCK(s, 1); ***/ flush_block_only(s, true); if (s.strm.avail_out === 0) { @@ -74862,7 +72145,7 @@ void main() s.window_size = 2 * s.w_size; /*** CLEAR_HASH(s); ***/ - zero$1(s.head); // Fill with NIL (= 0); + zero(s.head); // Fill with NIL (= 0); /* Set the default configuration parameters: */ @@ -74875,7 +72158,7 @@ void main() s.block_start = 0; s.lookahead = 0; s.insert = 0; - s.match_length = s.prev_length = MIN_MATCH$1 - 1; + s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; s.ins_h = 0; }; @@ -74891,7 +72174,7 @@ void main() this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ this.gzhead = null; /* gzip header information to write */ this.gzindex = 0; /* where in extra, name, or comment */ - this.method = Z_DEFLATED; /* can only be DEFLATED */ + this.method = Z_DEFLATED$2; /* can only be DEFLATED */ this.last_flush = -1; /* value of flush param for previous deflate call */ this.w_size = 0; /* LZ77 window size (32K by default) */ @@ -74984,24 +72267,24 @@ void main() // Use flat array of DOUBLE size, with interleaved fata, // because JS does not support effective - this.dyn_ltree = new Uint16Array(HEAP_SIZE$1 * 2); - this.dyn_dtree = new Uint16Array((2 * D_CODES$1 + 1) * 2); - this.bl_tree = new Uint16Array((2 * BL_CODES$1 + 1) * 2); - zero$1(this.dyn_ltree); - zero$1(this.dyn_dtree); - zero$1(this.bl_tree); + this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2); + this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2); + this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2); + zero(this.dyn_ltree); + zero(this.dyn_dtree); + zero(this.bl_tree); this.l_desc = null; /* desc. for literal tree */ this.d_desc = null; /* desc. for distance tree */ this.bl_desc = null; /* desc. for bit length tree */ //ush bl_count[MAX_BITS+1]; - this.bl_count = new Uint16Array(MAX_BITS$1 + 1); + this.bl_count = new Uint16Array(MAX_BITS + 1); /* number of codes at each bit length for an optimal tree */ //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - this.heap = new Uint16Array(2 * L_CODES$1 + 1); /* heap used to build the Huffman trees */ - zero$1(this.heap); + this.heap = new Uint16Array(2 * L_CODES + 1); /* heap used to build the Huffman trees */ + zero(this.heap); this.heap_len = 0; /* number of elements in the heap */ this.heap_max = 0; /* element of largest frequency */ @@ -75009,8 +72292,8 @@ void main() * The same heap array is used to build all trees. */ - this.depth = new Uint16Array(2 * L_CODES$1 + 1); //uch depth[2*L_CODES+1]; - zero$1(this.depth); + this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; + zero(this.depth); /* Depth of each subtree used as tie breaker for trees of equal frequency */ @@ -75073,11 +72356,11 @@ void main() const deflateResetKeep = (strm) => { if (!strm || !strm.state) { - return err(strm, Z_STREAM_ERROR); + return err(strm, Z_STREAM_ERROR$2); } strm.total_in = strm.total_out = 0; - strm.data_type = Z_UNKNOWN$1; + strm.data_type = Z_UNKNOWN; const s = strm.state; s.pending = 0; @@ -75092,16 +72375,16 @@ void main() 0 // crc32(0, Z_NULL, 0) : 1; // adler32(0, Z_NULL, 0) - s.last_flush = Z_NO_FLUSH; - _tr_init$1(s); - return Z_OK; + s.last_flush = Z_NO_FLUSH$2; + _tr_init(s); + return Z_OK$3; }; const deflateReset = (strm) => { const ret = deflateResetKeep(strm); - if (ret === Z_OK) { + if (ret === Z_OK$3) { lm_init(strm.state); } return ret; @@ -75110,21 +72393,21 @@ void main() const deflateSetHeader = (strm, head) => { - if (!strm || !strm.state) { return Z_STREAM_ERROR; } - if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; } + if (!strm || !strm.state) { return Z_STREAM_ERROR$2; } + if (strm.state.wrap !== 2) { return Z_STREAM_ERROR$2; } strm.state.gzhead = head; - return Z_OK; + return Z_OK$3; }; const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => { if (!strm) { // === Z_NULL - return Z_STREAM_ERROR; + return Z_STREAM_ERROR$2; } let wrap = 1; - if (level === Z_DEFAULT_COMPRESSION) { + if (level === Z_DEFAULT_COMPRESSION$1) { level = 6; } @@ -75139,10 +72422,10 @@ void main() } - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED || + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED$1) { - return err(strm, Z_STREAM_ERROR); + strategy < 0 || strategy > Z_FIXED) { + return err(strm, Z_STREAM_ERROR$2); } @@ -75165,7 +72448,7 @@ void main() s.hash_bits = memLevel + 7; s.hash_size = 1 << s.hash_bits; s.hash_mask = s.hash_size - 1; - s.hash_shift = ~~((s.hash_bits + MIN_MATCH$1 - 1) / MIN_MATCH$1); + s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); s.window = new Uint8Array(s.w_size * 2); s.head = new Uint16Array(s.hash_size); @@ -75198,25 +72481,25 @@ void main() const deflateInit = (strm, level) => { - return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1); }; - const deflate = (strm, flush) => { + const deflate$2 = (strm, flush) => { let beg, val; // for gzip header write only if (!strm || !strm.state || - flush > Z_BLOCK || flush < 0) { - return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR; + flush > Z_BLOCK$1 || flush < 0) { + return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2; } const s = strm.state; if (!strm.output || (!strm.input && strm.avail_in !== 0) || - (s.status === FINISH_STATE && flush !== Z_FINISH)) { - return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR); + (s.status === FINISH_STATE && flush !== Z_FINISH$3)) { + return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2); } s.strm = strm; /* just in case */ @@ -75271,7 +72554,7 @@ void main() } else // DEFLATE header { - let header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; + let header = (Z_DEFLATED$2 + ((s.w_bits - 8) << 4)) << 8; let level_flags = -1; if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { @@ -75434,7 +72717,7 @@ void main() * return OK instead of BUF_ERROR at next call of deflate: */ s.last_flush = -1; - return Z_OK; + return Z_OK$3; } /* Make sure there is something to do and avoid duplicate consecutive @@ -75442,19 +72725,19 @@ void main() * returning Z_STREAM_END instead of Z_BUF_ERROR. */ } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && - flush !== Z_FINISH) { - return err(strm, Z_BUF_ERROR); + flush !== Z_FINISH$3) { + return err(strm, Z_BUF_ERROR$1); } /* User must not provide more input after the first FINISH: */ if (s.status === FINISH_STATE && strm.avail_in !== 0) { - return err(strm, Z_BUF_ERROR); + return err(strm, Z_BUF_ERROR$1); } /* Start a new block or continue the current one. */ if (strm.avail_in !== 0 || s.lookahead !== 0 || - (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) { + (flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE)) { let bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : (s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush)); @@ -75467,7 +72750,7 @@ void main() s.last_flush = -1; /* avoid BUF_ERROR next call, see above */ } - return Z_OK; + return Z_OK$3; /* If flush != Z_NO_FLUSH && avail_out == 0, the next call * of deflate should use the same flush parameter to make sure * that the flush is complete. So we don't have to output an @@ -75478,17 +72761,17 @@ void main() } if (bstate === BS_BLOCK_DONE) { if (flush === Z_PARTIAL_FLUSH) { - _tr_align$1(s); + _tr_align(s); } - else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + else if (flush !== Z_BLOCK$1) { /* FULL_FLUSH or SYNC_FLUSH */ - _tr_stored_block$1(s, 0, 0, false); + _tr_stored_block(s, 0, 0, false); /* For a full flush, this empty block will be recognized * as a special marker by inflate_sync(). */ - if (flush === Z_FULL_FLUSH) { + if (flush === Z_FULL_FLUSH$1) { /*** CLEAR_HASH(s); ***/ /* forget history */ - zero$1(s.head); // Fill with NIL (= 0); + zero(s.head); // Fill with NIL (= 0); if (s.lookahead === 0) { s.strstart = 0; @@ -75500,15 +72783,15 @@ void main() flush_pending(strm); if (strm.avail_out === 0) { s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; + return Z_OK$3; } } } //Assert(strm->avail_out > 0, "bug2"); //if (strm.avail_out <= 0) { throw new Error("bug2");} - if (flush !== Z_FINISH) { return Z_OK; } - if (s.wrap <= 0) { return Z_STREAM_END; } + if (flush !== Z_FINISH$3) { return Z_OK$3; } + if (s.wrap <= 0) { return Z_STREAM_END$3; } /* Write the trailer */ if (s.wrap === 2) { @@ -75533,14 +72816,14 @@ void main() */ if (s.wrap > 0) { s.wrap = -s.wrap; } /* write the trailer only once! */ - return s.pending !== 0 ? Z_OK : Z_STREAM_END; + return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3; }; const deflateEnd = (strm) => { if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; + return Z_STREAM_ERROR$2; } const status = strm.state.status; @@ -75552,12 +72835,12 @@ void main() status !== BUSY_STATE && status !== FINISH_STATE ) { - return err(strm, Z_STREAM_ERROR); + return err(strm, Z_STREAM_ERROR$2); } strm.state = null; - return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK; + return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3; }; @@ -75570,14 +72853,14 @@ void main() let dictLength = dictionary.length; if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) { - return Z_STREAM_ERROR; + return Z_STREAM_ERROR$2; } const s = strm.state; const wrap = s.wrap; if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { - return Z_STREAM_ERROR; + return Z_STREAM_ERROR$2; } /* when using zlib wrappers, compute Adler-32 for provided dictionary */ @@ -75592,7 +72875,7 @@ void main() if (dictLength >= s.w_size) { if (wrap === 0) { /* already empty otherwise */ /*** CLEAR_HASH(s); ***/ - zero$1(s.head); // Fill with NIL (= 0); + zero(s.head); // Fill with NIL (= 0); s.strstart = 0; s.block_start = 0; s.insert = 0; @@ -75612,12 +72895,12 @@ void main() strm.next_in = 0; strm.input = dictionary; fill_window(s); - while (s.lookahead >= MIN_MATCH$1) { + while (s.lookahead >= MIN_MATCH) { let str = s.strstart; - let n = s.lookahead - (MIN_MATCH$1 - 1); + let n = s.lookahead - (MIN_MATCH - 1); do { /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ - s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH$1 - 1]); + s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); s.prev[str & s.w_mask] = s.head[s.ins_h]; @@ -75625,20 +72908,20 @@ void main() str++; } while (--n); s.strstart = str; - s.lookahead = MIN_MATCH$1 - 1; + s.lookahead = MIN_MATCH - 1; fill_window(s); } s.strstart += s.lookahead; s.block_start = s.strstart; s.insert = s.lookahead; s.lookahead = 0; - s.match_length = s.prev_length = MIN_MATCH$1 - 1; + s.match_length = s.prev_length = MIN_MATCH - 1; s.match_available = 0; strm.next_in = next; strm.input = input; strm.avail_in = avail; s.wrap = wrap; - return Z_OK; + return Z_OK$3; }; @@ -75647,7 +72930,7 @@ void main() var deflateReset_1 = deflateReset; var deflateResetKeep_1 = deflateResetKeep; var deflateSetHeader_1 = deflateSetHeader; - var deflate_2 = deflate; + var deflate_2$1 = deflate$2; var deflateEnd_1 = deflateEnd; var deflateSetDictionary_1 = deflateSetDictionary; var deflateInfo = 'pako deflate (from Nodeca project)'; @@ -75661,13 +72944,13 @@ void main() module.exports.deflateTune = deflateTune; */ - var deflate_1 = { + var deflate_1$2 = { deflateInit: deflateInit_1, deflateInit2: deflateInit2_1, deflateReset: deflateReset_1, deflateResetKeep: deflateResetKeep_1, deflateSetHeader: deflateSetHeader_1, - deflate: deflate_2, + deflate: deflate_2$1, deflateEnd: deflateEnd_1, deflateSetDictionary: deflateSetDictionary_1, deflateInfo: deflateInfo @@ -75749,6 +73032,10 @@ void main() // convert string to array (typed, when possible) var string2buf = (str) => { + if (typeof TextEncoder === 'function' && TextEncoder.prototype.encode) { + return new TextEncoder().encode(str); + } + let buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; // count binary size @@ -75822,9 +73109,14 @@ void main() // convert array to string var buf2string = (buf, max) => { - let i, out; const len = max || buf.length; + if (typeof TextDecoder === 'function' && TextDecoder.prototype.decode) { + return new TextDecoder().decode(buf.subarray(0, max)); + } + + let i, out; + // Reserve max possible length (2 words per char) // NB: by unknown reasons, Array is significantly faster for // String.fromCharCode.apply than Uint16Array. @@ -75941,18 +73233,18 @@ void main() var zstream = ZStream; - const toString = Object.prototype.toString; + const toString$1 = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { - Z_NO_FLUSH: Z_NO_FLUSH$1, Z_SYNC_FLUSH, Z_FULL_FLUSH: Z_FULL_FLUSH$1, Z_FINISH: Z_FINISH$1, - Z_OK: Z_OK$1, Z_STREAM_END: Z_STREAM_END$1, - Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1, - Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1, + Z_NO_FLUSH: Z_NO_FLUSH$1, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH: Z_FINISH$2, + Z_OK: Z_OK$2, Z_STREAM_END: Z_STREAM_END$2, + Z_DEFAULT_COMPRESSION, + Z_DEFAULT_STRATEGY, Z_DEFLATED: Z_DEFLATED$1 - } = constants; + } = constants$2; /* ===========================================================================*/ @@ -76042,14 +73334,14 @@ void main() * console.log(deflate.result); * ``` **/ - function Deflate(options) { + function Deflate$1(options) { this.options = common.assign({ - level: Z_DEFAULT_COMPRESSION$1, + level: Z_DEFAULT_COMPRESSION, method: Z_DEFLATED$1, chunkSize: 16384, windowBits: 15, memLevel: 8, - strategy: Z_DEFAULT_STRATEGY$1 + strategy: Z_DEFAULT_STRATEGY }, options || {}); let opt = this.options; @@ -76070,7 +73362,7 @@ void main() this.strm = new zstream(); this.strm.avail_out = 0; - let status = deflate_1.deflateInit2( + let status = deflate_1$2.deflateInit2( this.strm, opt.level, opt.method, @@ -76079,12 +73371,12 @@ void main() opt.strategy ); - if (status !== Z_OK$1) { + if (status !== Z_OK$2) { throw new Error(messages[status]); } if (opt.header) { - deflate_1.deflateSetHeader(this.strm, opt.header); + deflate_1$2.deflateSetHeader(this.strm, opt.header); } if (opt.dictionary) { @@ -76093,15 +73385,15 @@ void main() if (typeof opt.dictionary === 'string') { // If we need to compress text, change encoding to utf8. dict = strings.string2buf(opt.dictionary); - } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') { + } else if (toString$1.call(opt.dictionary) === '[object ArrayBuffer]') { dict = new Uint8Array(opt.dictionary); } else { dict = opt.dictionary; } - status = deflate_1.deflateSetDictionary(this.strm, dict); + status = deflate_1$2.deflateSetDictionary(this.strm, dict); - if (status !== Z_OK$1) { + if (status !== Z_OK$2) { throw new Error(messages[status]); } @@ -76131,7 +73423,7 @@ void main() * push(chunk, true); // push last chunk * ``` **/ - Deflate.prototype.push = function (data, flush_mode) { + Deflate$1.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; let status, _flush_mode; @@ -76139,13 +73431,13 @@ void main() if (this.ended) { return false; } if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; - else _flush_mode = flush_mode === true ? Z_FINISH$1 : Z_NO_FLUSH$1; + else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1; // Convert data if needed if (typeof data === 'string') { // If we need to compress text, change encoding to utf8. strm.input = strings.string2buf(data); - } else if (toString.call(data) === '[object ArrayBuffer]') { + } else if (toString$1.call(data) === '[object ArrayBuffer]') { strm.input = new Uint8Array(data); } else { strm.input = data; @@ -76162,23 +73454,23 @@ void main() } // Make sure avail_out > 6 to avoid repeating markers - if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH$1) && strm.avail_out <= 6) { + if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) { this.onData(strm.output.subarray(0, strm.next_out)); strm.avail_out = 0; continue; } - status = deflate_1.deflate(strm, _flush_mode); + status = deflate_1$2.deflate(strm, _flush_mode); // Ended => flush and finish - if (status === Z_STREAM_END$1) { + if (status === Z_STREAM_END$2) { if (strm.next_out > 0) { this.onData(strm.output.subarray(0, strm.next_out)); } - status = deflate_1.deflateEnd(this.strm); + status = deflate_1$2.deflateEnd(this.strm); this.onEnd(status); this.ended = true; - return status === Z_OK$1; + return status === Z_OK$2; } // Flush if out buffer full @@ -76208,7 +73500,7 @@ void main() * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ - Deflate.prototype.onData = function (chunk) { + Deflate$1.prototype.onData = function (chunk) { this.chunks.push(chunk); }; @@ -76222,9 +73514,9 @@ void main() * complete (Z_FINISH). By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ - Deflate.prototype.onEnd = function (status) { + Deflate$1.prototype.onEnd = function (status) { // On success - join - if (status === Z_OK$1) { + if (status === Z_OK$2) { this.result = common.flattenChunks(this.chunks); } this.chunks = []; @@ -76252,8 +73544,8 @@ void main() // 3. This notice may not be removed or altered from any source distribution. // See state defs from inflate.js - const BAD = 30; /* got a data error -- remain here until reset */ - const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + const BAD$1 = 30; /* got a data error -- remain here until reset */ + const TYPE$1 = 12; /* i: waiting for type bits, including last-flag bit */ /* Decode literal, length, and distance codes and write out the resulting @@ -76415,7 +73707,7 @@ void main() //#ifdef INFLATE_STRICT if (dist > dmax) { strm.msg = 'invalid distance too far back'; - state.mode = BAD; + state.mode = BAD$1; break top; } //#endif @@ -76428,7 +73720,7 @@ void main() if (op > whave) { if (state.sane) { strm.msg = 'invalid distance too far back'; - state.mode = BAD; + state.mode = BAD$1; break top; } @@ -76533,7 +73825,7 @@ void main() } else { strm.msg = 'invalid distance code'; - state.mode = BAD; + state.mode = BAD$1; break top; } @@ -76546,12 +73838,12 @@ void main() } else if (op & 32) { /* end-of-block */ //Tracevv((stderr, "inflate: end of block\n")); - state.mode = TYPE; + state.mode = TYPE$1; break top; } else { strm.msg = 'invalid literal/length code'; - state.mode = BAD; + state.mode = BAD$1; break top; } @@ -76595,13 +73887,13 @@ void main() // 3. This notice may not be removed or altered from any source distribution. const MAXBITS = 15; - const ENOUGH_LENS = 852; - const ENOUGH_DISTS = 592; + const ENOUGH_LENS$1 = 852; + const ENOUGH_DISTS$1 = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - const CODES = 0; - const LENS = 1; - const DISTS = 2; + const CODES$1 = 0; + const LENS$1 = 1; + const DISTS$1 = 2; const lbase = new Uint16Array([ /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, @@ -76733,7 +74025,7 @@ void main() return -1; } /* over-subscribed */ } - if (left > 0 && (type === CODES || max !== 1)) { + if (left > 0 && (type === CODES$1 || max !== 1)) { return -1; /* incomplete set */ } @@ -76784,11 +74076,11 @@ void main() /* set up for code type */ // poor man optimization - use if-else instead of switch, // to avoid deopts in old v8 - if (type === CODES) { + if (type === CODES$1) { base = extra = work; /* dummy value--not used */ end = 19; - } else if (type === LENS) { + } else if (type === LENS$1) { base = lbase; base_index -= 257; extra = lext; @@ -76813,8 +74105,8 @@ void main() mask = used - 1; /* mask for comparing low */ /* check available table space */ - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { + if ((type === LENS$1 && used > ENOUGH_LENS$1) || + (type === DISTS$1 && used > ENOUGH_DISTS$1)) { return 1; } @@ -76885,8 +74177,8 @@ void main() /* check for enough space */ used += 1 << curr; - if ((type === LENS && used > ENOUGH_LENS) || - (type === DISTS && used > ENOUGH_DISTS)) { + if ((type === LENS$1 && used > ENOUGH_LENS$1) || + (type === DISTS$1 && used > ENOUGH_DISTS$1)) { return 1; } @@ -76942,18 +74234,18 @@ void main() - const CODES$1 = 0; - const LENS$1 = 1; - const DISTS$1 = 2; + const CODES = 0; + const LENS = 1; + const DISTS = 2; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { - Z_FINISH: Z_FINISH$2, Z_BLOCK: Z_BLOCK$1, Z_TREES, - Z_OK: Z_OK$2, Z_STREAM_END: Z_STREAM_END$2, Z_NEED_DICT, Z_STREAM_ERROR: Z_STREAM_ERROR$1, Z_DATA_ERROR: Z_DATA_ERROR$1, Z_MEM_ERROR, Z_BUF_ERROR: Z_BUF_ERROR$1, - Z_DEFLATED: Z_DEFLATED$2 - } = constants; + Z_FINISH: Z_FINISH$1, Z_BLOCK, Z_TREES, + Z_OK: Z_OK$1, Z_STREAM_END: Z_STREAM_END$1, Z_NEED_DICT: Z_NEED_DICT$1, Z_STREAM_ERROR: Z_STREAM_ERROR$1, Z_DATA_ERROR: Z_DATA_ERROR$1, Z_MEM_ERROR: Z_MEM_ERROR$1, Z_BUF_ERROR, + Z_DEFLATED + } = constants$2; /* STATES ====================================================================*/ @@ -76971,7 +74263,7 @@ void main() const HCRC = 9; /* i: waiting for header crc (gzip) */ const DICTID = 10; /* i: waiting for dictionary check value */ const DICT = 11; /* waiting for inflateSetDictionary() call */ - const TYPE$1 = 12; /* i: waiting for type bits, including last-flag bit */ + const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ const TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ const STORED = 14; /* i: waiting for stored size (length and complement) */ const COPY_ = 15; /* i/o: same as COPY below, but only first time in */ @@ -76989,7 +74281,7 @@ void main() const CHECK = 27; /* i: waiting for 32-bit check value */ const LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ const DONE = 29; /* finished check, done -- remain here until reset */ - const BAD$1 = 30; /* got a data error -- remain here until reset */ + const BAD = 30; /* got a data error -- remain here until reset */ const MEM = 31; /* got an inflate() memory error -- remain here until reset */ const SYNC = 32; /* looking for synchronization bytes to restart inflate() */ @@ -76997,13 +74289,13 @@ void main() - const ENOUGH_LENS$1 = 852; - const ENOUGH_DISTS$1 = 592; + const ENOUGH_LENS = 852; + const ENOUGH_DISTS = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); - const MAX_WBITS$1 = 15; + const MAX_WBITS = 15; /* 32K LZ77 window */ - const DEF_WBITS = MAX_WBITS$1; + const DEF_WBITS = MAX_WBITS; const zswap32 = (q) => { @@ -77091,13 +74383,13 @@ void main() state.hold = 0; state.bits = 0; //state.lencode = state.distcode = state.next = state.codes; - state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS$1); - state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS$1); + state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); + state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); state.sane = 1; state.back = -1; //Tracev((stderr, "inflate: reset\n")); - return Z_OK$2; + return Z_OK$1; }; @@ -77159,7 +74451,7 @@ void main() strm.state = state; state.window = null/*Z_NULL*/; const ret = inflateReset2(strm, windowBits); - if (ret !== Z_OK$2) { + if (ret !== Z_OK$1) { strm.state = null/*Z_NULL*/; } return ret; @@ -77201,13 +74493,13 @@ void main() while (sym < 280) { state.lens[sym++] = 7; } while (sym < 288) { state.lens[sym++] = 8; } - inftrees(LENS$1, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); + inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); /* distance table */ sym = 0; while (sym < 32) { state.lens[sym++] = 5; } - inftrees(DISTS$1, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); /* do this just once */ virgin = false; @@ -77278,7 +74570,7 @@ void main() }; - const inflate = (strm, flush) => { + const inflate$2 = (strm, flush) => { let state; let input, output; // input/output buffers @@ -77312,7 +74604,7 @@ void main() } state = strm.state; - if (state.mode === TYPE$1) { state.mode = TYPEDO; } /* skip check */ + if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ //--- LOAD() --- @@ -77328,7 +74620,7 @@ void main() _in = have; _out = left; - ret = Z_OK$2; + ret = Z_OK$1; inf_leave: // goto emulation for (;;) { @@ -77368,12 +74660,12 @@ void main() if (!(state.wrap & 1) || /* check if zlib header allowed */ (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { strm.msg = 'incorrect header check'; - state.mode = BAD$1; + state.mode = BAD; break; } - if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED$2) { + if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { strm.msg = 'unknown compression method'; - state.mode = BAD$1; + state.mode = BAD; break; } //--- DROPBITS(4) ---// @@ -77386,7 +74678,7 @@ void main() } else if (len > state.wbits) { strm.msg = 'invalid window size'; - state.mode = BAD$1; + state.mode = BAD; break; } @@ -77397,7 +74689,7 @@ void main() //Tracev((stderr, "inflate: zlib header ok\n")); strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = hold & 0x200 ? DICTID : TYPE$1; + state.mode = hold & 0x200 ? DICTID : TYPE; //=== INITBITS(); hold = 0; bits = 0; @@ -77413,14 +74705,14 @@ void main() } //===// state.flags = hold; - if ((state.flags & 0xff) !== Z_DEFLATED$2) { + if ((state.flags & 0xff) !== Z_DEFLATED) { strm.msg = 'unknown compression method'; - state.mode = BAD$1; + state.mode = BAD; break; } if (state.flags & 0xe000) { strm.msg = 'unknown header flags set'; - state.mode = BAD$1; + state.mode = BAD; break; } if (state.head) { @@ -77623,7 +74915,7 @@ void main() //===// if (hold !== (state.check & 0xffff)) { strm.msg = 'header crc mismatch'; - state.mode = BAD$1; + state.mode = BAD; break; } //=== INITBITS(); @@ -77636,7 +74928,7 @@ void main() state.head.done = true; } strm.adler = state.check = 0; - state.mode = TYPE$1; + state.mode = TYPE; break; case DICTID: //=== NEEDBITS(32); */ @@ -77664,13 +74956,13 @@ void main() state.hold = hold; state.bits = bits; //--- - return Z_NEED_DICT; + return Z_NEED_DICT$1; } strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; - state.mode = TYPE$1; + state.mode = TYPE; /* falls through */ - case TYPE$1: - if (flush === Z_BLOCK$1 || flush === Z_TREES) { break inf_leave; } + case TYPE: + if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } /* falls through */ case TYPEDO: if (state.last) { @@ -77721,7 +75013,7 @@ void main() break; case 3: strm.msg = 'invalid block type'; - state.mode = BAD$1; + state.mode = BAD; } //--- DROPBITS(2) ---// hold >>>= 2; @@ -77743,7 +75035,7 @@ void main() //===// if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { strm.msg = 'invalid stored block lengths'; - state.mode = BAD$1; + state.mode = BAD; break; } state.length = hold & 0xffff; @@ -77776,7 +75068,7 @@ void main() break; } //Tracev((stderr, "inflate: stored end\n")); - state.mode = TYPE$1; + state.mode = TYPE; break; case TABLE: //=== NEEDBITS(14); */ @@ -77805,7 +75097,7 @@ void main() //#ifndef PKZIP_BUG_WORKAROUND if (state.nlen > 286 || state.ndist > 30) { strm.msg = 'too many length or distance symbols'; - state.mode = BAD$1; + state.mode = BAD; break; } //#endif @@ -77840,12 +75132,12 @@ void main() state.lenbits = 7; opts = { bits: state.lenbits }; - ret = inftrees(CODES$1, state.lens, 0, 19, state.lencode, 0, state.work, opts); + ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); state.lenbits = opts.bits; if (ret) { strm.msg = 'invalid code lengths set'; - state.mode = BAD$1; + state.mode = BAD; break; } //Tracev((stderr, "inflate: code lengths ok\n")); @@ -77892,7 +75184,7 @@ void main() //---// if (state.have === 0) { strm.msg = 'invalid bit length repeat'; - state.mode = BAD$1; + state.mode = BAD; break; } len = state.lens[state.have - 1]; @@ -77946,7 +75238,7 @@ void main() } if (state.have + copy > state.nlen + state.ndist) { strm.msg = 'invalid bit length repeat'; - state.mode = BAD$1; + state.mode = BAD; break; } while (copy--) { @@ -77956,12 +75248,12 @@ void main() } /* handle error breaks in while */ - if (state.mode === BAD$1) { break; } + if (state.mode === BAD) { break; } /* check for end-of-block code (better have one) */ if (state.lens[256] === 0) { strm.msg = 'invalid code -- missing end-of-block'; - state.mode = BAD$1; + state.mode = BAD; break; } @@ -77971,7 +75263,7 @@ void main() state.lenbits = 9; opts = { bits: state.lenbits }; - ret = inftrees(LENS$1, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.lenbits = opts.bits; @@ -77979,7 +75271,7 @@ void main() if (ret) { strm.msg = 'invalid literal/lengths set'; - state.mode = BAD$1; + state.mode = BAD; break; } @@ -77988,7 +75280,7 @@ void main() // Switch to use dynamic table state.distcode = state.distdyn; opts = { bits: state.distbits }; - ret = inftrees(DISTS$1, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed. // state.next_index = opts.table_index; state.distbits = opts.bits; @@ -77996,7 +75288,7 @@ void main() if (ret) { strm.msg = 'invalid distances set'; - state.mode = BAD$1; + state.mode = BAD; break; } //Tracev((stderr, 'inflate: codes ok\n')); @@ -78028,7 +75320,7 @@ void main() bits = state.bits; //--- - if (state.mode === TYPE$1) { + if (state.mode === TYPE) { state.back = -1; } break; @@ -78089,12 +75381,12 @@ void main() if (here_op & 32) { //Tracevv((stderr, "inflate: end of block\n")); state.back = -1; - state.mode = TYPE$1; + state.mode = TYPE; break; } if (here_op & 64) { strm.msg = 'invalid literal/length code'; - state.mode = BAD$1; + state.mode = BAD; break; } state.extra = here_op & 15; @@ -78169,7 +75461,7 @@ void main() state.back += here_bits; if (here_op & 64) { strm.msg = 'invalid distance code'; - state.mode = BAD$1; + state.mode = BAD; break; } state.offset = here_val; @@ -78197,7 +75489,7 @@ void main() //#ifdef INFLATE_STRICT if (state.offset > state.dmax) { strm.msg = 'invalid distance too far back'; - state.mode = BAD$1; + state.mode = BAD; break; } //#endif @@ -78212,7 +75504,7 @@ void main() if (copy > state.whave) { if (state.sane) { strm.msg = 'invalid distance too far back'; - state.mode = BAD$1; + state.mode = BAD; break; } // (!) This block is disabled in zlib defaults, @@ -78284,7 +75576,7 @@ void main() // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too if ((state.flags ? hold : zswap32(hold)) !== state.check) { strm.msg = 'incorrect data check'; - state.mode = BAD$1; + state.mode = BAD; break; } //=== INITBITS(); @@ -78307,7 +75599,7 @@ void main() //===// if (hold !== (state.total & 0xffffffff)) { strm.msg = 'incorrect length check'; - state.mode = BAD$1; + state.mode = BAD; break; } //=== INITBITS(); @@ -78319,13 +75611,13 @@ void main() state.mode = DONE; /* falls through */ case DONE: - ret = Z_STREAM_END$2; + ret = Z_STREAM_END$1; break inf_leave; - case BAD$1: + case BAD: ret = Z_DATA_ERROR$1; break inf_leave; case MEM: - return Z_MEM_ERROR; + return Z_MEM_ERROR$1; case SYNC: /* falls through */ default: @@ -78351,8 +75643,8 @@ void main() state.bits = bits; //--- - if (state.wsize || (_out !== strm.avail_out && state.mode < BAD$1 && - (state.mode < CHECK || flush !== Z_FINISH$2))) { + if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && + (state.mode < CHECK || flush !== Z_FINISH$1))) { if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ; } _in -= strm.avail_in; @@ -78365,10 +75657,10 @@ void main() (state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out)); } strm.data_type = state.bits + (state.last ? 64 : 0) + - (state.mode === TYPE$1 ? 128 : 0) + + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); - if (((_in === 0 && _out === 0) || flush === Z_FINISH$2) && ret === Z_OK$2) { - ret = Z_BUF_ERROR$1; + if (((_in === 0 && _out === 0) || flush === Z_FINISH$1) && ret === Z_OK$1) { + ret = Z_BUF_ERROR; } return ret; }; @@ -78385,7 +75677,7 @@ void main() state.window = null; } strm.state = null; - return Z_OK$2; + return Z_OK$1; }; @@ -78399,7 +75691,7 @@ void main() /* save header structure */ state.head = head; head.done = false; - return Z_OK$2; + return Z_OK$1; }; @@ -78432,11 +75724,11 @@ void main() ret = updatewindow(strm, dictionary, dictLength, dictLength); if (ret) { state.mode = MEM; - return Z_MEM_ERROR; + return Z_MEM_ERROR$1; } state.havedict = 1; // Tracev((stderr, "inflate: dictionary set\n")); - return Z_OK$2; + return Z_OK$1; }; @@ -78445,7 +75737,7 @@ void main() var inflateResetKeep_1 = inflateResetKeep; var inflateInit_1 = inflateInit; var inflateInit2_1 = inflateInit2; - var inflate_2 = inflate; + var inflate_2$1 = inflate$2; var inflateEnd_1 = inflateEnd; var inflateGetHeader_1 = inflateGetHeader; var inflateSetDictionary_1 = inflateSetDictionary; @@ -78461,13 +75753,13 @@ void main() module.exports.inflateUndermine = inflateUndermine; */ - var inflate_1 = { + var inflate_1$2 = { inflateReset: inflateReset_1, inflateReset2: inflateReset2_1, inflateResetKeep: inflateResetKeep_1, inflateInit: inflateInit_1, inflateInit2: inflateInit2_1, - inflate: inflate_2, + inflate: inflate_2$1, inflateEnd: inflateEnd_1, inflateGetHeader: inflateGetHeader_1, inflateSetDictionary: inflateSetDictionary_1, @@ -78531,15 +75823,15 @@ void main() var gzheader = GZheader; - const toString$1 = Object.prototype.toString; + const toString = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ const { - Z_NO_FLUSH: Z_NO_FLUSH$2, Z_FINISH: Z_FINISH$3, - Z_OK: Z_OK$3, Z_STREAM_END: Z_STREAM_END$3, Z_NEED_DICT: Z_NEED_DICT$1, Z_STREAM_ERROR: Z_STREAM_ERROR$2, Z_DATA_ERROR: Z_DATA_ERROR$2, Z_MEM_ERROR: Z_MEM_ERROR$1 - } = constants; + Z_NO_FLUSH, Z_FINISH, + Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR + } = constants$2; /* ===========================================================================*/ @@ -78621,7 +75913,7 @@ void main() * console.log(inflate.result); * ``` **/ - function Inflate(options) { + function Inflate$1(options) { this.options = common.assign({ chunkSize: 1024 * 64, windowBits: 15, @@ -78661,30 +75953,30 @@ void main() this.strm = new zstream(); this.strm.avail_out = 0; - let status = inflate_1.inflateInit2( + let status = inflate_1$2.inflateInit2( this.strm, opt.windowBits ); - if (status !== Z_OK$3) { + if (status !== Z_OK) { throw new Error(messages[status]); } this.header = new gzheader(); - inflate_1.inflateGetHeader(this.strm, this.header); + inflate_1$2.inflateGetHeader(this.strm, this.header); // Setup dictionary if (opt.dictionary) { // Convert data if needed if (typeof opt.dictionary === 'string') { opt.dictionary = strings.string2buf(opt.dictionary); - } else if (toString$1.call(opt.dictionary) === '[object ArrayBuffer]') { + } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') { opt.dictionary = new Uint8Array(opt.dictionary); } if (opt.raw) { //In raw mode we need to set the dictionary early - status = inflate_1.inflateSetDictionary(this.strm, opt.dictionary); - if (status !== Z_OK$3) { + status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary); + if (status !== Z_OK) { throw new Error(messages[status]); } } @@ -78716,7 +76008,7 @@ void main() * push(chunk, true); // push last chunk * ``` **/ - Inflate.prototype.push = function (data, flush_mode) { + Inflate$1.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; const dictionary = this.options.dictionary; @@ -78725,10 +76017,10 @@ void main() if (this.ended) return false; if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; - else _flush_mode = flush_mode === true ? Z_FINISH$3 : Z_NO_FLUSH$2; + else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed - if (toString$1.call(data) === '[object ArrayBuffer]') { + if (toString.call(data) === '[object ArrayBuffer]') { strm.input = new Uint8Array(data); } else { strm.input = data; @@ -78744,34 +76036,34 @@ void main() strm.avail_out = chunkSize; } - status = inflate_1.inflate(strm, _flush_mode); + status = inflate_1$2.inflate(strm, _flush_mode); - if (status === Z_NEED_DICT$1 && dictionary) { - status = inflate_1.inflateSetDictionary(strm, dictionary); + if (status === Z_NEED_DICT && dictionary) { + status = inflate_1$2.inflateSetDictionary(strm, dictionary); - if (status === Z_OK$3) { - status = inflate_1.inflate(strm, _flush_mode); - } else if (status === Z_DATA_ERROR$2) { + if (status === Z_OK) { + status = inflate_1$2.inflate(strm, _flush_mode); + } else if (status === Z_DATA_ERROR) { // Replace code with more verbose - status = Z_NEED_DICT$1; + status = Z_NEED_DICT; } } // Skip snyc markers if more data follows and not raw mode while (strm.avail_in > 0 && - status === Z_STREAM_END$3 && + status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) { - inflate_1.inflateReset(strm); - status = inflate_1.inflate(strm, _flush_mode); + inflate_1$2.inflateReset(strm); + status = inflate_1$2.inflate(strm, _flush_mode); } switch (status) { - case Z_STREAM_ERROR$2: - case Z_DATA_ERROR$2: - case Z_NEED_DICT$1: - case Z_MEM_ERROR$1: + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: this.onEnd(status); this.ended = true; return false; @@ -78782,7 +76074,7 @@ void main() last_avail_out = strm.avail_out; if (strm.next_out) { - if (strm.avail_out === 0 || status === Z_STREAM_END$3) { + if (strm.avail_out === 0 || status === Z_STREAM_END) { if (this.options.to === 'string') { @@ -78805,11 +76097,11 @@ void main() } // Must repeat iteration if out buffer is full - if (status === Z_OK$3 && last_avail_out === 0) continue; + if (status === Z_OK && last_avail_out === 0) continue; // Finalize if end of stream reached. - if (status === Z_STREAM_END$3) { - status = inflate_1.inflateEnd(this.strm); + if (status === Z_STREAM_END) { + status = inflate_1$2.inflateEnd(this.strm); this.onEnd(status); this.ended = true; return true; @@ -78830,7 +76122,7 @@ void main() * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ - Inflate.prototype.onData = function (chunk) { + Inflate$1.prototype.onData = function (chunk) { this.chunks.push(chunk); }; @@ -78844,9 +76136,9 @@ void main() * complete (Z_FINISH). By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ - Inflate.prototype.onEnd = function (status) { + Inflate$1.prototype.onEnd = function (status) { // On success - join - if (status === Z_OK$3) { + if (status === Z_OK) { if (this.options.to === 'string') { this.result = this.chunks.join(''); } else { @@ -78893,13 +76185,13 @@ void main() * * try { * output = pako.inflate(input); - * } catch (err) + * } catch (err) { * console.log(err); * } * ``` **/ function inflate$1(input, options) { - const inflator = new Inflate(options); + const inflator = new Inflate$1(options); inflator.push(input); @@ -78918,7 +76210,7 @@ void main() * The same as [[inflate]], but creates raw data, without wrapper * (header and adler32 crc). **/ - function inflateRaw(input, options) { + function inflateRaw$1(input, options) { options = options || {}; options.raw = true; return inflate$1(input, options); @@ -78935,25 +76227,28 @@ void main() **/ - var Inflate_1 = Inflate; - var inflate_2$1 = inflate$1; - var inflateRaw_1 = inflateRaw; - var ungzip = inflate$1; - var constants$2 = constants; + var Inflate_1$1 = Inflate$1; + var inflate_2 = inflate$1; + var inflateRaw_1$1 = inflateRaw$1; + var ungzip$1 = inflate$1; + var constants = constants$2; var inflate_1$1 = { - Inflate: Inflate_1, - inflate: inflate_2$1, - inflateRaw: inflateRaw_1, - ungzip: ungzip, - constants: constants$2 + Inflate: Inflate_1$1, + inflate: inflate_2, + inflateRaw: inflateRaw_1$1, + ungzip: ungzip$1, + constants: constants }; - const { Inflate: Inflate$1, inflate: inflate$2, inflateRaw: inflateRaw$1, ungzip: ungzip$1 } = inflate_1$1; - var inflate_1$2 = inflate$2; + const { Inflate, inflate, inflateRaw, ungzip } = inflate_1$1; + var inflate_1 = inflate; + + var ieee754$1 = {}; /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ - var read = function (buffer, offset, isLE, mLen, nBytes) { + + ieee754$1.read = function (buffer, offset, isLE, mLen, nBytes) { var e, m; var eLen = (nBytes * 8) - mLen - 1; var eMax = (1 << eLen) - 1; @@ -78986,7 +76281,7 @@ void main() return (s ? -1 : 1) * m * Math.pow(2, e - mLen) }; - var write = function (buffer, value, offset, isLE, mLen, nBytes) { + ieee754$1.write = function (buffer, value, offset, isLE, mLen, nBytes) { var e, m, c; var eLen = (nBytes * 8) - mLen - 1; var eMax = (1 << eLen) - 1; @@ -79038,14 +76333,9 @@ void main() buffer[offset + i - d] |= s * 128; }; - var ieee754 = { - read: read, - write: write - }; - var pbf = Pbf; - + var ieee754 = ieee754$1; function Pbf(buf) { this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0); @@ -79695,7 +76985,7 @@ void main() * @returns {Object} Parsed object. */ function decompress(buffer) { - const inflated = inflate_1$2(buffer, { to: "string" }); + const inflated = inflate_1(buffer, { to: "string" }); return JSON.parse(inflated); } /** @@ -79776,131 +77066,6 @@ void main() } } - /** - * @class GeometryProviderBase - * - * @classdesc Base class to extend if implementing a geometry - * provider class. - * - * @example - * ```js - * class MyGeometryProvider extends GeometryProviderBase { - * ... - * } - * ``` - */ - class GeometryProviderBase { - /** - * Create a new geometry provider base instance. - */ - constructor() { } - /** - * Convert a geodetic bounding box to the the minimum set - * of cell ids containing the bounding box. - * - * @description The bounding box needs - * to be sufficiently small to be contained in an area with the size - * of maximally four tiles. Up to nine adjacent tiles may be returned. - * - * @param {LngLat} sw - South west corner of bounding box. - * @param {LngLat} ne - North east corner of bounding box. - * - * @returns {Array} Array of cell ids. - */ - bboxToCellIds(sw, ne) { - throw new MapillaryError("Not implemented"); - } - /** - * Get the cell ids of all adjacent cells. - * - * @description In the case of approximately rectangular cells - * this is typically the eight orthogonally and diagonally adjacent - * cells. - * - * @param {string} cellId - Id of cell. - * @returns {Array} Array of cell ids. No specific - * order is guaranteed. - */ - getAdjacent(cellId) { - throw new MapillaryError("Not implemented"); - } - /** - * Get the vertices of a cell. - * - * @description The vertices form an unclosed - * clockwise polygon in the 2D longitude, latitude - * space. No assumption on the position of the first - * vertex relative to the others can be made. - * - * @param {string} cellId - Id of cell. - * @returns {Array} Unclosed clockwise polygon. - */ - getVertices(cellId) { - throw new MapillaryError("Not implemented"); - } - /** - * Convert geodetic coordinates to a cell id. - * - * @param {LngLat} lngLat - Longitude, latitude to convert. - * @returns {string} Cell id for the longitude, latitude. - */ - lngLatToCellId(lngLat) { - throw new MapillaryError("Not implemented"); - } - /** @ignore */ - _approxBboxToCellIds(sw, ne) { - if (ne.lat <= sw.lat || ne.lng <= sw.lng) { - throw new MapillaryError("North east needs to be top right of south west"); - } - const centerLat = (sw.lat + ne.lat) / 2; - const centerLng = (sw.lng + ne.lng) / 2; - const enu = geodeticToEnu(ne.lng, ne.lat, 0, centerLng, centerLat, 0); - const threshold = Math.max(enu[0], enu[1]); - return this._lngLatToCellIds({ lat: centerLat, lng: centerLng }, threshold); - } - /** @ignore */ - _enuToGeodetic(point, reference) { - const [lng, lat] = enuToGeodetic(point[0], point[1], point[2], reference.lng, reference.lat, 0); - return { lat, lng }; - } - /** @ignore */ - _getLngLatBoundingBoxCorners(lngLat, threshold) { - return [ - [-threshold, threshold, 0], - [threshold, threshold, 0], - [threshold, -threshold, 0], - [-threshold, -threshold, 0], - ].map((point) => { - return this._enuToGeodetic(point, lngLat); - }); - } - /** - * Convert a geodetic square to cell ids. - * - * The square is specified as a longitude, latitude - * and a threshold from the position using Manhattan distance. - * - * @param {LngLat} lngLat - Longitude, latitude. - * @param {number} threshold - Threshold of the conversion in meters. - * - * @returns {Array} Array of cell ids reachable within - * the threshold. - * - * @ignore - */ - _lngLatToCellIds(lngLat, threshold) { - const cellId = this.lngLatToCellId(lngLat); - const bboxCorners = this._getLngLatBoundingBoxCorners(lngLat, threshold); - for (const corner of bboxCorners) { - const cid = this.lngLatToCellId(corner); - if (cid !== cellId) { - return [cellId, ...this.getAdjacent(cellId)]; - } - } - return [cellId]; - } - } - /** * @class DataProviderBase * @@ -79923,20 +77088,17 @@ void main() /** * Create a new data provider base instance. * - * @param {GeometryProviderBase} geometry - Geometry + * @param {IGeometryProvider} geometry - Geometry * provider instance. */ constructor(_geometry) { super(); this._geometry = _geometry; - if (!(this._geometry instanceof GeometryProviderBase)) { - throw new MapillaryError("The data provider requires a geometry provider base instance."); - } } /** * Get geometry property. * - * @returns {GeometryProviderBase} Geometry provider instance. + * @returns {IGeometryProvider} Geometry provider instance. */ get geometry() { return this._geometry; @@ -80067,6 +77229,135 @@ void main() } } + /** + * @class GeometryProviderBase + * + * @classdesc Base class to extend if implementing a geometry + * provider class. + * + * @example + * ```js + * class MyGeometryProvider extends GeometryProviderBase { + * ... + * } + * ``` + */ + class GeometryProviderBase { + /** + * Create a new geometry provider base instance. + */ + constructor() { } + /** + * Convert a geodetic bounding box to the the minimum set + * of cell ids containing the bounding box. + * + * @description The bounding box needs + * to be sufficiently small to be contained in an area with the size + * of maximally four tiles. Up to nine adjacent tiles may be returned. + * + * @param {LngLat} sw - South west corner of bounding box. + * @param {LngLat} ne - North east corner of bounding box. + * + * @returns {Array} Array of cell ids. + */ + bboxToCellIds(sw, ne) { + throw new MapillaryError("Not implemented"); + } + /** + * Get the cell ids of all adjacent cells. + * + * @description In the case of approximately rectangular cells + * this is typically the eight orthogonally and diagonally adjacent + * cells. + * + * @param {string} cellId - Id of cell. + * @returns {Array} Array of cell ids. No specific + * order is guaranteed. + */ + getAdjacent(cellId) { + throw new MapillaryError("Not implemented"); + } + /** + * Get the vertices of a cell. + * + * @description The vertices form an unclosed + * clockwise polygon in the 2D longitude, latitude + * space. No assumption on the position of the first + * vertex relative to the others can be made. + * + * @param {string} cellId - Id of cell. + * @returns {Array} Unclosed clockwise polygon. + */ + getVertices(cellId) { + throw new MapillaryError("Not implemented"); + } + /** + * Convert geodetic coordinates to a cell id. + * + * @param {LngLat} lngLat - Longitude, latitude to convert. + * @returns {string} Cell id for the longitude, latitude. + */ + lngLatToCellId(lngLat) { + throw new MapillaryError("Not implemented"); + } + /** @ignore */ + _approxBboxToCellIds(sw, ne) { + if (ne.lat <= sw.lat || ne.lng <= sw.lng) { + throw new MapillaryError("North east needs to be top right of south west"); + } + const centerLat = (sw.lat + ne.lat) / 2; + const centerLng = (sw.lng + ne.lng) / 2; + const enu = geodeticToEnu(ne.lng, ne.lat, 0, centerLng, centerLat, 0); + const threshold = Math.max(enu[0], enu[1]); + return this._lngLatToCellIds({ lat: centerLat, lng: centerLng }, threshold); + } + /** @ignore */ + _enuToGeodetic(point, reference) { + const [lng, lat] = enuToGeodetic(point[0], point[1], point[2], reference.lng, reference.lat, 0); + return { lat, lng }; + } + /** @ignore */ + _getLngLatBoundingBoxCorners(lngLat, threshold) { + return [ + [-threshold, threshold, 0], + [threshold, threshold, 0], + [threshold, -threshold, 0], + [-threshold, -threshold, 0], + ].map((point) => { + return this._enuToGeodetic(point, lngLat); + }); + } + /** + * Convert a geodetic square to cell ids. + * + * The square is specified as a longitude, latitude + * and a threshold from the position using Manhattan distance. + * + * @param {LngLat} lngLat - Longitude, latitude. + * @param {number} threshold - Threshold of the conversion in meters. + * + * @returns {Array} Array of cell ids reachable within + * the threshold. + * + * @ignore + */ + _lngLatToCellIds(lngLat, threshold) { + const cellId = this.lngLatToCellId(lngLat); + const bboxCorners = this._getLngLatBoundingBoxCorners(lngLat, threshold); + for (const corner of bboxCorners) { + const cid = this.lngLatToCellId(corner); + if (cid !== cellId) { + return [cellId, ...this.getAdjacent(cellId)]; + } + } + return [cellId]; + } + } + + var s2geometry = {exports: {}}; + + var long = {exports: {}}; + /* Copyright 2013 Daniel Wirtz Copyright 2009 The Closure Library Authors. All Rights Reserved. @@ -80084,7 +77375,7 @@ void main() limitations under the License. */ - var long = createCommonjsModule(function (module) { + (function (module) { /** * @license long.js (c) 2013 Daniel Wirtz * Released under the Apache License, Version 2.0 @@ -81274,9 +78565,9 @@ void main() return Long; }); - }); + }(long)); - var s2geometry = createCommonjsModule(function (module) { + (function (module) { /// S2 Geometry functions // the regional scoreboard is based on a level 6 S2 Cell // - https://docs.google.com/presentation/d/1Hl4KapfAENAOf4gv-pSngKwvS_jwNVHRPZTTDzXXn6Q/view?pli=1#slide=id.i22 @@ -81722,7 +79013,7 @@ void main() S2.POS_BITS = (2 * S2.MAX_LEVEL) + 1; // 61 (60 bits of data, 1 bit lsb marker) S2.facePosLevelToId = S2.S2Cell.facePosLevelToId = S2.fromFacePosLevel = function (faceN, posS, levelN) { - var Long = exports.dcodeIO && exports.dcodeIO.Long || long; + var Long = exports.dcodeIO && exports.dcodeIO.Long || long.exports; var faceB; var posB; var bin; @@ -81770,7 +79061,7 @@ void main() = S2.fromId = S2.fromCellId = S2.S2Cell.toHilbertQuadkey = S2.toHilbertQuadkey = function (idS) { - var Long = exports.dcodeIO && exports.dcodeIO.Long || long; + var Long = exports.dcodeIO && exports.dcodeIO.Long || long.exports; var bin = Long.fromString(idS, true, 10).toString(2); while (bin.length < (S2.FACE_BITS + S2.POS_BITS)) { @@ -81830,7 +79121,7 @@ void main() }; S2.stepKey = function (key, num) { - var Long = exports.dcodeIO && exports.dcodeIO.Long || long; + var Long = exports.dcodeIO && exports.dcodeIO.Long || long.exports; var parts = key.split('/'); var faceS = parts[0]; @@ -81869,7 +79160,7 @@ void main() }; })(module.exports ); - }); + }(s2geometry)); /** * @class S2GeometryProvider @@ -81900,7 +79191,7 @@ void main() } /** @inheritdoc */ getAdjacent(cellId) { - const k = s2geometry.S2.idToKey(cellId); + const k = s2geometry.exports.S2.idToKey(cellId); const position = k.split('/')[1]; const level = position.length; const [a0, a1, a2, a3] = this._getNeighbors(k, level); @@ -81926,12 +79217,12 @@ void main() adjacent.push(other); } } - return adjacent.map((a) => s2geometry.S2.keyToId(a)); + return adjacent.map((a) => s2geometry.exports.S2.keyToId(a)); } /** @inheritdoc */ getVertices(cellId) { - const key = s2geometry.S2.idToKey(cellId); - const cell = s2geometry.S2.S2Cell.FromHilbertQuadKey(key); + const key = s2geometry.exports.S2.idToKey(cellId); + const cell = s2geometry.exports.S2.S2Cell.FromHilbertQuadKey(key); return cell .getCornerLatLngs() .map((c) => { @@ -81943,13 +79234,13 @@ void main() return this._lngLatToId(lngLat, this._level); } _getNeighbors(s2key, level) { - const latlng = s2geometry.S2.keyToLatLng(s2key); - const neighbors = s2geometry.S2.latLngToNeighborKeys(latlng.lat, latlng.lng, level); + const latlng = s2geometry.exports.S2.keyToLatLng(s2key); + const neighbors = s2geometry.exports.S2.latLngToNeighborKeys(latlng.lat, latlng.lng, level); return neighbors; } _lngLatToId(lngLat, level) { - const s2key = s2geometry.S2.latLngToKey(lngLat.lat, lngLat.lng, level); - return s2geometry.S2.keyToId(s2key); + const s2key = s2geometry.exports.S2.latLngToKey(lngLat.lat, lngLat.lng, level); + return s2geometry.exports.S2.keyToId(s2key); } } @@ -82205,3667 +79496,4611 @@ void main() return result; }); } - setAccessToken(accessToken) { - this._accessToken = accessToken; + setAccessToken(accessToken) { + this._accessToken = accessToken; + } + _createHeaders() { + const headers = [ + { name: 'Accept', value: 'application/json' }, + { + name: 'Content-Type', + value: 'application/x-www-form-urlencoded', + }, + ]; + if (this._accessToken) { + headers.push({ + name: 'Authorization', + value: `OAuth ${this._accessToken}`, + }); + } + return headers; + } + _fetchGraphContract(body, url) { + const method = this._method; + const headers = this._createHeaders(); + const query = `${url}?${body}`; + return xhrFetch(query, method, "json", headers, null, null) + .catch((error) => { + const message = this._makeErrorMessage(error); + throw new MapillaryError(message); + }); + } + _makeErrorMessage(graphError) { + const error = graphError.error; + const message = error ? + `${error.code} (${error.type}, ${error.fbtrace_id}): ${error.message}` : + "Failed to fetch data"; + return message; + } + } + + /** + * @class Marker + * + * @classdesc Represents an abstract marker class that should be extended + * by marker implementations used in the marker component. + */ + class Marker { + constructor(id, lngLat) { + this._id = id; + this._lngLat = lngLat; + } + /** + * Get id. + * @returns {string} The id of the marker. + */ + get id() { + return this._id; + } + /** + * Get geometry. + * + * @ignore + */ + get geometry() { + return this._geometry; + } + /** + * Get lngLat. + * @returns {LngLat} The geographic coordinates of the marker. + */ + get lngLat() { + return this._lngLat; + } + /** @ignore */ + createGeometry(position) { + if (!!this._geometry) { + return; + } + this._createGeometry(position); + // update matrix world if raycasting occurs before first render + this._geometry.updateMatrixWorld(true); + } + /** @ignore */ + disposeGeometry() { + if (!this._geometry) { + return; + } + this._disposeGeometry(); + this._geometry = undefined; + } + /** @ignore */ + getInteractiveObjects() { + if (!this._geometry) { + return []; + } + return this._getInteractiveObjects(); + } + /** @ignore */ + lerpAltitude(alt, alpha) { + if (!this._geometry) { + return; + } + this._geometry.position.z = + (1 - alpha) * this._geometry.position.z + alpha * alt; + } + /** @ignore */ + updatePosition(position, lngLat) { + if (!!lngLat) { + this._lngLat.lat = lngLat.lat; + this._lngLat.lng = lngLat.lng; + } + if (!this._geometry) { + return; + } + this._geometry.position.fromArray(position); + this._geometry.updateMatrixWorld(true); + } + } + + /** + * @class CircleMarker + * + * @classdesc Non-interactive marker with a flat circle shape. The circle + * marker can not be configured to be interactive. + * + * Circle marker properties can not be updated after creation. + * + * To create and add one `CircleMarker` with default configuration + * and one with configuration use + * + * @example + * ```js + * var defaultMarker = new CircleMarker( + * "id-1", + * { lat: 0, lng: 0, }); + * + * var configuredMarker = new CircleMarker( + * "id-2", + * { lat: 0, lng: 0, }, + * { + * color: "#0ff", + * opacity: 0.3, + * radius: 0.7, + * }); + * + * markerComponent.add([defaultMarker, configuredMarker]); + * ``` + */ + class CircleMarker extends Marker { + constructor(id, lngLat, options) { + super(id, lngLat); + options = !!options ? options : {}; + this._color = options.color != null ? options.color : 0xffffff; + this._opacity = options.opacity != null ? options.opacity : 0.4; + this._radius = options.radius != null ? options.radius : 1; + } + _createGeometry(position) { + const circle = new Mesh(new CircleGeometry(this._radius, 16), new MeshBasicMaterial({ + color: this._color, + opacity: this._opacity, + transparent: true, + })); + circle.up.fromArray([0, 0, 1]); + circle.renderOrder = -1; + const group = new Object3D(); + group.add(circle); + group.position.fromArray(position); + this._geometry = group; + } + _disposeGeometry() { + for (let mesh of this._geometry.children) { + mesh.geometry.dispose(); + mesh.material.dispose(); + } + } + _getInteractiveObjects() { + return []; + } + } + + /** + * @class SimpleMarker + * + * @classdesc Interactive marker with ice cream shape. The sphere + * inside the ice cream can be configured to be interactive. + * + * Simple marker properties can not be updated after creation. + * + * To create and add one `SimpleMarker` with default configuration + * (non-interactive) and one interactive with configuration use + * + * @example + * ```js + * var defaultMarker = new SimpleMarker( + * "id-1", + * { lat: 0, lng: 0, }); + * + * var interactiveMarker = new SimpleMarker( + * "id-2", + * { lat: 0, lng: 0, }, + * { + * ballColor: "#00f", + * ballOpacity: 0.5, + * color: "#00f", + * interactive: true, + * opacity: 0.3, + * radius: 0.7, + * }); + * + * markerComponent.add([defaultMarker, interactiveMarker]); + * ``` + */ + class SimpleMarker extends Marker { + constructor(id, lngLat, options) { + super(id, lngLat); + options = !!options ? options : {}; + this._ballColor = options.ballColor != null ? options.ballColor : 0xff0000; + this._ballOpacity = options.ballOpacity != null ? options.ballOpacity : 0.8; + this._circleToRayAngle = 2; + this._color = options.color != null ? options.color : 0xff0000; + this._interactive = !!options.interactive; + this._opacity = options.opacity != null ? options.opacity : 0.4; + this._radius = options.radius != null ? options.radius : 1; + } + _createGeometry(position) { + const radius = this._radius; + const height = this._markerHeight(radius); + const markerMaterial = new MeshBasicMaterial({ + color: this._color, + opacity: this._opacity, + transparent: true, + depthWrite: false, + }); + const marker = new Mesh(this._createMarkerGeometry(radius, 8, 8), markerMaterial); + const interactive = new Mesh(new SphereGeometry(radius / 2, 8, 8), new MeshBasicMaterial({ + color: this._ballColor, + opacity: this._ballOpacity, + transparent: true, + })); + interactive.position.z = height; + interactive.renderOrder = 1; + const group = new Object3D(); + group.add(interactive); + group.add(marker); + group.position.fromArray(position); + this._geometry = group; + } + _disposeGeometry() { + for (const mesh of this._geometry.children) { + mesh.geometry.dispose(); + mesh.material.dispose(); + } + } + _getInteractiveObjects() { + return this._interactive ? [this._geometry.children[0]] : []; + } + _markerHeight(radius) { + const t = Math.tan(Math.PI - this._circleToRayAngle); + return radius * Math.sqrt(1 + t * t); + } + _createMarkerGeometry(radius, widthSegments, heightSegments) { + const height = this._markerHeight(radius); + const circleToRayAngle = this._circleToRayAngle; + const indexRows = []; + const positions = new Float32Array(3 * (widthSegments + 1) * (heightSegments + 1)); + let positionIndex = 0; + for (let y = 0; y <= heightSegments; ++y) { + const indexRow = []; + for (let x = 0; x <= widthSegments; ++x) { + const u = x / widthSegments * Math.PI * 2; + const v = y / heightSegments * Math.PI; + let r = radius; + if (v > circleToRayAngle) { + const t = Math.tan(v - circleToRayAngle); + r = radius * Math.sqrt(1 + t * t); + } + const arrayIndex = 3 * positionIndex; + const sinv = Math.sin(v); + positions[arrayIndex + 0] = r * Math.cos(u) * sinv; + positions[arrayIndex + 1] = r * Math.sin(u) * sinv; + positions[arrayIndex + 2] = r * Math.cos(v) + height; + indexRow.push(positionIndex++); + } + indexRows.push(indexRow); + } + const indices = new Uint16Array(6 * widthSegments * heightSegments); + let index = 0; + for (let y = 0; y < heightSegments; ++y) { + for (let x = 0; x < widthSegments; ++x) { + const pi1 = indexRows[y][x + 1]; + const pi2 = indexRows[y][x]; + const pi3 = indexRows[y + 1][x]; + const pi4 = indexRows[y + 1][x + 1]; + indices[index++] = pi1; + indices[index++] = pi2; + indices[index++] = pi4; + indices[index++] = pi2; + indices[index++] = pi3; + indices[index++] = pi4; + } + } + const geometry = new BufferGeometry(); + const positionAttribute = new BufferAttribute(positions, 3); + geometry.setAttribute("position", positionAttribute); + geometry.setIndex(new BufferAttribute(indices, 1)); + return geometry; + } + } + + /** + * @class Popup + * + * @classdesc Popup instance for rendering custom HTML content + * on top of images. Popups are based on 2D basic image coordinates + * (see the {@link Viewer} class documentation for more information about coordinate + * systems) and a certain popup is therefore only relevant to a single image. + * Popups related to a certain image should be removed when moving + * to another image. + * + * A popup must have both its content and its point or rect set to be + * rendered. Popup options can not be updated after creation but the + * basic point or rect as well as its content can be changed by calling + * the appropriate methods. + * + * To create and add one `Popup` with default configuration + * (tooltip visuals and automatic float) and one with specific options + * use + * + * @example + * ```js + * var defaultSpan = document.createElement('span'); + * defaultSpan.innerHTML = 'hello default'; + * + * var defaultPopup = new Popup(); + * defaultPopup.setDOMContent(defaultSpan); + * defaultPopup.setBasicPoint([0.3, 0.3]); + * + * var cleanSpan = document.createElement('span'); + * cleanSpan.innerHTML = 'hello clean'; + * + * var cleanPopup = new Popup({ + * clean: true, + * float: Alignment.Top, + * offset: 10, + * opacity: 0.7, + * }); + * + * cleanPopup.setDOMContent(cleanSpan); + * cleanPopup.setBasicPoint([0.6, 0.6]); + * + * popupComponent.add([defaultPopup, cleanPopup]); + * ``` + * + * @description Implementation of API methods and API documentation inspired + * by/used from https://github.com/mapbox/mapbox-gl-js/blob/v0.38.0/src/ui/popup.js + */ + class Popup { + constructor(options, viewportCoords, dom) { + this._options = {}; + options = !!options ? options : {}; + this._options.capturePointer = options.capturePointer === false ? + options.capturePointer : true; + this._options.clean = options.clean; + this._options.float = options.float; + this._options.offset = options.offset; + this._options.opacity = options.opacity; + this._options.position = options.position; + this._dom = !!dom ? dom : new DOM(); + this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); + this._notifyChanged$ = new Subject(); + } + /** + * @description Internal observable used by the component to + * render the popup when its position or content has changed. + * @ignore + */ + get changed$() { + return this._notifyChanged$; } - _createHeaders() { - const headers = [ - { name: 'Accept', value: 'application/json' }, - { - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', - }, - ]; - if (this._accessToken) { - headers.push({ - name: 'Authorization', - value: `OAuth ${this._accessToken}`, - }); + /** + * @description Internal method used by the component to + * remove all references to the popup. + * @ignore + */ + remove() { + if (this._content && this._content.parentNode) { + this._content.parentNode.removeChild(this._content); + } + if (this._container) { + this._container.parentNode.removeChild(this._container); + delete this._container; + } + if (this._parentContainer) { + delete this._parentContainer; } - return headers; } - _fetchGraphContract(body, url) { - const method = this._method; - const headers = this._createHeaders(); - const query = `${url}?${body}`; - return xhrFetch(query, method, "json", headers, null, null) - .catch((error) => { - const message = this._makeErrorMessage(error); - throw new MapillaryError(message); - }); + /** + * Sets a 2D basic image coordinates point to the popup's anchor, and + * moves the popup to it. + * + * @description Overwrites any previously set point or rect. + * + * @param {Array} basicPoint - Point in 2D basic image coordinates. + * + * @example + * ```js + * var popup = new Popup(); + * popup.setText('hello image'); + * popup.setBasicPoint([0.3, 0.3]); + * + * popupComponent.add([popup]); + * ``` + */ + setBasicPoint(basicPoint) { + this._point = basicPoint.slice(); + this._rect = null; + this._notifyChanged$.next(this); } - _makeErrorMessage(graphError) { - const error = graphError.error; - const message = error ? - `${error.code} (${error.type}, ${error.fbtrace_id}): ${error.message}` : - "Failed to fetch data"; - return message; + /** + * Sets a 2D basic image coordinates rect to the popup's anchor, and + * moves the popup to it. + * + * @description Overwrites any previously set point or rect. + * + * @param {Array} basicRect - Rect in 2D basic image + * coordinates ([topLeftX, topLeftY, bottomRightX, bottomRightY]) . + * + * @example + * ```js + * var popup = new Popup(); + * popup.setText('hello image'); + * popup.setBasicRect([0.3, 0.3, 0.5, 0.6]); + * + * popupComponent.add([popup]); + * ``` + */ + setBasicRect(basicRect) { + this._rect = basicRect.slice(); + this._point = null; + this._notifyChanged$.next(this); } - } - - /** - * @class Marker - * - * @classdesc Represents an abstract marker class that should be extended - * by marker implementations used in the marker component. - */ - class Marker { - constructor(id, lngLat) { - this._id = id; - this._lngLat = lngLat; + /** + * Sets the popup's content to the element provided as a DOM node. + * + * @param {Node} htmlNode - A DOM node to be used as content for the popup. + * + * @example + * ```js + * var div = document.createElement('div'); + * div.innerHTML = 'hello image'; + * + * var popup = new Popup(); + * popup.setDOMContent(div); + * popup.setBasicPoint([0.3, 0.3]); + * + * popupComponent.add([popup]); + * ``` + */ + setDOMContent(htmlNode) { + if (this._content && this._content.parentNode) { + this._content.parentNode.removeChild(this._content); + } + const className = "mapillary-popup-content" + + (this._options.clean === true ? "-clean" : "") + + (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); + this._content = this._dom.createElement("div", className, this._container); + this._content.appendChild(htmlNode); + this._notifyChanged$.next(this); } /** - * Get id. - * @returns {string} The id of the marker. + * Sets the popup's content to the HTML provided as a string. + * + * @description This method does not perform HTML filtering or sanitization, + * and must be used only with trusted content. Consider + * {@link Popup.setText} if the + * content is an untrusted text string. + * + * @param {string} html - A string representing HTML content for the popup. + * + * @example + * ```js + * var popup = new Popup(); + * popup.setHTML('
hello image
'); + * popup.setBasicPoint([0.3, 0.3]); + * + * popupComponent.add([popup]); + * ``` */ - get id() { - return this._id; + setHTML(html) { + const frag = this._dom.document.createDocumentFragment(); + const temp = this._dom.createElement("body"); + let child; + temp.innerHTML = html; + while (true) { + child = temp.firstChild; + if (!child) { + break; + } + frag.appendChild(child); + } + this.setDOMContent(frag); } /** - * Get geometry. + * Sets the popup's content to a string of text. * - * @ignore + * @description This function creates a Text node in the DOM, so it cannot insert raw HTML. + * Use this method for security against XSS if the popup content is user-provided. + * + * @param {string} text - Textual content for the popup. + * + * @example + * ```js + * var popup = new Popup(); + * popup.setText('hello image'); + * popup.setBasicPoint([0.3, 0.3]); + * + * popupComponent.add([popup]); + * ``` */ - get geometry() { - return this._geometry; + setText(text) { + this.setDOMContent(this._dom.document.createTextNode(text)); } /** - * Get lngLat. - * @returns {LngLat} The geographic coordinates of the marker. + * @description Internal method for attaching the popup to + * its parent container so that it is rendered in the DOM tree. + * @ignore */ - get lngLat() { - return this._lngLat; + setParentContainer(parentContainer) { + this._parentContainer = parentContainer; } - /** @ignore */ - createGeometry(position) { - if (!!this._geometry) { + /** + * @description Internal method for updating the rendered + * position of the popup called by the popup component. + * @ignore + */ + update(renderCamera, size, transform) { + if (!this._parentContainer || !this._content) { return; } - this._createGeometry(position); - // update matrix world if raycasting occurs before first render - this._geometry.updateMatrixWorld(true); - } - /** @ignore */ - disposeGeometry() { - if (!this._geometry) { + if (!this._point && !this._rect) { return; } - this._disposeGeometry(); - this._geometry = undefined; + if (!this._container) { + this._container = this._dom.createElement("div", "mapillary-popup", this._parentContainer); + const showTip = this._options.clean !== true && + this._options.float !== exports.Alignment.Center; + if (showTip) { + const tipClassName = "mapillary-popup-tip" + + (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); + this._tip = this._dom.createElement("div", tipClassName, this._container); + this._dom.createElement("div", "mapillary-popup-tip-inner", this._tip); + } + this._container.appendChild(this._content); + this._parentContainer.appendChild(this._container); + if (this._options.opacity != null) { + this._container.style.opacity = this._options.opacity.toString(); + } + } + let pointPixel = null; + let position = this._alignmentToPopupAligment(this._options.position); + let float = this._alignmentToPopupAligment(this._options.float); + const classList = this._container.classList; + if (this._point != null) { + pointPixel = + this._viewportCoords.basicToCanvasSafe(this._point[0], this._point[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); + } + else { + const alignments = ["center", "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"]; + let appliedPosition = null; + for (const alignment of alignments) { + if (classList.contains(`mapillary-popup-float-${alignment}`)) { + appliedPosition = alignment; + break; + } + } + [pointPixel, position] = this._rectToPixel(this._rect, position, appliedPosition, renderCamera, size, transform); + if (!float) { + float = position; + } + } + if (pointPixel == null) { + this._container.style.display = "none"; + return; + } + this._container.style.display = ""; + if (!float) { + const width = this._container.offsetWidth; + const height = this._container.offsetHeight; + const floats = this._pixelToFloats(pointPixel, size, width, height); + float = floats.length === 0 ? "top" : floats.join("-"); + } + const offset = this._normalizeOffset(this._options.offset); + pointPixel = [pointPixel[0] + offset[float][0], pointPixel[1] + offset[float][1]]; + pointPixel = [Math.round(pointPixel[0]), Math.round(pointPixel[1])]; + const floatTranslate = { + "bottom": "translate(-50%,0)", + "bottom-left": "translate(-100%,0)", + "bottom-right": "translate(0,0)", + "center": "translate(-50%,-50%)", + "left": "translate(-100%,-50%)", + "right": "translate(0,-50%)", + "top": "translate(-50%,-100%)", + "top-left": "translate(-100%,-100%)", + "top-right": "translate(0,-100%)", + }; + for (const key in floatTranslate) { + if (!floatTranslate.hasOwnProperty(key)) { + continue; + } + classList.remove(`mapillary-popup-float-${key}`); + } + classList.add(`mapillary-popup-float-${float}`); + this._container.style.transform = `${floatTranslate[float]} translate(${pointPixel[0]}px,${pointPixel[1]}px)`; + } + _rectToPixel(rect, position, appliedPosition, renderCamera, size, transform) { + if (!position) { + const width = this._container.offsetWidth; + const height = this._container.offsetHeight; + const floatOffsets = { + "bottom": [0, height / 2], + "bottom-left": [-width / 2, height / 2], + "bottom-right": [width / 2, height / 2], + "left": [-width / 2, 0], + "right": [width / 2, 0], + "top": [0, -height / 2], + "top-left": [-width / 2, -height / 2], + "top-right": [width / 2, -height / 2], + }; + const automaticPositions = ["top", "bottom", "left", "right"]; + let largestVisibleArea = [0, null, null]; + for (const automaticPosition of automaticPositions) { + const autoPointBasic = this._pointFromRectPosition(rect, automaticPosition); + const autoPointPixel = this._viewportCoords.basicToCanvasSafe(autoPointBasic[0], autoPointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); + if (autoPointPixel == null) { + continue; + } + const floatOffset = floatOffsets[automaticPosition]; + const offsetedPosition = [autoPointPixel[0] + floatOffset[0], autoPointPixel[1] + floatOffset[1]]; + const staticCoeff = appliedPosition != null && appliedPosition === automaticPosition ? 1 : 0.7; + const floats = this._pixelToFloats(offsetedPosition, size, width / staticCoeff, height / (2 * staticCoeff)); + if (floats.length === 0 && + autoPointPixel[0] > 0 && + autoPointPixel[0] < size.width && + autoPointPixel[1] > 0 && + autoPointPixel[1] < size.height) { + return [autoPointPixel, automaticPosition]; + } + const minX = Math.max(offsetedPosition[0] - width / 2, 0); + const maxX = Math.min(offsetedPosition[0] + width / 2, size.width); + const minY = Math.max(offsetedPosition[1] - height / 2, 0); + const maxY = Math.min(offsetedPosition[1] + height / 2, size.height); + const visibleX = Math.max(0, maxX - minX); + const visibleY = Math.max(0, maxY - minY); + const visibleArea = staticCoeff * visibleX * visibleY; + if (visibleArea > largestVisibleArea[0]) { + largestVisibleArea[0] = visibleArea; + largestVisibleArea[1] = autoPointPixel; + largestVisibleArea[2] = automaticPosition; + } + } + if (largestVisibleArea[0] > 0) { + return [largestVisibleArea[1], largestVisibleArea[2]]; + } + } + const pointBasic = this._pointFromRectPosition(rect, position); + const pointPixel = this._viewportCoords.basicToCanvasSafe(pointBasic[0], pointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); + return [pointPixel, position != null ? position : "top"]; + } + _alignmentToPopupAligment(float) { + switch (float) { + case exports.Alignment.Bottom: + return "bottom"; + case exports.Alignment.BottomLeft: + return "bottom-left"; + case exports.Alignment.BottomRight: + return "bottom-right"; + case exports.Alignment.Center: + return "center"; + case exports.Alignment.Left: + return "left"; + case exports.Alignment.Right: + return "right"; + case exports.Alignment.Top: + return "top"; + case exports.Alignment.TopLeft: + return "top-left"; + case exports.Alignment.TopRight: + return "top-right"; + default: + return null; + } + } + _normalizeOffset(offset) { + if (offset == null) { + return this._normalizeOffset(0); + } + if (typeof offset === "number") { + // input specifies a radius + const sideOffset = offset; + const sign = sideOffset >= 0 ? 1 : -1; + const cornerOffset = sign * Math.round(Math.sqrt(0.5 * Math.pow(sideOffset, 2))); + return { + "bottom": [0, sideOffset], + "bottom-left": [-cornerOffset, cornerOffset], + "bottom-right": [cornerOffset, cornerOffset], + "center": [0, 0], + "left": [-sideOffset, 0], + "right": [sideOffset, 0], + "top": [0, -sideOffset], + "top-left": [-cornerOffset, -cornerOffset], + "top-right": [cornerOffset, -cornerOffset], + }; + } + else { + // input specifes a value for each position + return { + "bottom": offset.bottom || [0, 0], + "bottom-left": offset.bottomLeft || [0, 0], + "bottom-right": offset.bottomRight || [0, 0], + "center": offset.center || [0, 0], + "left": offset.left || [0, 0], + "right": offset.right || [0, 0], + "top": offset.top || [0, 0], + "top-left": offset.topLeft || [0, 0], + "top-right": offset.topRight || [0, 0], + }; + } + } + _pixelToFloats(pointPixel, size, width, height) { + const floats = []; + if (pointPixel[1] < height) { + floats.push("bottom"); + } + else if (pointPixel[1] > size.height - height) { + floats.push("top"); + } + if (pointPixel[0] < width / 2) { + floats.push("right"); + } + else if (pointPixel[0] > size.width - width / 2) { + floats.push("left"); + } + return floats; + } + _pointFromRectPosition(rect, position) { + const x0 = rect[0]; + const x1 = rect[0] < rect[2] ? rect[2] : rect[2] + 1; + const y0 = rect[1]; + const y1 = rect[3]; + switch (position) { + case "bottom": + return [(x0 + x1) / 2, y1]; + case "bottom-left": + return [x0, y1]; + case "bottom-right": + return [x1, y1]; + case "center": + return [(x0 + x1) / 2, (y0 + y1) / 2]; + case "left": + return [x0, (y0 + y1) / 2]; + case "right": + return [x1, (y0 + y1) / 2]; + case "top": + return [(x0 + x1) / 2, y0]; + case "top-left": + return [x0, y0]; + case "top-right": + return [x1, y0]; + default: + return [(x0 + x1) / 2, y1]; + } + } + } + + function isBrowser() { + return (typeof window !== "undefined" && + typeof document !== "undefined"); + } + function isArraySupported() { + return !!(Array.prototype && + Array.prototype.concat && + Array.prototype.filter && + Array.prototype.includes && + Array.prototype.indexOf && + Array.prototype.join && + Array.prototype.map && + Array.prototype.push && + Array.prototype.pop && + Array.prototype.reverse && + Array.prototype.shift && + Array.prototype.slice && + Array.prototype.splice && + Array.prototype.sort && + Array.prototype.unshift); + } + function isBlobSupported() { + return ("Blob" in window && + "URL" in window); + } + function isFunctionSupported() { + return !!(Function.prototype && + Function.prototype.apply && + Function.prototype.bind); + } + function isJSONSupported() { + return ("JSON" in window && + "parse" in JSON && + "stringify" in JSON); + } + function isMapSupported() { + return "Map" in window; + } + function isObjectSupported() { + return !!(Object.assign && + Object.keys && + Object.values); + } + function isPromiseSupported() { + return !!("Promise" in window && + Promise.resolve && + Promise.reject && + Promise.prototype && + Promise.prototype.catch && + Promise.prototype.then); + } + function isSetSupported() { + return "Set" in window; + } + let isWebGLSupportedCache = undefined; + function isWebGLSupportedCached() { + if (isWebGLSupportedCache === undefined) { + isWebGLSupportedCache = isWebGLSupported(); } - /** @ignore */ - getInteractiveObjects() { - if (!this._geometry) { - return []; - } - return this._getInteractiveObjects(); + return isWebGLSupportedCache; + } + function isWebGLSupported() { + const attributes = { + alpha: false, + antialias: false, + depth: true, + failIfMajorPerformanceCaveat: false, + premultipliedAlpha: true, + preserveDrawingBuffer: false, + stencil: true, + }; + const canvas = document.createElement("canvas"); + const webGL2Context = canvas.getContext("webgl2", attributes); + if (!!webGL2Context) { + return true; } - /** @ignore */ - lerpAltitude(alt, alpha) { - if (!this._geometry) { - return; - } - this._geometry.position.z = - (1 - alpha) * this._geometry.position.z + alpha * alt; + const context = canvas.getContext("webgl", attributes) || + canvas + .getContext("experimental-webgl", attributes); + if (!context) { + return false; } - /** @ignore */ - updatePosition(position, lngLat) { - if (!!lngLat) { - this._lngLat.lat = lngLat.lat; - this._lngLat.lng = lngLat.lng; - } - if (!this._geometry) { - return; + const requiredExtensions = ["OES_standard_derivatives"]; + const supportedExtensions = context.getSupportedExtensions(); + for (const requiredExtension of requiredExtensions) { + if (supportedExtensions.indexOf(requiredExtension) === -1) { + return false; } - this._geometry.position.fromArray(position); - this._geometry.updateMatrixWorld(true); } + return true; } - /** - * @class CircleMarker - * - * @classdesc Non-interactive marker with a flat circle shape. The circle - * marker can not be configured to be interactive. - * - * Circle marker properties can not be updated after creation. - * - * To create and add one `CircleMarker` with default configuration - * and one with configuration use + * Test whether the current browser supports the full + * functionality of MapillaryJS. * - * @example - * ```js - * var defaultMarker = new CircleMarker( - * "id-1", - * { lat: 0, lng: 0, }); + * @description The full functionality includes WebGL rendering. * - * var configuredMarker = new CircleMarker( - * "id-2", - * { lat: 0, lng: 0, }, - * { - * color: "#0ff", - * opacity: 0.3, - * radius: 0.7, - * }); + * @return {boolean} * - * markerComponent.add([defaultMarker, configuredMarker]); - * ``` + * @example `var supported = isSupported();` */ - class CircleMarker extends Marker { - constructor(id, lngLat, options) { - super(id, lngLat); - options = !!options ? options : {}; - this._color = options.color != null ? options.color : 0xffffff; - this._opacity = options.opacity != null ? options.opacity : 0.4; - this._radius = options.radius != null ? options.radius : 1; - } - _createGeometry(position) { - const circle = new Mesh(new CircleGeometry(this._radius, 16), new MeshBasicMaterial({ - color: this._color, - opacity: this._opacity, - transparent: true, - })); - circle.up.fromArray([0, 0, 1]); - circle.renderOrder = -1; - const group = new Object3D(); - group.add(circle); - group.position.fromArray(position); - this._geometry = group; - } - _disposeGeometry() { - for (let mesh of this._geometry.children) { - mesh.geometry.dispose(); - mesh.material.dispose(); - } - } - _getInteractiveObjects() { - return []; - } + function isSupported() { + return isFallbackSupported() && + isWebGLSupportedCached(); } - /** - * @class SimpleMarker - * - * @classdesc Interactive marker with ice cream shape. The sphere - * inside the ice cream can be configured to be interactive. - * - * Simple marker properties can not be updated after creation. - * - * To create and add one `SimpleMarker` with default configuration - * (non-interactive) and one interactive with configuration use + * Test whether the current browser supports the fallback + * functionality of MapillaryJS. * - * @example - * ```js - * var defaultMarker = new SimpleMarker( - * "id-1", - * { lat: 0, lng: 0, }); + * @description The fallback functionality does not include WebGL + * rendering, only 2D canvas rendering. * - * var interactiveMarker = new SimpleMarker( - * "id-2", - * { lat: 0, lng: 0, }, - * { - * ballColor: "#00f", - * ballOpacity: 0.5, - * color: "#00f", - * interactive: true, - * opacity: 0.3, - * radius: 0.7, - * }); + * @return {boolean} * - * markerComponent.add([defaultMarker, interactiveMarker]); - * ``` + * @example `var fallbackSupported = isFallbackSupported();` */ - class SimpleMarker extends Marker { - constructor(id, lngLat, options) { - super(id, lngLat); - options = !!options ? options : {}; - this._ballColor = options.ballColor != null ? options.ballColor : 0xff0000; - this._ballOpacity = options.ballOpacity != null ? options.ballOpacity : 0.8; - this._circleToRayAngle = 2; - this._color = options.color != null ? options.color : 0xff0000; - this._interactive = !!options.interactive; - this._opacity = options.opacity != null ? options.opacity : 0.4; - this._radius = options.radius != null ? options.radius : 1; - } - _createGeometry(position) { - const radius = this._radius; - const height = this._markerHeight(radius); - const markerMaterial = new MeshBasicMaterial({ - color: this._color, - opacity: this._opacity, - transparent: true, - depthWrite: false, - }); - const marker = new Mesh(this._createMarkerGeometry(radius, 8, 8), markerMaterial); - const interactive = new Mesh(new SphereGeometry(radius / 2, 8, 8), new MeshBasicMaterial({ - color: this._ballColor, - opacity: this._ballOpacity, - transparent: true, - })); - interactive.position.z = height; - interactive.renderOrder = 1; - const group = new Object3D(); - group.add(interactive); - group.add(marker); - group.position.fromArray(position); - this._geometry = group; - } - _disposeGeometry() { - for (const mesh of this._geometry.children) { - mesh.geometry.dispose(); - mesh.material.dispose(); - } - } - _getInteractiveObjects() { - return this._interactive ? [this._geometry.children[0]] : []; - } - _markerHeight(radius) { - const t = Math.tan(Math.PI - this._circleToRayAngle); - return radius * Math.sqrt(1 + t * t); - } - _createMarkerGeometry(radius, widthSegments, heightSegments) { - const height = this._markerHeight(radius); - const circleToRayAngle = this._circleToRayAngle; - const indexRows = []; - const positions = new Float32Array(3 * (widthSegments + 1) * (heightSegments + 1)); - let positionIndex = 0; - for (let y = 0; y <= heightSegments; ++y) { - const indexRow = []; - for (let x = 0; x <= widthSegments; ++x) { - const u = x / widthSegments * Math.PI * 2; - const v = y / heightSegments * Math.PI; - let r = radius; - if (v > circleToRayAngle) { - const t = Math.tan(v - circleToRayAngle); - r = radius * Math.sqrt(1 + t * t); - } - const arrayIndex = 3 * positionIndex; - const sinv = Math.sin(v); - positions[arrayIndex + 0] = r * Math.cos(u) * sinv; - positions[arrayIndex + 1] = r * Math.sin(u) * sinv; - positions[arrayIndex + 2] = r * Math.cos(v) + height; - indexRow.push(positionIndex++); - } - indexRows.push(indexRow); - } - const indices = new Uint16Array(6 * widthSegments * heightSegments); - let index = 0; - for (let y = 0; y < heightSegments; ++y) { - for (let x = 0; x < widthSegments; ++x) { - const pi1 = indexRows[y][x + 1]; - const pi2 = indexRows[y][x]; - const pi3 = indexRows[y + 1][x]; - const pi4 = indexRows[y + 1][x + 1]; - indices[index++] = pi1; - indices[index++] = pi2; - indices[index++] = pi4; - indices[index++] = pi2; - indices[index++] = pi3; - indices[index++] = pi4; - } - } - const geometry = new BufferGeometry(); - const positionAttribute = new BufferAttribute(positions, 3); - geometry.setAttribute("position", positionAttribute); - geometry.setIndex(new BufferAttribute(indices, 1)); - return geometry; - } + function isFallbackSupported() { + return isBrowser() && + isArraySupported() && + isBlobSupported() && + isFunctionSupported() && + isJSONSupported() && + isMapSupported() && + isObjectSupported() && + isPromiseSupported() && + isSetSupported(); } /** - * @class Popup - * - * @classdesc Popup instance for rendering custom HTML content - * on top of images. Popups are based on 2D basic image coordinates - * (see the {@link Viewer} class documentation for more information about coordinate - * systems) and a certain popup is therefore only relevant to a single image. - * Popups related to a certain image should be removed when moving - * to another image. - * - * A popup must have both its content and its point or rect set to be - * rendered. Popup options can not be updated after creation but the - * basic point or rect as well as its content can be changed by calling - * the appropriate methods. - * - * To create and add one `Popup` with default configuration - * (tooltip visuals and automatic float) and one with specific options - * use - * - * @example - * ```js - * var defaultSpan = document.createElement('span'); - * defaultSpan.innerHTML = 'hello default'; - * - * var defaultPopup = new Popup(); - * defaultPopup.setDOMContent(defaultSpan); - * defaultPopup.setBasicPoint([0.3, 0.3]); - * - * var cleanSpan = document.createElement('span'); - * cleanSpan.innerHTML = 'hello clean'; - * - * var cleanPopup = new Popup({ - * clean: true, - * float: Alignment.Top, - * offset: 10, - * opacity: 0.7, - * }); - * - * cleanPopup.setDOMContent(cleanSpan); - * cleanPopup.setBasicPoint([0.6, 0.6]); + * Enumeration for camera controls. * - * popupComponent.add([defaultPopup, cleanPopup]); - * ``` + * @description Specifies different modes for how the + * camera is controlled through pointer, keyboard or + * other modes of input. * - * @description Implementation of API methods and API documentation inspired - * by/used from https://github.com/mapbox/mapbox-gl-js/blob/v0.38.0/src/ui/popup.js + * @enum {number} + * @readonly */ - class Popup { - constructor(options, viewportCoords, dom) { - this._options = {}; - options = !!options ? options : {}; - this._options.capturePointer = options.capturePointer === false ? - options.capturePointer : true; - this._options.clean = options.clean; - this._options.float = options.float; - this._options.offset = options.offset; - this._options.opacity = options.opacity; - this._options.position = options.position; - this._dom = !!dom ? dom : new DOM(); - this._viewportCoords = !!viewportCoords ? viewportCoords : new ViewportCoords(); - this._notifyChanged$ = new Subject(); - } + exports.CameraControls = void 0; + (function (CameraControls) { /** - * @description Internal observable used by the component to - * render the popup when its position or content has changed. - * @ignore + * Control the camera with custom logic by + * attaching a custom camera controls + * instance to the {@link Viewer}. */ - get changed$() { - return this._notifyChanged$; - } + CameraControls[CameraControls["Custom"] = 0] = "Custom"; /** - * @description Internal method used by the component to - * remove all references to the popup. - * @ignore + * Control the camera from a birds perspective + * to get an overview. */ - remove() { - if (this._content && this._content.parentNode) { - this._content.parentNode.removeChild(this._content); - } - if (this._container) { - this._container.parentNode.removeChild(this._container); - delete this._container; - } - if (this._parentContainer) { - delete this._parentContainer; - } - } + CameraControls[CameraControls["Earth"] = 1] = "Earth"; /** - * Sets a 2D basic image coordinates point to the popup's anchor, and - * moves the popup to it. - * - * @description Overwrites any previously set point or rect. - * - * @param {Array} basicPoint - Point in 2D basic image coordinates. - * - * @example - * ```js - * var popup = new Popup(); - * popup.setText('hello image'); - * popup.setBasicPoint([0.3, 0.3]); - * - * popupComponent.add([popup]); - * ``` + * Control the camera in a first person view + * from the street level perspective. */ - setBasicPoint(basicPoint) { - this._point = basicPoint.slice(); - this._rect = null; - this._notifyChanged$.next(this); - } + CameraControls[CameraControls["Street"] = 2] = "Street"; + })(exports.CameraControls || (exports.CameraControls = {})); + + /** + * Enumeration for render mode + * @enum {number} + * @readonly + * @description Modes for specifying how rendering is done + * in the viewer. All modes preserves the original aspect + * ratio of the images. + */ + exports.RenderMode = void 0; + (function (RenderMode) { /** - * Sets a 2D basic image coordinates rect to the popup's anchor, and - * moves the popup to it. - * - * @description Overwrites any previously set point or rect. - * - * @param {Array} basicRect - Rect in 2D basic image - * coordinates ([topLeftX, topLeftY, bottomRightX, bottomRightY]) . - * - * @example - * ```js - * var popup = new Popup(); - * popup.setText('hello image'); - * popup.setBasicRect([0.3, 0.3, 0.5, 0.6]); + * Displays all content within the viewer. * - * popupComponent.add([popup]); - * ``` + * @description Black bars shown on both + * sides of the content. Bars are shown + * either below and above or to the left + * and right of the content depending on + * the aspect ratio relation between the + * image and the viewer. */ - setBasicRect(basicRect) { - this._rect = basicRect.slice(); - this._point = null; - this._notifyChanged$.next(this); - } + RenderMode[RenderMode["Letterbox"] = 0] = "Letterbox"; /** - * Sets the popup's content to the element provided as a DOM node. - * - * @param {Node} htmlNode - A DOM node to be used as content for the popup. + * Fills the viewer by cropping content. * - * @example - * ```js - * var div = document.createElement('div'); - * div.innerHTML = 'hello image'; + * @description Cropping is done either + * in horizontal or vertical direction + * depending on the aspect ratio relation + * between the image and the viewer. + */ + RenderMode[RenderMode["Fill"] = 1] = "Fill"; + })(exports.RenderMode || (exports.RenderMode = {})); + + exports.RenderPass = void 0; + (function (RenderPass) { + /** + * Occurs after the background render pass. + */ + RenderPass[RenderPass["Opaque"] = 0] = "Opaque"; + })(exports.RenderPass || (exports.RenderPass = {})); + + /** + * Enumeration for transition mode + * @enum {number} + * @readonly + * @description Modes for specifying how transitions + * between images are performed. + */ + exports.TransitionMode = void 0; + (function (TransitionMode) { + /** + * Default transitions. * - * var popup = new Popup(); - * popup.setDOMContent(div); - * popup.setBasicPoint([0.3, 0.3]); + * @description The viewer dynamically determines + * whether transitions should be performed with or + * without motion and blending for each transition + * based on the underlying data. + */ + TransitionMode[TransitionMode["Default"] = 0] = "Default"; + /** + * Instantaneous transitions. * - * popupComponent.add([popup]); - * ``` + * @description All transitions are performed + * without motion or blending. */ - setDOMContent(htmlNode) { - if (this._content && this._content.parentNode) { - this._content.parentNode.removeChild(this._content); + TransitionMode[TransitionMode["Instantaneous"] = 1] = "Instantaneous"; + })(exports.TransitionMode || (exports.TransitionMode = {})); + + class ComponentController { + constructor(container, navigator, observer, key, options, componentService) { + this._container = container; + this._observer = observer; + this._navigator = navigator; + this._options = options != null ? options : {}; + this._key = key; + this._navigable = key == null; + this._componentService = !!componentService ? + componentService : + new ComponentService(this._container, this._navigator); + this._coverComponent = this._componentService.getCover(); + this._initializeComponents(); + if (key) { + this._initilizeCoverComponent(); + this._subscribeCoverComponent(); } - const className = "mapillary-popup-content" + - (this._options.clean === true ? "-clean" : "") + - (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); - this._content = this._dom.createElement("div", className, this._container); - this._content.appendChild(htmlNode); - this._notifyChanged$.next(this); + else { + this._navigator.movedToId$.pipe(first((k) => { + return k != null; + })) + .subscribe((k) => { + this._key = k; + this._componentService.deactivateCover(); + this._coverComponent.configure({ + id: this._key, + state: CoverState.Hidden, + }); + this._subscribeCoverComponent(); + this._navigator.stateService.start(); + this._navigator.cacheService.start(); + this._navigator.panService.start(); + this._observer.startEmit(); + }); + } + } + get navigable() { + return this._navigable; + } + get(name) { + return this._componentService.get(name); + } + activate(name) { + this._componentService.activate(name); } - /** - * Sets the popup's content to the HTML provided as a string. - * - * @description This method does not perform HTML filtering or sanitization, - * and must be used only with trusted content. Consider - * {@link Popup.setText} if the - * content is an untrusted text string. - * - * @param {string} html - A string representing HTML content for the popup. - * - * @example - * ```js - * var popup = new Popup(); - * popup.setHTML('
hello image
'); - * popup.setBasicPoint([0.3, 0.3]); - * - * popupComponent.add([popup]); - * ``` - */ - setHTML(html) { - const frag = this._dom.document.createDocumentFragment(); - const temp = this._dom.createElement("body"); - let child; - temp.innerHTML = html; - while (true) { - child = temp.firstChild; - if (!child) { - break; - } - frag.appendChild(child); + activateCover() { + this._coverComponent.configure({ state: CoverState.Visible }); + } + deactivate(name) { + this._componentService.deactivate(name); + } + deactivateCover() { + this._coverComponent.configure({ state: CoverState.Loading }); + } + remove() { + this._componentService.remove(); + if (this._configurationSubscription != null) { + this._configurationSubscription.unsubscribe(); } - this.setDOMContent(frag); } - /** - * Sets the popup's content to a string of text. - * - * @description This function creates a Text node in the DOM, so it cannot insert raw HTML. - * Use this method for security against XSS if the popup content is user-provided. - * - * @param {string} text - Textual content for the popup. - * - * @example - * ```js - * var popup = new Popup(); - * popup.setText('hello image'); - * popup.setBasicPoint([0.3, 0.3]); - * - * popupComponent.add([popup]); - * ``` - */ - setText(text) { - this.setDOMContent(this._dom.document.createTextNode(text)); + _initializeComponents() { + var _a, _b; + const options = this._options; + this._uFalse((_a = options.fallback) === null || _a === void 0 ? void 0 : _a.image, "imagefallback"); + this._uFalse((_b = options.fallback) === null || _b === void 0 ? void 0 : _b.navigation, "navigationfallback"); + this._uFalse(options.marker, "marker"); + this._uFalse(options.popup, "popup"); + this._uFalse(options.slider, "slider"); + this._uFalse(options.spatial, "spatial"); + this._uFalse(options.tag, "tag"); + this._uTrue(options.attribution, "attribution"); + this._uTrue(options.bearing, "bearing"); + this._uTrue(options.cache, "cache"); + this._uTrue(options.direction, "direction"); + this._uTrue(options.image, "image"); + this._uTrue(options.keyboard, "keyboard"); + this._uTrue(options.pointer, "pointer"); + this._uTrue(options.sequence, "sequence"); + this._uTrue(options.zoom, "zoom"); } - /** - * @description Internal method for attaching the popup to - * its parent container so that it is rendered in the DOM tree. - * @ignore - */ - setParentContainer(parentContainer) { - this._parentContainer = parentContainer; + _initilizeCoverComponent() { + let options = this._options; + this._coverComponent.configure({ id: this._key }); + if (options.cover === undefined || options.cover) { + this.activateCover(); + } + else { + this.deactivateCover(); + } } - /** - * @description Internal method for updating the rendered - * position of the popup called by the popup component. - * @ignore - */ - update(renderCamera, size, transform) { - if (!this._parentContainer || !this._content) { + _setNavigable(navigable) { + if (this._navigable === navigable) { return; } - if (!this._point && !this._rect) { + this._navigable = navigable; + this._observer.navigable$.next(navigable); + } + _subscribeCoverComponent() { + this._configurationSubscription = + this._coverComponent.configuration$.pipe(distinctUntilChanged(undefined, (c) => { + return c.state; + })) + .subscribe((conf) => { + if (conf.state === CoverState.Loading) { + this._navigator.stateService.currentId$.pipe(first(), switchMap((key) => { + const keyChanged = key == null || key !== conf.id; + if (keyChanged) { + this._setNavigable(false); + } + return keyChanged ? + this._navigator.moveTo$(conf.id) : + this._navigator.stateService.currentImage$.pipe(first()); + })) + .subscribe(() => { + this._navigator.stateService.start(); + this._navigator.cacheService.start(); + this._navigator.panService.start(); + this._observer.startEmit(); + this._coverComponent.configure({ state: CoverState.Hidden }); + this._componentService.deactivateCover(); + this._setNavigable(true); + }, (error) => { + console.error("Failed to deactivate cover.", error); + this._coverComponent.configure({ state: CoverState.Visible }); + }); + } + else if (conf.state === CoverState.Visible) { + this._observer.stopEmit(); + this._navigator.stateService.stop(); + this._navigator.cacheService.stop(); + this._navigator.playService.stop(); + this._navigator.panService.stop(); + this._componentService.activateCover(); + this._setNavigable(conf.id == null); + } + }); + } + _uFalse(option, name) { + if (option === undefined) { + this._componentService.deactivate(name); return; } - if (!this._container) { - this._container = this._dom.createElement("div", "mapillary-popup", this._parentContainer); - const showTip = this._options.clean !== true && - this._options.float !== exports.Alignment.Center; - if (showTip) { - const tipClassName = "mapillary-popup-tip" + - (this._options.capturePointer === true ? " mapillary-popup-capture-pointer" : ""); - this._tip = this._dom.createElement("div", tipClassName, this._container); - this._dom.createElement("div", "mapillary-popup-tip-inner", this._tip); + if (typeof option === "boolean") { + if (option) { + this._componentService.activate(name); } - this._container.appendChild(this._content); - this._parentContainer.appendChild(this._container); - if (this._options.opacity != null) { - this._container.style.opacity = this._options.opacity.toString(); + else { + this._componentService.deactivate(name); } + return; } - let pointPixel = null; - let position = this._alignmentToPopupAligment(this._options.position); - let float = this._alignmentToPopupAligment(this._options.float); - const classList = this._container.classList; - if (this._point != null) { - pointPixel = - this._viewportCoords.basicToCanvasSafe(this._point[0], this._point[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); + this._componentService.configure(name, option); + this._componentService.activate(name); + } + _uTrue(option, name) { + if (option === undefined) { + this._componentService.activate(name); + return; } - else { - const alignments = ["center", "top", "bottom", "left", "right", "top-left", "top-right", "bottom-left", "bottom-right"]; - let appliedPosition = null; - for (const alignment of alignments) { - if (classList.contains(`mapillary-popup-float-${alignment}`)) { - appliedPosition = alignment; - break; - } + if (typeof option === "boolean") { + if (option) { + this._componentService.activate(name); } - [pointPixel, position] = this._rectToPixel(this._rect, position, appliedPosition, renderCamera, size, transform); - if (!float) { - float = position; + else { + this._componentService.deactivate(name); } - } - if (pointPixel == null) { - this._container.style.display = "none"; return; } - this._container.style.display = ""; - if (!float) { - const width = this._container.offsetWidth; - const height = this._container.offsetHeight; - const floats = this._pixelToFloats(pointPixel, size, width, height); - float = floats.length === 0 ? "top" : floats.join("-"); - } - const offset = this._normalizeOffset(this._options.offset); - pointPixel = [pointPixel[0] + offset[float][0], pointPixel[1] + offset[float][1]]; - pointPixel = [Math.round(pointPixel[0]), Math.round(pointPixel[1])]; - const floatTranslate = { - "bottom": "translate(-50%,0)", - "bottom-left": "translate(-100%,0)", - "bottom-right": "translate(0,0)", - "center": "translate(-50%,-50%)", - "left": "translate(-100%,-50%)", - "right": "translate(0,-50%)", - "top": "translate(-50%,-100%)", - "top-left": "translate(-100%,-100%)", - "top-right": "translate(0,-100%)", - }; - for (const key in floatTranslate) { - if (!floatTranslate.hasOwnProperty(key)) { - continue; - } - classList.remove(`mapillary-popup-float-${key}`); - } - classList.add(`mapillary-popup-float-${float}`); - this._container.style.transform = `${floatTranslate[float]} translate(${pointPixel[0]}px,${pointPixel[1]}px)`; + this._componentService.configure(name, option); + this._componentService.activate(name); } - _rectToPixel(rect, position, appliedPosition, renderCamera, size, transform) { - if (!position) { - const width = this._container.offsetWidth; - const height = this._container.offsetHeight; - const floatOffsets = { - "bottom": [0, height / 2], - "bottom-left": [-width / 2, height / 2], - "bottom-right": [width / 2, height / 2], - "left": [-width / 2, 0], - "right": [width / 2, 0], - "top": [0, -height / 2], - "top-left": [-width / 2, -height / 2], - "top-right": [width / 2, -height / 2], + } + + class DOMRenderer { + constructor(element, renderService, currentFrame$) { + this._adaptiveOperation$ = new Subject(); + this._render$ = new Subject(); + this._renderAdaptive$ = new Subject(); + this._subscriptions = new SubscriptionHolder(); + this._renderService = renderService; + this._currentFrame$ = currentFrame$; + const subs = this._subscriptions; + const rootNode = virtualDom.create(virtualDom.h("div.mapillary-dom-renderer", [])); + element.appendChild(rootNode); + this._offset$ = this._adaptiveOperation$.pipe(scan((adaptive, operation) => { + return operation(adaptive); + }, { + elementHeight: element.offsetHeight, + elementWidth: element.offsetWidth, + imageAspect: 0, + renderMode: exports.RenderMode.Fill, + }), filter((adaptive) => { + return adaptive.imageAspect > 0 && adaptive.elementWidth > 0 && adaptive.elementHeight > 0; + }), map((adaptive) => { + const elementAspect = adaptive.elementWidth / adaptive.elementHeight; + const ratio = adaptive.imageAspect / elementAspect; + let verticalOffset = 0; + let horizontalOffset = 0; + if (adaptive.renderMode === exports.RenderMode.Letterbox) { + if (adaptive.imageAspect > elementAspect) { + verticalOffset = adaptive.elementHeight * (1 - 1 / ratio) / 2; + } + else { + horizontalOffset = adaptive.elementWidth * (1 - ratio) / 2; + } + } + else { + if (adaptive.imageAspect > elementAspect) { + horizontalOffset = -adaptive.elementWidth * (ratio - 1) / 2; + } + else { + verticalOffset = -adaptive.elementHeight * (1 / ratio - 1) / 2; + } + } + return { + bottom: verticalOffset, + left: horizontalOffset, + right: horizontalOffset, + top: verticalOffset, }; - const automaticPositions = ["top", "bottom", "left", "right"]; - let largestVisibleArea = [0, null, null]; - for (const automaticPosition of automaticPositions) { - const autoPointBasic = this._pointFromRectPosition(rect, automaticPosition); - const autoPointPixel = this._viewportCoords.basicToCanvasSafe(autoPointBasic[0], autoPointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); - if (autoPointPixel == null) { + })); + const imageAspectSubscription = this._currentFrame$.pipe(filter((frame) => { + return frame.state.currentImage != null; + }), distinctUntilChanged((k1, k2) => { + return k1 === k2; + }, (frame) => { + return frame.state.currentImage.id; + }), map((frame) => { + return frame.state.currentTransform.basicAspect; + }), map((aspect) => { + return (adaptive) => { + adaptive.imageAspect = aspect; + return adaptive; + }; + })) + .subscribe(this._adaptiveOperation$); + const renderAdaptiveSubscription = combineLatest(this._renderAdaptive$.pipe(scan((vNodeHashes, vNodeHash) => { + if (vNodeHash.vNode == null) { + delete vNodeHashes[vNodeHash.name]; + } + else { + vNodeHashes[vNodeHash.name] = vNodeHash.vNode; + } + return vNodeHashes; + }, {})), this._offset$).pipe(map((vo) => { + const vNodes = []; + const hashes = vo[0]; + for (const name in hashes) { + if (!hashes.hasOwnProperty(name)) { continue; } - const floatOffset = floatOffsets[automaticPosition]; - const offsetedPosition = [autoPointPixel[0] + floatOffset[0], autoPointPixel[1] + floatOffset[1]]; - const staticCoeff = appliedPosition != null && appliedPosition === automaticPosition ? 1 : 0.7; - const floats = this._pixelToFloats(offsetedPosition, size, width / staticCoeff, height / (2 * staticCoeff)); - if (floats.length === 0 && - autoPointPixel[0] > 0 && - autoPointPixel[0] < size.width && - autoPointPixel[1] > 0 && - autoPointPixel[1] < size.height) { - return [autoPointPixel, automaticPosition]; + vNodes.push(hashes[name]); + } + const offset = vo[1]; + const properties = { + style: { + bottom: offset.bottom + "px", + left: offset.left + "px", + "pointer-events": "none", + position: "absolute", + right: offset.right + "px", + top: offset.top + "px", + }, + }; + return { + name: "mapillary-dom-adaptive-renderer", + vNode: virtualDom.h("div.mapillary-dom-adaptive-renderer", properties, vNodes), + }; + })) + .subscribe(this._render$); + this._vNode$ = this._render$.pipe(scan((vNodeHashes, vNodeHash) => { + if (vNodeHash.vNode == null) { + delete vNodeHashes[vNodeHash.name]; + } + else { + vNodeHashes[vNodeHash.name] = vNodeHash.vNode; + } + return vNodeHashes; + }, {}), map((hashes) => { + const vNodes = []; + for (const name in hashes) { + if (!hashes.hasOwnProperty(name)) { + continue; } - const minX = Math.max(offsetedPosition[0] - width / 2, 0); - const maxX = Math.min(offsetedPosition[0] + width / 2, size.width); - const minY = Math.max(offsetedPosition[1] - height / 2, 0); - const maxY = Math.min(offsetedPosition[1] + height / 2, size.height); - const visibleX = Math.max(0, maxX - minX); - const visibleY = Math.max(0, maxY - minY); - const visibleArea = staticCoeff * visibleX * visibleY; - if (visibleArea > largestVisibleArea[0]) { - largestVisibleArea[0] = visibleArea; - largestVisibleArea[1] = autoPointPixel; - largestVisibleArea[2] = automaticPosition; + vNodes.push(hashes[name]); + } + return virtualDom.h("div.mapillary-dom-renderer", vNodes); + })); + this._vPatch$ = this._vNode$.pipe(scan((nodePatch, vNode) => { + nodePatch.vpatch = virtualDom.diff(nodePatch.vNode, vNode); + nodePatch.vNode = vNode; + return nodePatch; + }, { vNode: virtualDom.h("div.mapillary-dom-renderer", []), vpatch: null }), pluck("vpatch")); + this._element$ = this._vPatch$.pipe(scan((oldElement, vPatch) => { + return virtualDom.patch(oldElement, vPatch); + }, rootNode), publishReplay(1), refCount()); + subs.push(imageAspectSubscription); + subs.push(renderAdaptiveSubscription); + subs.push(this._element$.subscribe(() => { })); + subs.push(this._renderService.size$.pipe(map((size) => { + return (adaptive) => { + adaptive.elementWidth = size.width; + adaptive.elementHeight = size.height; + return adaptive; + }; + })) + .subscribe(this._adaptiveOperation$)); + subs.push(this._renderService.renderMode$.pipe(map((renderMode) => { + return (adaptive) => { + adaptive.renderMode = renderMode; + return adaptive; + }; + })) + .subscribe(this._adaptiveOperation$)); + } + get element$() { + return this._element$; + } + get render$() { + return this._render$; + } + get renderAdaptive$() { + return this._renderAdaptive$; + } + clear(name) { + this._renderAdaptive$.next({ name: name, vNode: null }); + this._render$.next({ name: name, vNode: null }); + } + remove() { + this._subscriptions.unsubscribe(); + } + } + + class GLRenderer { + constructor(canvas, canvasContainer, renderService) { + this._renderFrame$ = new Subject(); + this._renderCameraOperation$ = new Subject(); + this._render$ = new Subject(); + this._clear$ = new Subject(); + this._renderOperation$ = new Subject(); + this._rendererOperation$ = new Subject(); + this._eraserOperation$ = new Subject(); + this._triggerOperation$ = new Subject(); + this._subscriptions = new SubscriptionHolder(); + this._opaqueRender$ = new Subject(); + this._renderService = renderService; + const subs = this._subscriptions; + this._renderer$ = this._rendererOperation$.pipe(scan((renderer, operation) => { + return operation(renderer); + }, { needsRender: false, renderer: null }), filter((renderer) => { + return !!renderer.renderer; + })); + this._renderCollection$ = this._renderOperation$.pipe(scan((hashes, operation) => { + return operation(hashes); + }, {}), share()); + this._renderCamera$ = this._renderCameraOperation$.pipe(scan((rc, operation) => { + return operation(rc); + }, { frameId: -1, needsRender: false, perspective: null })); + this._eraser$ = this._eraserOperation$.pipe(startWith((eraser) => { + return eraser; + }), scan((eraser, operation) => { + return operation(eraser); + }, { needsRender: false })); + const trigger$ = this._triggerOperation$.pipe(startWith((trigger) => { + return trigger; + }), scan((trigger, operation) => { + return operation(trigger); + }, { needsRender: false })); + const clearColor = new Color(0x0F0F0F); + const renderSubscription = combineLatest(this._renderer$, this._renderCollection$, this._renderCamera$, this._eraser$, trigger$).pipe(map(([renderer, hashes, rc, eraser, trigger]) => { + const renders = Object.keys(hashes) + .map((key) => { + return hashes[key]; + }); + return { camera: rc, eraser: eraser, trigger: trigger, renderer: renderer, renders: renders }; + }), filter((co) => { + let needsRender = co.renderer.needsRender || + co.camera.needsRender || + co.eraser.needsRender || + co.trigger.needsRender; + const frameId = co.camera.frameId; + for (const render of co.renders) { + if (render.frameId !== frameId) { + return false; + } + needsRender = needsRender || render.needsRender; + } + return needsRender; + }), distinctUntilChanged((n1, n2) => { + return n1 === n2; + }, (co) => { + return co.eraser.needsRender || + co.trigger.needsRender ? -co.camera.frameId : co.camera.frameId; + })) + .subscribe((co) => { + co.renderer.needsRender = false; + co.camera.needsRender = false; + co.eraser.needsRender = false; + co.trigger.needsRender = false; + const perspectiveCamera = co.camera.perspective; + const backgroundRenders = []; + const opaqueRenders = []; + for (const render of co.renders) { + if (render.pass === RenderPass.Background) { + backgroundRenders.push(render.render); + } + else if (render.pass === RenderPass.Opaque) { + opaqueRenders.push(render.render); } } - if (largestVisibleArea[0] > 0) { - return [largestVisibleArea[1], largestVisibleArea[2]]; + const renderer = co.renderer.renderer; + renderer.resetState(); + renderer.setClearColor(clearColor, 1.0); + renderer.clear(); + for (const renderBackground of backgroundRenders) { + renderBackground(perspectiveCamera, renderer); } - } - const pointBasic = this._pointFromRectPosition(rect, position); - const pointPixel = this._viewportCoords.basicToCanvasSafe(pointBasic[0], pointBasic[1], { offsetHeight: size.height, offsetWidth: size.width }, transform, renderCamera.perspective); - return [pointPixel, position != null ? position : "top"]; - } - _alignmentToPopupAligment(float) { - switch (float) { - case exports.Alignment.Bottom: - return "bottom"; - case exports.Alignment.BottomLeft: - return "bottom-left"; - case exports.Alignment.BottomRight: - return "bottom-right"; - case exports.Alignment.Center: - return "center"; - case exports.Alignment.Left: - return "left"; - case exports.Alignment.Right: - return "right"; - case exports.Alignment.Top: - return "top"; - case exports.Alignment.TopLeft: - return "top-left"; - case exports.Alignment.TopRight: - return "top-right"; - default: - return null; - } - } - _normalizeOffset(offset) { - if (offset == null) { - return this._normalizeOffset(0); - } - if (typeof offset === "number") { - // input specifies a radius - const sideOffset = offset; - const sign = sideOffset >= 0 ? 1 : -1; - const cornerOffset = sign * Math.round(Math.sqrt(0.5 * Math.pow(sideOffset, 2))); - return { - "bottom": [0, sideOffset], - "bottom-left": [-cornerOffset, cornerOffset], - "bottom-right": [cornerOffset, cornerOffset], - "center": [0, 0], - "left": [-sideOffset, 0], - "right": [sideOffset, 0], - "top": [0, -sideOffset], - "top-left": [-cornerOffset, -cornerOffset], - "top-right": [cornerOffset, -cornerOffset], + renderer.clearDepth(); + for (const renderOpaque of opaqueRenders) { + renderOpaque(perspectiveCamera, renderer); + } + renderer.resetState(); + this._opaqueRender$.next(); + }); + subs.push(renderSubscription); + subs.push(this._renderFrame$.pipe(map((rc) => { + return (irc) => { + irc.frameId = rc.frameId; + irc.perspective = rc.perspective; + if (rc.changed === true) { + irc.needsRender = true; + } + return irc; }; - } - else { - // input specifes a value for each position - return { - "bottom": offset.bottom || [0, 0], - "bottom-left": offset.bottomLeft || [0, 0], - "bottom-right": offset.bottomRight || [0, 0], - "center": offset.center || [0, 0], - "left": offset.left || [0, 0], - "right": offset.right || [0, 0], - "top": offset.top || [0, 0], - "top-left": offset.topLeft || [0, 0], - "top-right": offset.topRight || [0, 0], + })) + .subscribe(this._renderCameraOperation$)); + this._renderFrameSubscribe(); + const renderHash$ = this._render$.pipe(map((hash) => { + return (hashes) => { + hashes[hash.name] = hash.renderer; + return hashes; }; - } - } - _pixelToFloats(pointPixel, size, width, height) { - const floats = []; - if (pointPixel[1] < height) { - floats.push("bottom"); - } - else if (pointPixel[1] > size.height - height) { - floats.push("top"); - } - if (pointPixel[0] < width / 2) { - floats.push("right"); - } - else if (pointPixel[0] > size.width - width / 2) { - floats.push("left"); - } - return floats; + })); + const clearHash$ = this._clear$.pipe(map((name) => { + return (hashes) => { + delete hashes[name]; + return hashes; + }; + })); + subs.push(merge(renderHash$, clearHash$) + .subscribe(this._renderOperation$)); + this._webGLRenderer$ = this._render$.pipe(first(), map(() => { + canvasContainer.appendChild(canvas); + const element = renderService.element; + const webGLRenderer = new WebGLRenderer({ canvas: canvas }); + webGLRenderer.setPixelRatio(window.devicePixelRatio); + webGLRenderer.setSize(element.offsetWidth, element.offsetHeight); + webGLRenderer.autoClear = false; + return webGLRenderer; + }), publishReplay(1), refCount()); + subs.push(this._webGLRenderer$ + .subscribe(() => { })); + const createRenderer$ = this._webGLRenderer$.pipe(first(), map((webGLRenderer) => { + return (renderer) => { + renderer.needsRender = true; + renderer.renderer = webGLRenderer; + return renderer; + }; + })); + const resizeRenderer$ = this._renderService.size$.pipe(map((size) => { + return (renderer) => { + if (renderer.renderer == null) { + return renderer; + } + renderer.renderer.setSize(size.width, size.height); + renderer.needsRender = true; + return renderer; + }; + })); + const clearRenderer$ = this._clear$.pipe(map(() => { + return (renderer) => { + if (renderer.renderer == null) { + return renderer; + } + renderer.needsRender = true; + return renderer; + }; + })); + subs.push(merge(createRenderer$, resizeRenderer$, clearRenderer$) + .subscribe(this._rendererOperation$)); + const renderCollectionEmpty$ = this._renderCollection$.pipe(filter((hashes) => { + return Object.keys(hashes).length === 0; + }), share()); + subs.push(renderCollectionEmpty$ + .subscribe(() => { + if (this._renderFrameSubscription == null) { + return; + } + this._renderFrameSubscription.unsubscribe(); + this._renderFrameSubscription = null; + this._renderFrameSubscribe(); + })); + subs.push(renderCollectionEmpty$.pipe(map(() => { + return (eraser) => { + eraser.needsRender = true; + return eraser; + }; + })) + .subscribe(this._eraserOperation$)); } - _pointFromRectPosition(rect, position) { - const x0 = rect[0]; - const x1 = rect[0] < rect[2] ? rect[2] : rect[2] + 1; - const y0 = rect[1]; - const y1 = rect[3]; - switch (position) { - case "bottom": - return [(x0 + x1) / 2, y1]; - case "bottom-left": - return [x0, y1]; - case "bottom-right": - return [x1, y1]; - case "center": - return [(x0 + x1) / 2, (y0 + y1) / 2]; - case "left": - return [x0, (y0 + y1) / 2]; - case "right": - return [x1, (y0 + y1) / 2]; - case "top": - return [(x0 + x1) / 2, y0]; - case "top-left": - return [x0, y0]; - case "top-right": - return [x1, y0]; - default: - return [(x0 + x1) / 2, y1]; - } + get render$() { + return this._render$; } - } - - function isBrowser() { - return (typeof window !== "undefined" && - typeof document !== "undefined"); - } - function isArraySupported() { - return !!(Array.prototype && - Array.prototype.concat && - Array.prototype.filter && - Array.prototype.includes && - Array.prototype.indexOf && - Array.prototype.join && - Array.prototype.map && - Array.prototype.push && - Array.prototype.pop && - Array.prototype.reverse && - Array.prototype.shift && - Array.prototype.slice && - Array.prototype.splice && - Array.prototype.sort && - Array.prototype.unshift); - } - function isBlobSupported() { - return ("Blob" in window && - "URL" in window); - } - function isFunctionSupported() { - return !!(Function.prototype && - Function.prototype.apply && - Function.prototype.bind); - } - function isJSONSupported() { - return ("JSON" in window && - "parse" in JSON && - "stringify" in JSON); - } - function isMapSupported() { - return "Map" in window; - } - function isObjectSupported() { - return !!(Object.assign && - Object.keys && - Object.values); - } - function isPromiseSupported() { - return !!("Promise" in window && - Promise.resolve && - Promise.reject && - Promise.prototype && - Promise.prototype.catch && - Promise.prototype.then); - } - function isSetSupported() { - return "Set" in window; - } - let isWebGLSupportedCache = undefined; - function isWebGLSupportedCached() { - if (isWebGLSupportedCache === undefined) { - isWebGLSupportedCache = isWebGLSupported(); + get opaqueRender$() { + return this._opaqueRender$; } - return isWebGLSupportedCache; - } - function isWebGLSupported() { - const attributes = { - alpha: false, - antialias: false, - depth: true, - failIfMajorPerformanceCaveat: false, - premultipliedAlpha: true, - preserveDrawingBuffer: false, - stencil: true, - }; - const canvas = document.createElement("canvas"); - const webGL2Context = canvas.getContext("webgl2", attributes); - if (!!webGL2Context) { - return true; + get webGLRenderer$() { + return this._webGLRenderer$; } - const context = canvas.getContext("webgl", attributes) || - canvas - .getContext("experimental-webgl", attributes); - if (!context) { - return false; + clear(name) { + this._clear$.next(name); } - const requiredExtensions = ["OES_standard_derivatives"]; - const supportedExtensions = context.getSupportedExtensions(); - for (const requiredExtension of requiredExtensions) { - if (supportedExtensions.indexOf(requiredExtension) === -1) { - return false; + remove() { + this._rendererOperation$.next((renderer) => { + if (renderer.renderer != null) { + const extension = renderer.renderer + .getContext() + .getExtension('WEBGL_lose_context'); + if (!!extension) { + extension.loseContext(); + } + renderer.renderer = null; + } + return renderer; + }); + if (this._renderFrameSubscription != null) { + this._renderFrameSubscription.unsubscribe(); } + this._subscriptions.unsubscribe(); + } + triggerRerender() { + this._renderService.renderCameraFrame$ + .pipe(skip(1), first()) + .subscribe(() => { + this._triggerOperation$.next((trigger) => { + trigger.needsRender = true; + return trigger; + }); + }); + } + _renderFrameSubscribe() { + this._render$.pipe(first(), map(() => { + return (irc) => { + irc.needsRender = true; + return irc; + }; + })) + .subscribe((operation) => { + this._renderCameraOperation$.next(operation); + }); + this._renderFrameSubscription = this._render$.pipe(first(), mergeMap(() => { + return this._renderService.renderCameraFrame$; + })) + .subscribe(this._renderFrame$); } - return true; - } - /** - * Test whether the current browser supports the full - * functionality of MapillaryJS. - * - * @description The full functionality includes WebGL rendering. - * - * @return {boolean} - * - * @example `var supported = isSupported();` - */ - function isSupported() { - return isFallbackSupported() && - isWebGLSupportedCached(); - } - /** - * Test whether the current browser supports the fallback - * functionality of MapillaryJS. - * - * @description The fallback functionality does not include WebGL - * rendering, only 2D canvas rendering. - * - * @return {boolean} - * - * @example `var fallbackSupported = isFallbackSupported();` - */ - function isFallbackSupported() { - return isBrowser() && - isArraySupported() && - isBlobSupported() && - isFunctionSupported() && - isJSONSupported() && - isMapSupported() && - isObjectSupported() && - isPromiseSupported() && - isSetSupported(); } /** - * Enumeration for camera controls. - * - * @description Specifies different modes for how the - * camera is controlled through pointer, keyboard or - * other modes of input. + * @class Camera * - * @enum {number} - * @readonly + * @classdesc Holds information about a camera. */ - exports.CameraControls = void 0; - (function (CameraControls) { + class Camera { /** - * Control the camera with custom logic by - * attaching a custom camera controls - * instance to the {@link Viewer}. + * Create a new camera instance. + * @param {Transform} [transform] - Optional transform instance. */ - CameraControls[CameraControls["Custom"] = 0] = "Custom"; + constructor(transform) { + if (transform != null) { + this._position = new Vector3().fromArray(transform.unprojectSfM([0, 0], 0)); + this._lookat = new Vector3().fromArray(transform.unprojectSfM([0, 0], 10)); + this._up = transform.upVector(); + this._focal = this._getFocal(transform); + } + else { + this._position = new Vector3(0, 0, 0); + this._lookat = new Vector3(1, 0, 0); + this._up = new Vector3(0, 0, 1); + this._focal = 1; + } + } /** - * Control the camera from a birds perspective - * to get an overview. + * Get position. + * @returns {THREE.Vector3} The position vector. */ - CameraControls[CameraControls["Earth"] = 1] = "Earth"; + get position() { + return this._position; + } /** - * Control the camera in a first person view - * from the street level perspective. + * Get lookat. + * @returns {THREE.Vector3} The lookat vector. */ - CameraControls[CameraControls["Street"] = 2] = "Street"; - })(exports.CameraControls || (exports.CameraControls = {})); - - /** - * Enumeration for render mode - * @enum {number} - * @readonly - * @description Modes for specifying how rendering is done - * in the viewer. All modes preserves the original aspect - * ratio of the images. - */ - exports.RenderMode = void 0; - (function (RenderMode) { + get lookat() { + return this._lookat; + } /** - * Displays all content within the viewer. + * Get up. + * @returns {THREE.Vector3} The up vector. + */ + get up() { + return this._up; + } + /** + * Get focal. + * @returns {number} The focal length. + */ + get focal() { + return this._focal; + } + /** + * Set focal. + */ + set focal(value) { + this._focal = value; + } + /** + * Update this camera to the linearly interpolated value of two other cameras. * - * @description Black bars shown on both - * sides of the content. Bars are shown - * either below and above or to the left - * and right of the content depending on - * the aspect ratio relation between the - * image and the viewer. + * @param {Camera} a - First camera. + * @param {Camera} b - Second camera. + * @param {number} alpha - Interpolation value on the interval [0, 1]. */ - RenderMode[RenderMode["Letterbox"] = 0] = "Letterbox"; + lerpCameras(a, b, alpha) { + this._position.subVectors(b.position, a.position).multiplyScalar(alpha).add(a.position); + this._lookat.subVectors(b.lookat, a.lookat).multiplyScalar(alpha).add(a.lookat); + this._up.subVectors(b.up, a.up).multiplyScalar(alpha).add(a.up); + this._focal = (1 - alpha) * a.focal + alpha * b.focal; + } /** - * Fills the viewer by cropping content. + * Copy the properties of another camera to this camera. * - * @description Cropping is done either - * in horizontal or vertical direction - * depending on the aspect ratio relation - * between the image and the viewer. + * @param {Camera} other - Another camera. */ - RenderMode[RenderMode["Fill"] = 1] = "Fill"; - })(exports.RenderMode || (exports.RenderMode = {})); - - exports.RenderPass = void 0; - (function (RenderPass) { + copy(other) { + this._position.copy(other.position); + this._lookat.copy(other.lookat); + this._up.copy(other.up); + this._focal = other.focal; + } + /** + * Clone this camera. + * + * @returns {Camera} A camera with cloned properties equal to this camera. + */ + clone() { + let camera = new Camera(); + camera.position.copy(this._position); + camera.lookat.copy(this._lookat); + camera.up.copy(this._up); + camera.focal = this._focal; + return camera; + } + /** + * Determine the distance between this camera and another camera. + * + * @param {Camera} other - Another camera. + * @returns {number} The distance between the cameras. + */ + diff(other) { + let pd = this._position.distanceToSquared(other.position); + let ld = this._lookat.distanceToSquared(other.lookat); + let ud = this._up.distanceToSquared(other.up); + let fd = 100 * Math.abs(this._focal - other.focal); + return Math.max(pd, ld, ud, fd); + } /** - * Occurs after the background render pass. + * Get the focal length based on the transform. + * + * @description Returns the focal length corresponding + * to a 90 degree field of view for spherical + * transforms. + * + * Returns the transform focal length for other + * projection types. + * + * @returns {number} Focal length. */ - RenderPass[RenderPass["Opaque"] = 0] = "Opaque"; - })(exports.RenderPass || (exports.RenderPass = {})); - - class ComponentController { - constructor(container, navigator, observer, key, options, componentService) { - this._container = container; - this._observer = observer; - this._navigator = navigator; - this._options = options != null ? options : {}; - this._key = key; - this._navigable = key == null; - this._componentService = !!componentService ? - componentService : - new ComponentService(this._container, this._navigator); - this._coverComponent = this._componentService.getCover(); - this._initializeComponents(); - if (key) { - this._initilizeCoverComponent(); - this._subscribeCoverComponent(); - } - else { - this._navigator.movedToId$.pipe(first((k) => { - return k != null; - })) - .subscribe((k) => { - this._key = k; - this._componentService.deactivateCover(); - this._coverComponent.configure({ - id: this._key, - state: CoverState.Hidden, - }); - this._subscribeCoverComponent(); - this._navigator.stateService.start(); - this._navigator.cacheService.start(); - this._navigator.panService.start(); - this._observer.startEmit(); - }); + _getFocal(transform) { + if (!isSpherical(transform.cameraType)) { + return transform.focal; } + return 0.5 / Math.tan(Math.PI / 2); } - get navigable() { - return this._navigable; + } + + class RenderCamera { + constructor(elementWidth, elementHeight, renderMode) { + this._spatial = new Spatial(); + this._viewportCoords = new ViewportCoords(); + this._size = { width: elementWidth, height: elementHeight }; + this._initialFov = 60; + this._alpha = -1; + this._stateTransitionAlpha = -1; + this._stateTransitionFov = -1; + this._renderMode = renderMode; + this._zoom = 0; + this._frameId = -1; + this._changed = false; + this._changedForFrame = -1; + this._currentImageId = null; + this._previousImageId = null; + this._currentSpherical = false; + this._previousSpherical = false; + this._state = null; + this._currentProjectedPoints = []; + this._previousProjectedPoints = []; + this._currentFov = this._initialFov; + this._previousFov = this._initialFov; + this._camera = new Camera(); + this._perspective = new PerspectiveCamera(this._initialFov, this._computeAspect(elementWidth, elementHeight), 0.1, 10000); + this._perspective.position.copy(this._camera.position); + this._perspective.up.copy(this._camera.up); + this._perspective.lookAt(this._camera.lookat); + this._perspective.updateMatrixWorld(true); + this._perspective.matrixAutoUpdate = false; + this._rotation = { phi: 0, theta: 0 }; } - get(name) { - return this._componentService.get(name); + get alpha() { + return this._alpha; } - activate(name) { - this._componentService.activate(name); + get camera() { + return this._camera; } - activateCover() { - this._coverComponent.configure({ state: CoverState.Visible }); + get changed() { + return this._frameId === this._changedForFrame; } - deactivate(name) { - this._componentService.deactivate(name); + get frameId() { + return this._frameId; } - deactivateCover() { - this._coverComponent.configure({ state: CoverState.Loading }); + get perspective() { + return this._perspective; } - remove() { - this._componentService.remove(); - if (this._configurationSubscription != null) { - this._configurationSubscription.unsubscribe(); - } + get renderMode() { + return this._renderMode; } - _initializeComponents() { - var _a, _b; - const options = this._options; - this._uFalse((_a = options.fallback) === null || _a === void 0 ? void 0 : _a.image, "imagefallback"); - this._uFalse((_b = options.fallback) === null || _b === void 0 ? void 0 : _b.navigation, "navigationfallback"); - this._uFalse(options.marker, "marker"); - this._uFalse(options.popup, "popup"); - this._uFalse(options.slider, "slider"); - this._uFalse(options.spatial, "spatial"); - this._uFalse(options.tag, "tag"); - this._uTrue(options.attribution, "attribution"); - this._uTrue(options.bearing, "bearing"); - this._uTrue(options.cache, "cache"); - this._uTrue(options.direction, "direction"); - this._uTrue(options.image, "image"); - this._uTrue(options.keyboard, "keyboard"); - this._uTrue(options.pointer, "pointer"); - this._uTrue(options.sequence, "sequence"); - this._uTrue(options.zoom, "zoom"); + get rotation() { + return this._rotation; } - _initilizeCoverComponent() { - let options = this._options; - this._coverComponent.configure({ id: this._key }); - if (options.cover === undefined || options.cover) { - this.activateCover(); - } - else { - this.deactivateCover(); - } + get zoom() { + return this._zoom; } - _setNavigable(navigable) { - if (this._navigable === navigable) { - return; - } - this._navigable = navigable; - this._observer.navigable$.next(navigable); + get size() { + return this._size; } - _subscribeCoverComponent() { - this._configurationSubscription = - this._coverComponent.configuration$.pipe(distinctUntilChanged(undefined, (c) => { - return c.state; - })) - .subscribe((conf) => { - if (conf.state === CoverState.Loading) { - this._navigator.stateService.currentId$.pipe(first(), switchMap((key) => { - const keyChanged = key == null || key !== conf.id; - if (keyChanged) { - this._setNavigable(false); - } - return keyChanged ? - this._navigator.moveTo$(conf.id) : - this._navigator.stateService.currentImage$.pipe(first()); - })) - .subscribe(() => { - this._navigator.stateService.start(); - this._navigator.cacheService.start(); - this._navigator.panService.start(); - this._observer.startEmit(); - this._coverComponent.configure({ state: CoverState.Hidden }); - this._componentService.deactivateCover(); - this._setNavigable(true); - }, (error) => { - console.error("Failed to deactivate cover.", error); - this._coverComponent.configure({ state: CoverState.Visible }); - }); - } - else if (conf.state === CoverState.Visible) { - this._observer.stopEmit(); - this._navigator.stateService.stop(); - this._navigator.cacheService.stop(); - this._navigator.playService.stop(); - this._navigator.panService.stop(); - this._componentService.activateCover(); - this._setNavigable(conf.id == null); - } - }); + getTilt() { + return 90 - this._spatial.radToDeg(this._rotation.theta); } - _uFalse(option, name) { - if (option === undefined) { - this._componentService.deactivate(name); - return; + fovToZoom(fov) { + fov = Math.min(90, Math.max(0, fov)); + const currentFov = this._computeCurrentFov(0); + const actualFov = this._alpha === 1 ? + currentFov : + this._interpolateFov(currentFov, this._computePreviousFov(0), this._alpha); + const y0 = Math.tan(actualFov / 2 * Math.PI / 180); + const y1 = Math.tan(fov / 2 * Math.PI / 180); + const zoom = Math.log(y0 / y1) / Math.log(2); + return zoom; + } + setFrame(frame) { + const state = frame.state; + if (state.state !== this._state) { + this._state = state.state; + if (this._state !== State.Custom) { + this.setRenderMode(this._renderMode); + this.setSize(this._size); + } + if (this._state === State.Earth) { + const y = this._fovToY(this._perspective.fov, this._zoom); + this._stateTransitionFov = this._yToFov(y, 0); + } + this._changed = true; } - if (typeof option === "boolean") { - if (option) { - this._componentService.activate(name); + const currentImageId = state.currentImage.id; + const previousImageId = !!state.previousImage ? state.previousImage.id : null; + if (currentImageId !== this._currentImageId) { + this._currentImageId = currentImageId; + this._currentSpherical = isSpherical(state.currentTransform.cameraType); + this._currentProjectedPoints = this._computeProjectedPoints(state.currentTransform); + this._changed = true; + } + if (previousImageId !== this._previousImageId) { + this._previousImageId = previousImageId; + this._previousSpherical = + isSpherical(state.previousTransform.cameraType); + this._previousProjectedPoints = this._computeProjectedPoints(state.previousTransform); + this._changed = true; + } + const zoom = state.zoom; + if (zoom !== this._zoom) { + this._changed = true; + } + if (this._changed) { + this._currentFov = this._computeCurrentFov(zoom); + this._previousFov = this._computePreviousFov(zoom); + } + const alpha = state.alpha; + const sta = state.stateTransitionAlpha; + if (this._changed || + alpha !== this._alpha || + sta !== this._stateTransitionAlpha) { + this._alpha = alpha; + this._stateTransitionAlpha = sta; + switch (this._state) { + case State.Earth: { + const startFov = this._stateTransitionFov; + const endFov = this._focalToFov(state.camera.focal); + const fov = MathUtils.lerp(startFov, endFov, sta); + const y = this._fovToY(fov, 0); + this._perspective.fov = this._yToFov(y, zoom); + break; + } + case State.Custom: + break; + default: + this._perspective.fov = + this._interpolateFov(this._currentFov, this._previousFov, this._alpha); + this._changed = true; + break; } - else { - this._componentService.deactivate(name); + this._zoom = zoom; + if (this._state !== State.Custom) { + this._perspective.updateProjectionMatrix(); } - return; } - this._componentService.configure(name, option); - this._componentService.activate(name); + const camera = state.camera; + if (this._camera.diff(camera) > 1e-9) { + this._camera.copy(camera); + this._rotation = this._computeRotation(camera); + this._perspective.up.copy(camera.up); + this._perspective.position.copy(camera.position); + // Workaround for shaking camera + this._perspective.matrixAutoUpdate = true; + this._perspective.lookAt(camera.lookat); + this._perspective.matrixAutoUpdate = false; + this._perspective.updateMatrix(); + this._perspective.updateMatrixWorld(false); + this._changed = true; + } + this._setFrameId(frame.id); } - _uTrue(option, name) { - if (option === undefined) { - this._componentService.activate(name); + setProjectionMatrix(matrix) { + this._perspective.fov = this._focalToFov(matrix[5] / 2); + this._perspective.projectionMatrix.fromArray(matrix); + this._perspective.projectionMatrixInverse + .copy(this._perspective.projectionMatrix) + .invert(); + this._changed = true; + } + setRenderMode(renderMode) { + this._renderMode = renderMode; + if (this._state === State.Custom) { return; } - if (typeof option === "boolean") { - if (option) { - this._componentService.activate(name); - } - else { - this._componentService.deactivate(name); - } + this._perspective.fov = this._computeFov(); + this._perspective.updateProjectionMatrix(); + this._changed = true; + } + setSize(size) { + this._size = size; + if (this._state === State.Custom) { return; } - this._componentService.configure(name, option); - this._componentService.activate(name); + this._perspective.aspect = this._computeAspect(size.width, size.height); + this._perspective.fov = this._computeFov(); + this._perspective.updateProjectionMatrix(); + this._changed = true; + } + _computeAspect(elementWidth, elementHeight) { + return elementWidth === 0 ? 0 : elementWidth / elementHeight; + } + _computeCurrentFov(zoom) { + if (this._perspective.aspect === 0) { + return 0; + } + if (!this._currentImageId) { + return this._initialFov; + } + return this._currentSpherical ? + this._yToFov(1, zoom) : + this._computeVerticalFov(this._currentProjectedPoints, this._renderMode, zoom, this.perspective.aspect); + } + _computeFov() { + this._currentFov = this._computeCurrentFov(this._zoom); + this._previousFov = this._computePreviousFov(this._zoom); + return this._interpolateFov(this._currentFov, this._previousFov, this._alpha); + } + _computePreviousFov(zoom) { + if (this._perspective.aspect === 0) { + return 0; + } + if (!this._currentImageId) { + return this._initialFov; + } + return !this._previousImageId ? + this._currentFov : + this._previousSpherical ? + this._yToFov(1, zoom) : + this._computeVerticalFov(this._previousProjectedPoints, this._renderMode, zoom, this.perspective.aspect); + } + _computeProjectedPoints(transform) { + const vertices = [[0.5, 0], [1, 0]]; + const directions = [[0.5, 0], [0, 0.5]]; + const pointsPerLine = 100; + return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords); + } + _computeRequiredVerticalFov(projectedPoint, zoom, aspect) { + const maxY = Math.max(projectedPoint[0] / aspect, projectedPoint[1]); + return this._yToFov(maxY, zoom); + } + _computeRotation(camera) { + let direction = camera.lookat.clone().sub(camera.position); + let up = camera.up.clone(); + let phi = this._spatial.azimuthal(direction.toArray(), up.toArray()); + let theta = Math.PI / 2 - this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); + return { phi: phi, theta: theta }; + } + _computeVerticalFov(projectedPoints, renderMode, zoom, aspect) { + const fovs = projectedPoints + .map((projectedPoint) => { + return this._computeRequiredVerticalFov(projectedPoint, zoom, aspect); + }); + const fov = renderMode === exports.RenderMode.Fill ? + Math.min(...fovs) * 0.995 : + Math.max(...fovs); + return fov; + } + _yToFov(y, zoom) { + return 2 * Math.atan(y / Math.pow(2, zoom)) * 180 / Math.PI; + } + _focalToFov(focal) { + return 2 * Math.atan2(1, 2 * focal) * 180 / Math.PI; + } + _fovToY(fov, zoom) { + return Math.pow(2, zoom) * Math.tan(Math.PI * fov / 360); + } + _interpolateFov(v1, v2, alpha) { + return alpha * v1 + (1 - alpha) * v2; + } + _setFrameId(frameId) { + this._frameId = frameId; + if (this._changed) { + this._changed = false; + this._changedForFrame = frameId; + } } } - class DOMRenderer { - constructor(element, renderService, currentFrame$) { - this._adaptiveOperation$ = new Subject(); - this._render$ = new Subject(); - this._renderAdaptive$ = new Subject(); + class RenderService { + constructor(element, currentFrame$, renderMode, renderCamera) { this._subscriptions = new SubscriptionHolder(); - this._renderService = renderService; + this._element = element; this._currentFrame$ = currentFrame$; + this._spatial = new Spatial(); + renderMode = renderMode != null ? renderMode : exports.RenderMode.Fill; + this._resize$ = new Subject(); + this._projectionMatrix$ = new Subject(); + this._renderCameraOperation$ = + new Subject(); + this._size$ = + new BehaviorSubject({ + height: this._element.offsetHeight, + width: this._element.offsetWidth, + }); const subs = this._subscriptions; - const rootNode = virtualDom.create(virtualDom.h("div.mapillary-dom-renderer", [])); - element.appendChild(rootNode); - this._offset$ = this._adaptiveOperation$.pipe(scan((adaptive, operation) => { - return operation(adaptive); - }, { - elementHeight: element.offsetHeight, - elementWidth: element.offsetWidth, - imageAspect: 0, - renderMode: exports.RenderMode.Fill, - }), filter((adaptive) => { - return adaptive.imageAspect > 0 && adaptive.elementWidth > 0 && adaptive.elementHeight > 0; - }), map((adaptive) => { - const elementAspect = adaptive.elementWidth / adaptive.elementHeight; - const ratio = adaptive.imageAspect / elementAspect; - let verticalOffset = 0; - let horizontalOffset = 0; - if (adaptive.renderMode === exports.RenderMode.Letterbox) { - if (adaptive.imageAspect > elementAspect) { - verticalOffset = adaptive.elementHeight * (1 - 1 / ratio) / 2; - } - else { - horizontalOffset = adaptive.elementWidth * (1 - ratio) / 2; - } - } - else { - if (adaptive.imageAspect > elementAspect) { - horizontalOffset = -adaptive.elementWidth * (ratio - 1) / 2; - } - else { - verticalOffset = -adaptive.elementHeight * (1 / ratio - 1) / 2; - } - } + subs.push(this._resize$.pipe(map(() => { return { - bottom: verticalOffset, - left: horizontalOffset, - right: horizontalOffset, - top: verticalOffset, - }; - })); - const imageAspectSubscription = this._currentFrame$.pipe(filter((frame) => { - return frame.state.currentImage != null; - }), distinctUntilChanged((k1, k2) => { - return k1 === k2; - }, (frame) => { - return frame.state.currentImage.id; - }), map((frame) => { - return frame.state.currentTransform.basicAspect; - }), map((aspect) => { - return (adaptive) => { - adaptive.imageAspect = aspect; - return adaptive; + height: this._element.offsetHeight, + width: this._element.offsetWidth, }; })) - .subscribe(this._adaptiveOperation$); - const renderAdaptiveSubscription = combineLatest(this._renderAdaptive$.pipe(scan((vNodeHashes, vNodeHash) => { - if (vNodeHash.vNode == null) { - delete vNodeHashes[vNodeHash.name]; - } - else { - vNodeHashes[vNodeHash.name] = vNodeHash.vNode; - } - return vNodeHashes; - }, {})), this._offset$).pipe(map((vo) => { - const vNodes = []; - const hashes = vo[0]; - for (const name in hashes) { - if (!hashes.hasOwnProperty(name)) { - continue; - } - vNodes.push(hashes[name]); - } - const offset = vo[1]; - const properties = { - style: { - bottom: offset.bottom + "px", - left: offset.left + "px", - "pointer-events": "none", - position: "absolute", - right: offset.right + "px", - top: offset.top + "px", - }, - }; - return { - name: "mapillary-dom-adaptive-renderer", - vNode: virtualDom.h("div.mapillary-dom-adaptive-renderer", properties, vNodes), + .subscribe(this._size$)); + this._renderMode$ = new BehaviorSubject(renderMode); + this._renderCameraHolder$ = this._renderCameraOperation$.pipe(startWith((rc) => { + return rc; + }), scan((rc, operation) => { + return operation(rc); + }, renderCamera !== null && renderCamera !== void 0 ? renderCamera : new RenderCamera(this._element.offsetWidth, this._element.offsetHeight, renderMode)), publishReplay(1), refCount()); + this._renderCameraFrame$ = this._currentFrame$.pipe(withLatestFrom(this._renderCameraHolder$), tap(([frame, rc]) => { + rc.setFrame(frame); + }), map((args) => { + return args[1]; + }), publishReplay(1), refCount()); + this._renderCamera$ = this._renderCameraFrame$.pipe(filter((rc) => { + return rc.changed; + }), publishReplay(1), refCount()); + this._bearing$ = this._renderCamera$.pipe(map((rc) => { + let bearing = this._spatial.radToDeg(this._spatial.azimuthalToBearing(rc.rotation.phi)); + return this._spatial.wrap(bearing, 0, 360); + }), publishReplay(1), refCount()); + subs.push(this._size$.pipe(skip(1), map((size) => { + return (rc) => { + rc.setSize(size); + return rc; }; })) - .subscribe(this._render$); - this._vNode$ = this._render$.pipe(scan((vNodeHashes, vNodeHash) => { - if (vNodeHash.vNode == null) { - delete vNodeHashes[vNodeHash.name]; - } - else { - vNodeHashes[vNodeHash.name] = vNodeHash.vNode; - } - return vNodeHashes; - }, {}), map((hashes) => { - const vNodes = []; - for (const name in hashes) { - if (!hashes.hasOwnProperty(name)) { - continue; - } - vNodes.push(hashes[name]); - } - return virtualDom.h("div.mapillary-dom-renderer", vNodes); - })); - this._vPatch$ = this._vNode$.pipe(scan((nodePatch, vNode) => { - nodePatch.vpatch = virtualDom.diff(nodePatch.vNode, vNode); - nodePatch.vNode = vNode; - return nodePatch; - }, { vNode: virtualDom.h("div.mapillary-dom-renderer", []), vpatch: null }), pluck("vpatch")); - this._element$ = this._vPatch$.pipe(scan((oldElement, vPatch) => { - return virtualDom.patch(oldElement, vPatch); - }, rootNode), publishReplay(1), refCount()); - subs.push(imageAspectSubscription); - subs.push(renderAdaptiveSubscription); - subs.push(this._element$.subscribe(() => { })); - subs.push(this._renderService.size$.pipe(map((size) => { - return (adaptive) => { - adaptive.elementWidth = size.width; - adaptive.elementHeight = size.height; - return adaptive; + .subscribe(this._renderCameraOperation$)); + subs.push(this._renderMode$.pipe(skip(1), map((rm) => { + return (rc) => { + rc.setRenderMode(rm); + return rc; }; })) - .subscribe(this._adaptiveOperation$)); - subs.push(this._renderService.renderMode$.pipe(map((renderMode) => { - return (adaptive) => { - adaptive.renderMode = renderMode; - return adaptive; + .subscribe(this._renderCameraOperation$)); + subs.push(this._projectionMatrix$.pipe(map((projectionMatrix) => { + return (rc) => { + rc.setProjectionMatrix(projectionMatrix); + return rc; }; })) - .subscribe(this._adaptiveOperation$)); + .subscribe(this._renderCameraOperation$)); + subs.push(this._bearing$.subscribe(() => { })); + subs.push(this._renderCameraHolder$.subscribe(() => { })); + subs.push(this._size$.subscribe(() => { })); + subs.push(this._renderMode$.subscribe(() => { })); + subs.push(this._renderCamera$.subscribe(() => { })); + subs.push(this._renderCameraFrame$.subscribe(() => { })); } - get element$() { - return this._element$; + get bearing$() { + return this._bearing$; } - get render$() { - return this._render$; + get element() { + return this._element; } - get renderAdaptive$() { - return this._renderAdaptive$; + get projectionMatrix$() { + return this._projectionMatrix$; } - clear(name) { - this._renderAdaptive$.next({ name: name, vNode: null }); - this._render$.next({ name: name, vNode: null }); + get renderCamera$() { + return this._renderCamera$; } - remove() { + get renderCameraFrame$() { + return this._renderCameraFrame$; + } + get renderMode$() { + return this._renderMode$; + } + get resize$() { + return this._resize$; + } + get size$() { + return this._size$; + } + dispose() { this._subscriptions.unsubscribe(); } } - class GLRenderer { - constructor(canvas, canvasContainer, renderService) { - this._renderFrame$ = new Subject(); - this._renderCameraOperation$ = new Subject(); - this._render$ = new Subject(); - this._clear$ = new Subject(); - this._renderOperation$ = new Subject(); - this._rendererOperation$ = new Subject(); - this._eraserOperation$ = new Subject(); - this._triggerOperation$ = new Subject(); + class KeyboardService { + constructor(canvasContainer) { + this._keyDown$ = fromEvent(canvasContainer, "keydown"); + this._keyUp$ = fromEvent(canvasContainer, "keyup"); + } + get keyDown$() { + return this._keyDown$; + } + get keyUp$() { + return this._keyUp$; + } + } + + // MouseEvent.button + const LEFT_BUTTON = 0; + const RIGHT_BUTTON = 2; + // MouseEvent.buttons + const BUTTONS_MAP = { + [LEFT_BUTTON]: 1, + [RIGHT_BUTTON]: 2 + }; + class MouseService { + constructor(container, canvasContainer, domContainer, doc) { this._subscriptions = new SubscriptionHolder(); - this._opaqueRender$ = new Subject(); - this._renderService = renderService; const subs = this._subscriptions; - this._renderer$ = this._rendererOperation$.pipe(scan((renderer, operation) => { - return operation(renderer); - }, { needsRender: false, renderer: null }), filter((renderer) => { - return !!renderer.renderer; - })); - this._renderCollection$ = this._renderOperation$.pipe(scan((hashes, operation) => { - return operation(hashes); - }, {}), share()); - this._renderCamera$ = this._renderCameraOperation$.pipe(scan((rc, operation) => { - return operation(rc); - }, { frameId: -1, needsRender: false, perspective: null })); - this._eraser$ = this._eraserOperation$.pipe(startWith((eraser) => { - return eraser; - }), scan((eraser, operation) => { - return operation(eraser); - }, { needsRender: false })); - const trigger$ = this._triggerOperation$.pipe(startWith((trigger) => { - return trigger; - }), scan((trigger, operation) => { - return operation(trigger); - }, { needsRender: false })); - const clearColor = new Color(0x0F0F0F); - const renderSubscription = combineLatest(this._renderer$, this._renderCollection$, this._renderCamera$, this._eraser$, trigger$).pipe(map(([renderer, hashes, rc, eraser, trigger]) => { - const renders = Object.keys(hashes) - .map((key) => { - return hashes[key]; - }); - return { camera: rc, eraser: eraser, trigger: trigger, renderer: renderer, renders: renders }; - }), filter((co) => { - let needsRender = co.renderer.needsRender || - co.camera.needsRender || - co.eraser.needsRender || - co.trigger.needsRender; - const frameId = co.camera.frameId; - for (const render of co.renders) { - if (render.frameId !== frameId) { - return false; - } - needsRender = needsRender || render.needsRender; - } - return needsRender; - }), distinctUntilChanged((n1, n2) => { - return n1 === n2; - }, (co) => { - return co.eraser.needsRender || - co.trigger.needsRender ? -co.camera.frameId : co.camera.frameId; - })) - .subscribe((co) => { - co.renderer.needsRender = false; - co.camera.needsRender = false; - co.eraser.needsRender = false; - co.trigger.needsRender = false; - const perspectiveCamera = co.camera.perspective; - const backgroundRenders = []; - const opaqueRenders = []; - for (const render of co.renders) { - if (render.pass === RenderPass.Background) { - backgroundRenders.push(render.render); - } - else if (render.pass === RenderPass.Opaque) { - opaqueRenders.push(render.render); - } - } - const renderer = co.renderer.renderer; - renderer.resetState(); - renderer.setClearColor(clearColor, 1.0); - renderer.clear(); - for (const renderBackground of backgroundRenders) { - renderBackground(perspectiveCamera, renderer); + this._activeSubject$ = new BehaviorSubject(false); + this._active$ = this._activeSubject$ + .pipe(distinctUntilChanged(), publishReplay(1), refCount()); + this._claimMouse$ = new Subject(); + this._claimWheel$ = new Subject(); + this._deferPixelClaims$ = new Subject(); + this._deferPixels$ = this._deferPixelClaims$ + .pipe(scan((claims, claim) => { + if (claim.deferPixels == null) { + delete claims[claim.name]; } - renderer.clearDepth(); - for (const renderOpaque of opaqueRenders) { - renderOpaque(perspectiveCamera, renderer); + else { + claims[claim.name] = claim.deferPixels; } - renderer.resetState(); - this._opaqueRender$.next(); - }); - subs.push(renderSubscription); - subs.push(this._renderFrame$.pipe(map((rc) => { - return (irc) => { - irc.frameId = rc.frameId; - irc.perspective = rc.perspective; - if (rc.changed === true) { - irc.needsRender = true; - } - return irc; - }; - })) - .subscribe(this._renderCameraOperation$)); - this._renderFrameSubscribe(); - const renderHash$ = this._render$.pipe(map((hash) => { - return (hashes) => { - hashes[hash.name] = hash.renderer; - return hashes; - }; - })); - const clearHash$ = this._clear$.pipe(map((name) => { - return (hashes) => { - delete hashes[name]; - return hashes; - }; - })); - subs.push(merge(renderHash$, clearHash$) - .subscribe(this._renderOperation$)); - this._webGLRenderer$ = this._render$.pipe(first(), map(() => { - canvasContainer.appendChild(canvas); - const element = renderService.element; - const webGLRenderer = new WebGLRenderer({ canvas: canvas }); - webGLRenderer.setPixelRatio(window.devicePixelRatio); - webGLRenderer.setSize(element.offsetWidth, element.offsetHeight); - webGLRenderer.autoClear = false; - return webGLRenderer; - }), publishReplay(1), refCount()); - subs.push(this._webGLRenderer$ - .subscribe(() => { })); - const createRenderer$ = this._webGLRenderer$.pipe(first(), map((webGLRenderer) => { - return (renderer) => { - renderer.needsRender = true; - renderer.renderer = webGLRenderer; - return renderer; - }; - })); - const resizeRenderer$ = this._renderService.size$.pipe(map((size) => { - return (renderer) => { - if (renderer.renderer == null) { - return renderer; + return claims; + }, {}), map((claims) => { + let deferPixelMax = -1; + for (const key in claims) { + if (!claims.hasOwnProperty(key)) { + continue; } - renderer.renderer.setSize(size.width, size.height); - renderer.needsRender = true; - return renderer; - }; - })); - const clearRenderer$ = this._clear$.pipe(map(() => { - return (renderer) => { - if (renderer.renderer == null) { - return renderer; + const deferPixels = claims[key]; + if (deferPixels > deferPixelMax) { + deferPixelMax = deferPixels; } - renderer.needsRender = true; - return renderer; - }; - })); - subs.push(merge(createRenderer$, resizeRenderer$, clearRenderer$) - .subscribe(this._rendererOperation$)); - const renderCollectionEmpty$ = this._renderCollection$.pipe(filter((hashes) => { - return Object.keys(hashes).length === 0; - }), share()); - subs.push(renderCollectionEmpty$ - .subscribe(() => { - if (this._renderFrameSubscription == null) { - return; } - this._renderFrameSubscription.unsubscribe(); - this._renderFrameSubscription = null; - this._renderFrameSubscribe(); + return deferPixelMax; + }), startWith(-1), publishReplay(1), refCount()); + subs.push(this._deferPixels$.subscribe(() => { })); + this._documentMouseMove$ = + fromEvent(doc, "pointermove") + .pipe(filter(this._isMousePen)); + this._documentMouseUp$ = + fromEvent(doc, "pointerup") + .pipe(filter(this._isMousePen)); + this._mouseDown$ = + fromEvent(canvasContainer, "pointerdown") + .pipe(filter(this._isMousePen)); + this._mouseEnter$ = + fromEvent(canvasContainer, "pointerenter") + .pipe(filter(this._isMousePen)); + this._mouseLeave$ = + fromEvent(canvasContainer, "pointerleave") + .pipe(filter(this._isMousePen)); + this._mouseMove$ = + fromEvent(canvasContainer, "pointermove") + .pipe(filter(this._isMousePen)); + this._mouseUp$ = + fromEvent(canvasContainer, "pointerup") + .pipe(filter(this._isMousePen)); + this._mouseOut$ = + fromEvent(canvasContainer, "pointerout") + .pipe(filter(this._isMousePen)); + this._mouseOver$ = + fromEvent(canvasContainer, "pointerover") + .pipe(filter(this._isMousePen)); + this._domMouseDown$ = + fromEvent(domContainer, "pointerdown") + .pipe(filter(this._isMousePen)); + this._domMouseMove$ = + fromEvent(domContainer, "pointermove") + .pipe(filter(this._isMousePen)); + this._click$ = + fromEvent(canvasContainer, "click"); + this._contextMenu$ = + fromEvent(canvasContainer, "contextmenu"); + this._windowBlur$ = + fromEvent(window, "blur"); + this._dblClick$ = merge(fromEvent(container, "click"), fromEvent(canvasContainer, "dblclick")) + .pipe(bufferCount(3, 1), filter((events) => { + const event1 = events[0]; + const event2 = events[1]; + const event3 = events[2]; + return event1.type === "click" && + event2.type === "click" && + event3.type === "dblclick" && + event1.target.parentNode === canvasContainer && + event2.target.parentNode === canvasContainer; + }), map((events) => { + return events[2]; + }), share()); + subs.push(merge(this._domMouseDown$, this._domMouseMove$, this._dblClick$, this._contextMenu$) + .subscribe((event) => { + event.preventDefault(); })); - subs.push(renderCollectionEmpty$.pipe(map(() => { - return (eraser) => { - eraser.needsRender = true; - return eraser; - }; - })) - .subscribe(this._eraserOperation$)); + this._mouseWheel$ = merge(fromEvent(canvasContainer, "wheel"), fromEvent(domContainer, "wheel")) + .pipe(share()); + this._consistentContextMenu$ = + merge(this._mouseDown$, this._mouseMove$, this._mouseOut$, this._mouseUp$, this._contextMenu$) + .pipe(bufferCount(3, 1), filter((events) => { + // fire context menu on mouse up both on mac and windows + return events[0].type === "pointerdown" && + events[1].type === "contextmenu" && + events[2].type === "pointerup"; + }), map((events) => { + return events[1]; + }), share()); + const dragStop$ = merge(this._windowBlur$, this._documentMouseMove$ + .pipe(filter((e) => { + return this._buttonReleased(e, LEFT_BUTTON); + })), this._documentMouseUp$ + .pipe(filter((e) => { + return this._mouseButton(e) === LEFT_BUTTON; + }))) + .pipe(share()); + const mouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._mouseDown$, dragStop$, true) + .pipe(share()); + this._mouseDragStart$ = + this._createMouseDragStart$(mouseDragInitiate$) + .pipe(share()); + this._mouseDrag$ = + this._createMouseDrag$(mouseDragInitiate$, dragStop$) + .pipe(share()); + this._mouseDragEnd$ = + this._createMouseDragEnd$(this._mouseDragStart$, dragStop$) + .pipe(share()); + const domMouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._domMouseDown$, dragStop$, false) + .pipe(share()); + this._domMouseDragStart$ = + this._createMouseDragStart$(domMouseDragInitiate$) + .pipe(share()); + this._domMouseDrag$ = + this._createMouseDrag$(domMouseDragInitiate$, dragStop$) + .pipe(share()); + this._domMouseDragEnd$ = + this._createMouseDragEnd$(this._domMouseDragStart$, dragStop$) + .pipe(share()); + const rightDragStop$ = merge(this._windowBlur$, this._documentMouseMove$.pipe(filter((e) => { + return this._buttonReleased(e, RIGHT_BUTTON); + })), this._documentMouseUp$.pipe(filter((e) => { + return this._mouseButton(e) === RIGHT_BUTTON; + }))) + .pipe(share()); + const mouseRightDragInitiate$ = this._createMouseDragInitiate$(RIGHT_BUTTON, this._mouseDown$, rightDragStop$, true) + .pipe(share()); + this._mouseRightDragStart$ = + this._createMouseDragStart$(mouseRightDragInitiate$) + .pipe(share()); + this._mouseRightDrag$ = + this._createMouseDrag$(mouseRightDragInitiate$, rightDragStop$) + .pipe(share()); + this._mouseRightDragEnd$ = + this._createMouseDragEnd$(this._mouseRightDragStart$, rightDragStop$) + .pipe(share()); + this._proximateClick$ = this._mouseDown$ + .pipe(switchMap((mouseDown) => { + return this._click$.pipe(takeUntil(this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$)), take(1)); + }), share()); + this._staticClick$ = this._mouseDown$ + .pipe(switchMap(() => { + return this._click$.pipe(takeUntil(this._documentMouseMove$), take(1)); + }), share()); + subs.push(this._mouseDragStart$.subscribe()); + subs.push(this._mouseDrag$.subscribe()); + subs.push(this._mouseDragEnd$.subscribe()); + subs.push(this._domMouseDragStart$.subscribe()); + subs.push(this._domMouseDrag$.subscribe()); + subs.push(this._domMouseDragEnd$.subscribe()); + subs.push(this._mouseRightDragStart$.subscribe()); + subs.push(this._mouseRightDrag$.subscribe()); + subs.push(this._mouseRightDragEnd$.subscribe()); + subs.push(this._staticClick$.subscribe()); + this._mouseOwner$ = this._createOwner$(this._claimMouse$) + .pipe(publishReplay(1), refCount()); + this._wheelOwner$ = this._createOwner$(this._claimWheel$) + .pipe(publishReplay(1), refCount()); + subs.push(this._mouseOwner$.subscribe(() => { })); + subs.push(this._wheelOwner$.subscribe(() => { })); + } + get active$() { + return this._active$; + } + get activate$() { + return this._activeSubject$; + } + get documentMouseMove$() { + return this._documentMouseMove$; + } + get documentMouseUp$() { + return this._documentMouseUp$; + } + get domMouseDragStart$() { + return this._domMouseDragStart$; + } + get domMouseDrag$() { + return this._domMouseDrag$; + } + get domMouseDragEnd$() { + return this._domMouseDragEnd$; + } + get domMouseDown$() { + return this._domMouseDown$; + } + get domMouseMove$() { + return this._domMouseMove$; + } + get mouseOwner$() { + return this._mouseOwner$; + } + get mouseDown$() { + return this._mouseDown$; + } + get mouseEnter$() { + return this._mouseEnter$; + } + get mouseMove$() { + return this._mouseMove$; + } + get mouseLeave$() { + return this._mouseLeave$; + } + get mouseOut$() { + return this._mouseOut$; + } + get mouseOver$() { + return this._mouseOver$; + } + get mouseUp$() { + return this._mouseUp$; + } + get click$() { + return this._click$; + } + get dblClick$() { + return this._dblClick$; + } + get contextMenu$() { + return this._consistentContextMenu$; + } + get mouseWheel$() { + return this._mouseWheel$; + } + get mouseDragStart$() { + return this._mouseDragStart$; } - get render$() { - return this._render$; + get mouseDrag$() { + return this._mouseDrag$; } - get opaqueRender$() { - return this._opaqueRender$; + get mouseDragEnd$() { + return this._mouseDragEnd$; } - get webGLRenderer$() { - return this._webGLRenderer$; + get mouseRightDragStart$() { + return this._mouseRightDragStart$; } - clear(name) { - this._clear$.next(name); + get mouseRightDrag$() { + return this._mouseRightDrag$; } - remove() { - this._rendererOperation$.next((renderer) => { - if (renderer.renderer != null) { - const extension = renderer.renderer - .getContext() - .getExtension('WEBGL_lose_context'); - if (!!extension) { - extension.loseContext(); - } - renderer.renderer = null; - } - return renderer; - }); - if (this._renderFrameSubscription != null) { - this._renderFrameSubscription.unsubscribe(); - } - this._subscriptions.unsubscribe(); + get mouseRightDragEnd$() { + return this._mouseRightDragEnd$; } - triggerRerender() { - this._renderService.renderCameraFrame$ - .pipe(skip(1), first()) - .subscribe(() => { - this._triggerOperation$.next((trigger) => { - trigger.needsRender = true; - return trigger; - }); - }); + get proximateClick$() { + return this._proximateClick$; } - _renderFrameSubscribe() { - this._render$.pipe(first(), map(() => { - return (irc) => { - irc.needsRender = true; - return irc; - }; - })) - .subscribe((operation) => { - this._renderCameraOperation$.next(operation); - }); - this._renderFrameSubscription = this._render$.pipe(first(), mergeMap(() => { - return this._renderService.renderCameraFrame$; - })) - .subscribe(this._renderFrame$); + get staticClick$() { + return this._staticClick$; } - } - - class RenderCamera { - constructor(elementWidth, elementHeight, renderMode) { - this._spatial = new Spatial(); - this._viewportCoords = new ViewportCoords(); - this._size = { width: elementWidth, height: elementHeight }; - this._initialFov = 60; - this._alpha = -1; - this._renderMode = renderMode; - this._zoom = 0; - this._frameId = -1; - this._changed = false; - this._changedForFrame = -1; - this._currentImageId = null; - this._previousImageId = null; - this._currentSpherical = false; - this._previousSpherical = false; - this._state = null; - this._currentProjectedPoints = []; - this._previousProjectedPoints = []; - this._currentFov = this._initialFov; - this._previousFov = this._initialFov; - this._camera = new Camera(); - this._perspective = new PerspectiveCamera(this._initialFov, this._computeAspect(elementWidth, elementHeight), 0.16, 10000); - this._perspective.position.copy(this._camera.position); - this._perspective.up.copy(this._camera.up); - this._perspective.lookAt(this._camera.lookat); - this._perspective.updateMatrixWorld(true); - this._perspective.matrixAutoUpdate = false; - this._rotation = { phi: 0, theta: 0 }; + get windowBlur$() { + return this._windowBlur$; } - get alpha() { - return this._alpha; + dispose() { + this._subscriptions.unsubscribe(); } - get camera() { - return this._camera; + claimMouse(name, zindex) { + this._claimMouse$.next({ name: name, zindex: zindex }); } - get changed() { - return this._frameId === this._changedForFrame; + unclaimMouse(name) { + this._claimMouse$.next({ name: name, zindex: null }); } - get frameId() { - return this._frameId; + deferPixels(name, deferPixels) { + this._deferPixelClaims$.next({ name: name, deferPixels: deferPixels }); } - get perspective() { - return this._perspective; + undeferPixels(name) { + this._deferPixelClaims$.next({ name: name, deferPixels: null }); } - get renderMode() { - return this._renderMode; + claimWheel(name, zindex) { + this._claimWheel$.next({ name: name, zindex: zindex }); } - get rotation() { - return this._rotation; + unclaimWheel(name) { + this._claimWheel$.next({ name: name, zindex: null }); } - get zoom() { - return this._zoom; + filtered$(name, observable$) { + return this._filtered(name, observable$, this._mouseOwner$); } - get size() { - return this._size; + filteredWheel$(name, observable$) { + return this._filtered(name, observable$, this._wheelOwner$); } - getTilt() { - return 90 - this._spatial.radToDeg(this._rotation.theta); + _createDeferredMouseMove$(origin, mouseMove$) { + return mouseMove$.pipe(map((mouseMove) => { + const deltaX = mouseMove.clientX - origin.clientX; + const deltaY = mouseMove.clientY - origin.clientY; + return [mouseMove, Math.sqrt(deltaX * deltaX + deltaY * deltaY)]; + }), withLatestFrom(this._deferPixels$), filter(([[, delta], deferPixels]) => { + return delta > deferPixels; + }), map(([[mouseMove]]) => { + return mouseMove; + })); } - fovToZoom(fov) { - fov = Math.min(90, Math.max(0, fov)); - const currentFov = this._computeCurrentFov(0); - const actualFov = this._alpha === 1 ? - currentFov : - this._interpolateFov(currentFov, this._computePreviousFov(0), this._alpha); - const y0 = Math.tan(actualFov / 2 * Math.PI / 180); - const y1 = Math.tan(fov / 2 * Math.PI / 180); - const zoom = Math.log(y0 / y1) / Math.log(2); - return zoom; + _createMouseDrag$(mouseDragStartInitiate$, stop$) { + return mouseDragStartInitiate$.pipe(map(([, mouseMove]) => { + return mouseMove; + }), switchMap((mouseMove) => { + return concat(of(mouseMove), this._documentMouseMove$).pipe(takeUntil(stop$)); + })); } - setFrame(frame) { - const state = frame.state; - if (state.state !== this._state) { - this._state = state.state; - if (this._state !== State.Custom) { - this.setRenderMode(this._renderMode); - this.setSize(this._size); + _createMouseDragEnd$(mouseDragStart$, stop$) { + return mouseDragStart$.pipe(switchMap(() => { + return stop$.pipe(first()); + })); + } + _createMouseDragStart$(mouseDragStartInitiate$) { + return mouseDragStartInitiate$.pipe(map(([mouseDown]) => { + return mouseDown; + })); + } + _createMouseDragInitiate$(button, mouseDown$, stop$, defer) { + return mouseDown$.pipe(filter((mouseDown) => { + return this._mouseButton(mouseDown) === button; + }), switchMap((mouseDown) => { + return combineLatest(of(mouseDown), defer ? + this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$) : + this._documentMouseMove$).pipe(takeUntil(stop$), take(1)); + })); + } + _createOwner$(claim$) { + return claim$.pipe(scan((claims, claim) => { + if (claim.zindex == null) { + delete claims[claim.name]; } - this._changed = true; - } - const currentImageId = state.currentImage.id; - const previousImageId = !!state.previousImage ? state.previousImage.id : null; - if (currentImageId !== this._currentImageId) { - this._currentImageId = currentImageId; - this._currentSpherical = isSpherical(state.currentTransform.cameraType); - this._currentProjectedPoints = this._computeProjectedPoints(state.currentTransform); - this._changed = true; - } - if (previousImageId !== this._previousImageId) { - this._previousImageId = previousImageId; - this._previousSpherical = - isSpherical(state.previousTransform.cameraType); - this._previousProjectedPoints = this._computeProjectedPoints(state.previousTransform); - this._changed = true; - } - const zoom = state.zoom; - if (zoom !== this._zoom) { - this._zoom = zoom; - this._changed = true; - } - if (this._changed) { - this._currentFov = this._computeCurrentFov(this.zoom); - this._previousFov = this._computePreviousFov(this._zoom); - } - const alpha = state.alpha; - if (this._changed || alpha !== this._alpha) { - this._alpha = alpha; - switch (this._state) { - case State.Earth: - this._perspective.fov = 60; - this._changed = true; - break; - case State.Custom: - break; - default: - this._perspective.fov = - this._interpolateFov(this._currentFov, this._previousFov, this._alpha); - this._changed = true; - break; + else { + claims[claim.name] = claim.zindex; } - if (this._state !== State.Custom) { - this._perspective.updateProjectionMatrix(); + return claims; + }, {}), map((claims) => { + let owner = null; + let zIndexMax = -1; + for (const name in claims) { + if (!claims.hasOwnProperty(name)) { + continue; + } + if (claims[name] > zIndexMax) { + zIndexMax = claims[name]; + owner = name; + } } - } - const camera = state.camera; - if (this._camera.diff(camera) > 1e-9) { - this._camera.copy(camera); - this._rotation = this._computeRotation(camera); - this._perspective.up.copy(camera.up); - this._perspective.position.copy(camera.position); - // Workaround for shaking camera - this._perspective.matrixAutoUpdate = true; - this._perspective.lookAt(camera.lookat); - this._perspective.matrixAutoUpdate = false; - this._perspective.updateMatrix(); - this._perspective.updateMatrixWorld(false); - this._changed = true; - } - this._setFrameId(frame.id); + return owner; + }), startWith(null)); } - setProjectionMatrix(matrix) { - this._perspective.projectionMatrix.fromArray(matrix); - this._perspective.projectionMatrixInverse - .copy(this._perspective.projectionMatrix) - .invert(); - this._changed = true; + _filtered(name, observable$, owner$) { + return observable$.pipe(withLatestFrom(owner$), filter(([, owner]) => { + return owner === name; + }), map(([item]) => { + return item; + })); } - setRenderMode(renderMode) { - this._renderMode = renderMode; - if (this._state === State.Custom) { - return; + _mouseButton(event) { + const upOrDown = event.type === "pointerdown" || event.type === "pointerup"; + const InstallTrigger = window.InstallTrigger; + if (upOrDown && + typeof InstallTrigger !== 'undefined' && + event.button === RIGHT_BUTTON && event.ctrlKey && + window.navigator.platform.toUpperCase().indexOf('MAC') >= 0) { + // Fix for the fact that Firefox (detected by InstallTrigger) + // on Mac determines e.button = 2 when using Control + left click. + return LEFT_BUTTON; } - this._perspective.fov = this._computeFov(); - this._perspective.updateProjectionMatrix(); - this._changed = true; + return event.button; } - setSize(size) { - this._size = size; - if (this._state === State.Custom) { - return; - } - this._perspective.aspect = this._computeAspect(size.width, size.height); - this._perspective.fov = this._computeFov(); - this._perspective.updateProjectionMatrix(); - this._changed = true; + _buttonReleased(event, button) { + // Right button `mouseup` is not fired in + // Chrome on Mac outside the window or iframe. If + // the button is no longer pressed during move + // it may have been released and drag stop + // should be emitted. + const flag = BUTTONS_MAP[button]; + return event.buttons === undefined || (event.buttons & flag) !== flag; } - _computeAspect(elementWidth, elementHeight) { - return elementWidth === 0 ? 0 : elementWidth / elementHeight; + _isMousePen(event) { + const type = event.pointerType; + return type === "mouse" || type === "pen"; } - _computeCurrentFov(zoom) { - if (this._perspective.aspect === 0) { - return 0; - } - if (!this._currentImageId) { - return this._initialFov; - } - return this._currentSpherical ? - this._yToFov(1, zoom) : - this._computeVerticalFov(this._currentProjectedPoints, this._renderMode, zoom, this.perspective.aspect); + } + + class SpriteAtlas { + set json(value) { + this._json = value; } - _computeFov() { - this._currentFov = this._computeCurrentFov(this._zoom); - this._previousFov = this._computePreviousFov(this._zoom); - return this._interpolateFov(this._currentFov, this._previousFov, this._alpha); + set image(value) { + this._image = value; + this._texture = new Texture(this._image); + this._texture.minFilter = NearestFilter; } - _computePreviousFov(zoom) { - if (this._perspective.aspect === 0) { - return 0; + get loaded() { + return !!(this._image && this._json); + } + getGLSprite(name) { + if (!this.loaded) { + throw new Error("Sprites cannot be retrieved before the atlas is loaded."); } - if (!this._currentImageId) { - return this._initialFov; + let definition = this._json[name]; + if (!definition) { + console.warn("Sprite with key" + name + "does not exist in sprite definition."); + return new Object3D(); } - return !this._previousImageId ? - this._currentFov : - this._previousSpherical ? - this._yToFov(1, zoom) : - this._computeVerticalFov(this._previousProjectedPoints, this._renderMode, zoom, this.perspective.aspect); - } - _computeProjectedPoints(transform) { - const vertices = [[0.5, 0], [1, 0]]; - const directions = [[0.5, 0], [0, 0.5]]; - const pointsPerLine = 100; - return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords); - } - _computeRequiredVerticalFov(projectedPoint, zoom, aspect) { - const maxY = Math.max(projectedPoint[0] / aspect, projectedPoint[1]); - return this._yToFov(maxY, zoom); - } - _computeRotation(camera) { - let direction = camera.lookat.clone().sub(camera.position); - let up = camera.up.clone(); - let phi = this._spatial.azimuthal(direction.toArray(), up.toArray()); - let theta = Math.PI / 2 - this._spatial.angleToPlane(direction.toArray(), [0, 0, 1]); - return { phi: phi, theta: theta }; + let texture = this._texture.clone(); + texture.needsUpdate = true; + let width = this._image.width; + let height = this._image.height; + texture.offset.x = definition.x / width; + texture.offset.y = (height - definition.y - definition.height) / height; + texture.repeat.x = definition.width / width; + texture.repeat.y = definition.height / height; + let material = new SpriteMaterial({ map: texture }); + return new Sprite(material); } - _computeVerticalFov(projectedPoints, renderMode, zoom, aspect) { - const fovs = projectedPoints - .map((projectedPoint) => { - return this._computeRequiredVerticalFov(projectedPoint, zoom, aspect); - }); - const fov = renderMode === exports.RenderMode.Fill ? - Math.min(...fovs) * 0.995 : - Math.max(...fovs); - return fov; + getDOMSprite(name, float) { + if (!this.loaded) { + throw new Error("Sprites cannot be retrieved before the atlas is loaded."); + } + if (float == null) { + float = exports.Alignment.Center; + } + let definition = this._json[name]; + if (!definition) { + console.warn("Sprite with key" + name + "does not exist in sprite definition."); + return virtualDom.h("div", {}, []); + } + let clipTop = definition.y; + let clipRigth = definition.x + definition.width; + let clipBottom = definition.y + definition.height; + let clipLeft = definition.x; + let left = -definition.x; + let top = -definition.y; + let height = this._image.height; + let width = this._image.width; + switch (float) { + case exports.Alignment.Bottom: + case exports.Alignment.Center: + case exports.Alignment.Top: + left -= definition.width / 2; + break; + case exports.Alignment.BottomLeft: + case exports.Alignment.Left: + case exports.Alignment.TopLeft: + left -= definition.width; + break; + case exports.Alignment.BottomRight: + case exports.Alignment.Right: + case exports.Alignment.TopRight: + } + switch (float) { + case exports.Alignment.Center: + case exports.Alignment.Left: + case exports.Alignment.Right: + top -= definition.height / 2; + break; + case exports.Alignment.Top: + case exports.Alignment.TopLeft: + case exports.Alignment.TopRight: + top -= definition.height; + break; + case exports.Alignment.Bottom: + case exports.Alignment.BottomLeft: + case exports.Alignment.BottomRight: + } + let pixelRatioInverse = 1 / definition.pixelRatio; + clipTop *= pixelRatioInverse; + clipRigth *= pixelRatioInverse; + clipBottom *= pixelRatioInverse; + clipLeft *= pixelRatioInverse; + left *= pixelRatioInverse; + top *= pixelRatioInverse; + height *= pixelRatioInverse; + width *= pixelRatioInverse; + let properties = { + src: this._image.src, + style: { + clip: `rect(${clipTop}px, ${clipRigth}px, ${clipBottom}px, ${clipLeft}px)`, + height: `${height}px`, + left: `${left}px`, + position: "absolute", + top: `${top}px`, + width: `${width}px`, + }, + }; + return virtualDom.h("img", properties, []); } - _yToFov(y, zoom) { - return 2 * Math.atan(y / Math.pow(2, zoom)) * 180 / Math.PI; + } + class SpriteService { + constructor(sprite) { + this._retina = window.devicePixelRatio > 1; + this._spriteAtlasOperation$ = new Subject(); + this._spriteAtlas$ = this._spriteAtlasOperation$.pipe(startWith((atlas) => { + return atlas; + }), scan((atlas, operation) => { + return operation(atlas); + }, new SpriteAtlas()), publishReplay(1), refCount()); + this._atlasSubscription = this._spriteAtlas$ + .subscribe(() => { }); + if (sprite == null) { + return; + } + let format = this._retina ? "@2x" : ""; + let imageXmlHTTP = new XMLHttpRequest(); + imageXmlHTTP.open("GET", sprite + format + ".png", true); + imageXmlHTTP.responseType = "arraybuffer"; + imageXmlHTTP.onload = () => { + let image = new Image(); + image.onload = () => { + this._spriteAtlasOperation$.next((atlas) => { + atlas.image = image; + return atlas; + }); + }; + let blob = new Blob([imageXmlHTTP.response]); + image.src = window.URL.createObjectURL(blob); + }; + imageXmlHTTP.onerror = (error) => { + console.error(new Error(`Failed to fetch sprite sheet (${sprite}${format}.png)`)); + }; + imageXmlHTTP.send(); + let jsonXmlHTTP = new XMLHttpRequest(); + jsonXmlHTTP.open("GET", sprite + format + ".json", true); + jsonXmlHTTP.responseType = "text"; + jsonXmlHTTP.onload = () => { + let json = JSON.parse(jsonXmlHTTP.response); + this._spriteAtlasOperation$.next((atlas) => { + atlas.json = json; + return atlas; + }); + }; + jsonXmlHTTP.onerror = (error) => { + console.error(new Error(`Failed to fetch sheet (${sprite}${format}.json)`)); + }; + jsonXmlHTTP.send(); } - _interpolateFov(v1, v2, alpha) { - return alpha * v1 + (1 - alpha) * v2; + get spriteAtlas$() { + return this._spriteAtlas$; } - _setFrameId(frameId) { - this._frameId = frameId; - if (this._changed) { - this._changed = false; - this._changedForFrame = frameId; - } + dispose() { + this._atlasSubscription.unsubscribe(); } } - class RenderService { - constructor(element, currentFrame$, renderMode, renderCamera) { + class TouchService { + constructor(canvasContainer, domContainer) { this._subscriptions = new SubscriptionHolder(); - this._element = element; - this._currentFrame$ = currentFrame$; - this._spatial = new Spatial(); - renderMode = renderMode != null ? renderMode : exports.RenderMode.Fill; - this._resize$ = new Subject(); - this._projectionMatrix$ = new Subject(); - this._renderCameraOperation$ = - new Subject(); - this._size$ = - new BehaviorSubject({ - height: this._element.offsetHeight, - width: this._element.offsetWidth, - }); const subs = this._subscriptions; - subs.push(this._resize$.pipe(map(() => { - return { - height: this._element.offsetHeight, - width: this._element.offsetWidth, - }; - })) - .subscribe(this._size$)); - this._renderMode$ = new BehaviorSubject(renderMode); - this._renderCameraHolder$ = this._renderCameraOperation$.pipe(startWith((rc) => { - return rc; - }), scan((rc, operation) => { - return operation(rc); - }, renderCamera !== null && renderCamera !== void 0 ? renderCamera : new RenderCamera(this._element.offsetWidth, this._element.offsetHeight, renderMode)), publishReplay(1), refCount()); - this._renderCameraFrame$ = this._currentFrame$.pipe(withLatestFrom(this._renderCameraHolder$), tap(([frame, rc]) => { - rc.setFrame(frame); - }), map((args) => { - return args[1]; - }), publishReplay(1), refCount()); - this._renderCamera$ = this._renderCameraFrame$.pipe(filter((rc) => { - return rc.changed; - }), publishReplay(1), refCount()); - this._bearing$ = this._renderCamera$.pipe(map((rc) => { - let bearing = this._spatial.radToDeg(this._spatial.azimuthalToBearing(rc.rotation.phi)); - return this._spatial.wrap(bearing, 0, 360); - }), publishReplay(1), refCount()); - subs.push(this._size$.pipe(skip(1), map((size) => { - return (rc) => { - rc.setSize(size); - return rc; - }; - })) - .subscribe(this._renderCameraOperation$)); - subs.push(this._renderMode$.pipe(skip(1), map((rm) => { - return (rc) => { - rc.setRenderMode(rm); - return rc; - }; - })) - .subscribe(this._renderCameraOperation$)); - subs.push(this._projectionMatrix$.pipe(map((projectionMatrix) => { - return (rc) => { - rc.setProjectionMatrix(projectionMatrix); - return rc; + this._activeSubject$ = new BehaviorSubject(false); + this._active$ = this._activeSubject$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); + subs.push(fromEvent(domContainer, "touchmove") + .subscribe((event) => { + event.preventDefault(); + })); + this._touchStart$ = fromEvent(canvasContainer, "touchstart"); + this._touchMove$ = fromEvent(canvasContainer, "touchmove"); + this._touchEnd$ = fromEvent(canvasContainer, "touchend"); + this._touchCancel$ = fromEvent(canvasContainer, "touchcancel"); + const tapStart$ = this._touchStart$.pipe(filter((te) => { + return te.touches.length === 1 && te.targetTouches.length === 1; + }), share()); + this._doubleTap$ = tapStart$.pipe(bufferWhen(() => { + return tapStart$.pipe(first(), switchMap(() => { + return merge(timer(300), tapStart$).pipe(take(1)); + })); + }), filter((events) => { + return events.length === 2; + }), map((events) => { + return events[events.length - 1]; + }), share()); + subs.push(this._doubleTap$ + .subscribe((event) => { + event.preventDefault(); + })); + this._singleTouchMove$ = this._touchMove$.pipe(filter((te) => { + return te.touches.length === 1 && te.targetTouches.length === 1; + }), share()); + let singleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { + return te.touches.length === 1 && te.targetTouches.length === 1; + })); + let multipleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { + return te.touches.length >= 1; + })); + let touchStop$ = merge(this._touchEnd$, this._touchCancel$).pipe(filter((te) => { + return te.touches.length === 0; + })); + this._singleTouchDragStart$ = singleTouchStart$.pipe(mergeMap(() => { + return this._singleTouchMove$.pipe(takeUntil(merge(touchStop$, multipleTouchStart$)), take(1)); + })); + this._singleTouchDragEnd$ = singleTouchStart$.pipe(mergeMap(() => { + return merge(touchStop$, multipleTouchStart$).pipe(first()); + })); + this._singleTouchDrag$ = singleTouchStart$.pipe(switchMap(() => { + return this._singleTouchMove$.pipe(skip(1), takeUntil(merge(multipleTouchStart$, touchStop$))); + })); + let touchesChanged$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$); + this._pinchStart$ = touchesChanged$.pipe(filter((te) => { + return te.touches.length === 2 && te.targetTouches.length === 2; + })); + this._pinchEnd$ = touchesChanged$.pipe(filter((te) => { + return te.touches.length !== 2 || te.targetTouches.length !== 2; + })); + this._pinchOperation$ = new Subject(); + this._pinch$ = this._pinchOperation$.pipe(scan((pinch, operation) => { + return operation(pinch); + }, { + changeX: 0, + changeY: 0, + clientX: 0, + clientY: 0, + distance: 0, + distanceChange: 0, + distanceX: 0, + distanceY: 0, + originalEvent: null, + pageX: 0, + pageY: 0, + screenX: 0, + screenY: 0, + touch1: null, + touch2: null, + })); + const pinchSubscription = this._touchMove$.pipe(filter((te) => { + return te.touches.length === 2 && te.targetTouches.length === 2; + }), map((te) => { + return (previous) => { + let touch1 = te.touches[0]; + let touch2 = te.touches[1]; + let minX = Math.min(touch1.clientX, touch2.clientX); + let maxX = Math.max(touch1.clientX, touch2.clientX); + let minY = Math.min(touch1.clientY, touch2.clientY); + let maxY = Math.max(touch1.clientY, touch2.clientY); + let centerClientX = minX + (maxX - minX) / 2; + let centerClientY = minY + (maxY - minY) / 2; + let centerPageX = centerClientX + touch1.pageX - touch1.clientX; + let centerPageY = centerClientY + touch1.pageY - touch1.clientY; + let centerScreenX = centerClientX + touch1.screenX - touch1.clientX; + let centerScreenY = centerClientY + touch1.screenY - touch1.clientY; + let distanceX = Math.abs(touch1.clientX - touch2.clientX); + let distanceY = Math.abs(touch1.clientY - touch2.clientY); + let distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); + let distanceChange = distance - previous.distance; + let changeX = distanceX - previous.distanceX; + let changeY = distanceY - previous.distanceY; + let current = { + changeX: changeX, + changeY: changeY, + clientX: centerClientX, + clientY: centerClientY, + distance: distance, + distanceChange: distanceChange, + distanceX: distanceX, + distanceY: distanceY, + originalEvent: te, + pageX: centerPageX, + pageY: centerPageY, + screenX: centerScreenX, + screenY: centerScreenY, + touch1: touch1, + touch2: touch2, + }; + return current; }; })) - .subscribe(this._renderCameraOperation$)); - subs.push(this._bearing$.subscribe(() => { })); - subs.push(this._renderCameraHolder$.subscribe(() => { })); - subs.push(this._size$.subscribe(() => { })); - subs.push(this._renderMode$.subscribe(() => { })); - subs.push(this._renderCamera$.subscribe(() => { })); - subs.push(this._renderCameraFrame$.subscribe(() => { })); + .subscribe(this._pinchOperation$); + subs.push(pinchSubscription); + this._pinchChange$ = this._pinchStart$.pipe(switchMap(() => { + return this._pinch$.pipe(skip(1), takeUntil(this._pinchEnd$)); + })); } - get bearing$() { - return this._bearing$; + get active$() { + return this._active$; } - get element() { - return this._element; + get activate$() { + return this._activeSubject$; } - get projectionMatrix$() { - return this._projectionMatrix$; + get doubleTap$() { + return this._doubleTap$; } - get renderCamera$() { - return this._renderCamera$; + get touchStart$() { + return this._touchStart$; } - get renderCameraFrame$() { - return this._renderCameraFrame$; + get touchMove$() { + return this._touchMove$; } - get renderMode$() { - return this._renderMode$; + get touchEnd$() { + return this._touchEnd$; } - get resize$() { - return this._resize$; + get touchCancel$() { + return this._touchCancel$; } - get size$() { - return this._size$; + get singleTouchDragStart$() { + return this._singleTouchDragStart$; + } + get singleTouchDrag$() { + return this._singleTouchDrag$; + } + get singleTouchDragEnd$() { + return this._singleTouchDragEnd$; + } + get pinch$() { + return this._pinchChange$; + } + get pinchStart$() { + return this._pinchStart$; + } + get pinchEnd$() { + return this._pinchEnd$; } dispose() { this._subscriptions.unsubscribe(); } } - class KeyboardService { - constructor(canvasContainer) { - this._keyDown$ = fromEvent(canvasContainer, "keydown"); - this._keyUp$ = fromEvent(canvasContainer, "keyup"); + class ConfigurationService { + constructor(options) { + var _a, _b, _c, _d; + const host = (_b = (_a = options === null || options === void 0 ? void 0 : options.url) === null || _a === void 0 ? void 0 : _a.exploreHost) !== null && _b !== void 0 ? _b : "www.mapillary.com"; + const scheme = (_d = (_c = options === null || options === void 0 ? void 0 : options.url) === null || _c === void 0 ? void 0 : _c.scheme) !== null && _d !== void 0 ? _d : "https"; + const exploreUrl = `${scheme}://${host}`; + this._exploreUrl$ = of(exploreUrl); + const imageTiling = (options === null || options === void 0 ? void 0 : options.imageTiling) === false ? false : true; + this._imageTiling$ = of(imageTiling); } - get keyDown$() { - return this._keyDown$; + get exploreUrl$() { + return this._exploreUrl$; } - get keyUp$() { - return this._keyUp$; + get imageTiling$() { + return this._imageTiling$; } } - // MouseEvent.button - const LEFT_BUTTON = 0; - const RIGHT_BUTTON = 2; - // MouseEvent.buttons - const BUTTONS_MAP = { - [LEFT_BUTTON]: 1, - [RIGHT_BUTTON]: 2 - }; - class MouseService { - constructor(container, canvasContainer, domContainer, doc) { - this._subscriptions = new SubscriptionHolder(); - const subs = this._subscriptions; - this._activeSubject$ = new BehaviorSubject(false); - this._active$ = this._activeSubject$ - .pipe(distinctUntilChanged(), publishReplay(1), refCount()); - this._claimMouse$ = new Subject(); - this._claimWheel$ = new Subject(); - this._deferPixelClaims$ = new Subject(); - this._deferPixels$ = this._deferPixelClaims$ - .pipe(scan((claims, claim) => { - if (claim.deferPixels == null) { - delete claims[claim.name]; - } - else { - claims[claim.name] = claim.deferPixels; + class Container { + constructor(options, stateService, dom) { + var _a; + this._onWindowResize = () => { + if (this._trackResize) { + this.renderService.resize$.next(); } - return claims; - }, {}), map((claims) => { - let deferPixelMax = -1; - for (const key in claims) { - if (!claims.hasOwnProperty(key)) { - continue; - } - const deferPixels = claims[key]; - if (deferPixels > deferPixelMax) { - deferPixelMax = deferPixels; - } + }; + this._dom = dom !== null && dom !== void 0 ? dom : new DOM(); + if (typeof options.container === "string") { + this._container = this._dom.document + .getElementById(options.container); + if (!this._container) { + throw new Error(`Container "${options.container}" not found.`); } - return deferPixelMax; - }), startWith(-1), publishReplay(1), refCount()); - subs.push(this._deferPixels$.subscribe(() => { })); - this._documentMouseMove$ = - fromEvent(doc, "pointermove") - .pipe(filter(this._isMousePen)); - this._documentMouseUp$ = - fromEvent(doc, "pointerup") - .pipe(filter(this._isMousePen)); - this._mouseDown$ = - fromEvent(canvasContainer, "pointerdown") - .pipe(filter(this._isMousePen)); - this._mouseEnter$ = - fromEvent(canvasContainer, "pointerenter") - .pipe(filter(this._isMousePen)); - this._mouseLeave$ = - fromEvent(canvasContainer, "pointerleave") - .pipe(filter(this._isMousePen)); - this._mouseMove$ = - fromEvent(canvasContainer, "pointermove") - .pipe(filter(this._isMousePen)); - this._mouseUp$ = - fromEvent(canvasContainer, "pointerup") - .pipe(filter(this._isMousePen)); - this._mouseOut$ = - fromEvent(canvasContainer, "pointerout") - .pipe(filter(this._isMousePen)); - this._mouseOver$ = - fromEvent(canvasContainer, "pointerover") - .pipe(filter(this._isMousePen)); - this._domMouseDown$ = - fromEvent(domContainer, "pointerdown") - .pipe(filter(this._isMousePen)); - this._domMouseMove$ = - fromEvent(domContainer, "pointermove") - .pipe(filter(this._isMousePen)); - this._click$ = - fromEvent(canvasContainer, "click"); - this._contextMenu$ = - fromEvent(canvasContainer, "contextmenu"); - this._windowBlur$ = - fromEvent(window, "blur"); - this._dblClick$ = merge(fromEvent(container, "click"), fromEvent(canvasContainer, "dblclick")) - .pipe(bufferCount(3, 1), filter((events) => { - const event1 = events[0]; - const event2 = events[1]; - const event3 = events[2]; - return event1.type === "click" && - event2.type === "click" && - event3.type === "dblclick" && - event1.target.parentNode === canvasContainer && - event2.target.parentNode === canvasContainer; - }), map((events) => { - return events[2]; - }), share()); - subs.push(merge(this._domMouseDown$, this._domMouseMove$, this._dblClick$, this._contextMenu$) - .subscribe((event) => { - event.preventDefault(); - })); - this._mouseWheel$ = merge(fromEvent(canvasContainer, "wheel"), fromEvent(domContainer, "wheel")) - .pipe(share()); - this._consistentContextMenu$ = - merge(this._mouseDown$, this._mouseMove$, this._mouseOut$, this._mouseUp$, this._contextMenu$) - .pipe(bufferCount(3, 1), filter((events) => { - // fire context menu on mouse up both on mac and windows - return events[0].type === "pointerdown" && - events[1].type === "contextmenu" && - events[2].type === "pointerup"; - }), map((events) => { - return events[1]; - }), share()); - const dragStop$ = merge(this._windowBlur$, this._documentMouseMove$ - .pipe(filter((e) => { - return this._buttonReleased(e, LEFT_BUTTON); - })), this._documentMouseUp$ - .pipe(filter((e) => { - return this._mouseButton(e) === LEFT_BUTTON; - }))) - .pipe(share()); - const mouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._mouseDown$, dragStop$, true) - .pipe(share()); - this._mouseDragStart$ = - this._createMouseDragStart$(mouseDragInitiate$) - .pipe(share()); - this._mouseDrag$ = - this._createMouseDrag$(mouseDragInitiate$, dragStop$) - .pipe(share()); - this._mouseDragEnd$ = - this._createMouseDragEnd$(this._mouseDragStart$, dragStop$) - .pipe(share()); - const domMouseDragInitiate$ = this._createMouseDragInitiate$(LEFT_BUTTON, this._domMouseDown$, dragStop$, false) - .pipe(share()); - this._domMouseDragStart$ = - this._createMouseDragStart$(domMouseDragInitiate$) - .pipe(share()); - this._domMouseDrag$ = - this._createMouseDrag$(domMouseDragInitiate$, dragStop$) - .pipe(share()); - this._domMouseDragEnd$ = - this._createMouseDragEnd$(this._domMouseDragStart$, dragStop$) - .pipe(share()); - const rightDragStop$ = merge(this._windowBlur$, this._documentMouseMove$.pipe(filter((e) => { - return this._buttonReleased(e, RIGHT_BUTTON); - })), this._documentMouseUp$.pipe(filter((e) => { - return this._mouseButton(e) === RIGHT_BUTTON; - }))) - .pipe(share()); - const mouseRightDragInitiate$ = this._createMouseDragInitiate$(RIGHT_BUTTON, this._mouseDown$, rightDragStop$, true) - .pipe(share()); - this._mouseRightDragStart$ = - this._createMouseDragStart$(mouseRightDragInitiate$) - .pipe(share()); - this._mouseRightDrag$ = - this._createMouseDrag$(mouseRightDragInitiate$, rightDragStop$) - .pipe(share()); - this._mouseRightDragEnd$ = - this._createMouseDragEnd$(this._mouseRightDragStart$, rightDragStop$) - .pipe(share()); - this._proximateClick$ = this._mouseDown$ - .pipe(switchMap((mouseDown) => { - return this._click$.pipe(takeUntil(this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$)), take(1)); - }), share()); - this._staticClick$ = this._mouseDown$ - .pipe(switchMap(() => { - return this._click$.pipe(takeUntil(this._documentMouseMove$), take(1)); - }), share()); - subs.push(this._mouseDragStart$.subscribe()); - subs.push(this._mouseDrag$.subscribe()); - subs.push(this._mouseDragEnd$.subscribe()); - subs.push(this._domMouseDragStart$.subscribe()); - subs.push(this._domMouseDrag$.subscribe()); - subs.push(this._domMouseDragEnd$.subscribe()); - subs.push(this._mouseRightDragStart$.subscribe()); - subs.push(this._mouseRightDrag$.subscribe()); - subs.push(this._mouseRightDragEnd$.subscribe()); - subs.push(this._staticClick$.subscribe()); - this._mouseOwner$ = this._createOwner$(this._claimMouse$) - .pipe(publishReplay(1), refCount()); - this._wheelOwner$ = this._createOwner$(this._claimWheel$) - .pipe(publishReplay(1), refCount()); - subs.push(this._mouseOwner$.subscribe(() => { })); - subs.push(this._wheelOwner$.subscribe(() => { })); + } + else if (options.container instanceof HTMLElement) { + this._container = options.container; + } + else { + throw new Error(`Invalid type: "container" must be ` + + `a String or HTMLElement.`); + } + this._trackResize = + options.trackResize === false ? + false : true; + this.id = (_a = this._container.id) !== null && _a !== void 0 ? _a : "mapillary-fallback-container-id"; + this._container.classList + .add("mapillary-viewer"); + this._canvasContainer = this._dom + .createElement("div", "mapillary-interactive", this._container); + this._canvas = this._dom + .createElement("canvas", "mapillary-canvas"); + this._canvas.style.position = "absolute"; + this._canvas.setAttribute("tabindex", "0"); + // Add DOM container after canvas container to + // render DOM elements on top of the interactive + // canvas. + this._domContainer = this._dom + .createElement("div", "mapillary-dom", this._container); + this.configurationService = new ConfigurationService(options); + this.renderService = + new RenderService(this._container, stateService.currentState$, options.renderMode); + this.glRenderer = + new GLRenderer(this._canvas, this._canvasContainer, this.renderService); + this.domRenderer = + new DOMRenderer(this._domContainer, this.renderService, stateService.currentState$); + this.keyboardService = + new KeyboardService(this._canvasContainer); + this.mouseService = + new MouseService(this._container, this._canvasContainer, this._domContainer, document); + this.touchService = + new TouchService(this._canvasContainer, this._domContainer); + this.spriteService = + new SpriteService(options.sprite); + window.addEventListener('resize', this._onWindowResize, false); } - get active$() { - return this._active$; + get canvas() { + return !!this._canvas.parentNode ? + this._canvas : null; } - get activate$() { - return this._activeSubject$; + get canvasContainer() { + return this._canvasContainer; } - get documentMouseMove$() { - return this._documentMouseMove$; + get container() { + return this._container; + } + get domContainer() { + return this._domContainer; } - get documentMouseUp$() { - return this._documentMouseUp$; + remove() { + window.removeEventListener('resize', this._onWindowResize, false); + this.spriteService.dispose(); + this.touchService.dispose(); + this.mouseService.dispose(); + this.glRenderer.remove(); + this.domRenderer.remove(); + this.renderService.dispose(); + this._removeNode(this._canvasContainer); + this._removeNode(this._domContainer); + this._container.classList + .remove("mapillary-viewer"); } - get domMouseDragStart$() { - return this._domMouseDragStart$; + _removeNode(node) { + if (node.parentNode) { + node.parentNode.removeChild(node); + } } - get domMouseDrag$() { - return this._domMouseDrag$; + } + + class CacheService { + constructor(_graphService, _stateService, _api) { + this._graphService = _graphService; + this._stateService = _stateService; + this._api = _api; + this._subscriptions = new SubscriptionHolder(); + this._started = false; + this._cellDepth = 1; } - get domMouseDragEnd$() { - return this._domMouseDragEnd$; + get started() { + return this._started; } - get domMouseDown$() { - return this._domMouseDown$; + configure(configuration) { + if (!configuration) { + this._cellDepth = 1; + return; + } + this._cellDepth = Math.max(1, Math.min(3, configuration.cellDepth)); } - get domMouseMove$() { - return this._domMouseMove$; + start() { + if (this._started) { + return; + } + const subs = this._subscriptions; + subs.push(this._stateService.currentState$ + .pipe(distinctUntilChanged(undefined, (frame) => { + return frame.state.currentImage.id; + }), map((frame) => { + const state = frame.state; + const trajectory = state.trajectory; + const trajectoryKeys = trajectory + .map((n) => { + return n.id; + }); + const sequenceKey = trajectory[trajectory.length - 1].sequenceId; + return [ + trajectoryKeys, + state.currentImage.originalLngLat, + sequenceKey, + ]; + }), bufferCount(1, 5), withLatestFrom(this._graphService.graphMode$), switchMap(([keepBuffer, graphMode]) => { + const keepKeys = keepBuffer[0][0]; + const lngLat = keepBuffer[0][1]; + const geometry = this._api.data.geometry; + const cellId = geometry.lngLatToCellId(lngLat); + const keepCellIds = connectedComponent(cellId, this._cellDepth, geometry); + const keepSequenceKey = graphMode === GraphMode.Sequence ? + keepBuffer[0][2] : + undefined; + return this._graphService + .uncache$(keepKeys, keepCellIds, keepSequenceKey); + })) + .subscribe(() => { })); + subs.push(this._graphService.graphMode$ + .pipe(skip(1), withLatestFrom(this._stateService.currentState$), switchMap(([mode, frame]) => { + return mode === GraphMode.Sequence ? + this._keyToEdges(frame.state.currentImage.id, (image) => { + return image.sequenceEdges$; + }) : + from(frame.state.trajectory + .map((image) => { + return image.id; + }) + .slice(frame.state.currentIndex)).pipe(mergeMap((key) => { + return this._keyToEdges(key, (image) => { + return image.spatialEdges$; + }); + }, 6)); + })) + .subscribe(() => { })); + subs.push(this._graphService.dataAdded$ + .pipe(withLatestFrom(this._stateService.currentId$), switchMap(([_, imageId]) => { + return this._graphService.cacheImage$(imageId); + })) + .subscribe(() => { })); + this._started = true; } - get mouseOwner$() { - return this._mouseOwner$; + stop() { + if (!this._started) { + return; + } + this._subscriptions.unsubscribe(); + this._started = false; } - get mouseDown$() { - return this._mouseDown$; + _keyToEdges(key, imageToEdgeMap) { + return this._graphService.cacheImage$(key).pipe(switchMap(imageToEdgeMap), first((status) => { + return status.cached; + }), timeout(15000), catchError((error) => { + console.error(`Failed to cache edges (${key}).`, error); + return empty(); + })); } - get mouseEnter$() { - return this._mouseEnter$; + } + + class LoadingService { + constructor() { + this._loadersSubject$ = new Subject(); + this._loaders$ = this._loadersSubject$.pipe(scan((loaders, loader) => { + if (loader.task !== undefined) { + loaders[loader.task] = loader.loading; + } + return loaders; + }, {}), startWith({}), publishReplay(1), refCount()); } - get mouseMove$() { - return this._mouseMove$; + get loading$() { + return this._loaders$.pipe(map((loaders) => { + for (const key in loaders) { + if (!loaders.hasOwnProperty(key)) { + continue; + } + if (loaders[key]) { + return true; + } + } + return false; + }), debounceTime(100), distinctUntilChanged()); } - get mouseLeave$() { - return this._mouseLeave$; + taskLoading$(task) { + return this._loaders$.pipe(map((loaders) => { + return !!loaders[task]; + }), debounceTime(100), distinctUntilChanged()); } - get mouseOut$() { - return this._mouseOut$; + startLoading(task) { + this._loadersSubject$.next({ loading: true, task: task }); } - get mouseOver$() { - return this._mouseOver$; + stopLoading(task) { + this._loadersSubject$.next({ loading: false, task: task }); } - get mouseUp$() { - return this._mouseUp$; + } + + var PanMode; + (function (PanMode) { + PanMode[PanMode["Disabled"] = 0] = "Disabled"; + PanMode[PanMode["Enabled"] = 1] = "Enabled"; + PanMode[PanMode["Started"] = 2] = "Started"; + })(PanMode || (PanMode = {})); + class PanService { + constructor(graphService, stateService, enabled, graphCalculator, spatial, viewportCoords) { + this._subscriptions = new SubscriptionHolder(); + this._graphService = graphService; + this._stateService = stateService; + this._graphCalculator = graphCalculator !== null && graphCalculator !== void 0 ? graphCalculator : new GraphCalculator(); + this._spatial = spatial !== null && spatial !== void 0 ? spatial : new Spatial(); + this._viewportCoords = viewportCoords !== null && viewportCoords !== void 0 ? viewportCoords : new ViewportCoords(); + this._mode = enabled !== false ? + PanMode.Enabled : PanMode.Disabled; + this._panImagesSubject$ = new Subject(); + this._panImages$ = this._panImagesSubject$.pipe(startWith([]), publishReplay(1), refCount()); + this._subscriptions.push(this._panImages$.subscribe()); } - get click$() { - return this._click$; + get panImages$() { + return this._panImages$; } - get dblClick$() { - return this._dblClick$; + dispose() { + this.stop(); + if (this._panImagesSubscription != null) { + this._panImagesSubscription.unsubscribe(); + } + this._subscriptions.unsubscribe(); } - get contextMenu$() { - return this._consistentContextMenu$; + enable() { + if (this._mode !== PanMode.Disabled) { + return; + } + this._mode = PanMode.Enabled; + this.start(); } - get mouseWheel$() { - return this._mouseWheel$; + disable() { + if (this._mode === PanMode.Disabled) { + return; + } + this.stop(); + this._mode = PanMode.Disabled; } - get mouseDragStart$() { - return this._mouseDragStart$; + start() { + if (this._mode !== PanMode.Enabled) { + return; + } + const panImages$ = this._stateService.currentImage$.pipe(switchMap((current) => { + if (!current.merged || isSpherical(current.cameraType)) { + return of([]); + } + const current$ = of(current); + const bounds = this._graphCalculator.boundingBoxCorners(current.lngLat, 20); + const adjacent$ = this._graphService + .cacheBoundingBox$(bounds[0], bounds[1]).pipe(catchError((error) => { + console.error(`Failed to cache periphery bounding box (${current.id})`, error); + return empty(); + }), map((images) => { + if (isSpherical(current.cameraType)) { + return []; + } + const potential = []; + for (const image of images) { + if (image.id === current.id) { + continue; + } + if (image.mergeId !== current.mergeId) { + continue; + } + if (isSpherical(image.cameraType)) { + continue; + } + if (this._distance(image, current) > 4) { + continue; + } + potential.push(image); + } + return potential; + })); + return combineLatest(current$, adjacent$).pipe(withLatestFrom(this._stateService.reference$), map(([[cn, adjacent], reference]) => { + const currentDirection = this._spatial.viewingDirection(cn.rotation); + const currentTranslation = computeTranslation({ lat: cn.lngLat.lat, lng: cn.lngLat.lng, alt: cn.computedAltitude }, cn.rotation, reference); + const currentTransform = this._createTransform(cn, currentTranslation); + const currentAzimuthal = this._spatial.wrap(this._spatial.azimuthal(currentDirection.toArray(), currentTransform.upVector().toArray()), 0, 2 * Math.PI); + const currentProjectedPoints = this._computeProjectedPoints(currentTransform); + const currentHFov = this._computeHorizontalFov(currentProjectedPoints) / 180 * Math.PI; + const preferredOverlap = Math.PI / 8; + let left = undefined; + let right = undefined; + for (const a of adjacent) { + const translation = computeTranslation({ lat: a.lngLat.lat, lng: a.lngLat.lng, alt: a.computedAltitude }, a.rotation, reference); + const transform = this._createTransform(a, translation); + const projectedPoints = this._computeProjectedPoints(transform); + const hFov = this._computeHorizontalFov(projectedPoints) / 180 * Math.PI; + const direction = this._spatial.viewingDirection(a.rotation); + const azimuthal = this._spatial.wrap(this._spatial.azimuthal(direction.toArray(), transform.upVector().toArray()), 0, 2 * Math.PI); + const directionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, direction.x, direction.y); + let overlap = Number.NEGATIVE_INFINITY; + if (directionChange > 0) { + if (currentAzimuthal > azimuthal) { + overlap = currentAzimuthal - 2 * Math.PI + currentHFov / 2 - (azimuthal - hFov / 2); + } + else { + overlap = currentAzimuthal + currentHFov / 2 - (azimuthal - hFov / 2); + } + } + else { + if (currentAzimuthal < azimuthal) { + overlap = azimuthal + hFov / 2 - (currentAzimuthal + 2 * Math.PI - currentHFov / 2); + } + else { + overlap = azimuthal + hFov / 2 - (currentAzimuthal - currentHFov / 2); + } + } + const nonOverlap = Math.abs(hFov - overlap); + const distanceCost = this._distance(a, cn); + const timeCost = Math.min(this._timeDifference(a, cn), 4); + const overlapCost = 20 * Math.abs(overlap - preferredOverlap); + const fovCost = Math.min(5, 1 / Math.min(hFov / currentHFov, 1)); + const nonOverlapCost = overlap > 0 ? -2 * nonOverlap : 0; + const cost = distanceCost + timeCost + overlapCost + fovCost + nonOverlapCost; + if (overlap > 0 && + overlap < 0.5 * currentHFov && + overlap < 0.5 * hFov && + nonOverlap > 0.5 * currentHFov) { + if (directionChange > 0) { + if (!left) { + left = [cost, a, transform, hFov]; + } + else { + if (cost < left[0]) { + left = [cost, a, transform, hFov]; + } + } + } + else { + if (!right) { + right = [cost, a, transform, hFov]; + } + else { + if (cost < right[0]) { + right = [cost, a, transform, hFov]; + } + } + } + } + } + const panImagess = []; + if (!!left) { + panImagess.push([left[1], left[2], left[3]]); + } + if (!!right) { + panImagess.push([right[1], right[2], right[3]]); + } + return panImagess; + }), startWith([])); + })); + this._panImagesSubscription = this._stateService.currentState$.pipe(map((frame) => { + return frame.state.imagesAhead > 0; + }), distinctUntilChanged(), switchMap((traversing) => { + return traversing ? of([]) : panImages$; + })) + .subscribe((panImages) => { + this._panImagesSubject$.next(panImages); + }); + this._mode = PanMode.Started; } - get mouseDrag$() { - return this._mouseDrag$; + stop() { + if (this._mode !== PanMode.Started) { + return; + } + this._panImagesSubscription.unsubscribe(); + this._panImagesSubject$.next([]); + this._mode = PanMode.Enabled; } - get mouseDragEnd$() { - return this._mouseDragEnd$; + _distance(image, reference) { + const [x, y, z] = geodeticToEnu(image.lngLat.lng, image.lngLat.lat, image.computedAltitude, reference.lngLat.lng, reference.lngLat.lat, reference.computedAltitude); + return Math.sqrt(x * x + y * y + z * z); } - get mouseRightDragStart$() { - return this._mouseRightDragStart$; + _timeDifference(image, reference) { + const milliSecond = (1000 * 60 * 60 * 24 * 30); + return Math.abs(image.capturedAt - reference.capturedAt) / milliSecond; } - get mouseRightDrag$() { - return this._mouseRightDrag$; + _createTransform(image, translation) { + return new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.assetsCached ? image.image : undefined, undefined, image.cameraParameters, image.cameraType); } - get mouseRightDragEnd$() { - return this._mouseRightDragEnd$; + _computeProjectedPoints(transform) { + const vertices = [[1, 0]]; + const directions = [[0, 0.5]]; + const pointsPerLine = 20; + return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords); } - get proximateClick$() { - return this._proximateClick$; + _computeHorizontalFov(projectedPoints) { + const fovs = projectedPoints + .map((projectedPoint) => { + return this._coordToFov(projectedPoint[0]); + }); + const fov = Math.min(...fovs); + return fov; } - get staticClick$() { - return this._staticClick$; + _coordToFov(x) { + return 2 * Math.atan(x) * 180 / Math.PI; } - get windowBlur$() { - return this._windowBlur$; + } + + /** + * @class API + * + * @classdesc Provides methods for access to the API. + */ + class APIWrapper { + constructor(_data) { + this._data = _data; } - dispose() { - this._subscriptions.unsubscribe(); + get data() { + return this._data; } - claimMouse(name, zindex) { - this._claimMouse$.next({ name: name, zindex: zindex }); + getCoreImages$(cellId) { + return this._wrap$(this._data.getCoreImages(cellId)); } - unclaimMouse(name) { - this._claimMouse$.next({ name: name, zindex: null }); + getImages$(imageIds) { + return this._wrap$(this._data.getImages(imageIds)); } - deferPixels(name, deferPixels) { - this._deferPixelClaims$.next({ name: name, deferPixels: deferPixels }); + getImageTiles$(tiles) { + return this._wrap$(this._data.getImageTiles(tiles)); } - undeferPixels(name) { - this._deferPixelClaims$.next({ name: name, deferPixels: null }); + getSequence$(sequenceId) { + return this._wrap$(this._data.getSequence(sequenceId)); } - claimWheel(name, zindex) { - this._claimWheel$.next({ name: name, zindex: zindex }); + getSpatialImages$(imageIds) { + return this._wrap$(this._data.getSpatialImages(imageIds)); } - unclaimWheel(name) { - this._claimWheel$.next({ name: name, zindex: null }); + setAccessToken(accessToken) { + this._data.setAccessToken(accessToken); } - filtered$(name, observable$) { - return this._filtered(name, observable$, this._mouseOwner$); + _wrap$(promise) { + return Observable.create((subscriber) => { + promise.then((value) => { + subscriber.next(value); + subscriber.complete(); + }, (error) => { + subscriber.error(error); + }); + }); } - filteredWheel$(name, observable$) { - return this._filtered(name, observable$, this._wheelOwner$); + } + + /** + * @class GraphService + * + * @classdesc Represents a service for graph operations. + */ + class GraphService { + /** + * Create a new graph service instance. + * + * @param {Graph} graph - Graph instance to be operated on. + */ + constructor(graph) { + this._dataAdded$ = new Subject(); + this._subscriptions = new SubscriptionHolder(); + this._onDataAdded = (event) => { + this._graph$ + .pipe(first(), mergeMap(graph => { + return graph.updateCells$(event.cellIds).pipe(tap(() => { graph.resetSpatialEdges(); })); + })) + .subscribe(cellId => { this._dataAdded$.next(cellId); }); + }; + const subs = this._subscriptions; + this._graph$ = concat(of(graph), graph.changed$).pipe(publishReplay(1), refCount()); + subs.push(this._graph$.subscribe(() => { })); + this._graphMode = GraphMode.Spatial; + this._graphModeSubject$ = new Subject(); + this._graphMode$ = this._graphModeSubject$.pipe(startWith(this._graphMode), publishReplay(1), refCount()); + subs.push(this._graphMode$.subscribe(() => { })); + this._firstGraphSubjects$ = []; + this._initializeCacheSubscriptions = []; + this._sequenceSubscriptions = []; + this._spatialSubscriptions = []; + graph.api.data.on("datacreate", this._onDataAdded); } - _createDeferredMouseMove$(origin, mouseMove$) { - return mouseMove$.pipe(map((mouseMove) => { - const deltaX = mouseMove.clientX - origin.clientX; - const deltaY = mouseMove.clientY - origin.clientY; - return [mouseMove, Math.sqrt(deltaX * deltaX + deltaY * deltaY)]; - }), withLatestFrom(this._deferPixels$), filter(([[, delta], deferPixels]) => { - return delta > deferPixels; - }), map(([[mouseMove]]) => { - return mouseMove; - })); + /** + * Get dataAdded$. + * + * @returns {Observable} Observable emitting + * a cell id every time data has been added to a cell. + */ + get dataAdded$() { + return this._dataAdded$; } - _createMouseDrag$(mouseDragStartInitiate$, stop$) { - return mouseDragStartInitiate$.pipe(map(([, mouseMove]) => { - return mouseMove; - }), switchMap((mouseMove) => { - return concat(of(mouseMove), this._documentMouseMove$).pipe(takeUntil(stop$)); + /** + * Get filter observable. + * + * @desciption Emits the filter every time it has changed. + * + * @returns {Observable} Observable + * emitting the filter function every time it is set. + */ + get filter$() { + return this._graph$.pipe(first(), mergeMap((graph) => { + return graph.filter$; })); } - _createMouseDragEnd$(mouseDragStart$, stop$) { - return mouseDragStart$.pipe(switchMap(() => { - return stop$.pipe(first()); - })); + /** + * Get graph mode observable. + * + * @description Emits the current graph mode. + * + * @returns {Observable} Observable + * emitting the current graph mode when it changes. + */ + get graphMode$() { + return this._graphMode$; } - _createMouseDragStart$(mouseDragStartInitiate$) { - return mouseDragStartInitiate$.pipe(map(([mouseDown]) => { - return mouseDown; + /** + * Cache full images in a bounding box. + * + * @description When called, the full properties of + * the image are retrieved. The image cache is not initialized + * for any new images retrieved and the image assets are not + * retrieved, {@link cacheImage$} needs to be called for caching + * assets. + * + * @param {LngLat} sw - South west corner of bounding box. + * @param {LngLat} ne - North east corner of bounding box. + * @return {Observable>} Observable emitting a single item, + * the images of the bounding box, when they have all been retrieved. + * @throws {Error} Propagates any IO image caching errors to the caller. + */ + cacheBoundingBox$(sw, ne) { + return this._graph$.pipe(first(), mergeMap((graph) => { + return graph.cacheBoundingBox$(sw, ne); })); } - _createMouseDragInitiate$(button, mouseDown$, stop$, defer) { - return mouseDown$.pipe(filter((mouseDown) => { - return this._mouseButton(mouseDown) === button; - }), switchMap((mouseDown) => { - return combineLatest(of(mouseDown), defer ? - this._createDeferredMouseMove$(mouseDown, this._documentMouseMove$) : - this._documentMouseMove$).pipe(takeUntil(stop$), take(1)); + /** + * Cache full images in a cell. + * + * @description When called, the full properties of + * the image are retrieved. The image cache is not initialized + * for any new images retrieved and the image assets are not + * retrieved, {@link cacheImage$} needs to be called for caching + * assets. + * + * @param {string} cellId - Id of the cell. + * @return {Observable>} Observable emitting a single item, + * the images of the cell, when they have all been retrieved. + * @throws {Error} Propagates any IO image caching errors to the caller. + */ + cacheCell$(cellId) { + return this._graph$.pipe(first(), mergeMap((graph) => { + return graph.cacheCell$(cellId); })); } - _createOwner$(claim$) { - return claim$.pipe(scan((claims, claim) => { - if (claim.zindex == null) { - delete claims[claim.name]; + /** + * Cache a image in the graph and retrieve it. + * + * @description When called, the full properties of + * the image are retrieved and the image cache is initialized. + * After that the image assets are cached and the image + * is emitted to the observable when. + * In parallel to caching the image assets, the sequence and + * spatial edges of the image are cached. For this, the sequence + * of the image and the required tiles and spatial images are + * retrieved. The sequence and spatial edges may be set before + * or after the image is returned. + * + * @param {string} id - Id of the image to cache. + * @return {Observable} Observable emitting a single item, + * the image, when it has been retrieved and its assets are cached. + * @throws {Error} Propagates any IO image caching errors to the caller. + */ + cacheImage$(id) { + const firstGraphSubject$ = new Subject(); + this._firstGraphSubjects$.push(firstGraphSubject$); + const firstGraph$ = firstGraphSubject$.pipe(publishReplay(1), refCount()); + const image$ = firstGraph$.pipe(map((graph) => { + return graph.getNode(id); + }), mergeMap((image) => { + return image.assetsCached ? + of(image) : + image.cacheAssets$(); + }), publishReplay(1), refCount()); + image$.subscribe(undefined, (error) => { + console.error(`Failed to cache image (${id}).`, error); + }); + let initializeCacheSubscription; + initializeCacheSubscription = this._graph$.pipe(first(), mergeMap((graph) => { + if (graph.isCachingFull(id) || !graph.hasNode(id)) { + return graph.cacheFull$(id); } - else { - claims[claim.name] = claim.zindex; + if (graph.isCachingFill(id) || !graph.getNode(id).complete) { + return graph.cacheFill$(id); } - return claims; - }, {}), map((claims) => { - let owner = null; - let zIndexMax = -1; - for (const name in claims) { - if (!claims.hasOwnProperty(name)) { - continue; + return of(graph); + }), tap((graph) => { + if (!graph.hasNode(id)) { + throw new GraphMapillaryError(`Failed to cache image (${id})`); + } + if (!graph.hasInitializedCache(id)) { + graph.initializeCache(id); + } + }), finalize(() => { + if (initializeCacheSubscription == null) { + return; + } + this._removeFromArray(initializeCacheSubscription, this._initializeCacheSubscriptions); + this._removeFromArray(firstGraphSubject$, this._firstGraphSubjects$); + })) + .subscribe((graph) => { + firstGraphSubject$.next(graph); + firstGraphSubject$.complete(); + }, (error) => { + firstGraphSubject$.error(error); + }); + if (!initializeCacheSubscription.closed) { + this._initializeCacheSubscriptions.push(initializeCacheSubscription); + } + const graphSequence$ = firstGraph$.pipe(catchError(() => { + return empty(); + }), mergeMap((graph) => { + if (graph.isCachingNodeSequence(id) || !graph.hasNodeSequence(id)) { + return graph.cacheNodeSequence$(id); + } + return of(graph); + }), publishReplay(1), refCount()); + let sequenceSubscription; + sequenceSubscription = graphSequence$.pipe(tap((graph) => { + if (!graph.getNode(id).sequenceEdges.cached) { + graph.cacheSequenceEdges(id); + } + }), finalize(() => { + if (sequenceSubscription == null) { + return; + } + this._removeFromArray(sequenceSubscription, this._sequenceSubscriptions); + })) + .subscribe(() => { return; }, (error) => { + console.error(`Failed to cache sequence edges (${id}).`, error); + }); + if (!sequenceSubscription.closed) { + this._sequenceSubscriptions.push(sequenceSubscription); + } + if (this._graphMode === GraphMode.Spatial) { + let spatialSubscription; + spatialSubscription = firstGraph$.pipe(catchError(() => { + return empty(); + }), expand((graph) => { + if (graph.hasTiles(id)) { + return empty(); } - if (claims[name] > zIndexMax) { - zIndexMax = claims[name]; - owner = name; + return from(graph.cacheTiles$(id)).pipe(mergeMap((graph$) => { + return graph$.pipe(mergeMap((g) => { + if (g.isCachingTiles(id)) { + return empty(); + } + return of(g); + }), catchError((error) => { + console.error(`Failed to cache tile data (${id}).`, error); + return empty(); + })); + })); + }), takeLast(1), mergeMap((graph) => { + if (graph.hasSpatialArea(id)) { + return of(graph); + } + return from(graph.cacheSpatialArea$(id)).pipe(mergeMap((graph$) => { + return graph$.pipe(catchError((error) => { + console.error(`Failed to cache spatial images (${id}).`, error); + return empty(); + })); + })); + }), takeLast(1), mergeMap((graph) => { + return graph.hasNodeSequence(id) ? + of(graph) : + graph.cacheNodeSequence$(id); + }), tap((graph) => { + if (!graph.getNode(id).spatialEdges.cached) { + graph.cacheSpatialEdges(id); + } + }), finalize(() => { + if (spatialSubscription == null) { + return; } + this._removeFromArray(spatialSubscription, this._spatialSubscriptions); + })) + .subscribe(() => { return; }, (error) => { + const message = `Failed to cache spatial edges (${id}).`; + console.error(message, error); + }); + if (!spatialSubscription.closed) { + this._spatialSubscriptions.push(spatialSubscription); } - return owner; - }), startWith(null)); - } - _filtered(name, observable$, owner$) { - return observable$.pipe(withLatestFrom(owner$), filter(([, owner]) => { - return owner === name; - }), map(([item]) => { - return item; - })); - } - _mouseButton(event) { - const upOrDown = event.type === "pointerdown" || event.type === "pointerup"; - const InstallTrigger = window.InstallTrigger; - if (upOrDown && - typeof InstallTrigger !== 'undefined' && - event.button === RIGHT_BUTTON && event.ctrlKey && - window.navigator.platform.toUpperCase().indexOf('MAC') >= 0) { - // Fix for the fact that Firefox (detected by InstallTrigger) - // on Mac determines e.button = 2 when using Control + left click. - return LEFT_BUTTON; } - return event.button; - } - _buttonReleased(event, button) { - // Right button `mouseup` is not fired in - // Chrome on Mac outside the window or iframe. If - // the button is no longer pressed during move - // it may have been released and drag stop - // should be emitted. - const flag = BUTTONS_MAP[button]; - return event.buttons === undefined || (event.buttons & flag) !== flag; + return image$.pipe(first((image) => { + return image.assetsCached; + })); } - _isMousePen(event) { - const type = event.pointerType; - return type === "mouse" || type === "pen"; + /** + * Cache a sequence in the graph and retrieve it. + * + * @param {string} sequenceId - Sequence id. + * @returns {Observable} Observable emitting a single item, + * the sequence, when it has been retrieved and its assets are cached. + * @throws {Error} Propagates any IO image caching errors to the caller. + */ + cacheSequence$(sequenceId) { + return this._graph$.pipe(first(), mergeMap((graph) => { + if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { + return graph.cacheSequence$(sequenceId); + } + return of(graph); + }), map((graph) => { + return graph.getSequence(sequenceId); + })); } - } - - class SpriteAtlas { - set json(value) { - this._json = value; + /** + * Cache a sequence and its images in the graph and retrieve the sequence. + * + * @description Caches a sequence and its assets are cached and + * retrieves all images belonging to the sequence. The image assets + * or edges will not be cached. + * + * @param {string} sequenceId - Sequence id. + * @param {string} referenceImageId - Id of image to use as reference + * for optimized caching. + * @returns {Observable} Observable emitting a single item, + * the sequence, when it has been retrieved, its assets are cached and + * all images belonging to the sequence has been retrieved. + * @throws {Error} Propagates any IO image caching errors to the caller. + */ + cacheSequenceImages$(sequenceId, referenceImageId) { + return this._graph$.pipe(first(), mergeMap((graph) => { + if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { + return graph.cacheSequence$(sequenceId); + } + return of(graph); + }), mergeMap((graph) => { + if (graph.isCachingSequenceNodes(sequenceId) || !graph.hasSequenceNodes(sequenceId)) { + return graph.cacheSequenceNodes$(sequenceId, referenceImageId); + } + return of(graph); + }), map((graph) => { + return graph.getSequence(sequenceId); + })); } - set image(value) { - this._image = value; - this._texture = new Texture(this._image); - this._texture.minFilter = NearestFilter; + /** + * Dispose the graph service and its children. + */ + dispose() { + this._graph$ + .pipe(first()) + .subscribe((graph) => { graph.unsubscribe(); }); + this._subscriptions.unsubscribe(); } - get loaded() { - return !!(this._image && this._json); + /** + * Set a spatial edge filter on the graph. + * + * @description Resets the spatial edges of all cached images. + * + * @param {FilterExpression} filter - Filter expression to be applied. + * @return {Observable} Observable emitting a single item, + * the graph, when the spatial edges have been reset. + */ + setFilter$(filter) { + this._resetSubscriptions(this._spatialSubscriptions); + return this._graph$.pipe(first(), tap((graph) => { + graph.resetSpatialEdges(); + graph.setFilter(filter); + }), map(() => { + return undefined; + })); } - getGLSprite(name) { - if (!this.loaded) { - throw new Error("Sprites cannot be retrieved before the atlas is loaded."); + /** + * Set the graph mode. + * + * @description If graph mode is set to spatial, caching + * is performed with emphasis on spatial edges. If graph + * mode is set to sequence no tile data is requested and + * no spatial edges are computed. + * + * When setting graph mode to sequence all spatial + * subscriptions are aborted. + * + * @param {GraphMode} mode - Graph mode to set. + */ + setGraphMode(mode) { + if (this._graphMode === mode) { + return; } - let definition = this._json[name]; - if (!definition) { - console.warn("Sprite with key" + name + "does not exist in sprite definition."); - return new Object3D(); + if (mode === GraphMode.Sequence) { + this._resetSubscriptions(this._spatialSubscriptions); } - let texture = this._texture.clone(); - texture.needsUpdate = true; - let width = this._image.width; - let height = this._image.height; - texture.offset.x = definition.x / width; - texture.offset.y = (height - definition.y - definition.height) / height; - texture.repeat.x = definition.width / width; - texture.repeat.y = definition.height / height; - let material = new SpriteMaterial({ map: texture }); - return new Sprite(material); + this._graphMode = mode; + this._graphModeSubject$.next(this._graphMode); } - getDOMSprite(name, float) { - if (!this.loaded) { - throw new Error("Sprites cannot be retrieved before the atlas is loaded."); - } - if (float == null) { - float = exports.Alignment.Center; - } - let definition = this._json[name]; - if (!definition) { - console.warn("Sprite with key" + name + "does not exist in sprite definition."); - return virtualDom.h("div", {}, []); - } - let clipTop = definition.y; - let clipRigth = definition.x + definition.width; - let clipBottom = definition.y + definition.height; - let clipLeft = definition.x; - let left = -definition.x; - let top = -definition.y; - let height = this._image.height; - let width = this._image.width; - switch (float) { - case exports.Alignment.Bottom: - case exports.Alignment.Center: - case exports.Alignment.Top: - left -= definition.width / 2; - break; - case exports.Alignment.BottomLeft: - case exports.Alignment.Left: - case exports.Alignment.TopLeft: - left -= definition.width; - break; - case exports.Alignment.BottomRight: - case exports.Alignment.Right: - case exports.Alignment.TopRight: - } - switch (float) { - case exports.Alignment.Center: - case exports.Alignment.Left: - case exports.Alignment.Right: - top -= definition.height / 2; - break; - case exports.Alignment.Top: - case exports.Alignment.TopLeft: - case exports.Alignment.TopRight: - top -= definition.height; - break; - case exports.Alignment.Bottom: - case exports.Alignment.BottomLeft: - case exports.Alignment.BottomRight: - } - let pixelRatioInverse = 1 / definition.pixelRatio; - clipTop *= pixelRatioInverse; - clipRigth *= pixelRatioInverse; - clipBottom *= pixelRatioInverse; - clipLeft *= pixelRatioInverse; - left *= pixelRatioInverse; - top *= pixelRatioInverse; - height *= pixelRatioInverse; - width *= pixelRatioInverse; - let properties = { - src: this._image.src, - style: { - clip: `rect(${clipTop}px, ${clipRigth}px, ${clipBottom}px, ${clipLeft}px)`, - height: `${height}px`, - left: `${left}px`, - position: "absolute", - top: `${top}px`, - width: `${width}px`, - }, - }; - return virtualDom.h("img", properties, []); + /** + * Reset the graph. + * + * @description Resets the graph but keeps the images of the + * supplied ids. + * + * @param {Array} keepIds - Ids of images to keep in graph. + * @return {Observable} Observable emitting a single item, + * the graph, when it has been reset. + */ + reset$(keepIds) { + this._abortSubjects(this._firstGraphSubjects$); + this._resetSubscriptions(this._initializeCacheSubscriptions); + this._resetSubscriptions(this._sequenceSubscriptions); + this._resetSubscriptions(this._spatialSubscriptions); + return this._graph$.pipe(first(), tap((graph) => { + graph.reset(keepIds); + }), map(() => { + return undefined; + })); } - } - class SpriteService { - constructor(sprite) { - this._retina = window.devicePixelRatio > 1; - this._spriteAtlasOperation$ = new Subject(); - this._spriteAtlas$ = this._spriteAtlasOperation$.pipe(startWith((atlas) => { - return atlas; - }), scan((atlas, operation) => { - return operation(atlas); - }, new SpriteAtlas()), publishReplay(1), refCount()); - this._atlasSubscription = this._spriteAtlas$ - .subscribe(() => { }); - if (sprite == null) { - return; - } - let format = this._retina ? "@2x" : ""; - let imageXmlHTTP = new XMLHttpRequest(); - imageXmlHTTP.open("GET", sprite + format + ".png", true); - imageXmlHTTP.responseType = "arraybuffer"; - imageXmlHTTP.onload = () => { - let image = new Image(); - image.onload = () => { - this._spriteAtlasOperation$.next((atlas) => { - atlas.image = image; - return atlas; - }); - }; - let blob = new Blob([imageXmlHTTP.response]); - image.src = window.URL.createObjectURL(blob); - }; - imageXmlHTTP.onerror = (error) => { - console.error(new Error(`Failed to fetch sprite sheet (${sprite}${format}.png)`)); - }; - imageXmlHTTP.send(); - let jsonXmlHTTP = new XMLHttpRequest(); - jsonXmlHTTP.open("GET", sprite + format + ".json", true); - jsonXmlHTTP.responseType = "text"; - jsonXmlHTTP.onload = () => { - let json = JSON.parse(jsonXmlHTTP.response); - this._spriteAtlasOperation$.next((atlas) => { - atlas.json = json; - return atlas; - }); - }; - jsonXmlHTTP.onerror = (error) => { - console.error(new Error(`Failed to fetch sheet (${sprite}${format}.json)`)); - }; - jsonXmlHTTP.send(); + /** + * Uncache the graph. + * + * @description Uncaches the graph by removing tiles, images and + * sequences. Keeps the images of the supplied ids and the tiles + * related to those images. + * + * @param {Array} keepIds - Ids of images to keep in graph. + * @param {Array} keepCellIds - Ids of cells to keep in graph. + * @param {string} keepSequenceId - Optional id of sequence + * for which the belonging images should not be disposed or + * removed from the graph. These images may still be uncached if + * not specified in keep ids param. + * @return {Observable} Observable emitting a single item, + * the graph, when the graph has been uncached. + */ + uncache$(keepIds, keepCellIds, keepSequenceId) { + return this._graph$.pipe(first(), tap((graph) => { + graph.uncache(keepIds, keepCellIds, keepSequenceId); + }), map(() => { + return undefined; + })); } - get spriteAtlas$() { - return this._spriteAtlas$; + _abortSubjects(subjects) { + for (const subject of subjects.slice()) { + this._removeFromArray(subject, subjects); + subject.error(new Error("Cache image request was aborted.")); + } } - dispose() { - this._atlasSubscription.unsubscribe(); + _removeFromArray(object, objects) { + const index = objects.indexOf(object); + if (index !== -1) { + objects.splice(index, 1); + } + } + _resetSubscriptions(subscriptions) { + for (const subscription of subscriptions.slice()) { + this._removeFromArray(subscription, subscriptions); + if (!subscription.closed) { + subscription.unsubscribe(); + } + } } } - class TouchService { - constructor(canvasContainer, domContainer) { - this._subscriptions = new SubscriptionHolder(); - const subs = this._subscriptions; - this._activeSubject$ = new BehaviorSubject(false); - this._active$ = this._activeSubject$.pipe(distinctUntilChanged(), publishReplay(1), refCount()); - subs.push(fromEvent(domContainer, "touchmove") - .subscribe((event) => { - event.preventDefault(); - })); - this._touchStart$ = fromEvent(canvasContainer, "touchstart"); - this._touchMove$ = fromEvent(canvasContainer, "touchmove"); - this._touchEnd$ = fromEvent(canvasContainer, "touchend"); - this._touchCancel$ = fromEvent(canvasContainer, "touchcancel"); - const tapStart$ = this._touchStart$.pipe(filter((te) => { - return te.touches.length === 1 && te.targetTouches.length === 1; - }), share()); - this._doubleTap$ = tapStart$.pipe(bufferWhen(() => { - return tapStart$.pipe(first(), switchMap(() => { - return merge(timer(300), tapStart$).pipe(take(1)); - })); - }), filter((events) => { - return events.length === 2; - }), map((events) => { - return events[events.length - 1]; - }), share()); - subs.push(this._doubleTap$ - .subscribe((event) => { - event.preventDefault(); - })); - this._singleTouchMove$ = this._touchMove$.pipe(filter((te) => { - return te.touches.length === 1 && te.targetTouches.length === 1; - }), share()); - let singleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { - return te.touches.length === 1 && te.targetTouches.length === 1; - })); - let multipleTouchStart$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$).pipe(filter((te) => { - return te.touches.length >= 1; - })); - let touchStop$ = merge(this._touchEnd$, this._touchCancel$).pipe(filter((te) => { - return te.touches.length === 0; - })); - this._singleTouchDragStart$ = singleTouchStart$.pipe(mergeMap(() => { - return this._singleTouchMove$.pipe(takeUntil(merge(touchStop$, multipleTouchStart$)), take(1)); - })); - this._singleTouchDragEnd$ = singleTouchStart$.pipe(mergeMap(() => { - return merge(touchStop$, multipleTouchStart$).pipe(first()); - })); - this._singleTouchDrag$ = singleTouchStart$.pipe(switchMap(() => { - return this._singleTouchMove$.pipe(skip(1), takeUntil(merge(multipleTouchStart$, touchStop$))); - })); - let touchesChanged$ = merge(this._touchStart$, this._touchEnd$, this._touchCancel$); - this._pinchStart$ = touchesChanged$.pipe(filter((te) => { - return te.touches.length === 2 && te.targetTouches.length === 2; - })); - this._pinchEnd$ = touchesChanged$.pipe(filter((te) => { - return te.touches.length !== 2 || te.targetTouches.length !== 2; - })); - this._pinchOperation$ = new Subject(); - this._pinch$ = this._pinchOperation$.pipe(scan((pinch, operation) => { - return operation(pinch); - }, { - changeX: 0, - changeY: 0, - clientX: 0, - clientY: 0, - distance: 0, - distanceChange: 0, - distanceX: 0, - distanceY: 0, - originalEvent: null, - pageX: 0, - pageY: 0, - screenX: 0, - screenY: 0, - touch1: null, - touch2: null, - })); - const pinchSubscription = this._touchMove$.pipe(filter((te) => { - return te.touches.length === 2 && te.targetTouches.length === 2; - }), map((te) => { - return (previous) => { - let touch1 = te.touches[0]; - let touch2 = te.touches[1]; - let minX = Math.min(touch1.clientX, touch2.clientX); - let maxX = Math.max(touch1.clientX, touch2.clientX); - let minY = Math.min(touch1.clientY, touch2.clientY); - let maxY = Math.max(touch1.clientY, touch2.clientY); - let centerClientX = minX + (maxX - minX) / 2; - let centerClientY = minY + (maxY - minY) / 2; - let centerPageX = centerClientX + touch1.pageX - touch1.clientX; - let centerPageY = centerClientY + touch1.pageY - touch1.clientY; - let centerScreenX = centerClientX + touch1.screenX - touch1.clientX; - let centerScreenY = centerClientY + touch1.screenY - touch1.clientY; - let distanceX = Math.abs(touch1.clientX - touch2.clientX); - let distanceY = Math.abs(touch1.clientY - touch2.clientY); - let distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); - let distanceChange = distance - previous.distance; - let changeX = distanceX - previous.distanceX; - let changeY = distanceY - previous.distanceY; - let current = { - changeX: changeX, - changeY: changeY, - clientX: centerClientX, - clientY: centerClientY, - distance: distance, - distanceChange: distanceChange, - distanceX: distanceX, - distanceY: distanceY, - originalEvent: te, - pageX: centerPageX, - pageY: centerPageY, - screenX: centerScreenX, - screenY: centerScreenY, - touch1: touch1, - touch2: touch2, - }; - return current; - }; - })) - .subscribe(this._pinchOperation$); - subs.push(pinchSubscription); - this._pinchChange$ = this._pinchStart$.pipe(switchMap(() => { - return this._pinch$.pipe(skip(1), takeUntil(this._pinchEnd$)); - })); + class FrameGenerator { + constructor(root) { + if (root.requestAnimationFrame) { + this._cancelAnimationFrame = root.cancelAnimationFrame.bind(root); + this._requestAnimationFrame = root.requestAnimationFrame.bind(root); + } + else if (root.mozRequestAnimationFrame) { + this._cancelAnimationFrame = root.mozCancelAnimationFrame.bind(root); + this._requestAnimationFrame = root.mozRequestAnimationFrame.bind(root); + } + else if (root.webkitRequestAnimationFrame) { + this._cancelAnimationFrame = root.webkitCancelAnimationFrame.bind(root); + this._requestAnimationFrame = root.webkitRequestAnimationFrame.bind(root); + } + else if (root.msRequestAnimationFrame) { + this._cancelAnimationFrame = root.msCancelAnimationFrame.bind(root); + this._requestAnimationFrame = root.msRequestAnimationFrame.bind(root); + } + else if (root.oRequestAnimationFrame) { + this._cancelAnimationFrame = root.oCancelAnimationFrame.bind(root); + this._requestAnimationFrame = root.oRequestAnimationFrame.bind(root); + } + else { + this._cancelAnimationFrame = root.clearTimeout.bind(root); + this._requestAnimationFrame = (cb) => { return root.setTimeout(cb, 1000 / 60); }; + } } - get active$() { - return this._active$; + get cancelAnimationFrame() { + return this._cancelAnimationFrame; } - get activate$() { - return this._activeSubject$; + get requestAnimationFrame() { + return this._requestAnimationFrame; } - get doubleTap$() { - return this._doubleTap$; + } + + class StateBase { + constructor(state) { + this._spatial = new Spatial(); + this._referenceThreshold = 0.01; + this._transitionMode = state.transitionMode; + this._reference = state.reference; + this._alpha = state.alpha; + this._stateTransitionAlpha = 0; + this._camera = state.camera.clone(); + this._zoom = state.zoom; + this._currentIndex = state.currentIndex; + this._trajectory = state.trajectory.slice(); + this._trajectoryTransforms = []; + this._trajectoryCameras = []; + for (let image of this._trajectory) { + let translation = this._imageToTranslation(image, this._reference); + let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, undefined, image.cameraParameters, image.cameraType); + this._trajectoryTransforms.push(transform); + this._trajectoryCameras.push(new Camera(transform)); + } + this._currentImage = this._trajectory.length > 0 ? + this._trajectory[this._currentIndex] : + null; + this._previousImage = this._trajectory.length > 1 && this.currentIndex > 0 ? + this._trajectory[this._currentIndex - 1] : + null; + this._currentCamera = this._trajectoryCameras.length > 0 ? + this._trajectoryCameras[this._currentIndex].clone() : + new Camera(); + this._previousCamera = this._trajectoryCameras.length > 1 && this.currentIndex > 0 ? + this._trajectoryCameras[this._currentIndex - 1].clone() : + this._currentCamera.clone(); } - get touchStart$() { - return this._touchStart$; + get reference() { + return this._reference; } - get touchMove$() { - return this._touchMove$; + get alpha() { + return this._getAlpha(); } - get touchEnd$() { - return this._touchEnd$; + get stateTransitionAlpha() { + return this._getStateTransitionAlpha(); } - get touchCancel$() { - return this._touchCancel$; + get camera() { + return this._camera; } - get singleTouchDragStart$() { - return this._singleTouchDragStart$; + get zoom() { + return this._zoom; } - get singleTouchDrag$() { - return this._singleTouchDrag$; + get trajectory() { + return this._trajectory; } - get singleTouchDragEnd$() { - return this._singleTouchDragEnd$; + get currentIndex() { + return this._currentIndex; } - get pinch$() { - return this._pinchChange$; + get currentImage() { + return this._currentImage; } - get pinchStart$() { - return this._pinchStart$; + get previousImage() { + return this._previousImage; } - get pinchEnd$() { - return this._pinchEnd$; + get currentCamera() { + return this._currentCamera; } - dispose() { - this._subscriptions.unsubscribe(); + get currentTransform() { + return this._trajectoryTransforms.length > 0 ? + this._trajectoryTransforms[this.currentIndex] : null; } - } - - class ConfigurationService { - constructor(options) { - var _a, _b, _c, _d; - const host = (_b = (_a = options === null || options === void 0 ? void 0 : options.url) === null || _a === void 0 ? void 0 : _a.exploreHost) !== null && _b !== void 0 ? _b : "www.mapillary.com"; - const scheme = (_d = (_c = options === null || options === void 0 ? void 0 : options.url) === null || _c === void 0 ? void 0 : _c.scheme) !== null && _d !== void 0 ? _d : "https"; - const exploreUrl = `${scheme}://${host}`; - this._exploreUrl$ = of(exploreUrl); - const imageTiling = (options === null || options === void 0 ? void 0 : options.imageTiling) === false ? false : true; - this._imageTiling$ = of(imageTiling); + get previousTransform() { + return this._trajectoryTransforms.length > 1 && this.currentIndex > 0 ? + this._trajectoryTransforms[this.currentIndex - 1] : null; } - get exploreUrl$() { - return this._exploreUrl$; + get motionless() { + return this._motionless; } - get imageTiling$() { - return this._imageTiling$; + get transitionMode() { + return this._transitionMode; } - } - - class Container { - constructor(options, stateService, dom) { - var _a; - this._onWindowResize = () => { - if (this._trackResize) { - this.renderService.resize$.next(); - } - }; - this._dom = dom !== null && dom !== void 0 ? dom : new DOM(); - if (typeof options.container === "string") { - this._container = this._dom.document - .getElementById(options.container); - if (!this._container) { - throw new Error(`Container "${options.container}" not found.`); - } + move(delta) { } + moveTo(position) { } + rotate(delta) { } + rotateUnbounded(delta) { } + rotateWithoutInertia(delta) { } + rotateBasic(basicRotation) { } + rotateBasicUnbounded(basicRotation) { } + rotateBasicWithoutInertia(basicRotation) { } + rotateToBasic(basic) { } + setSpeed(speed) { } + zoomIn(delta, reference) { } + update(delta) { } + setCenter(center) { } + setZoom(zoom) { } + dolly(delta) { } + orbit(rotation) { } + setViewMatrix(matrix) { } + truck(direction) { } + append(images) { + if (images.length < 1) { + throw Error("Trajectory can not be empty"); } - else if (options.container instanceof HTMLElement) { - this._container = options.container; + if (this._currentIndex < 0) { + this.set(images); } else { - throw new Error(`Invalid type: "container" must be ` + - `a String or HTMLElement.`); + this._trajectory = this._trajectory.concat(images); + this._appendToTrajectories(images); } - this._trackResize = - options.trackResize === false ? - false : true; - this.id = (_a = this._container.id) !== null && _a !== void 0 ? _a : "mapillary-fallback-container-id"; - this._container.classList - .add("mapillary-viewer"); - this._canvasContainer = this._dom - .createElement("div", "mapillary-interactive", this._container); - this._canvas = this._dom - .createElement("canvas", "mapillary-canvas"); - this._canvas.style.position = "absolute"; - this._canvas.setAttribute("tabindex", "0"); - // Add DOM container after canvas container to - // render DOM elements on top of the interactive - // canvas. - this._domContainer = this._dom - .createElement("div", "mapillary-dom", this._container); - this.configurationService = new ConfigurationService(options); - this.renderService = - new RenderService(this._container, stateService.currentState$, options.renderMode); - this.glRenderer = - new GLRenderer(this._canvas, this._canvasContainer, this.renderService); - this.domRenderer = - new DOMRenderer(this._domContainer, this.renderService, stateService.currentState$); - this.keyboardService = - new KeyboardService(this._canvasContainer); - this.mouseService = - new MouseService(this._container, this._canvasContainer, this._domContainer, document); - this.touchService = - new TouchService(this._canvasContainer, this._domContainer); - this.spriteService = - new SpriteService(options.sprite); - window.addEventListener('resize', this._onWindowResize, false); - } - get canvas() { - return !!this._canvas.parentNode ? - this._canvas : null; - } - get canvasContainer() { - return this._canvasContainer; - } - get container() { - return this._container; - } - get domContainer() { - return this._domContainer; - } - remove() { - window.removeEventListener('resize', this._onWindowResize, false); - this.spriteService.dispose(); - this.touchService.dispose(); - this.mouseService.dispose(); - this.glRenderer.remove(); - this.domRenderer.remove(); - this.renderService.dispose(); - this._removeNode(this._canvasContainer); - this._removeNode(this._domContainer); - this._container.classList - .remove("mapillary-viewer"); } - _removeNode(node) { - if (node.parentNode) { - node.parentNode.removeChild(node); + prepend(images) { + if (images.length < 1) { + throw Error("Trajectory can not be empty"); + } + this._trajectory = images.slice().concat(this._trajectory); + this._currentIndex += images.length; + this._setCurrentImage(); + let referenceReset = this._setReference(this._currentImage); + if (referenceReset) { + this._setTrajectories(); + } + else { + this._prependToTrajectories(images); } + this._setCurrentCamera(); } - } - - class CacheService { - constructor(_graphService, _stateService, _api) { - this._graphService = _graphService; - this._stateService = _stateService; - this._api = _api; - this._subscriptions = new SubscriptionHolder(); - this._started = false; - this._cellDepth = 1; + remove(n) { + if (n < 0) { + throw Error("n must be a positive integer"); + } + if (this._currentIndex - 1 < n) { + throw Error("Current and previous images can not be removed"); + } + for (let i = 0; i < n; i++) { + this._trajectory.shift(); + this._trajectoryTransforms.shift(); + this._trajectoryCameras.shift(); + this._currentIndex--; + } + this._setCurrentImage(); } - get started() { - return this._started; + clearPrior() { + if (this._currentIndex > 0) { + this.remove(this._currentIndex - 1); + } } - configure(configuration) { - if (!configuration) { - this._cellDepth = 1; - return; + clear() { + this.cut(); + if (this._currentIndex > 0) { + this.remove(this._currentIndex - 1); } - this._cellDepth = Math.max(1, Math.min(3, configuration.cellDepth)); } - start() { - if (this._started) { - return; + cut() { + while (this._trajectory.length - 1 > this._currentIndex) { + this._trajectory.pop(); + this._trajectoryTransforms.pop(); + this._trajectoryCameras.pop(); } - const subs = this._subscriptions; - subs.push(this._stateService.currentState$ - .pipe(distinctUntilChanged(undefined, (frame) => { - return frame.state.currentImage.id; - }), map((frame) => { - const state = frame.state; - const trajectory = state.trajectory; - const trajectoryKeys = trajectory - .map((n) => { - return n.id; - }); - const sequenceKey = trajectory[trajectory.length - 1].sequenceId; - return [ - trajectoryKeys, - state.currentImage.originalLngLat, - sequenceKey, - ]; - }), bufferCount(1, 5), withLatestFrom(this._graphService.graphMode$), switchMap(([keepBuffer, graphMode]) => { - const keepKeys = keepBuffer[0][0]; - const lngLat = keepBuffer[0][1]; - const geometry = this._api.data.geometry; - const cellId = geometry.lngLatToCellId(lngLat); - const keepCellIds = connectedComponent(cellId, this._cellDepth, geometry); - const keepSequenceKey = graphMode === GraphMode.Sequence ? - keepBuffer[0][2] : - undefined; - return this._graphService - .uncache$(keepKeys, keepCellIds, keepSequenceKey); - })) - .subscribe(() => { })); - subs.push(this._graphService.graphMode$ - .pipe(skip(1), withLatestFrom(this._stateService.currentState$), switchMap(([mode, frame]) => { - return mode === GraphMode.Sequence ? - this._keyToEdges(frame.state.currentImage.id, (image) => { - return image.sequenceEdges$; - }) : - from(frame.state.trajectory - .map((image) => { - return image.id; - }) - .slice(frame.state.currentIndex)).pipe(mergeMap((key) => { - return this._keyToEdges(key, (image) => { - return image.spatialEdges$; - }); - }, 6)); - })) - .subscribe(() => { })); - subs.push(this._graphService.dataAdded$ - .pipe(withLatestFrom(this._stateService.currentId$), switchMap(([_, imageId]) => { - return this._graphService.cacheImage$(imageId); - })) - .subscribe(() => { })); - this._started = true; } - stop() { - if (!this._started) { - return; + set(images) { + this._setTrajectory(images); + this._setCurrentImage(); + this._setReference(this._currentImage); + this._setTrajectories(); + this._setCurrentCamera(); + } + getCenter() { + return this._currentImage != null ? + this.currentTransform.projectBasic(this._camera.lookat.toArray()) : + [0.5, 0.5]; + } + setTransitionMode(mode) { + this._transitionMode = mode; + } + _getAlpha() { return 1; } + _getStateTransitionAlpha() { return 1; } + _setCurrent() { + this._setCurrentImage(); + let referenceReset = this._setReference(this._currentImage); + if (referenceReset) { + this._setTrajectories(); } - this._subscriptions.unsubscribe(); - this._started = false; + this._setCurrentCamera(); } - _keyToEdges(key, imageToEdgeMap) { - return this._graphService.cacheImage$(key).pipe(switchMap(imageToEdgeMap), first((status) => { - return status.cached; - }), timeout(15000), catchError((error) => { - console.error(`Failed to cache edges (${key}).`, error); - return empty(); - })); + _setCurrentCamera() { + this._currentCamera = this._trajectoryCameras[this._currentIndex].clone(); + this._previousCamera = this._currentIndex > 0 ? + this._trajectoryCameras[this._currentIndex - 1].clone() : + this._currentCamera.clone(); } - } - - class LoadingService { - constructor() { - this._loadersSubject$ = new Subject(); - this._loaders$ = this._loadersSubject$.pipe(scan((loaders, loader) => { - if (loader.task !== undefined) { - loaders[loader.task] = loader.loading; + _motionlessTransition() { + let imagesSet = this._currentImage != null && this._previousImage != null; + return imagesSet && (this._transitionMode === exports.TransitionMode.Instantaneous || !(this._currentImage.merged && + this._previousImage.merged && + this._withinOriginalDistance() && + this._sameConnectedComponent())); + } + _setReference(image) { + // do not reset reference if image is within threshold distance + if (Math.abs(image.lngLat.lat - this.reference.lat) < this._referenceThreshold && + Math.abs(image.lngLat.lng - this.reference.lng) < this._referenceThreshold) { + return false; + } + // do not reset reference if previous image exist and transition is with motion + if (this._previousImage != null && !this._motionlessTransition()) { + return false; + } + this._reference.lat = image.lngLat.lat; + this._reference.lng = image.lngLat.lng; + this._reference.alt = image.computedAltitude; + return true; + } + _setCurrentImage() { + this._currentImage = this._trajectory.length > 0 ? + this._trajectory[this._currentIndex] : + null; + this._previousImage = this._currentIndex > 0 ? + this._trajectory[this._currentIndex - 1] : + null; + } + _setTrajectory(images) { + if (images.length < 1) { + throw new ArgumentMapillaryError("Trajectory can not be empty"); + } + if (this._currentImage != null) { + this._trajectory = [this._currentImage].concat(images); + this._currentIndex = 1; + } + else { + this._trajectory = images.slice(); + this._currentIndex = 0; + } + } + _setTrajectories() { + this._trajectoryTransforms.length = 0; + this._trajectoryCameras.length = 0; + this._appendToTrajectories(this._trajectory); + } + _appendToTrajectories(images) { + for (let image of images) { + if (!image.assetsCached) { + throw new ArgumentMapillaryError("Assets must be cached when image is added to trajectory"); } - return loaders; - }, {}), startWith({}), publishReplay(1), refCount()); + let translation = this._imageToTranslation(image, this.reference); + let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, undefined, image.cameraParameters, image.cameraType); + this._trajectoryTransforms.push(transform); + this._trajectoryCameras.push(new Camera(transform)); + } } - get loading$() { - return this._loaders$.pipe(map((loaders) => { - for (const key in loaders) { - if (!loaders.hasOwnProperty(key)) { - continue; - } - if (loaders[key]) { - return true; - } + _prependToTrajectories(images) { + for (let image of images.reverse()) { + if (!image.assetsCached) { + throw new ArgumentMapillaryError("Assets must be cached when added to trajectory"); } - return false; - }), debounceTime(100), distinctUntilChanged()); + let translation = this._imageToTranslation(image, this.reference); + let transform = new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.image, undefined, image.cameraParameters, image.cameraType); + this._trajectoryTransforms.unshift(transform); + this._trajectoryCameras.unshift(new Camera(transform)); + } } - taskLoading$(task) { - return this._loaders$.pipe(map((loaders) => { - return !!loaders[task]; - }), debounceTime(100), distinctUntilChanged()); + _imageToTranslation(image, reference) { + return computeTranslation({ alt: image.computedAltitude, lat: image.lngLat.lat, lng: image.lngLat.lng }, image.rotation, reference); } - startLoading(task) { - this._loadersSubject$.next({ loading: true, task: task }); + _sameConnectedComponent() { + let current = this._currentImage; + let previous = this._previousImage; + return !!current && !!previous && + current.mergeId === previous.mergeId; } - stopLoading(task) { - this._loadersSubject$.next({ loading: false, task: task }); + _withinOriginalDistance() { + let current = this._currentImage; + let previous = this._previousImage; + if (!current || !previous) { + return true; + } + // 50 km/h moves 28m in 2s + let distance = this._spatial.distanceFromLngLat(current.originalLngLat.lng, current.originalLngLat.lat, previous.originalLngLat.lng, previous.originalLngLat.lat); + return distance < 25; } } - var PanMode; - (function (PanMode) { - PanMode[PanMode["Disabled"] = 0] = "Disabled"; - PanMode[PanMode["Enabled"] = 1] = "Enabled"; - PanMode[PanMode["Started"] = 2] = "Started"; - })(PanMode || (PanMode = {})); - class PanService { - constructor(graphService, stateService, enabled, graphCalculator, spatial, viewportCoords) { - this._subscriptions = new SubscriptionHolder(); - this._graphService = graphService; - this._stateService = stateService; - this._graphCalculator = graphCalculator !== null && graphCalculator !== void 0 ? graphCalculator : new GraphCalculator(); - this._spatial = spatial !== null && spatial !== void 0 ? spatial : new Spatial(); - this._viewportCoords = viewportCoords !== null && viewportCoords !== void 0 ? viewportCoords : new ViewportCoords(); - this._mode = enabled !== false ? - PanMode.Enabled : PanMode.Disabled; - this._panImagesSubject$ = new Subject(); - this._panImages$ = this._panImagesSubject$.pipe(startWith([]), publishReplay(1), refCount()); - this._subscriptions.push(this._panImages$.subscribe()); + class CustomState extends StateBase { + constructor(state) { + super(state); } - get panImages$() { - return this._panImages$; + setViewMatrix(viewMatrix) { + const viewMatrixInverse = new Matrix4() + .fromArray(viewMatrix) + .invert(); + const me = viewMatrixInverse.elements; + const eye = new Vector3(me[12], me[13], me[14]); + const forward = new Vector3(-me[8], -me[9], -me[10]); + const up = new Vector3(me[4], me[5], me[6]); + const camera = this._camera; + camera.position.copy(eye); + camera.lookat.copy(eye + .clone() + .add(forward)); + camera.up.copy(up); + const focal = 0.5 / Math.tan(Math.PI / 3); + camera.focal = focal; } - dispose() { - this.stop(); - if (this._panImagesSubscription != null) { - this._panImagesSubscription.unsubscribe(); + } + + class EarthState extends StateBase { + constructor(state) { + super(state); + this._transition = 0; + const eye = this._camera.position.clone(); + const forward = this._camera.lookat + .clone() + .sub(eye) + .normalize(); + const xy = Math.sqrt(forward.x * forward.x + forward.y * forward.y); + const angle = Math.atan2(forward.z, xy); + const lookat = new Vector3(); + if (angle > -Math.PI / 45) { + lookat.copy(eye); + eye.add(new Vector3(forward.x, forward.y, 0) + .multiplyScalar(-50)); + eye.z = 30; } - this._subscriptions.unsubscribe(); + else { + // Target a point on invented ground and keep forward direction + const l0 = eye.clone(); + const n = new Vector3(0, 0, 1); + const p0 = new Vector3(0, 0, -2); + const d = new Vector3().subVectors(p0, l0).dot(n) / forward.dot(n); + const maxDistance = 10000; + const intersection = l0 + .clone() + .add(forward. + clone() + .multiplyScalar(Math.min(maxDistance, d))); + lookat.copy(intersection); + const t = eye + .clone() + .sub(intersection) + .normalize(); + eye.copy(intersection.add(t.multiplyScalar(Math.max(50, t.length())))); + } + const eye1 = this._camera.position.clone(); + const lookat1 = eye1.clone().add(forward.clone().normalize().multiplyScalar(10)); + const up1 = this._camera.up.clone(); + const eye0 = lookat1.clone(); + const lookat0 = eye0.clone().add(forward.clone().normalize().multiplyScalar(10)); + const up0 = up1.clone(); + const eye2 = eye.clone(); + const lookat2 = lookat.clone(); + const up2 = new Vector3(0, 0, 1); + const eye3 = eye.clone().add(lookat2.clone().sub(eye2).normalize().multiplyScalar(-10)); + const lookat3 = lookat2.clone(); + const up3 = up2.clone(); + this._curveE = new CatmullRomCurve3([eye0, eye1, eye2, eye3]); + this._curveL = new CatmullRomCurve3([lookat0, lookat1, lookat2, lookat3]); + this._curveU = new CatmullRomCurve3([up0, up1, up2, up3]); + this._zoom0 = this._zoom; + this._zoom1 = 0; + this._camera.focal = 0.5 / Math.tan(Math.PI / 4); + } + get _isTransitioning() { + return this._transition < 1; } - enable() { - if (this._mode !== PanMode.Disabled) { + dolly(delta) { + if (this._isTransitioning) { return; } - this._mode = PanMode.Enabled; - this.start(); + const camera = this._camera; + const offset = camera.position + .clone() + .sub(camera.lookat); + const length = offset.length(); + const scaled = length * Math.pow(2, -delta); + const clipped = Math.max(1, Math.min(scaled, 4000)); + offset.normalize(); + offset.multiplyScalar(clipped); + camera.position + .copy(camera.lookat) + .add(offset); } - disable() { - if (this._mode === PanMode.Disabled) { + orbit(rotation) { + if (this._isTransitioning) { return; } - this.stop(); - this._mode = PanMode.Disabled; + const camera = this._camera; + const q = new Quaternion() + .setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); + const qInverse = q + .clone() + .invert(); + const offset = camera.position + .clone() + .sub(camera.lookat); + offset.applyQuaternion(q); + const length = offset.length(); + let phi = Math.atan2(offset.y, offset.x); + phi += rotation.phi; + let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); + theta += rotation.theta; + const threshold = Math.PI / 36; + theta = Math.max(threshold, Math.min(Math.PI / 2 - threshold, theta)); + offset.x = Math.sin(theta) * Math.cos(phi); + offset.y = Math.sin(theta) * Math.sin(phi); + offset.z = Math.cos(theta); + offset.applyQuaternion(qInverse); + camera.position + .copy(camera.lookat) + .add(offset.multiplyScalar(length)); } - start() { - if (this._mode !== PanMode.Enabled) { + truck(direction) { + if (this._isTransitioning) { return; } - const panImages$ = this._stateService.currentImage$.pipe(switchMap((current) => { - if (!current.merged || isSpherical(current.cameraType)) { - return of([]); - } - const current$ = of(current); - const bounds = this._graphCalculator.boundingBoxCorners(current.lngLat, 20); - const adjacent$ = this._graphService - .cacheBoundingBox$(bounds[0], bounds[1]).pipe(catchError((error) => { - console.error(`Failed to cache periphery bounding box (${current.id})`, error); - return empty(); - }), map((images) => { - if (isSpherical(current.cameraType)) { - return []; - } - const potential = []; - for (const image of images) { - if (image.id === current.id) { - continue; - } - if (image.mergeId !== current.mergeId) { - continue; - } - if (isSpherical(image.cameraType)) { - continue; - } - if (this._distance(image, current) > 4) { - continue; - } - potential.push(image); - } - return potential; - })); - return combineLatest(current$, adjacent$).pipe(withLatestFrom(this._stateService.reference$), map(([[cn, adjacent], reference]) => { - const currentDirection = this._spatial.viewingDirection(cn.rotation); - const currentTranslation = computeTranslation({ lat: cn.lngLat.lat, lng: cn.lngLat.lng, alt: cn.computedAltitude }, cn.rotation, reference); - const currentTransform = this._createTransform(cn, currentTranslation); - const currentAzimuthal = this._spatial.wrap(this._spatial.azimuthal(currentDirection.toArray(), currentTransform.upVector().toArray()), 0, 2 * Math.PI); - const currentProjectedPoints = this._computeProjectedPoints(currentTransform); - const currentHFov = this._computeHorizontalFov(currentProjectedPoints) / 180 * Math.PI; - const preferredOverlap = Math.PI / 8; - let left = undefined; - let right = undefined; - for (const a of adjacent) { - const translation = computeTranslation({ lat: a.lngLat.lat, lng: a.lngLat.lng, alt: a.computedAltitude }, a.rotation, reference); - const transform = this._createTransform(a, translation); - const projectedPoints = this._computeProjectedPoints(transform); - const hFov = this._computeHorizontalFov(projectedPoints) / 180 * Math.PI; - const direction = this._spatial.viewingDirection(a.rotation); - const azimuthal = this._spatial.wrap(this._spatial.azimuthal(direction.toArray(), transform.upVector().toArray()), 0, 2 * Math.PI); - const directionChange = this._spatial.angleBetweenVector2(currentDirection.x, currentDirection.y, direction.x, direction.y); - let overlap = Number.NEGATIVE_INFINITY; - if (directionChange > 0) { - if (currentAzimuthal > azimuthal) { - overlap = currentAzimuthal - 2 * Math.PI + currentHFov / 2 - (azimuthal - hFov / 2); - } - else { - overlap = currentAzimuthal + currentHFov / 2 - (azimuthal - hFov / 2); - } - } - else { - if (currentAzimuthal < azimuthal) { - overlap = azimuthal + hFov / 2 - (currentAzimuthal + 2 * Math.PI - currentHFov / 2); - } - else { - overlap = azimuthal + hFov / 2 - (currentAzimuthal - currentHFov / 2); - } - } - const nonOverlap = Math.abs(hFov - overlap); - const distanceCost = this._distance(a, cn); - const timeCost = Math.min(this._timeDifference(a, cn), 4); - const overlapCost = 20 * Math.abs(overlap - preferredOverlap); - const fovCost = Math.min(5, 1 / Math.min(hFov / currentHFov, 1)); - const nonOverlapCost = overlap > 0 ? -2 * nonOverlap : 0; - const cost = distanceCost + timeCost + overlapCost + fovCost + nonOverlapCost; - if (overlap > 0 && - overlap < 0.5 * currentHFov && - overlap < 0.5 * hFov && - nonOverlap > 0.5 * currentHFov) { - if (directionChange > 0) { - if (!left) { - left = [cost, a, transform, hFov]; - } - else { - if (cost < left[0]) { - left = [cost, a, transform, hFov]; - } - } - } - else { - if (!right) { - right = [cost, a, transform, hFov]; - } - else { - if (cost < right[0]) { - right = [cost, a, transform, hFov]; - } - } - } - } - } - const panImagess = []; - if (!!left) { - panImagess.push([left[1], left[2], left[3]]); - } - if (!!right) { - panImagess.push([right[1], right[2], right[3]]); - } - return panImagess; - }), startWith([])); - })); - this._panImagesSubscription = this._stateService.currentState$.pipe(map((frame) => { - return frame.state.imagesAhead > 0; - }), distinctUntilChanged(), switchMap((traversing) => { - return traversing ? of([]) : panImages$; - })) - .subscribe((panImages) => { - this._panImagesSubject$.next(panImages); - }); - this._mode = PanMode.Started; + const camera = this._camera; + camera.position + .add(new Vector3().fromArray(direction)); + camera.lookat + .add(new Vector3().fromArray(direction)); } - stop() { - if (this._mode !== PanMode.Started) { + update(delta) { + if (!this._isTransitioning) { return; } - this._panImagesSubscription.unsubscribe(); - this._panImagesSubject$.next([]); - this._mode = PanMode.Enabled; - } - _distance(image, reference) { - const [x, y, z] = geodeticToEnu(image.lngLat.lng, image.lngLat.lat, image.computedAltitude, reference.lngLat.lng, reference.lngLat.lat, reference.computedAltitude); - return Math.sqrt(x * x + y * y + z * z); - } - _timeDifference(image, reference) { - const milliSecond = (1000 * 60 * 60 * 24 * 30); - return Math.abs(image.capturedAt - reference.capturedAt) / milliSecond; - } - _createTransform(image, translation) { - return new Transform(image.exifOrientation, image.width, image.height, image.scale, image.rotation, translation, image.assetsCached ? image.image : undefined, undefined, image.cameraParameters, image.cameraType); - } - _computeProjectedPoints(transform) { - const vertices = [[1, 0]]; - const directions = [[0, 0.5]]; - const pointsPerLine = 20; - return computeProjectedPoints(transform, vertices, directions, pointsPerLine, this._viewportCoords); - } - _computeHorizontalFov(projectedPoints) { - const fovs = projectedPoints - .map((projectedPoint) => { - return this._coordToFov(projectedPoint[0]); - }); - const fov = Math.min(...fovs); - return fov; + this._transition = Math.min(this._transition + 2 * delta / 3, 1); + const sta = MathUtils.smootherstep(this._transition, 0, 1); + const t = (sta + 1) / 3; + const eye = this._curveE.getPoint(t); + const lookat = this._curveL.getPoint(t); + const up = this._curveU.getPoint(t); + this._camera.position.copy(eye); + this._camera.lookat.copy(lookat); + this._camera.up.copy(up); + this._zoom = MathUtils.lerp(this._zoom0, this._zoom1, sta); + this._stateTransitionAlpha = sta; } - _coordToFov(x) { - return 2 * Math.atan(x) * 180 / Math.PI; + _getStateTransitionAlpha() { + return this._stateTransitionAlpha; } } - /** - * @class API - * - * @classdesc Provides methods for access to the API. - */ - class APIWrapper { - constructor(_data) { - this._data = _data; - } - get data() { - return this._data; - } - getCoreImages$(cellId) { - return this._wrap$(this._data.getCoreImages(cellId)); - } - getImages$(imageIds) { - return this._wrap$(this._data.getImages(imageIds)); - } - getImageTiles$(tiles) { - return this._wrap$(this._data.getImageTiles(tiles)); - } - getSequence$(sequenceId) { - return this._wrap$(this._data.getSequence(sequenceId)); - } - getSpatialImages$(imageIds) { - return this._wrap$(this._data.getSpatialImages(imageIds)); + class EulerRotationDelta { + constructor(phi, theta) { + this._phi = phi; + this._theta = theta; } - setAccessToken(accessToken) { - this._data.setAccessToken(accessToken); + get phi() { + return this._phi; } - _wrap$(promise) { - return Observable.create((subscriber) => { - promise.then((value) => { - subscriber.next(value); - subscriber.complete(); - }, (error) => { - subscriber.error(error); - }); - }); + set phi(value) { + this._phi = value; } - } - - /** - * @class GraphService - * - * @classdesc Represents a service for graph operations. - */ - class GraphService { - /** - * Create a new graph service instance. - * - * @param {Graph} graph - Graph instance to be operated on. - */ - constructor(graph) { - this._dataAdded$ = new Subject(); - this._subscriptions = new SubscriptionHolder(); - this._onDataAdded = (event) => { - this._graph$ - .pipe(first(), mergeMap(graph => { - return graph.updateCells$(event.cellIds).pipe(tap(() => { graph.resetSpatialEdges(); })); - })) - .subscribe(cellId => { this._dataAdded$.next(cellId); }); - }; - const subs = this._subscriptions; - this._graph$ = concat(of(graph), graph.changed$).pipe(publishReplay(1), refCount()); - subs.push(this._graph$.subscribe(() => { })); - this._graphMode = GraphMode.Spatial; - this._graphModeSubject$ = new Subject(); - this._graphMode$ = this._graphModeSubject$.pipe(startWith(this._graphMode), publishReplay(1), refCount()); - subs.push(this._graphMode$.subscribe(() => { })); - this._firstGraphSubjects$ = []; - this._initializeCacheSubscriptions = []; - this._sequenceSubscriptions = []; - this._spatialSubscriptions = []; - graph.api.data.on("datacreate", this._onDataAdded); + get theta() { + return this._theta; } - /** - * Get dataAdded$. - * - * @returns {Observable} Observable emitting - * a cell id every time data has been added to a cell. - */ - get dataAdded$() { - return this._dataAdded$; + set theta(value) { + this._theta = value; } - /** - * Get filter observable. - * - * @desciption Emits the filter every time it has changed. - * - * @returns {Observable} Observable - * emitting the filter function every time it is set. - */ - get filter$() { - return this._graph$.pipe(first(), mergeMap((graph) => { - return graph.filter$; - })); + get isZero() { + return this._phi === 0 && this._theta === 0; } - /** - * Get graph mode observable. - * - * @description Emits the current graph mode. - * - * @returns {Observable} Observable - * emitting the current graph mode when it changes. - */ - get graphMode$() { - return this._graphMode$; + copy(delta) { + this._phi = delta.phi; + this._theta = delta.theta; } - /** - * Cache full images in a bounding box. - * - * @description When called, the full properties of - * the image are retrieved. The image cache is not initialized - * for any new images retrieved and the image assets are not - * retrieved, {@link cacheImage$} needs to be called for caching - * assets. - * - * @param {LngLat} sw - South west corner of bounding box. - * @param {LngLat} ne - North east corner of bounding box. - * @return {Observable>} Observable emitting a single item, - * the images of the bounding box, when they have all been retrieved. - * @throws {Error} Propagates any IO image caching errors to the caller. - */ - cacheBoundingBox$(sw, ne) { - return this._graph$.pipe(first(), mergeMap((graph) => { - return graph.cacheBoundingBox$(sw, ne); - })); + lerp(other, alpha) { + this._phi = (1 - alpha) * this._phi + alpha * other.phi; + this._theta = (1 - alpha) * this._theta + alpha * other.theta; } - /** - * Cache full images in a cell. - * - * @description When called, the full properties of - * the image are retrieved. The image cache is not initialized - * for any new images retrieved and the image assets are not - * retrieved, {@link cacheImage$} needs to be called for caching - * assets. - * - * @param {string} cellId - Id of the cell. - * @return {Observable>} Observable emitting a single item, - * the images of the cell, when they have all been retrieved. - * @throws {Error} Propagates any IO image caching errors to the caller. - */ - cacheCell$(cellId) { - return this._graph$.pipe(first(), mergeMap((graph) => { - return graph.cacheCell$(cellId); - })); + multiply(value) { + this._phi *= value; + this._theta *= value; } - /** - * Cache a image in the graph and retrieve it. - * - * @description When called, the full properties of - * the image are retrieved and the image cache is initialized. - * After that the image assets are cached and the image - * is emitted to the observable when. - * In parallel to caching the image assets, the sequence and - * spatial edges of the image are cached. For this, the sequence - * of the image and the required tiles and spatial images are - * retrieved. The sequence and spatial edges may be set before - * or after the image is returned. - * - * @param {string} id - Id of the image to cache. - * @return {Observable} Observable emitting a single item, - * the image, when it has been retrieved and its assets are cached. - * @throws {Error} Propagates any IO image caching errors to the caller. - */ - cacheImage$(id) { - const firstGraphSubject$ = new Subject(); - this._firstGraphSubjects$.push(firstGraphSubject$); - const firstGraph$ = firstGraphSubject$.pipe(publishReplay(1), refCount()); - const image$ = firstGraph$.pipe(map((graph) => { - return graph.getNode(id); - }), mergeMap((image) => { - return image.assetsCached ? - of(image) : - image.cacheAssets$(); - }), publishReplay(1), refCount()); - image$.subscribe(undefined, (error) => { - console.error(`Failed to cache image (${id}).`, error); - }); - let initializeCacheSubscription; - initializeCacheSubscription = this._graph$.pipe(first(), mergeMap((graph) => { - if (graph.isCachingFull(id) || !graph.hasNode(id)) { - return graph.cacheFull$(id); - } - if (graph.isCachingFill(id) || !graph.getNode(id).complete) { - return graph.cacheFill$(id); - } - return of(graph); - }), tap((graph) => { - if (!graph.hasNode(id)) { - throw new GraphMapillaryError(`Failed to cache image (${id})`); - } - if (!graph.hasInitializedCache(id)) { - graph.initializeCache(id); - } - }), finalize(() => { - if (initializeCacheSubscription == null) { - return; - } - this._removeFromArray(initializeCacheSubscription, this._initializeCacheSubscriptions); - this._removeFromArray(firstGraphSubject$, this._firstGraphSubjects$); - })) - .subscribe((graph) => { - firstGraphSubject$.next(graph); - firstGraphSubject$.complete(); - }, (error) => { - firstGraphSubject$.error(error); - }); - if (!initializeCacheSubscription.closed) { - this._initializeCacheSubscriptions.push(initializeCacheSubscription); + threshold(value) { + this._phi = Math.abs(this._phi) > value ? this._phi : 0; + this._theta = Math.abs(this._theta) > value ? this._theta : 0; + } + lengthSquared() { + return this._phi * this._phi + this._theta * this._theta; + } + reset() { + this._phi = 0; + this._theta = 0; + } + } + + class InteractiveStateBase extends StateBase { + constructor(state) { + super(state); + this._animationSpeed = 1 / 40; + this._rotationDelta = new EulerRotationDelta(0, 0); + this._requestedRotationDelta = null; + this._basicRotation = [0, 0]; + this._requestedBasicRotation = null; + this._requestedBasicRotationUnbounded = null; + this._rotationAcceleration = 0.86; + this._rotationIncreaseAlpha = 0.97; + this._rotationDecreaseAlpha = 0.9; + this._rotationThreshold = 1e-3; + this._unboundedRotationAlpha = 0.8; + this._desiredZoom = state.zoom; + this._minZoom = 0; + this._maxZoom = 3; + this._lookatDepth = 10; + this._desiredLookat = null; + this._desiredCenter = null; + } + rotate(rotationDelta) { + if (this._currentImage == null) { + return; } - const graphSequence$ = firstGraph$.pipe(catchError(() => { - return empty(); - }), mergeMap((graph) => { - if (graph.isCachingNodeSequence(id) || !graph.hasNodeSequence(id)) { - return graph.cacheNodeSequence$(id); - } - return of(graph); - }), publishReplay(1), refCount()); - let sequenceSubscription; - sequenceSubscription = graphSequence$.pipe(tap((graph) => { - if (!graph.getNode(id).sequenceEdges.cached) { - graph.cacheSequenceEdges(id); - } - }), finalize(() => { - if (sequenceSubscription == null) { - return; - } - this._removeFromArray(sequenceSubscription, this._sequenceSubscriptions); - })) - .subscribe(() => { return; }, (error) => { - console.error(`Failed to cache sequence edges (${id}).`, error); - }); - if (!sequenceSubscription.closed) { - this._sequenceSubscriptions.push(sequenceSubscription); + if (rotationDelta.phi === 0 && rotationDelta.theta === 0) { + return; } - if (this._graphMode === GraphMode.Spatial) { - let spatialSubscription; - spatialSubscription = firstGraph$.pipe(catchError(() => { - return empty(); - }), expand((graph) => { - if (graph.hasTiles(id)) { - return empty(); - } - return from(graph.cacheTiles$(id)).pipe(mergeMap((graph$) => { - return graph$.pipe(mergeMap((g) => { - if (g.isCachingTiles(id)) { - return empty(); - } - return of(g); - }), catchError((error) => { - console.error(`Failed to cache tile data (${id}).`, error); - return empty(); - })); - })); - }), takeLast(1), mergeMap((graph) => { - if (graph.hasSpatialArea(id)) { - return of(graph); - } - return from(graph.cacheSpatialArea$(id)).pipe(mergeMap((graph$) => { - return graph$.pipe(catchError((error) => { - console.error(`Failed to cache spatial images (${id}).`, error); - return empty(); - })); - })); - }), takeLast(1), mergeMap((graph) => { - return graph.hasNodeSequence(id) ? - of(graph) : - graph.cacheNodeSequence$(id); - }), tap((graph) => { - if (!graph.getNode(id).spatialEdges.cached) { - graph.cacheSpatialEdges(id); - } - }), finalize(() => { - if (spatialSubscription == null) { - return; - } - this._removeFromArray(spatialSubscription, this._spatialSubscriptions); - })) - .subscribe(() => { return; }, (error) => { - const message = `Failed to cache spatial edges (${id}).`; - console.error(message, error); - }); - if (!spatialSubscription.closed) { - this._spatialSubscriptions.push(spatialSubscription); - } + this._desiredZoom = this._zoom; + this._desiredLookat = null; + this._requestedBasicRotation = null; + if (this._requestedRotationDelta != null) { + this._requestedRotationDelta.phi = this._requestedRotationDelta.phi + rotationDelta.phi; + this._requestedRotationDelta.theta = this._requestedRotationDelta.theta + rotationDelta.theta; + } + else { + this._requestedRotationDelta = new EulerRotationDelta(rotationDelta.phi, rotationDelta.theta); } - return image$.pipe(first((image) => { - return image.assetsCached; - })); - } - /** - * Cache a sequence in the graph and retrieve it. - * - * @param {string} sequenceId - Sequence id. - * @returns {Observable} Observable emitting a single item, - * the sequence, when it has been retrieved and its assets are cached. - * @throws {Error} Propagates any IO image caching errors to the caller. - */ - cacheSequence$(sequenceId) { - return this._graph$.pipe(first(), mergeMap((graph) => { - if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { - return graph.cacheSequence$(sequenceId); - } - return of(graph); - }), map((graph) => { - return graph.getSequence(sequenceId); - })); } - /** - * Cache a sequence and its images in the graph and retrieve the sequence. - * - * @description Caches a sequence and its assets are cached and - * retrieves all images belonging to the sequence. The image assets - * or edges will not be cached. - * - * @param {string} sequenceId - Sequence id. - * @param {string} referenceImageId - Id of image to use as reference - * for optimized caching. - * @returns {Observable} Observable emitting a single item, - * the sequence, when it has been retrieved, its assets are cached and - * all images belonging to the sequence has been retrieved. - * @throws {Error} Propagates any IO image caching errors to the caller. - */ - cacheSequenceImages$(sequenceId, referenceImageId) { - return this._graph$.pipe(first(), mergeMap((graph) => { - if (graph.isCachingSequence(sequenceId) || !graph.hasSequence(sequenceId)) { - return graph.cacheSequence$(sequenceId); - } - return of(graph); - }), mergeMap((graph) => { - if (graph.isCachingSequenceNodes(sequenceId) || !graph.hasSequenceNodes(sequenceId)) { - return graph.cacheSequenceNodes$(sequenceId, referenceImageId); - } - return of(graph); - }), map((graph) => { - return graph.getSequence(sequenceId); - })); + rotateUnbounded(delta) { + if (this._currentImage == null) { + return; + } + this._requestedBasicRotation = null; + this._requestedRotationDelta = null; + this._applyRotation(delta, this._currentCamera); + this._applyRotation(delta, this._previousCamera); + if (!this._desiredLookat) { + return; + } + const q = new Quaternion().setFromUnitVectors(this._currentCamera.up, new Vector3(0, 0, 1)); + const qInverse = q.clone().invert(); + const offset = new Vector3() + .copy(this._desiredLookat) + .sub(this._camera.position) + .applyQuaternion(q); + const length = offset.length(); + let phi = Math.atan2(offset.y, offset.x); + phi += delta.phi; + let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); + theta += delta.theta; + theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); + offset.x = Math.sin(theta) * Math.cos(phi); + offset.y = Math.sin(theta) * Math.sin(phi); + offset.z = Math.cos(theta); + offset.applyQuaternion(qInverse); + this._desiredLookat + .copy(this._camera.position) + .add(offset.multiplyScalar(length)); } - /** - * Dispose the graph service and its children. - */ - dispose() { - this._graph$ - .pipe(first()) - .subscribe((graph) => { graph.unsubscribe(); }); - this._subscriptions.unsubscribe(); + rotateWithoutInertia(rotationDelta) { + if (this._currentImage == null) { + return; + } + this._desiredZoom = this._zoom; + this._desiredLookat = null; + this._requestedBasicRotation = null; + this._requestedRotationDelta = null; + const threshold = Math.PI / (10 * Math.pow(2, this._zoom)); + const delta = { + phi: this._spatial.clamp(rotationDelta.phi, -threshold, threshold), + theta: this._spatial.clamp(rotationDelta.theta, -threshold, threshold), + }; + this._applyRotation(delta, this._currentCamera); + this._applyRotation(delta, this._previousCamera); } - /** - * Set a spatial edge filter on the graph. - * - * @description Resets the spatial edges of all cached images. - * - * @param {FilterExpression} filter - Filter expression to be applied. - * @return {Observable} Observable emitting a single item, - * the graph, when the spatial edges have been reset. - */ - setFilter$(filter) { - this._resetSubscriptions(this._spatialSubscriptions); - return this._graph$.pipe(first(), tap((graph) => { - graph.resetSpatialEdges(); - graph.setFilter(filter); - }), map(() => { - return undefined; - })); + rotateBasic(basicRotation) { + if (this._currentImage == null) { + return; + } + this._desiredZoom = this._zoom; + this._desiredLookat = null; + this._requestedRotationDelta = null; + if (this._requestedBasicRotation != null) { + this._requestedBasicRotation[0] += basicRotation[0]; + this._requestedBasicRotation[1] += basicRotation[1]; + let threshold = 0.05 / Math.pow(2, this._zoom); + this._requestedBasicRotation[0] = + this._spatial.clamp(this._requestedBasicRotation[0], -threshold, threshold); + this._requestedBasicRotation[1] = + this._spatial.clamp(this._requestedBasicRotation[1], -threshold, threshold); + } + else { + this._requestedBasicRotation = basicRotation.slice(); + } } - /** - * Set the graph mode. - * - * @description If graph mode is set to spatial, caching - * is performed with emphasis on spatial edges. If graph - * mode is set to sequence no tile data is requested and - * no spatial edges are computed. - * - * When setting graph mode to sequence all spatial - * subscriptions are aborted. - * - * @param {GraphMode} mode - Graph mode to set. - */ - setGraphMode(mode) { - if (this._graphMode === mode) { + rotateBasicUnbounded(basicRotation) { + if (this._currentImage == null) { return; } - if (mode === GraphMode.Sequence) { - this._resetSubscriptions(this._spatialSubscriptions); + if (this._requestedBasicRotationUnbounded != null) { + this._requestedBasicRotationUnbounded[0] += basicRotation[0]; + this._requestedBasicRotationUnbounded[1] += basicRotation[1]; + } + else { + this._requestedBasicRotationUnbounded = basicRotation.slice(); } - this._graphMode = mode; - this._graphModeSubject$.next(this._graphMode); } - /** - * Reset the graph. - * - * @description Resets the graph but keeps the images of the - * supplied ids. - * - * @param {Array} keepIds - Ids of images to keep in graph. - * @return {Observable} Observable emitting a single item, - * the graph, when it has been reset. - */ - reset$(keepIds) { - this._abortSubjects(this._firstGraphSubjects$); - this._resetSubscriptions(this._initializeCacheSubscriptions); - this._resetSubscriptions(this._sequenceSubscriptions); - this._resetSubscriptions(this._spatialSubscriptions); - return this._graph$.pipe(first(), tap((graph) => { - graph.reset(keepIds); - }), map(() => { - return undefined; - })); + rotateBasicWithoutInertia(basic) { + if (this._currentImage == null) { + return; + } + this._desiredZoom = this._zoom; + this._desiredLookat = null; + this._requestedRotationDelta = null; + this._requestedBasicRotation = null; + const threshold = 0.05 / Math.pow(2, this._zoom); + const basicRotation = basic.slice(); + basicRotation[0] = this._spatial.clamp(basicRotation[0], -threshold, threshold); + basicRotation[1] = this._spatial.clamp(basicRotation[1], -threshold, threshold); + this._applyRotationBasic(basicRotation); } - /** - * Uncache the graph. - * - * @description Uncaches the graph by removing tiles, images and - * sequences. Keeps the images of the supplied ids and the tiles - * related to those images. - * - * @param {Array} keepIds - Ids of images to keep in graph. - * @param {Array} keepCellIds - Ids of cells to keep in graph. - * @param {string} keepSequenceId - Optional id of sequence - * for which the belonging images should not be disposed or - * removed from the graph. These images may still be uncached if - * not specified in keep ids param. - * @return {Observable} Observable emitting a single item, - * the graph, when the graph has been uncached. - */ - uncache$(keepIds, keepCellIds, keepSequenceId) { - return this._graph$.pipe(first(), tap((graph) => { - graph.uncache(keepIds, keepCellIds, keepSequenceId); - }), map(() => { - return undefined; - })); + rotateToBasic(basic) { + if (this._currentImage == null) { + return; + } + this._desiredZoom = this._zoom; + this._desiredLookat = null; + basic[0] = this._spatial.clamp(basic[0], 0, 1); + basic[1] = this._spatial.clamp(basic[1], 0, 1); + let lookat = this.currentTransform.unprojectBasic(basic, this._lookatDepth); + this._currentCamera.lookat.fromArray(lookat); } - _abortSubjects(subjects) { - for (const subject of subjects.slice()) { - this._removeFromArray(subject, subjects); - subject.error(new Error("Cache image request was aborted.")); + zoomIn(delta, reference) { + if (this._currentImage == null) { + return; + } + this._desiredZoom = Math.max(this._minZoom, Math.min(this._maxZoom, this._desiredZoom + delta)); + let currentCenter = this.currentTransform.projectBasic(this._currentCamera.lookat.toArray()); + let currentCenterX = currentCenter[0]; + let currentCenterY = currentCenter[1]; + let zoom0 = Math.pow(2, this._zoom); + let zoom1 = Math.pow(2, this._desiredZoom); + let refX = reference[0]; + let refY = reference[1]; + if (isSpherical(this.currentTransform.cameraType)) { + if (refX - currentCenterX > 0.5) { + refX = refX - 1; + } + else if (currentCenterX - refX > 0.5) { + refX = 1 + refX; + } + } + let newCenterX = refX - zoom0 / zoom1 * (refX - currentCenterX); + let newCenterY = refY - zoom0 / zoom1 * (refY - currentCenterY); + if (isSpherical(this._currentImage.cameraType)) { + newCenterX = this._spatial + .wrap(newCenterX + this._basicRotation[0], 0, 1); + newCenterY = this._spatial + .clamp(newCenterY + this._basicRotation[1], 0.05, 0.95); + } + else { + newCenterX = this._spatial.clamp(newCenterX, 0, 1); + newCenterY = this._spatial.clamp(newCenterY, 0, 1); } + this._desiredLookat = new Vector3() + .fromArray(this.currentTransform.unprojectBasic([newCenterX, newCenterY], this._lookatDepth)); } - _removeFromArray(object, objects) { - const index = objects.indexOf(object); - if (index !== -1) { - objects.splice(index, 1); + setCenter(center) { + this._desiredLookat = null; + this._requestedRotationDelta = null; + this._requestedBasicRotation = null; + this._desiredZoom = this._zoom; + let clamped = [ + this._spatial.clamp(center[0], 0, 1), + this._spatial.clamp(center[1], 0, 1), + ]; + if (this._currentImage == null) { + this._desiredCenter = clamped; + return; } + this._desiredCenter = null; + let currentLookat = new Vector3() + .fromArray(this.currentTransform.unprojectBasic(clamped, this._lookatDepth)); + let previousTransform = this.previousTransform != null ? + this.previousTransform : + this.currentTransform; + let previousLookat = new Vector3() + .fromArray(previousTransform.unprojectBasic(clamped, this._lookatDepth)); + this._currentCamera.lookat.copy(currentLookat); + this._previousCamera.lookat.copy(previousLookat); } - _resetSubscriptions(subscriptions) { - for (const subscription of subscriptions.slice()) { - this._removeFromArray(subscription, subscriptions); - if (!subscription.closed) { - subscription.unsubscribe(); - } + setZoom(zoom) { + this._desiredLookat = null; + this._requestedRotationDelta = null; + this._requestedBasicRotation = null; + this._zoom = this._spatial.clamp(zoom, this._minZoom, this._maxZoom); + this._desiredZoom = this._zoom; + } + _applyRotation(delta, camera) { + if (camera == null) { + return; } + let q = new Quaternion().setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); + let qInverse = q.clone().invert(); + let offset = new Vector3(); + offset.copy(camera.lookat).sub(camera.position); + offset.applyQuaternion(q); + let length = offset.length(); + let phi = Math.atan2(offset.y, offset.x); + phi += delta.phi; + let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); + theta += delta.theta; + theta = Math.max(0.1, Math.min(Math.PI - 0.1, theta)); + offset.x = Math.sin(theta) * Math.cos(phi); + offset.y = Math.sin(theta) * Math.sin(phi); + offset.z = Math.cos(theta); + offset.applyQuaternion(qInverse); + camera.lookat.copy(camera.position).add(offset.multiplyScalar(length)); } - } - - class FrameGenerator { - constructor(root) { - if (root.requestAnimationFrame) { - this._cancelAnimationFrame = root.cancelAnimationFrame.bind(root); - this._requestAnimationFrame = root.requestAnimationFrame.bind(root); + _applyRotationBasic(basicRotation) { + let currentImage = this._currentImage; + let previousImage = this._previousImage != null ? + this.previousImage : + this.currentImage; + let currentCamera = this._currentCamera; + let previousCamera = this._previousCamera; + let currentTransform = this.currentTransform; + let previousTransform = this.previousTransform != null ? + this.previousTransform : + this.currentTransform; + let currentBasic = currentTransform.projectBasic(currentCamera.lookat.toArray()); + let previousBasic = previousTransform.projectBasic(previousCamera.lookat.toArray()); + if (isSpherical(currentImage.cameraType)) { + currentBasic[0] = this._spatial.wrap(currentBasic[0] + basicRotation[0], 0, 1); + currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0.05, 0.95); } - else if (root.mozRequestAnimationFrame) { - this._cancelAnimationFrame = root.mozCancelAnimationFrame.bind(root); - this._requestAnimationFrame = root.mozRequestAnimationFrame.bind(root); + else { + currentBasic[0] = this._spatial.clamp(currentBasic[0] + basicRotation[0], 0, 1); + currentBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); } - else if (root.webkitRequestAnimationFrame) { - this._cancelAnimationFrame = root.webkitCancelAnimationFrame.bind(root); - this._requestAnimationFrame = root.webkitRequestAnimationFrame.bind(root); + if (isSpherical(previousImage.cameraType)) { + previousBasic[0] = this._spatial.wrap(previousBasic[0] + basicRotation[0], 0, 1); + previousBasic[1] = this._spatial.clamp(previousBasic[1] + basicRotation[1], 0.05, 0.95); } - else if (root.msRequestAnimationFrame) { - this._cancelAnimationFrame = root.msCancelAnimationFrame.bind(root); - this._requestAnimationFrame = root.msRequestAnimationFrame.bind(root); + else { + previousBasic[0] = this._spatial.clamp(previousBasic[0] + basicRotation[0], 0, 1); + previousBasic[1] = this._spatial.clamp(currentBasic[1] + basicRotation[1], 0, 1); } - else if (root.oRequestAnimationFrame) { - this._cancelAnimationFrame = root.oCancelAnimationFrame.bind(root); - this._requestAnimationFrame = root.oRequestAnimationFrame.bind(root); + let currentLookat = currentTransform.unprojectBasic(currentBasic, this._lookatDepth); + currentCamera.lookat.fromArray(currentLookat); + let previousLookat = previousTransform.unprojectBasic(previousBasic, this._lookatDepth); + previousCamera.lookat.fromArray(previousLookat); + } + _updateZoom(animationSpeed) { + let diff = this._desiredZoom - this._zoom; + let sign = diff > 0 ? 1 : diff < 0 ? -1 : 0; + if (diff === 0) { + return; + } + else if (Math.abs(diff) < 2e-3) { + this._zoom = this._desiredZoom; + if (this._desiredLookat != null) { + this._desiredLookat = null; + } } else { - this._cancelAnimationFrame = root.clearTimeout.bind(root); - this._requestAnimationFrame = (cb) => { return root.setTimeout(cb, 1000 / 60); }; + this._zoom += sign * Math.max(Math.abs(5 * animationSpeed * diff), 2e-3); } } - get cancelAnimationFrame() { - return this._cancelAnimationFrame; - } - get requestAnimationFrame() { - return this._requestAnimationFrame; - } - } - - class CustomState extends StateBase { - constructor(state) { - super(state); + _updateLookat(animationSpeed) { + if (this._desiredLookat === null) { + return; + } + let diff = this._desiredLookat.distanceToSquared(this._currentCamera.lookat); + if (Math.abs(diff) < 1e-6) { + this._currentCamera.lookat.copy(this._desiredLookat); + this._desiredLookat = null; + } + else { + this._currentCamera.lookat.lerp(this._desiredLookat, 5 * animationSpeed); + } } - setViewMatrix(viewMatrix) { - const viewMatrixInverse = new Matrix4() - .fromArray(viewMatrix) - .invert(); - const me = viewMatrixInverse.elements; - const eye = new Vector3(me[12], me[13], me[14]); - const forward = new Vector3(-me[8], -me[9], -me[10]); - const up = new Vector3(me[4], me[5], me[6]); - const camera = this._camera; - camera.position.copy(eye); - camera.lookat.copy(eye - .clone() - .add(forward)); - camera.up.copy(up); - const focal = 0.5 / Math.tan(Math.PI / 3); - camera.focal = focal; + _updateRotation() { + if (this._requestedRotationDelta != null) { + let length = this._rotationDelta.lengthSquared(); + let requestedLength = this._requestedRotationDelta.lengthSquared(); + if (requestedLength > length) { + this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationIncreaseAlpha); + } + else { + this._rotationDelta.lerp(this._requestedRotationDelta, this._rotationDecreaseAlpha); + } + this._requestedRotationDelta = null; + return; + } + if (this._rotationDelta.isZero) { + return; + } + const alpha = isSpherical(this.currentImage.cameraType) ? + 1 : this._alpha; + this._rotationDelta.multiply(this._rotationAcceleration * alpha); + this._rotationDelta.threshold(this._rotationThreshold); } - } - - class EarthState extends StateBase { - constructor(state) { - super(state); - const eye = this._camera.position.clone(); - const forward = this._camera.lookat - .clone() - .sub(eye) - .normalize(); - const xy = Math.sqrt(forward.x * forward.x + forward.y * forward.y); - const angle = Math.atan2(forward.z, xy); - const lookat = new Vector3(); - if (angle > -Math.PI / 45) { - lookat.copy(eye); - eye.add(new Vector3(forward.x, forward.y, 0) - .multiplyScalar(-50)); - eye.z = 30; + _updateRotationBasic() { + if (this._requestedBasicRotation != null) { + let x = this._basicRotation[0]; + let y = this._basicRotation[1]; + let reqX = this._requestedBasicRotation[0]; + let reqY = this._requestedBasicRotation[1]; + if (Math.abs(reqX) > Math.abs(x)) { + this._basicRotation[0] = (1 - this._rotationIncreaseAlpha) * x + this._rotationIncreaseAlpha * reqX; + } + else { + this._basicRotation[0] = (1 - this._rotationDecreaseAlpha) * x + this._rotationDecreaseAlpha * reqX; + } + if (Math.abs(reqY) > Math.abs(y)) { + this._basicRotation[1] = (1 - this._rotationIncreaseAlpha) * y + this._rotationIncreaseAlpha * reqY; + } + else { + this._basicRotation[1] = (1 - this._rotationDecreaseAlpha) * y + this._rotationDecreaseAlpha * reqY; + } + this._requestedBasicRotation = null; + return; } - else { - // Target a point on invented ground and keep forward direction - const l0 = eye.clone(); - const n = new Vector3(0, 0, 1); - const p0 = new Vector3(0, 0, -2); - const d = new Vector3().subVectors(p0, l0).dot(n) / forward.dot(n); - const maxDistance = 10000; - const intersection = l0 - .clone() - .add(forward. - clone() - .multiplyScalar(Math.min(maxDistance, d))); - lookat.copy(intersection); - const t = eye - .clone() - .sub(intersection) - .normalize(); - eye.copy(intersection.add(t.multiplyScalar(Math.max(50, t.length())))); + if (this._requestedBasicRotationUnbounded != null) { + let reqX = this._requestedBasicRotationUnbounded[0]; + let reqY = this._requestedBasicRotationUnbounded[1]; + if (Math.abs(reqX) > 0) { + this._basicRotation[0] = (1 - this._unboundedRotationAlpha) * this._basicRotation[0] + this._unboundedRotationAlpha * reqX; + } + if (Math.abs(reqY) > 0) { + this._basicRotation[1] = (1 - this._unboundedRotationAlpha) * this._basicRotation[1] + this._unboundedRotationAlpha * reqY; + } + if (this._desiredLookat != null) { + let desiredBasicLookat = this.currentTransform.projectBasic(this._desiredLookat.toArray()); + desiredBasicLookat[0] += reqX; + desiredBasicLookat[1] += reqY; + this._desiredLookat = new Vector3() + .fromArray(this.currentTransform.unprojectBasic(desiredBasicLookat, this._lookatDepth)); + } + this._requestedBasicRotationUnbounded = null; + } + if (this._basicRotation[0] === 0 && this._basicRotation[1] === 0) { + return; + } + this._basicRotation[0] = this._rotationAcceleration * this._basicRotation[0]; + this._basicRotation[1] = this._rotationAcceleration * this._basicRotation[1]; + if (Math.abs(this._basicRotation[0]) < this._rotationThreshold / Math.pow(2, this._zoom) && + Math.abs(this._basicRotation[1]) < this._rotationThreshold / Math.pow(2, this._zoom)) { + this._basicRotation = [0, 0]; } - this._camera.position.copy(eye); - this._camera.lookat.copy(lookat); - this._camera.up.set(0, 0, 1); } - dolly(delta) { - const camera = this._camera; - const offset = camera.position - .clone() - .sub(camera.lookat); - const length = offset.length(); - const scaled = length * Math.pow(2, -delta); - const clipped = Math.max(1, Math.min(scaled, 4000)); - offset.normalize(); - offset.multiplyScalar(clipped); - camera.position - .copy(camera.lookat) - .add(offset); + _clearRotation() { + if (isSpherical(this._currentImage.cameraType)) { + return; + } + if (this._requestedRotationDelta != null) { + this._requestedRotationDelta = null; + } + if (!this._rotationDelta.isZero) { + this._rotationDelta.reset(); + } + if (this._requestedBasicRotation != null) { + this._requestedBasicRotation = null; + } + if (this._basicRotation[0] > 0 || this._basicRotation[1] > 0) { + this._basicRotation = [0, 0]; + } } - orbit(rotation) { - const camera = this._camera; - const q = new Quaternion() - .setFromUnitVectors(camera.up, new Vector3(0, 0, 1)); - const qInverse = q - .clone() - .invert(); - const offset = camera.position - .clone() - .sub(camera.lookat); - offset.applyQuaternion(q); - const length = offset.length(); - let phi = Math.atan2(offset.y, offset.x); - phi += rotation.phi; - let theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y), offset.z); - theta += rotation.theta; - const threshold = Math.PI / 36; - theta = Math.max(threshold, Math.min(Math.PI / 2 - threshold, theta)); - offset.x = Math.sin(theta) * Math.cos(phi); - offset.y = Math.sin(theta) * Math.sin(phi); - offset.z = Math.cos(theta); - offset.applyQuaternion(qInverse); - camera.position - .copy(camera.lookat) - .add(offset.multiplyScalar(length)); + _setDesiredCenter() { + if (this._desiredCenter == null) { + return; + } + let lookatDirection = new Vector3() + .fromArray(this.currentTransform.unprojectBasic(this._desiredCenter, this._lookatDepth)) + .sub(this._currentCamera.position); + this._currentCamera.lookat.copy(this._currentCamera.position.clone().add(lookatDirection)); + this._previousCamera.lookat.copy(this._previousCamera.position.clone().add(lookatDirection)); + this._desiredCenter = null; } - truck(direction) { - const camera = this._camera; - camera.position - .add(new Vector3().fromArray(direction)); - camera.lookat - .add(new Vector3().fromArray(direction)); + _setDesiredZoom() { + this._desiredZoom = + isSpherical(this._currentImage.cameraType) || + this._previousImage == null ? + this._zoom : 0; } - update() { } } class InteractiveWaitingState extends InteractiveStateBase { @@ -85888,7 +84123,7 @@ void main() moveTo(position) { this._alpha = Math.max(0, Math.min(1, position)); } - update(fps) { + update(delta) { this._updateRotation(); if (!this._rotationDelta.isZero) { this._applyRotation(this._rotationDelta, this._previousCamera); @@ -85898,7 +84133,7 @@ void main() if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { this._applyRotationBasic(this._basicRotation); } - let animationSpeed = this._animationSpeed * (60 / fps); + let animationSpeed = this._animationSpeed * delta / 1e-1 * 6; this._updateZoom(animationSpeed); this._updateLookat(animationSpeed); this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); @@ -85925,6 +84160,109 @@ void main() } } + class TraversingState extends InteractiveStateBase { + constructor(state) { + super(state); + this._adjustCameras(); + this._motionless = this._motionlessTransition(); + this._baseAlpha = this._alpha; + this._speedCoefficient = 1; + this._smoothing = false; + } + append(images) { + let emptyTrajectory = this._trajectory.length === 0; + if (emptyTrajectory) { + this._resetTransition(); + } + super.append(images); + if (emptyTrajectory) { + this._setDesiredCenter(); + this._setDesiredZoom(); + } + } + prepend(images) { + let emptyTrajectory = this._trajectory.length === 0; + if (emptyTrajectory) { + this._resetTransition(); + } + super.prepend(images); + if (emptyTrajectory) { + this._setDesiredCenter(); + this._setDesiredZoom(); + } + } + set(images) { + super.set(images); + this._desiredLookat = null; + this._resetTransition(); + this._clearRotation(); + this._setDesiredCenter(); + this._setDesiredZoom(); + if (this._trajectory.length < 3) { + this._smoothing = true; + } + } + setSpeed(speed) { + this._speedCoefficient = this._spatial.clamp(speed, 0, 10); + } + update(delta) { + if (this._alpha === 1 && this._currentIndex + this._alpha < this._trajectory.length) { + this._currentIndex += 1; + this._smoothing = this._trajectory.length < 3 && + this._currentIndex + 1 === this._trajectory.length; + this._setCurrent(); + this._resetTransition(); + this._clearRotation(); + this._desiredZoom = + isSpherical(this._currentImage.cameraType) ? + this._zoom : 0; + this._desiredLookat = null; + } + let animationSpeed = this._animationSpeed * delta / 1e-1 * 6; + this._baseAlpha = Math.min(1, this._baseAlpha + this._speedCoefficient * animationSpeed); + if (this._smoothing) { + this._alpha = MathUtils.smootherstep(this._baseAlpha, 0, 1); + } + else { + this._alpha = this._baseAlpha; + } + this._updateRotation(); + if (!this._rotationDelta.isZero) { + this._applyRotation(this._rotationDelta, this._previousCamera); + this._applyRotation(this._rotationDelta, this._currentCamera); + } + this._updateRotationBasic(); + if (this._basicRotation[0] !== 0 || this._basicRotation[1] !== 0) { + this._applyRotationBasic(this._basicRotation); + } + this._updateZoom(animationSpeed); + this._updateLookat(animationSpeed); + this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); + } + _getAlpha() { + return this._motionless ? Math.ceil(this._alpha) : this._alpha; + } + _setCurrentCamera() { + super._setCurrentCamera(); + this._adjustCameras(); + } + _adjustCameras() { + if (this._previousImage == null) { + return; + } + let lookat = this._camera.lookat.clone().sub(this._camera.position); + this._previousCamera.lookat.copy(lookat.clone().add(this._previousCamera.position)); + if (isSpherical(this._currentImage.cameraType)) { + this._currentCamera.lookat.copy(lookat.clone().add(this._currentCamera.position)); + } + } + _resetTransition() { + this._alpha = 0; + this._baseAlpha = 0; + this._motionless = this._motionlessTransition(); + } + } + class WaitingState extends StateBase { constructor(state) { super(state); @@ -85946,7 +84284,7 @@ void main() moveTo(position) { this._alpha = Math.max(0, Math.min(1, position)); } - update(fps) { + update() { this._camera.lerpCameras(this._previousCamera, this._currentCamera, this.alpha); } _getAlpha() { @@ -86052,6 +84390,9 @@ void main() get alpha() { return this._state.alpha; } + get stateTransitionAlpha() { + return this._state.stateTransitionAlpha; + } get camera() { return this._state.camera; } @@ -86112,8 +84453,8 @@ void main() setZoom(zoom) { this._state.setZoom(zoom); } - update(fps) { - this._state.update(fps); + update(delta) { + this._state.update(delta); } append(images) { this._state.append(images); @@ -86198,11 +84539,11 @@ void main() class StateService { constructor(initialState, transitionMode) { this._appendImage$ = new Subject(); + this._clock = new Clock(); this._subscriptions = new SubscriptionHolder(); const subs = this._subscriptions; this._start$ = new Subject(); this._frame$ = new Subject(); - this._fpsSampleRate = 30; this._contextOperation$ = new BehaviorSubject((context) => { return context; }); @@ -86212,21 +84553,14 @@ void main() this._state$ = this._context$.pipe(map((context) => { return context.state; }), distinctUntilChanged(), publishReplay(1), refCount()); - this._fps$ = this._start$.pipe(switchMap(() => { - return this._frame$.pipe(bufferCount(1, this._fpsSampleRate), map(() => { - return new Date().getTime(); - }), pairwise(), map((times) => { - return Math.max(20, 1000 * this._fpsSampleRate / (times[1] - times[0])); - }), startWith(60)); - }), share()); - this._currentState$ = this._frame$.pipe(withLatestFrom(this._fps$, this._context$, (frameId, fps, context) => { - return [frameId, fps, context]; + this._currentState$ = this._frame$.pipe(withLatestFrom(this._context$, (frameId, context) => { + return [frameId, context]; }), filter((fc) => { - return fc[2].currentImage != null; + return fc[1].currentImage != null; }), tap((fc) => { - fc[2].update(fc[1]); + fc[1].update(this._clock.getDelta()); }), map((fc) => { - return { fps: fc[1], id: fc[0], state: fc[2] }; + return { fps: 60, id: fc[0], state: fc[1] }; }), share()); this._lastState$ = this._currentState$.pipe(publishReplay(1), refCount()); let imageChanged$ = this._currentState$.pipe(distinctUntilChanged(undefined, (f) => { @@ -86487,6 +84821,7 @@ void main() this._invokeContextOperation((context) => { context.setZoom(zoom); }); } start() { + this._clock.start(); if (this._frameId == null) { this._start$.next(null); this._frameId = this._frameGenerator.requestAnimationFrame(this._frame.bind(this)); @@ -86494,6 +84829,7 @@ void main() } } stop() { + this._clock.stop(); if (this._frameId != null) { this._frameGenerator.cancelAnimationFrame(this._frameId); this._frameId = null; @@ -86532,12 +84868,7 @@ void main() this._api = api; } else if (options.dataProvider) { - if (options.dataProvider instanceof DataProviderBase) { - this._api = new APIWrapper(options.dataProvider); - } - else { - throw new Error("Incorrect type: 'dataProvider' must extend the DataProviderBase class."); - } + this._api = new APIWrapper(options.dataProvider); } else { this._api = new APIWrapper(new GraphDataProvider({ @@ -86883,7 +85214,7 @@ void main() .subscribe((image) => { const type = "image"; const event = { - image: image, + image, target: this._viewer, type, }; @@ -86913,6 +85244,16 @@ void main() }; this._viewer.fire(type, event); })); + subs.push(this._navigator.stateService.reference$ + .subscribe((reference) => { + const type = "reference"; + const event = { + reference, + target: this._viewer, + type, + }; + this._viewer.fire(type, event); + })); subs.push(combineLatest(this._navigator.stateService.inMotion$, this._container.mouseService.active$, this._container.touchService.active$).pipe(map((values) => { return values[0] || values[1] || values[2]; }), distinctUntilChanged()) @@ -87089,6 +85430,7 @@ void main() if (this._controls) { throw new MapillaryError('Custom camera controls already attached'); } + this._controls = controls; const attach$ = new Subject(); const active$ = attach$ .pipe(switchMap(() => { @@ -87160,25 +85502,33 @@ void main() attach$.next(); attach$.complete(); })); - this._controls = controls; - } - dispose(viewer) { - this.detach(viewer); } detach(viewer) { - if (!this._controls) { - return; - } + const controls = this._controls; + this._controls = null; this._subscriptions.unsubscribe(); - this._navigator.stateService.state$ - .subscribe(state => { - if (state === State.Custom) { - this._controls.onDeactivate(viewer); - } - this._controls.onDetach(viewer); - this._controls = null; + return new Promise(resolve => { + this._navigator.stateService.state$ + .pipe(take(1)) + .subscribe(state => { + if (!controls) { + resolve(null); + return; + } + if (state === State.Custom) { + controls.onDeactivate(viewer); + } + controls.onDetach(viewer); + resolve(controls); + }); }); } + dispose(viewer) { + this.detach(viewer); + } + has(controls) { + return !!this._controls && controls === this._controls; + } _updateProjectionMatrix(projectionMatrix) { this._navigator.stateService.state$ .pipe(first()) @@ -87212,7 +85562,7 @@ void main() * @class Viewer * * @classdesc The Viewer object represents the navigable image viewer. - * Create a Viewer by specifying a container, client ID, image id and + * Create a Viewer by specifying a container, client ID, image ID and * other options. The viewer exposes methods and events for programmatic * interaction. * @@ -87223,38 +85573,43 @@ void main() /** * Create a new viewer instance. * - * @description It is possible to initialize the viewer with or - * without a id. + * @description The `Viewer` object represents the street imagery + * viewer on your web page. It exposes methods and properties that + * you can use to programatically change the view, and fires + * events as users interact with it. + * + * It is possible to initialize the viewer with or + * without a ID. * * When you want to show a specific image in the viewer from - * the start you should initialize it with a id. + * the start you should initialize it with a ID. * - * When you do not know the first image id at implementation + * When you do not know the first image ID at implementation * time, e.g. in a map-viewer application you should initialize - * the viewer without a id and call `moveTo` instead. + * the viewer without a ID and call `moveTo` instead. * - * When initializing with a id the viewer is bound to that id - * until the image for that id has been successfully loaded. - * Also, a cover with the image of the id will be shown. - * If the data for that id can not be loaded because the id is + * When initializing with an ID the viewer is bound to that ID + * until the image for that ID has been successfully loaded. + * Also, a cover with the image of the ID will be shown. + * If the data for that ID can not be loaded because the ID is * faulty or other errors occur it is not possible to navigate - * to another id because the viewer is not navigable. The viewer - * becomes navigable when the data for the id has been loaded and + * to another ID because the viewer is not navigable. The viewer + * becomes navigable when the data for the ID has been loaded and * the image is shown in the viewer. This way of initializing * the viewer is mostly for embedding in blog posts and similar * where one wants to show a specific image initially. * - * If the viewer is initialized without a id (with null or - * undefined) it is not bound to any particular id and it is - * possible to move to any id with `viewer.moveTo("")`. - * If the first move to a id fails it is possible to move to another - * id. The viewer will show a black background until a move + * If the viewer is initialized without a ID (with null or + * undefined) it is not bound to any particular ID and it is + * possible to move to any ID with `viewer.moveTo("")`. + * If the first move to a ID fails it is possible to move to another + * ID. The viewer will show a black background until a move * succeeds. This way of intitializing is suited for a map-viewer - * application when the initial id is not known at implementation + * application when the initial ID is not known at implementation * time. * * @param {ViewerOptions} options - Optional configuration object - * specifing Viewer's and the components' initial setup. + * specifying Viewer's and the components' initial setup. * * @example * ```js @@ -87279,6 +85634,19 @@ void main() this._customCameraControls = new CustomCameraControls(this._container, this._navigator); } + /** + * Returns the data provider used by the viewer to fetch + * all contracts, ents, and buffers. + * + * @description The viewer's data provider can be set + * upon initialization through the {@link ViewerOptions.dataProvider} + * property. + * + * @returns {IDataProvider} The viewer's data provider. + */ + get dataProvider() { + return this._navigator.api.data; + } /** * Return a boolean indicating if the viewer is in a navigable state. * @@ -87286,9 +85654,9 @@ void main() * moving, i.e. calling the {@link moveTo} and {@link moveDir} * methods or changing the authentication state, * i.e. calling {@link setAccessToken}. The viewer will not be in a navigable - * state if the cover is activated and the viewer has been supplied a id. + * state if the cover is activated and the viewer has been supplied a ID. * When the cover is deactivated or the viewer is activated without being - * supplied a id it will be navigable. + * supplied a ID it will be navigable. * * @returns {boolean} Boolean indicating whether the viewer is navigable. */ @@ -87409,7 +85777,7 @@ void main() * control instance. */ detachCustomCameraControls() { - this._customCameraControls.detach(this); + return this._customCameraControls.detach(this); } fire(type, event) { super.fire(type, event); @@ -87448,7 +85816,7 @@ void main() * * @description The camera control mode determines * how the camera is controlled when the viewer - * recieves pointer and keyboard input. + * receives pointer and keyboard input. * * @returns {CameraControls} controls - Camera control mode. * @@ -87635,6 +86003,25 @@ void main() }); }); } + /** + * Get the viewer's current reference position. + * + * @description The reference position specifies the origin in + * the viewer's topocentric coordinate system. + * + * @returns {Promise} Promise to the reference position. + * + * @example + * ```js + * viewer.getReference().then(reference => { console.log(reference); }); + * ``` + */ + getReference() { + return new Promise((resolve, reject) => { + this._navigator.stateService.reference$.pipe(first()) + .subscribe((reference) => { resolve(reference); }, (error) => { reject(error); }); + }); + } /** * Get the image's current zoom level. * @@ -87656,11 +86043,22 @@ void main() }); }); } + /** + * Check if a controls instance is the camera controls that are + * currently attached to the viewer. + * + * @param {ICustomCameraControls} controls - Camera controls instance. + * @returns {boolean} Value indicating whether the controls instance + * is currently attached. + */ + hasCustomCameraControls(controls) { + return this._customCameraControls.has(controls); + } /** * Check if a custom renderer has been added to the viewer's * rendering pipeline. * - * @param {string} id - Unique id of the custom renderer. + * @param {string} id - Unique ID of the custom renderer. * @returns {boolean} Value indicating whether the customer * renderer has been added. */ @@ -87699,14 +86097,14 @@ void main() }); } /** - * Navigate to a given image id. + * Navigate to a given image ID. * * @param {string} imageId - Id of the image to move to. * @returns {Promise} Promise to the image that was navigated to. * @throws Propagates any IO errors to the caller. * @throws When viewer is not navigable. * @throws {@link CancelMapillaryError} When a subsequent - * move request is made before the move to id call has completed. + * move request is made before the move to ID call has completed. * * @example * ```js @@ -87841,7 +86239,7 @@ void main() /** * Remove a custom renderer from the viewer's rendering pipeline. * - * @param id - Unique id of the custom renderer. + * @param id - Unique ID of the custom renderer. */ removeCustomRenderer(rendererId) { this._customRenderer.remove(rendererId, this); @@ -87873,7 +86271,7 @@ void main() * * @description The camera control mode determines * how the camera is controlled when the viewer - * recieves pointer and keyboard input. + * receives pointer and keyboard input. * * Changing the camera control mode is not possible * when the slider component is active and attempts @@ -87986,7 +86384,8 @@ void main() * documentation for a full list of properties that can be used * in a filter) are shown the the example code. * - * @param {FilterExpression} filter - The filter expression. + * @param {FilterExpression} [filter] - The filter expression. + * Applied filter is cleared if omitted. * @returns {Promise} Promise that resolves after filter is applied. * * @example @@ -88170,13 +86569,12 @@ void main() * * This is a workaround to make the CommonJS unit testing * work with Jest. Once Jest/Node supports ES6 modules - * fully this should be removed. GeoRBush and UnitBezier - * are registered here only to avoid loading them during + * fully this should be removed. GeoRBush is registered + * here only to avoid loading it during * unit tests. */ Graph.register(GeoRBush); MarkerSet.register(GeoRBush); - TraversingState.register(unitbezier); ComponentService.registerCover(CoverComponent); ComponentService.register(AttributionComponent); ComponentService.register(BearingComponent); @@ -88204,6 +86602,7 @@ void main() exports.DataProviderBase = DataProviderBase; exports.DirectionComponent = DirectionComponent; exports.DragPanHandler = DragPanHandler; + exports.EventEmitter = EventEmitter; exports.ExtremePointTag = ExtremePointTag; exports.Geometry = Geometry; exports.GeometryProviderBase = GeometryProviderBase; @@ -88241,8 +86640,12 @@ void main() exports.Viewer = Viewer; exports.ZoomComponent = ZoomComponent; exports.decompress = decompress; + exports.ecefToEnu = ecefToEnu; + exports.ecefToGeodetic = ecefToGeodetic; + exports.enuToEcef = enuToEcef; exports.enuToGeodetic = enuToGeodetic; exports.fetchArrayBuffer = fetchArrayBuffer; + exports.geodeticToEcef = geodeticToEcef; exports.geodeticToEnu = geodeticToEnu; exports.isFallbackSupported = isFallbackSupported; exports.isSupported = isSupported; @@ -88250,5 +86653,5 @@ void main() Object.defineProperty(exports, '__esModule', { value: true }); -}))); +})); //# sourceMappingURL=mapillary.unminified.js.map