From 7b28c0ef1bad35dc21b8631c017a9a707efdf85c Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Fri, 28 Feb 2025 16:58:10 +0100 Subject: [PATCH] launcher / handmenu --- make | 1 + src/assets/aframe.min.js | 46403 +------------------------------------ src/index.html | 32 +- src/xrsh-com | 2 +- 4 files changed, 25 insertions(+), 46413 deletions(-) diff --git a/make b/make index ebc50f4..823a65a 100755 --- a/make +++ b/make @@ -76,6 +76,7 @@ standalone(){ # build standalone xrsh.com binary } dev(){ # start dev http server + set -x cd "$DIR" test -f xrsh.com || standalone test -f /tmp/cert.pem || openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /tmp/key.pem -out /tmp/cert.pem diff --git a/src/assets/aframe.min.js b/src/assets/aframe.min.js index 6e78e9b..567b907 100644 --- a/src/assets/aframe.min.js +++ b/src/assets/aframe.min.js @@ -1,46401 +1,2 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else if(typeof exports === 'object') - exports["AFRAME"] = factory(); - else - root["AFRAME"] = factory(); -})(self, () => { -return /******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ "./node_modules/an-array/index.js": -/*!****************************************!*\ - !*** ./node_modules/an-array/index.js ***! - \****************************************/ -/***/ ((module) => { - -var str = Object.prototype.toString; -module.exports = anArray; -function anArray(arr) { - return arr.BYTES_PER_ELEMENT && str.call(arr.buffer) === '[object ArrayBuffer]' || Array.isArray(arr); -} - -/***/ }), - -/***/ "./node_modules/as-number/index.js": -/*!*****************************************!*\ - !*** ./node_modules/as-number/index.js ***! - \*****************************************/ -/***/ ((module) => { - -module.exports = function numtype(num, def) { - return typeof num === 'number' ? num : typeof def === 'number' ? def : 0; -}; - -/***/ }), - -/***/ "./node_modules/base64-js/index.js": -/*!*****************************************!*\ - !*** ./node_modules/base64-js/index.js ***! - \*****************************************/ -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -exports.byteLength = byteLength; -exports.toByteArray = toByteArray; -exports.fromByteArray = fromByteArray; -var lookup = []; -var revLookup = []; -var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array; -var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; -for (var i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i]; - revLookup[code.charCodeAt(i)] = i; -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup['-'.charCodeAt(0)] = 62; -revLookup['_'.charCodeAt(0)] = 63; -function getLens(b64) { - var len = b64.length; - if (len % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4'); - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - var validLen = b64.indexOf('='); - if (validLen === -1) validLen = len; - var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4; - return [validLen, placeHoldersLen]; -} - -// base64 is 4/3 + up to two characters of the original data -function byteLength(b64) { - var lens = getLens(b64); - var validLen = lens[0]; - var placeHoldersLen = lens[1]; - return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; -} -function _byteLength(b64, validLen, placeHoldersLen) { - return (validLen + placeHoldersLen) * 3 / 4 - placeHoldersLen; -} -function toByteArray(b64) { - var tmp; - var lens = getLens(b64); - var validLen = lens[0]; - var placeHoldersLen = lens[1]; - var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)); - var curByte = 0; - - // if there are placeholders, only get up to the last complete 4 chars - var len = placeHoldersLen > 0 ? validLen - 4 : validLen; - var i; - for (i = 0; i < len; i += 4) { - tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)]; - arr[curByte++] = tmp >> 16 & 0xFF; - arr[curByte++] = tmp >> 8 & 0xFF; - arr[curByte++] = tmp & 0xFF; - } - if (placeHoldersLen === 2) { - tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4; - arr[curByte++] = tmp & 0xFF; - } - if (placeHoldersLen === 1) { - tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2; - arr[curByte++] = tmp >> 8 & 0xFF; - arr[curByte++] = tmp & 0xFF; - } - return arr; -} -function tripletToBase64(num) { - return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]; -} -function encodeChunk(uint8, start, end) { - var tmp; - var output = []; - for (var i = start; i < end; i += 3) { - tmp = (uint8[i] << 16 & 0xFF0000) + (uint8[i + 1] << 8 & 0xFF00) + (uint8[i + 2] & 0xFF); - output.push(tripletToBase64(tmp)); - } - return output.join(''); -} -function fromByteArray(uint8) { - var tmp; - var len = uint8.length; - var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes - var parts = []; - var maxChunkLength = 16383; // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength)); - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1]; - parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 0x3F] + '=='); - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1]; - parts.push(lookup[tmp >> 10] + lookup[tmp >> 4 & 0x3F] + lookup[tmp << 2 & 0x3F] + '='); - } - return parts.join(''); -} - -/***/ }), - -/***/ "./node_modules/buffer-equal/index.js": -/*!********************************************!*\ - !*** ./node_modules/buffer-equal/index.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var Buffer = (__webpack_require__(/*! buffer */ "./node_modules/buffer/index.js").Buffer); // for use with browserify - -module.exports = function (a, b) { - if (!Buffer.isBuffer(a)) return undefined; - if (!Buffer.isBuffer(b)) return undefined; - if (typeof a.equals === 'function') return a.equals(b); - if (a.length !== b.length) return false; - for (var i = 0; i < a.length; i++) { - if (a[i] !== b[i]) return false; - } - return true; -}; - -/***/ }), - -/***/ "./node_modules/buffer/index.js": -/*!**************************************!*\ - !*** ./node_modules/buffer/index.js ***! - \**************************************/ -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -/* eslint-disable no-proto */ - - - -const base64 = __webpack_require__(/*! base64-js */ "./node_modules/base64-js/index.js"); -const ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js"); -const customInspectSymbol = typeof Symbol === 'function' && typeof Symbol['for'] === 'function' // eslint-disable-line dot-notation -? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation -: null; -exports.Buffer = Buffer; -exports.SlowBuffer = SlowBuffer; -exports.INSPECT_MAX_BYTES = 50; -const K_MAX_LENGTH = 0x7fffffff; -exports.kMaxLength = K_MAX_LENGTH; - -/** - * If `Buffer.TYPED_ARRAY_SUPPORT`: - * === true Use Uint8Array implementation (fastest) - * === false Print warning and recommend using `buffer` v4.x which has an Object - * implementation (most compatible, even IE6) - * - * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, - * Opera 11.6+, iOS 4.2+. - * - * We report that the browser does not support typed arrays if the are not subclassable - * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` - * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support - * for __proto__ and has a buggy typed array implementation. - */ -Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport(); -if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && typeof console.error === 'function') { - console.error('This browser lacks typed array (Uint8Array) support which is required by ' + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'); -} -function typedArraySupport() { - // Can typed array instances can be augmented? - try { - const arr = new Uint8Array(1); - const proto = { - foo: function () { - return 42; - } - }; - Object.setPrototypeOf(proto, Uint8Array.prototype); - Object.setPrototypeOf(arr, proto); - return arr.foo() === 42; - } catch (e) { - return false; - } -} -Object.defineProperty(Buffer.prototype, 'parent', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined; - return this.buffer; - } -}); -Object.defineProperty(Buffer.prototype, 'offset', { - enumerable: true, - get: function () { - if (!Buffer.isBuffer(this)) return undefined; - return this.byteOffset; - } -}); -function createBuffer(length) { - if (length > K_MAX_LENGTH) { - throw new RangeError('The value "' + length + '" is invalid for option "size"'); - } - // Return an augmented `Uint8Array` instance - const buf = new Uint8Array(length); - Object.setPrototypeOf(buf, Buffer.prototype); - return buf; -} - -/** - * The Buffer constructor returns instances of `Uint8Array` that have their - * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of - * `Uint8Array`, so the returned instances will have all the node `Buffer` methods - * and the `Uint8Array` methods. Square bracket notation works as expected -- it - * returns a single octet. - * - * The `Uint8Array` prototype remains unmodified. - */ - -function Buffer(arg, encodingOrOffset, length) { - // Common case. - if (typeof arg === 'number') { - if (typeof encodingOrOffset === 'string') { - throw new TypeError('The "string" argument must be of type string. Received type number'); - } - return allocUnsafe(arg); - } - return from(arg, encodingOrOffset, length); -} -Buffer.poolSize = 8192; // not used by this implementation - -function from(value, encodingOrOffset, length) { - if (typeof value === 'string') { - return fromString(value, encodingOrOffset); - } - if (ArrayBuffer.isView(value)) { - return fromArrayView(value); - } - if (value == null) { - throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + 'or Array-like Object. Received type ' + typeof value); - } - if (isInstance(value, ArrayBuffer) || value && isInstance(value.buffer, ArrayBuffer)) { - return fromArrayBuffer(value, encodingOrOffset, length); - } - if (typeof SharedArrayBuffer !== 'undefined' && (isInstance(value, SharedArrayBuffer) || value && isInstance(value.buffer, SharedArrayBuffer))) { - return fromArrayBuffer(value, encodingOrOffset, length); - } - if (typeof value === 'number') { - throw new TypeError('The "value" argument must not be of type number. Received type number'); - } - const valueOf = value.valueOf && value.valueOf(); - if (valueOf != null && valueOf !== value) { - return Buffer.from(valueOf, encodingOrOffset, length); - } - const b = fromObject(value); - if (b) return b; - if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && typeof value[Symbol.toPrimitive] === 'function') { - return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length); - } - throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + 'or Array-like Object. Received type ' + typeof value); -} - -/** - * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError - * if value is a number. - * Buffer.from(str[, encoding]) - * Buffer.from(array) - * Buffer.from(buffer) - * Buffer.from(arrayBuffer[, byteOffset[, length]]) - **/ -Buffer.from = function (value, encodingOrOffset, length) { - return from(value, encodingOrOffset, length); -}; - -// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: -// https://github.com/feross/buffer/pull/148 -Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype); -Object.setPrototypeOf(Buffer, Uint8Array); -function assertSize(size) { - if (typeof size !== 'number') { - throw new TypeError('"size" argument must be of type number'); - } else if (size < 0) { - throw new RangeError('The value "' + size + '" is invalid for option "size"'); - } -} -function alloc(size, fill, encoding) { - assertSize(size); - if (size <= 0) { - return createBuffer(size); - } - if (fill !== undefined) { - // Only pay attention to encoding if it's a string. This - // prevents accidentally sending in a number that would - // be interpreted as a start offset. - return typeof encoding === 'string' ? createBuffer(size).fill(fill, encoding) : createBuffer(size).fill(fill); - } - return createBuffer(size); -} - -/** - * Creates a new filled Buffer instance. - * alloc(size[, fill[, encoding]]) - **/ -Buffer.alloc = function (size, fill, encoding) { - return alloc(size, fill, encoding); -}; -function allocUnsafe(size) { - assertSize(size); - return createBuffer(size < 0 ? 0 : checked(size) | 0); -} - -/** - * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. - * */ -Buffer.allocUnsafe = function (size) { - return allocUnsafe(size); -}; -/** - * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. - */ -Buffer.allocUnsafeSlow = function (size) { - return allocUnsafe(size); -}; -function fromString(string, encoding) { - if (typeof encoding !== 'string' || encoding === '') { - encoding = 'utf8'; - } - if (!Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding); - } - const length = byteLength(string, encoding) | 0; - let buf = createBuffer(length); - const actual = buf.write(string, encoding); - if (actual !== length) { - // Writing a hex string, for example, that contains invalid characters will - // cause everything after the first invalid character to be ignored. (e.g. - // 'abxxcd' will be treated as 'ab') - buf = buf.slice(0, actual); - } - return buf; -} -function fromArrayLike(array) { - const length = array.length < 0 ? 0 : checked(array.length) | 0; - const buf = createBuffer(length); - for (let i = 0; i < length; i += 1) { - buf[i] = array[i] & 255; - } - return buf; -} -function fromArrayView(arrayView) { - if (isInstance(arrayView, Uint8Array)) { - const copy = new Uint8Array(arrayView); - return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength); - } - return fromArrayLike(arrayView); -} -function fromArrayBuffer(array, byteOffset, length) { - if (byteOffset < 0 || array.byteLength < byteOffset) { - throw new RangeError('"offset" is outside of buffer bounds'); - } - if (array.byteLength < byteOffset + (length || 0)) { - throw new RangeError('"length" is outside of buffer bounds'); - } - let buf; - if (byteOffset === undefined && length === undefined) { - buf = new Uint8Array(array); - } else if (length === undefined) { - buf = new Uint8Array(array, byteOffset); - } else { - buf = new Uint8Array(array, byteOffset, length); - } - - // Return an augmented `Uint8Array` instance - Object.setPrototypeOf(buf, Buffer.prototype); - return buf; -} -function fromObject(obj) { - if (Buffer.isBuffer(obj)) { - const len = checked(obj.length) | 0; - const buf = createBuffer(len); - if (buf.length === 0) { - return buf; - } - obj.copy(buf, 0, 0, len); - return buf; - } - if (obj.length !== undefined) { - if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { - return createBuffer(0); - } - return fromArrayLike(obj); - } - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { - return fromArrayLike(obj.data); - } -} -function checked(length) { - // Note: cannot use `length < K_MAX_LENGTH` here because that fails when - // length is NaN (which is otherwise coerced to zero.) - if (length >= K_MAX_LENGTH) { - throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes'); - } - return length | 0; -} -function SlowBuffer(length) { - if (+length != length) { - // eslint-disable-line eqeqeq - length = 0; - } - return Buffer.alloc(+length); -} -Buffer.isBuffer = function isBuffer(b) { - return b != null && b._isBuffer === true && b !== Buffer.prototype; // so Buffer.isBuffer(Buffer.prototype) will be false -}; - -Buffer.compare = function compare(a, b) { - if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength); - if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength); - if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { - throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'); - } - if (a === b) return 0; - let x = a.length; - let y = b.length; - for (let i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i]; - y = b[i]; - break; - } - } - if (x < y) return -1; - if (y < x) return 1; - return 0; -}; -Buffer.isEncoding = function isEncoding(encoding) { - switch (String(encoding).toLowerCase()) { - case 'hex': - case 'utf8': - case 'utf-8': - case 'ascii': - case 'latin1': - case 'binary': - case 'base64': - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return true; - default: - return false; - } -}; -Buffer.concat = function concat(list, length) { - if (!Array.isArray(list)) { - throw new TypeError('"list" argument must be an Array of Buffers'); - } - if (list.length === 0) { - return Buffer.alloc(0); - } - let i; - if (length === undefined) { - length = 0; - for (i = 0; i < list.length; ++i) { - length += list[i].length; - } - } - const buffer = Buffer.allocUnsafe(length); - let pos = 0; - for (i = 0; i < list.length; ++i) { - let buf = list[i]; - if (isInstance(buf, Uint8Array)) { - if (pos + buf.length > buffer.length) { - if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf); - buf.copy(buffer, pos); - } else { - Uint8Array.prototype.set.call(buffer, buf, pos); - } - } else if (!Buffer.isBuffer(buf)) { - throw new TypeError('"list" argument must be an Array of Buffers'); - } else { - buf.copy(buffer, pos); - } - pos += buf.length; - } - return buffer; -}; -function byteLength(string, encoding) { - if (Buffer.isBuffer(string)) { - return string.length; - } - if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { - return string.byteLength; - } - if (typeof string !== 'string') { - throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + 'Received type ' + typeof string); - } - const len = string.length; - const mustMatch = arguments.length > 2 && arguments[2] === true; - if (!mustMatch && len === 0) return 0; - - // Use a for loop to avoid recursion - let loweredCase = false; - for (;;) { - switch (encoding) { - case 'ascii': - case 'latin1': - case 'binary': - return len; - case 'utf8': - case 'utf-8': - return utf8ToBytes(string).length; - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return len * 2; - case 'hex': - return len >>> 1; - case 'base64': - return base64ToBytes(string).length; - default: - if (loweredCase) { - return mustMatch ? -1 : utf8ToBytes(string).length; // assume utf8 - } - - encoding = ('' + encoding).toLowerCase(); - loweredCase = true; - } - } -} -Buffer.byteLength = byteLength; -function slowToString(encoding, start, end) { - let loweredCase = false; - - // No need to verify that "this.length <= MAX_UINT32" since it's a read-only - // property of a typed array. - - // This behaves neither like String nor Uint8Array in that we set start/end - // to their upper/lower bounds if the value passed is out of range. - // undefined is handled specially as per ECMA-262 6th Edition, - // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. - if (start === undefined || start < 0) { - start = 0; - } - // Return early if start > this.length. Done here to prevent potential uint32 - // coercion fail below. - if (start > this.length) { - return ''; - } - if (end === undefined || end > this.length) { - end = this.length; - } - if (end <= 0) { - return ''; - } - - // Force coercion to uint32. This will also coerce falsey/NaN values to 0. - end >>>= 0; - start >>>= 0; - if (end <= start) { - return ''; - } - if (!encoding) encoding = 'utf8'; - while (true) { - switch (encoding) { - case 'hex': - return hexSlice(this, start, end); - case 'utf8': - case 'utf-8': - return utf8Slice(this, start, end); - case 'ascii': - return asciiSlice(this, start, end); - case 'latin1': - case 'binary': - return latin1Slice(this, start, end); - case 'base64': - return base64Slice(this, start, end); - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return utf16leSlice(this, start, end); - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding); - encoding = (encoding + '').toLowerCase(); - loweredCase = true; - } - } -} - -// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) -// to detect a Buffer instance. It's not possible to use `instanceof Buffer` -// reliably in a browserify context because there could be multiple different -// copies of the 'buffer' package in use. This method works even for Buffer -// instances that were created from another copy of the `buffer` package. -// See: https://github.com/feross/buffer/issues/154 -Buffer.prototype._isBuffer = true; -function swap(b, n, m) { - const i = b[n]; - b[n] = b[m]; - b[m] = i; -} -Buffer.prototype.swap16 = function swap16() { - const len = this.length; - if (len % 2 !== 0) { - throw new RangeError('Buffer size must be a multiple of 16-bits'); - } - for (let i = 0; i < len; i += 2) { - swap(this, i, i + 1); - } - return this; -}; -Buffer.prototype.swap32 = function swap32() { - const len = this.length; - if (len % 4 !== 0) { - throw new RangeError('Buffer size must be a multiple of 32-bits'); - } - for (let i = 0; i < len; i += 4) { - swap(this, i, i + 3); - swap(this, i + 1, i + 2); - } - return this; -}; -Buffer.prototype.swap64 = function swap64() { - const len = this.length; - if (len % 8 !== 0) { - throw new RangeError('Buffer size must be a multiple of 64-bits'); - } - for (let i = 0; i < len; i += 8) { - swap(this, i, i + 7); - swap(this, i + 1, i + 6); - swap(this, i + 2, i + 5); - swap(this, i + 3, i + 4); - } - return this; -}; -Buffer.prototype.toString = function toString() { - const length = this.length; - if (length === 0) return ''; - if (arguments.length === 0) return utf8Slice(this, 0, length); - return slowToString.apply(this, arguments); -}; -Buffer.prototype.toLocaleString = Buffer.prototype.toString; -Buffer.prototype.equals = function equals(b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer'); - if (this === b) return true; - return Buffer.compare(this, b) === 0; -}; -Buffer.prototype.inspect = function inspect() { - let str = ''; - const max = exports.INSPECT_MAX_BYTES; - str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim(); - if (this.length > max) str += ' ... '; - return ''; -}; -if (customInspectSymbol) { - Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect; -} -Buffer.prototype.compare = function compare(target, start, end, thisStart, thisEnd) { - if (isInstance(target, Uint8Array)) { - target = Buffer.from(target, target.offset, target.byteLength); - } - if (!Buffer.isBuffer(target)) { - throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. ' + 'Received type ' + typeof target); - } - if (start === undefined) { - start = 0; - } - if (end === undefined) { - end = target ? target.length : 0; - } - if (thisStart === undefined) { - thisStart = 0; - } - if (thisEnd === undefined) { - thisEnd = this.length; - } - if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { - throw new RangeError('out of range index'); - } - if (thisStart >= thisEnd && start >= end) { - return 0; - } - if (thisStart >= thisEnd) { - return -1; - } - if (start >= end) { - return 1; - } - start >>>= 0; - end >>>= 0; - thisStart >>>= 0; - thisEnd >>>= 0; - if (this === target) return 0; - let x = thisEnd - thisStart; - let y = end - start; - const len = Math.min(x, y); - const thisCopy = this.slice(thisStart, thisEnd); - const targetCopy = target.slice(start, end); - for (let i = 0; i < len; ++i) { - if (thisCopy[i] !== targetCopy[i]) { - x = thisCopy[i]; - y = targetCopy[i]; - break; - } - } - if (x < y) return -1; - if (y < x) return 1; - return 0; -}; - -// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, -// OR the last index of `val` in `buffer` at offset <= `byteOffset`. -// -// Arguments: -// - buffer - a Buffer to search -// - val - a string, Buffer, or number -// - byteOffset - an index into `buffer`; will be clamped to an int32 -// - encoding - an optional encoding, relevant is val is a string -// - dir - true for indexOf, false for lastIndexOf -function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { - // Empty buffer means no match - if (buffer.length === 0) return -1; - - // Normalize byteOffset - if (typeof byteOffset === 'string') { - encoding = byteOffset; - byteOffset = 0; - } else if (byteOffset > 0x7fffffff) { - byteOffset = 0x7fffffff; - } else if (byteOffset < -0x80000000) { - byteOffset = -0x80000000; - } - byteOffset = +byteOffset; // Coerce to Number. - if (numberIsNaN(byteOffset)) { - // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer - byteOffset = dir ? 0 : buffer.length - 1; - } - - // Normalize byteOffset: negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = buffer.length + byteOffset; - if (byteOffset >= buffer.length) { - if (dir) return -1;else byteOffset = buffer.length - 1; - } else if (byteOffset < 0) { - if (dir) byteOffset = 0;else return -1; - } - - // Normalize val - if (typeof val === 'string') { - val = Buffer.from(val, encoding); - } - - // Finally, search either indexOf (if dir is true) or lastIndexOf - if (Buffer.isBuffer(val)) { - // Special case: looking for empty string/buffer always fails - if (val.length === 0) { - return -1; - } - return arrayIndexOf(buffer, val, byteOffset, encoding, dir); - } else if (typeof val === 'number') { - val = val & 0xFF; // Search for a byte value [0-255] - if (typeof Uint8Array.prototype.indexOf === 'function') { - if (dir) { - return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset); - } else { - return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset); - } - } - return arrayIndexOf(buffer, [val], byteOffset, encoding, dir); - } - throw new TypeError('val must be string, number or Buffer'); -} -function arrayIndexOf(arr, val, byteOffset, encoding, dir) { - let indexSize = 1; - let arrLength = arr.length; - let valLength = val.length; - if (encoding !== undefined) { - encoding = String(encoding).toLowerCase(); - if (encoding === 'ucs2' || encoding === 'ucs-2' || encoding === 'utf16le' || encoding === 'utf-16le') { - if (arr.length < 2 || val.length < 2) { - return -1; - } - indexSize = 2; - arrLength /= 2; - valLength /= 2; - byteOffset /= 2; - } - } - function read(buf, i) { - if (indexSize === 1) { - return buf[i]; - } else { - return buf.readUInt16BE(i * indexSize); - } - } - let i; - if (dir) { - let foundIndex = -1; - for (i = byteOffset; i < arrLength; i++) { - if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { - if (foundIndex === -1) foundIndex = i; - if (i - foundIndex + 1 === valLength) return foundIndex * indexSize; - } else { - if (foundIndex !== -1) i -= i - foundIndex; - foundIndex = -1; - } - } - } else { - if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength; - for (i = byteOffset; i >= 0; i--) { - let found = true; - for (let j = 0; j < valLength; j++) { - if (read(arr, i + j) !== read(val, j)) { - found = false; - break; - } - } - if (found) return i; - } - } - return -1; -} -Buffer.prototype.includes = function includes(val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1; -}; -Buffer.prototype.indexOf = function indexOf(val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, true); -}; -Buffer.prototype.lastIndexOf = function lastIndexOf(val, byteOffset, encoding) { - return bidirectionalIndexOf(this, val, byteOffset, encoding, false); -}; -function hexWrite(buf, string, offset, length) { - offset = Number(offset) || 0; - const remaining = buf.length - offset; - if (!length) { - length = remaining; - } else { - length = Number(length); - if (length > remaining) { - length = remaining; - } - } - const strLen = string.length; - if (length > strLen / 2) { - length = strLen / 2; - } - let i; - for (i = 0; i < length; ++i) { - const parsed = parseInt(string.substr(i * 2, 2), 16); - if (numberIsNaN(parsed)) return i; - buf[offset + i] = parsed; - } - return i; -} -function utf8Write(buf, string, offset, length) { - return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length); -} -function asciiWrite(buf, string, offset, length) { - return blitBuffer(asciiToBytes(string), buf, offset, length); -} -function base64Write(buf, string, offset, length) { - return blitBuffer(base64ToBytes(string), buf, offset, length); -} -function ucs2Write(buf, string, offset, length) { - return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length); -} -Buffer.prototype.write = function write(string, offset, length, encoding) { - // Buffer#write(string) - if (offset === undefined) { - encoding = 'utf8'; - length = this.length; - offset = 0; - // Buffer#write(string, encoding) - } else if (length === undefined && typeof offset === 'string') { - encoding = offset; - length = this.length; - offset = 0; - // Buffer#write(string, offset[, length][, encoding]) - } else if (isFinite(offset)) { - offset = offset >>> 0; - if (isFinite(length)) { - length = length >>> 0; - if (encoding === undefined) encoding = 'utf8'; - } else { - encoding = length; - length = undefined; - } - } else { - throw new Error('Buffer.write(string, encoding, offset[, length]) is no longer supported'); - } - const remaining = this.length - offset; - if (length === undefined || length > remaining) length = remaining; - if (string.length > 0 && (length < 0 || offset < 0) || offset > this.length) { - throw new RangeError('Attempt to write outside buffer bounds'); - } - if (!encoding) encoding = 'utf8'; - let loweredCase = false; - for (;;) { - switch (encoding) { - case 'hex': - return hexWrite(this, string, offset, length); - case 'utf8': - case 'utf-8': - return utf8Write(this, string, offset, length); - case 'ascii': - case 'latin1': - case 'binary': - return asciiWrite(this, string, offset, length); - case 'base64': - // Warning: maxLength not taken into account in base64Write - return base64Write(this, string, offset, length); - case 'ucs2': - case 'ucs-2': - case 'utf16le': - case 'utf-16le': - return ucs2Write(this, string, offset, length); - default: - if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding); - encoding = ('' + encoding).toLowerCase(); - loweredCase = true; - } - } -}; -Buffer.prototype.toJSON = function toJSON() { - return { - type: 'Buffer', - data: Array.prototype.slice.call(this._arr || this, 0) - }; -}; -function base64Slice(buf, start, end) { - if (start === 0 && end === buf.length) { - return base64.fromByteArray(buf); - } else { - return base64.fromByteArray(buf.slice(start, end)); - } -} -function utf8Slice(buf, start, end) { - end = Math.min(buf.length, end); - const res = []; - let i = start; - while (i < end) { - const firstByte = buf[i]; - let codePoint = null; - let bytesPerSequence = firstByte > 0xEF ? 4 : firstByte > 0xDF ? 3 : firstByte > 0xBF ? 2 : 1; - if (i + bytesPerSequence <= end) { - let secondByte, thirdByte, fourthByte, tempCodePoint; - switch (bytesPerSequence) { - case 1: - if (firstByte < 0x80) { - codePoint = firstByte; - } - break; - case 2: - secondByte = buf[i + 1]; - if ((secondByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0x1F) << 0x6 | secondByte & 0x3F; - if (tempCodePoint > 0x7F) { - codePoint = tempCodePoint; - } - } - break; - case 3: - secondByte = buf[i + 1]; - thirdByte = buf[i + 2]; - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | thirdByte & 0x3F; - if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { - codePoint = tempCodePoint; - } - } - break; - case 4: - secondByte = buf[i + 1]; - thirdByte = buf[i + 2]; - fourthByte = buf[i + 3]; - if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { - tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | fourthByte & 0x3F; - if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { - codePoint = tempCodePoint; - } - } - } - } - if (codePoint === null) { - // we did not generate a valid codePoint so insert a - // replacement char (U+FFFD) and advance only 1 byte - codePoint = 0xFFFD; - bytesPerSequence = 1; - } else if (codePoint > 0xFFFF) { - // encode to utf16 (surrogate pair dance) - codePoint -= 0x10000; - res.push(codePoint >>> 10 & 0x3FF | 0xD800); - codePoint = 0xDC00 | codePoint & 0x3FF; - } - res.push(codePoint); - i += bytesPerSequence; - } - return decodeCodePointsArray(res); -} - -// Based on http://stackoverflow.com/a/22747272/680742, the browser with -// the lowest limit is Chrome, with 0x10000 args. -// We go 1 magnitude less, for safety -const MAX_ARGUMENTS_LENGTH = 0x1000; -function decodeCodePointsArray(codePoints) { - const len = codePoints.length; - if (len <= MAX_ARGUMENTS_LENGTH) { - return String.fromCharCode.apply(String, codePoints); // avoid extra slice() - } - - // Decode in chunks to avoid "call stack size exceeded". - let res = ''; - let i = 0; - while (i < len) { - res += String.fromCharCode.apply(String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)); - } - return res; -} -function asciiSlice(buf, start, end) { - let ret = ''; - end = Math.min(buf.length, end); - for (let i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i] & 0x7F); - } - return ret; -} -function latin1Slice(buf, start, end) { - let ret = ''; - end = Math.min(buf.length, end); - for (let i = start; i < end; ++i) { - ret += String.fromCharCode(buf[i]); - } - return ret; -} -function hexSlice(buf, start, end) { - const len = buf.length; - if (!start || start < 0) start = 0; - if (!end || end < 0 || end > len) end = len; - let out = ''; - for (let i = start; i < end; ++i) { - out += hexSliceLookupTable[buf[i]]; - } - return out; -} -function utf16leSlice(buf, start, end) { - const bytes = buf.slice(start, end); - let res = ''; - // If bytes.length is odd, the last 8 bits must be ignored (same as node.js) - for (let i = 0; i < bytes.length - 1; i += 2) { - res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256); - } - return res; -} -Buffer.prototype.slice = function slice(start, end) { - const len = this.length; - start = ~~start; - end = end === undefined ? len : ~~end; - if (start < 0) { - start += len; - if (start < 0) start = 0; - } else if (start > len) { - start = len; - } - if (end < 0) { - end += len; - if (end < 0) end = 0; - } else if (end > len) { - end = len; - } - if (end < start) end = start; - const newBuf = this.subarray(start, end); - // Return an augmented `Uint8Array` instance - Object.setPrototypeOf(newBuf, Buffer.prototype); - return newBuf; -}; - -/* - * Need to make sure that buffer isn't trying to write out of bounds. - */ -function checkOffset(offset, ext, length) { - if (offset % 1 !== 0 || offset < 0) throw new RangeError('offset is not uint'); - if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length'); -} -Buffer.prototype.readUintLE = Buffer.prototype.readUIntLE = function readUIntLE(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) checkOffset(offset, byteLength, this.length); - let val = this[offset]; - let mul = 1; - let i = 0; - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul; - } - return val; -}; -Buffer.prototype.readUintBE = Buffer.prototype.readUIntBE = function readUIntBE(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) { - checkOffset(offset, byteLength, this.length); - } - let val = this[offset + --byteLength]; - let mul = 1; - while (byteLength > 0 && (mul *= 0x100)) { - val += this[offset + --byteLength] * mul; - } - return val; -}; -Buffer.prototype.readUint8 = Buffer.prototype.readUInt8 = function readUInt8(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 1, this.length); - return this[offset]; -}; -Buffer.prototype.readUint16LE = Buffer.prototype.readUInt16LE = function readUInt16LE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 2, this.length); - return this[offset] | this[offset + 1] << 8; -}; -Buffer.prototype.readUint16BE = Buffer.prototype.readUInt16BE = function readUInt16BE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 2, this.length); - return this[offset] << 8 | this[offset + 1]; -}; -Buffer.prototype.readUint32LE = Buffer.prototype.readUInt32LE = function readUInt32LE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 4, this.length); - return (this[offset] | this[offset + 1] << 8 | this[offset + 2] << 16) + this[offset + 3] * 0x1000000; -}; -Buffer.prototype.readUint32BE = Buffer.prototype.readUInt32BE = function readUInt32BE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 4, this.length); - return this[offset] * 0x1000000 + (this[offset + 1] << 16 | this[offset + 2] << 8 | this[offset + 3]); -}; -Buffer.prototype.readBigUInt64LE = defineBigIntMethod(function readBigUInt64LE(offset) { - offset = offset >>> 0; - validateNumber(offset, 'offset'); - const first = this[offset]; - const last = this[offset + 7]; - if (first === undefined || last === undefined) { - boundsError(offset, this.length - 8); - } - const lo = first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 24; - const hi = this[++offset] + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + last * 2 ** 24; - return BigInt(lo) + (BigInt(hi) << BigInt(32)); -}); -Buffer.prototype.readBigUInt64BE = defineBigIntMethod(function readBigUInt64BE(offset) { - offset = offset >>> 0; - validateNumber(offset, 'offset'); - const first = this[offset]; - const last = this[offset + 7]; - if (first === undefined || last === undefined) { - boundsError(offset, this.length - 8); - } - const hi = first * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + this[++offset]; - const lo = this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + last; - return (BigInt(hi) << BigInt(32)) + BigInt(lo); -}); -Buffer.prototype.readIntLE = function readIntLE(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) checkOffset(offset, byteLength, this.length); - let val = this[offset]; - let mul = 1; - let i = 0; - while (++i < byteLength && (mul *= 0x100)) { - val += this[offset + i] * mul; - } - mul *= 0x80; - if (val >= mul) val -= Math.pow(2, 8 * byteLength); - return val; -}; -Buffer.prototype.readIntBE = function readIntBE(offset, byteLength, noAssert) { - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) checkOffset(offset, byteLength, this.length); - let i = byteLength; - let mul = 1; - let val = this[offset + --i]; - while (i > 0 && (mul *= 0x100)) { - val += this[offset + --i] * mul; - } - mul *= 0x80; - if (val >= mul) val -= Math.pow(2, 8 * byteLength); - return val; -}; -Buffer.prototype.readInt8 = function readInt8(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 1, this.length); - if (!(this[offset] & 0x80)) return this[offset]; - return (0xff - this[offset] + 1) * -1; -}; -Buffer.prototype.readInt16LE = function readInt16LE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 2, this.length); - const val = this[offset] | this[offset + 1] << 8; - return val & 0x8000 ? val | 0xFFFF0000 : val; -}; -Buffer.prototype.readInt16BE = function readInt16BE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 2, this.length); - const val = this[offset + 1] | this[offset] << 8; - return val & 0x8000 ? val | 0xFFFF0000 : val; -}; -Buffer.prototype.readInt32LE = function readInt32LE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 4, this.length); - return this[offset] | this[offset + 1] << 8 | this[offset + 2] << 16 | this[offset + 3] << 24; -}; -Buffer.prototype.readInt32BE = function readInt32BE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 4, this.length); - return this[offset] << 24 | this[offset + 1] << 16 | this[offset + 2] << 8 | this[offset + 3]; -}; -Buffer.prototype.readBigInt64LE = defineBigIntMethod(function readBigInt64LE(offset) { - offset = offset >>> 0; - validateNumber(offset, 'offset'); - const first = this[offset]; - const last = this[offset + 7]; - if (first === undefined || last === undefined) { - boundsError(offset, this.length - 8); - } - const val = this[offset + 4] + this[offset + 5] * 2 ** 8 + this[offset + 6] * 2 ** 16 + (last << 24); // Overflow - - return (BigInt(val) << BigInt(32)) + BigInt(first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 24); -}); -Buffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE(offset) { - offset = offset >>> 0; - validateNumber(offset, 'offset'); - const first = this[offset]; - const last = this[offset + 7]; - if (first === undefined || last === undefined) { - boundsError(offset, this.length - 8); - } - const val = (first << 24) + - // Overflow - this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + this[++offset]; - return (BigInt(val) << BigInt(32)) + BigInt(this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + this[++offset] * 2 ** 8 + last); -}); -Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 4, this.length); - return ieee754.read(this, offset, true, 23, 4); -}; -Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 4, this.length); - return ieee754.read(this, offset, false, 23, 4); -}; -Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 8, this.length); - return ieee754.read(this, offset, true, 52, 8); -}; -Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { - offset = offset >>> 0; - if (!noAssert) checkOffset(offset, 8, this.length); - return ieee754.read(this, offset, false, 52, 8); -}; -function checkInt(buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance'); - if (value > max || value < min) throw new RangeError('"value" argument is out of bounds'); - if (offset + ext > buf.length) throw new RangeError('Index out of range'); -} -Buffer.prototype.writeUintLE = Buffer.prototype.writeUIntLE = function writeUIntLE(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) { - const maxBytes = Math.pow(2, 8 * byteLength) - 1; - checkInt(this, value, offset, byteLength, maxBytes, 0); - } - let mul = 1; - let i = 0; - this[offset] = value & 0xFF; - while (++i < byteLength && (mul *= 0x100)) { - this[offset + i] = value / mul & 0xFF; - } - return offset + byteLength; -}; -Buffer.prototype.writeUintBE = Buffer.prototype.writeUIntBE = function writeUIntBE(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - byteLength = byteLength >>> 0; - if (!noAssert) { - const maxBytes = Math.pow(2, 8 * byteLength) - 1; - checkInt(this, value, offset, byteLength, maxBytes, 0); - } - let i = byteLength - 1; - let mul = 1; - this[offset + i] = value & 0xFF; - while (--i >= 0 && (mul *= 0x100)) { - this[offset + i] = value / mul & 0xFF; - } - return offset + byteLength; -}; -Buffer.prototype.writeUint8 = Buffer.prototype.writeUInt8 = function writeUInt8(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0); - this[offset] = value & 0xff; - return offset + 1; -}; -Buffer.prototype.writeUint16LE = Buffer.prototype.writeUInt16LE = function writeUInt16LE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0); - this[offset] = value & 0xff; - this[offset + 1] = value >>> 8; - return offset + 2; -}; -Buffer.prototype.writeUint16BE = Buffer.prototype.writeUInt16BE = function writeUInt16BE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0); - this[offset] = value >>> 8; - this[offset + 1] = value & 0xff; - return offset + 2; -}; -Buffer.prototype.writeUint32LE = Buffer.prototype.writeUInt32LE = function writeUInt32LE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0); - this[offset + 3] = value >>> 24; - this[offset + 2] = value >>> 16; - this[offset + 1] = value >>> 8; - this[offset] = value & 0xff; - return offset + 4; -}; -Buffer.prototype.writeUint32BE = Buffer.prototype.writeUInt32BE = function writeUInt32BE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0); - this[offset] = value >>> 24; - this[offset + 1] = value >>> 16; - this[offset + 2] = value >>> 8; - this[offset + 3] = value & 0xff; - return offset + 4; -}; -function wrtBigUInt64LE(buf, value, offset, min, max) { - checkIntBI(value, min, max, buf, offset, 7); - let lo = Number(value & BigInt(0xffffffff)); - buf[offset++] = lo; - lo = lo >> 8; - buf[offset++] = lo; - lo = lo >> 8; - buf[offset++] = lo; - lo = lo >> 8; - buf[offset++] = lo; - let hi = Number(value >> BigInt(32) & BigInt(0xffffffff)); - buf[offset++] = hi; - hi = hi >> 8; - buf[offset++] = hi; - hi = hi >> 8; - buf[offset++] = hi; - hi = hi >> 8; - buf[offset++] = hi; - return offset; -} -function wrtBigUInt64BE(buf, value, offset, min, max) { - checkIntBI(value, min, max, buf, offset, 7); - let lo = Number(value & BigInt(0xffffffff)); - buf[offset + 7] = lo; - lo = lo >> 8; - buf[offset + 6] = lo; - lo = lo >> 8; - buf[offset + 5] = lo; - lo = lo >> 8; - buf[offset + 4] = lo; - let hi = Number(value >> BigInt(32) & BigInt(0xffffffff)); - buf[offset + 3] = hi; - hi = hi >> 8; - buf[offset + 2] = hi; - hi = hi >> 8; - buf[offset + 1] = hi; - hi = hi >> 8; - buf[offset] = hi; - return offset + 8; -} -Buffer.prototype.writeBigUInt64LE = defineBigIntMethod(function writeBigUInt64LE(value, offset = 0) { - return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')); -}); -Buffer.prototype.writeBigUInt64BE = defineBigIntMethod(function writeBigUInt64BE(value, offset = 0) { - return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')); -}); -Buffer.prototype.writeIntLE = function writeIntLE(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) { - const limit = Math.pow(2, 8 * byteLength - 1); - checkInt(this, value, offset, byteLength, limit - 1, -limit); - } - let i = 0; - let mul = 1; - let sub = 0; - this[offset] = value & 0xFF; - while (++i < byteLength && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { - sub = 1; - } - this[offset + i] = (value / mul >> 0) - sub & 0xFF; - } - return offset + byteLength; -}; -Buffer.prototype.writeIntBE = function writeIntBE(value, offset, byteLength, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) { - const limit = Math.pow(2, 8 * byteLength - 1); - checkInt(this, value, offset, byteLength, limit - 1, -limit); - } - let i = byteLength - 1; - let mul = 1; - let sub = 0; - this[offset + i] = value & 0xFF; - while (--i >= 0 && (mul *= 0x100)) { - if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { - sub = 1; - } - this[offset + i] = (value / mul >> 0) - sub & 0xFF; - } - return offset + byteLength; -}; -Buffer.prototype.writeInt8 = function writeInt8(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80); - if (value < 0) value = 0xff + value + 1; - this[offset] = value & 0xff; - return offset + 1; -}; -Buffer.prototype.writeInt16LE = function writeInt16LE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000); - this[offset] = value & 0xff; - this[offset + 1] = value >>> 8; - return offset + 2; -}; -Buffer.prototype.writeInt16BE = function writeInt16BE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000); - this[offset] = value >>> 8; - this[offset + 1] = value & 0xff; - return offset + 2; -}; -Buffer.prototype.writeInt32LE = function writeInt32LE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); - this[offset] = value & 0xff; - this[offset + 1] = value >>> 8; - this[offset + 2] = value >>> 16; - this[offset + 3] = value >>> 24; - return offset + 4; -}; -Buffer.prototype.writeInt32BE = function writeInt32BE(value, offset, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000); - if (value < 0) value = 0xffffffff + value + 1; - this[offset] = value >>> 24; - this[offset + 1] = value >>> 16; - this[offset + 2] = value >>> 8; - this[offset + 3] = value & 0xff; - return offset + 4; -}; -Buffer.prototype.writeBigInt64LE = defineBigIntMethod(function writeBigInt64LE(value, offset = 0) { - return wrtBigUInt64LE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff')); -}); -Buffer.prototype.writeBigInt64BE = defineBigIntMethod(function writeBigInt64BE(value, offset = 0) { - return wrtBigUInt64BE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff')); -}); -function checkIEEE754(buf, value, offset, ext, max, min) { - if (offset + ext > buf.length) throw new RangeError('Index out of range'); - if (offset < 0) throw new RangeError('Index out of range'); -} -function writeFloat(buf, value, offset, littleEndian, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) { - checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38); - } - ieee754.write(buf, value, offset, littleEndian, 23, 4); - return offset + 4; -} -Buffer.prototype.writeFloatLE = function writeFloatLE(value, offset, noAssert) { - return writeFloat(this, value, offset, true, noAssert); -}; -Buffer.prototype.writeFloatBE = function writeFloatBE(value, offset, noAssert) { - return writeFloat(this, value, offset, false, noAssert); -}; -function writeDouble(buf, value, offset, littleEndian, noAssert) { - value = +value; - offset = offset >>> 0; - if (!noAssert) { - checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308); - } - ieee754.write(buf, value, offset, littleEndian, 52, 8); - return offset + 8; -} -Buffer.prototype.writeDoubleLE = function writeDoubleLE(value, offset, noAssert) { - return writeDouble(this, value, offset, true, noAssert); -}; -Buffer.prototype.writeDoubleBE = function writeDoubleBE(value, offset, noAssert) { - return writeDouble(this, value, offset, false, noAssert); -}; - -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) -Buffer.prototype.copy = function copy(target, targetStart, start, end) { - if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer'); - if (!start) start = 0; - if (!end && end !== 0) end = this.length; - if (targetStart >= target.length) targetStart = target.length; - if (!targetStart) targetStart = 0; - if (end > 0 && end < start) end = start; - - // Copy 0 bytes; we're done - if (end === start) return 0; - if (target.length === 0 || this.length === 0) return 0; - - // Fatal error conditions - if (targetStart < 0) { - throw new RangeError('targetStart out of bounds'); - } - if (start < 0 || start >= this.length) throw new RangeError('Index out of range'); - if (end < 0) throw new RangeError('sourceEnd out of bounds'); - - // Are we oob? - if (end > this.length) end = this.length; - if (target.length - targetStart < end - start) { - end = target.length - targetStart + start; - } - const len = end - start; - if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { - // Use built-in when available, missing from IE11 - this.copyWithin(targetStart, start, end); - } else { - Uint8Array.prototype.set.call(target, this.subarray(start, end), targetStart); - } - return len; -}; - -// Usage: -// buffer.fill(number[, offset[, end]]) -// buffer.fill(buffer[, offset[, end]]) -// buffer.fill(string[, offset[, end]][, encoding]) -Buffer.prototype.fill = function fill(val, start, end, encoding) { - // Handle string cases: - if (typeof val === 'string') { - if (typeof start === 'string') { - encoding = start; - start = 0; - end = this.length; - } else if (typeof end === 'string') { - encoding = end; - end = this.length; - } - if (encoding !== undefined && typeof encoding !== 'string') { - throw new TypeError('encoding must be a string'); - } - if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { - throw new TypeError('Unknown encoding: ' + encoding); - } - if (val.length === 1) { - const code = val.charCodeAt(0); - if (encoding === 'utf8' && code < 128 || encoding === 'latin1') { - // Fast path: If `val` fits into a single byte, use that numeric value. - val = code; - } - } - } else if (typeof val === 'number') { - val = val & 255; - } else if (typeof val === 'boolean') { - val = Number(val); - } - - // Invalid ranges are not set to a default, so can range check early. - if (start < 0 || this.length < start || this.length < end) { - throw new RangeError('Out of range index'); - } - if (end <= start) { - return this; - } - start = start >>> 0; - end = end === undefined ? this.length : end >>> 0; - if (!val) val = 0; - let i; - if (typeof val === 'number') { - for (i = start; i < end; ++i) { - this[i] = val; - } - } else { - const bytes = Buffer.isBuffer(val) ? val : Buffer.from(val, encoding); - const len = bytes.length; - if (len === 0) { - throw new TypeError('The value "' + val + '" is invalid for argument "value"'); - } - for (i = 0; i < end - start; ++i) { - this[i + start] = bytes[i % len]; - } - } - return this; -}; - -// CUSTOM ERRORS -// ============= - -// Simplified versions from Node, changed for Buffer-only usage -const errors = {}; -function E(sym, getMessage, Base) { - errors[sym] = class NodeError extends Base { - constructor() { - super(); - Object.defineProperty(this, 'message', { - value: getMessage.apply(this, arguments), - writable: true, - configurable: true - }); - - // Add the error code to the name to include it in the stack trace. - this.name = `${this.name} [${sym}]`; - // Access the stack to generate the error message including the error code - // from the name. - this.stack; // eslint-disable-line no-unused-expressions - // Reset the name to the actual name. - delete this.name; - } - get code() { - return sym; - } - set code(value) { - Object.defineProperty(this, 'code', { - configurable: true, - enumerable: true, - value, - writable: true - }); - } - toString() { - return `${this.name} [${sym}]: ${this.message}`; - } - }; -} -E('ERR_BUFFER_OUT_OF_BOUNDS', function (name) { - if (name) { - return `${name} is outside of buffer bounds`; - } - return 'Attempt to access memory outside buffer bounds'; -}, RangeError); -E('ERR_INVALID_ARG_TYPE', function (name, actual) { - return `The "${name}" argument must be of type number. Received type ${typeof actual}`; -}, TypeError); -E('ERR_OUT_OF_RANGE', function (str, range, input) { - let msg = `The value of "${str}" is out of range.`; - let received = input; - if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { - received = addNumericalSeparator(String(input)); - } else if (typeof input === 'bigint') { - received = String(input); - if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) { - received = addNumericalSeparator(received); - } - received += 'n'; - } - msg += ` It must be ${range}. Received ${received}`; - return msg; -}, RangeError); -function addNumericalSeparator(val) { - let res = ''; - let i = val.length; - const start = val[0] === '-' ? 1 : 0; - for (; i >= start + 4; i -= 3) { - res = `_${val.slice(i - 3, i)}${res}`; - } - return `${val.slice(0, i)}${res}`; -} - -// CHECK FUNCTIONS -// =============== - -function checkBounds(buf, offset, byteLength) { - validateNumber(offset, 'offset'); - if (buf[offset] === undefined || buf[offset + byteLength] === undefined) { - boundsError(offset, buf.length - (byteLength + 1)); - } -} -function checkIntBI(value, min, max, buf, offset, byteLength) { - if (value > max || value < min) { - const n = typeof min === 'bigint' ? 'n' : ''; - let range; - if (byteLength > 3) { - if (min === 0 || min === BigInt(0)) { - range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`; - } else { - range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` + `${(byteLength + 1) * 8 - 1}${n}`; - } - } else { - range = `>= ${min}${n} and <= ${max}${n}`; - } - throw new errors.ERR_OUT_OF_RANGE('value', range, value); - } - checkBounds(buf, offset, byteLength); -} -function validateNumber(value, name) { - if (typeof value !== 'number') { - throw new errors.ERR_INVALID_ARG_TYPE(name, 'number', value); - } -} -function boundsError(value, length, type) { - if (Math.floor(value) !== value) { - validateNumber(value, type); - throw new errors.ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value); - } - if (length < 0) { - throw new errors.ERR_BUFFER_OUT_OF_BOUNDS(); - } - throw new errors.ERR_OUT_OF_RANGE(type || 'offset', `>= ${type ? 1 : 0} and <= ${length}`, value); -} - -// HELPER FUNCTIONS -// ================ - -const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g; -function base64clean(str) { - // Node takes equal signs as end of the Base64 encoding - str = str.split('=')[0]; - // Node strips out invalid characters like \n and \t from the string, base64-js does not - str = str.trim().replace(INVALID_BASE64_RE, ''); - // Node converts strings with length < 2 to '' - if (str.length < 2) return ''; - // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not - while (str.length % 4 !== 0) { - str = str + '='; - } - return str; -} -function utf8ToBytes(string, units) { - units = units || Infinity; - let codePoint; - const length = string.length; - let leadSurrogate = null; - const bytes = []; - for (let i = 0; i < length; ++i) { - codePoint = string.charCodeAt(i); - - // is surrogate component - if (codePoint > 0xD7FF && codePoint < 0xE000) { - // last char was a lead - if (!leadSurrogate) { - // no lead yet - if (codePoint > 0xDBFF) { - // unexpected trail - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD); - continue; - } else if (i + 1 === length) { - // unpaired lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD); - continue; - } - - // valid lead - leadSurrogate = codePoint; - continue; - } - - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD); - leadSurrogate = codePoint; - continue; - } - - // valid surrogate pair - codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000; - } else if (leadSurrogate) { - // valid bmp char, but last char was a lead - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD); - } - leadSurrogate = null; - - // encode utf8 - if (codePoint < 0x80) { - if ((units -= 1) < 0) break; - bytes.push(codePoint); - } else if (codePoint < 0x800) { - if ((units -= 2) < 0) break; - bytes.push(codePoint >> 0x6 | 0xC0, codePoint & 0x3F | 0x80); - } else if (codePoint < 0x10000) { - if ((units -= 3) < 0) break; - bytes.push(codePoint >> 0xC | 0xE0, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80); - } else if (codePoint < 0x110000) { - if ((units -= 4) < 0) break; - bytes.push(codePoint >> 0x12 | 0xF0, codePoint >> 0xC & 0x3F | 0x80, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80); - } else { - throw new Error('Invalid code point'); - } - } - return bytes; -} -function asciiToBytes(str) { - const byteArray = []; - for (let i = 0; i < str.length; ++i) { - // Node's code seems to be doing this and not & 0x7F.. - byteArray.push(str.charCodeAt(i) & 0xFF); - } - return byteArray; -} -function utf16leToBytes(str, units) { - let c, hi, lo; - const byteArray = []; - for (let i = 0; i < str.length; ++i) { - if ((units -= 2) < 0) break; - c = str.charCodeAt(i); - hi = c >> 8; - lo = c % 256; - byteArray.push(lo); - byteArray.push(hi); - } - return byteArray; -} -function base64ToBytes(str) { - return base64.toByteArray(base64clean(str)); -} -function blitBuffer(src, dst, offset, length) { - let i; - for (i = 0; i < length; ++i) { - if (i + offset >= dst.length || i >= src.length) break; - dst[i + offset] = src[i]; - } - return i; -} - -// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass -// the `instanceof` check but they should be treated as of that type. -// See: https://github.com/feross/buffer/issues/166 -function isInstance(obj, type) { - return obj instanceof type || obj != null && obj.constructor != null && obj.constructor.name != null && obj.constructor.name === type.name; -} -function numberIsNaN(obj) { - // For IE11 support - return obj !== obj; // eslint-disable-line no-self-compare -} - -// Create lookup table for `toString('hex')` -// See: https://github.com/feross/buffer/issues/219 -const hexSliceLookupTable = function () { - const alphabet = '0123456789abcdef'; - const table = new Array(256); - for (let i = 0; i < 16; ++i) { - const i16 = i * 16; - for (let j = 0; j < 16; ++j) { - table[i16 + j] = alphabet[i] + alphabet[j]; - } - } - return table; -}(); - -// Return not function with Error if BigInt not supported -function defineBigIntMethod(fn) { - return typeof BigInt === 'undefined' ? BufferBigIntNotDefined : fn; -} -function BufferBigIntNotDefined() { - throw new Error('BigInt not supported'); -} - -/***/ }), - -/***/ "./node_modules/css-loader/dist/runtime/api.js": -/*!*****************************************************!*\ - !*** ./node_modules/css-loader/dist/runtime/api.js ***! - \*****************************************************/ -/***/ ((module) => { - -"use strict"; - - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -module.exports = function (cssWithMappingToString) { - var list = []; - - // return the list of modules as css string - list.toString = function toString() { - return this.map(function (item) { - var content = ""; - var needLayer = typeof item[5] !== "undefined"; - if (item[4]) { - content += "@supports (".concat(item[4], ") {"); - } - if (item[2]) { - content += "@media ".concat(item[2], " {"); - } - if (needLayer) { - content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {"); - } - content += cssWithMappingToString(item); - if (needLayer) { - content += "}"; - } - if (item[2]) { - content += "}"; - } - if (item[4]) { - content += "}"; - } - return content; - }).join(""); - }; - - // import a list of modules into the list - list.i = function i(modules, media, dedupe, supports, layer) { - if (typeof modules === "string") { - modules = [[null, modules, undefined]]; - } - var alreadyImportedModules = {}; - if (dedupe) { - for (var k = 0; k < this.length; k++) { - var id = this[k][0]; - if (id != null) { - alreadyImportedModules[id] = true; - } - } - } - for (var _k = 0; _k < modules.length; _k++) { - var item = [].concat(modules[_k]); - if (dedupe && alreadyImportedModules[item[0]]) { - continue; - } - if (typeof layer !== "undefined") { - if (typeof item[5] === "undefined") { - item[5] = layer; - } else { - item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}"); - item[5] = layer; - } - } - if (media) { - if (!item[2]) { - item[2] = media; - } else { - item[1] = "@media ".concat(item[2], " {").concat(item[1], "}"); - item[2] = media; - } - } - if (supports) { - if (!item[4]) { - item[4] = "".concat(supports); - } else { - item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}"); - item[4] = supports; - } - } - list.push(item); - } - }; - return list; -}; - -/***/ }), - -/***/ "./node_modules/css-loader/dist/runtime/getUrl.js": -/*!********************************************************!*\ - !*** ./node_modules/css-loader/dist/runtime/getUrl.js ***! - \********************************************************/ -/***/ ((module) => { - -"use strict"; - - -module.exports = function (url, options) { - if (!options) { - options = {}; - } - if (!url) { - return url; - } - url = String(url.__esModule ? url.default : url); - - // If url is already wrapped in quotes, remove them - if (/^['"].*['"]$/.test(url)) { - url = url.slice(1, -1); - } - if (options.hash) { - url += options.hash; - } - - // Should url be wrapped? - // See https://drafts.csswg.org/css-values-3/#urls - if (/["'() \t\n]|(%20)/.test(url) || options.needQuotes) { - return "\"".concat(url.replace(/"/g, '\\"').replace(/\n/g, "\\n"), "\""); - } - return url; -}; - -/***/ }), - -/***/ "./node_modules/css-loader/dist/runtime/sourceMaps.js": -/*!************************************************************!*\ - !*** ./node_modules/css-loader/dist/runtime/sourceMaps.js ***! - \************************************************************/ -/***/ ((module) => { - -"use strict"; - - -module.exports = function (item) { - var content = item[1]; - var cssMapping = item[3]; - if (!cssMapping) { - return content; - } - if (typeof btoa === "function") { - var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping)))); - var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64); - var sourceMapping = "/*# ".concat(data, " */"); - return [content].concat([sourceMapping]).join("\n"); - } - return [content].join("\n"); -}; - -/***/ }), - -/***/ "./node_modules/debug/src/browser.js": -/*!*******************************************!*\ - !*** ./node_modules/debug/src/browser.js ***! - \*******************************************/ -/***/ ((module, exports, __webpack_require__) => { - -/* eslint-env browser */ - -/** - * This is the web browser implementation of `debug()`. - */ - -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = localstorage(); -exports.destroy = (() => { - let warned = false; - return () => { - if (!warned) { - warned = true; - console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'); - } - }; -})(); - -/** - * Colors. - */ - -exports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33']; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -// eslint-disable-next-line complexity -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) { - return true; - } - - // Internet Explorer and Edge do not support colors. - if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { - return false; - } - - // Is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || - // Is firebug? http://stackoverflow.com/a/398120/376773 - typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || - // Is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || - // Double check webkit in userAgent just in case we are in a worker - typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/); -} - -/** - * Colorize log arguments if enabled. - * - * @api public - */ - -function formatArgs(args) { - args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff); - if (!this.useColors) { - return; - } - const c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit'); - - // The final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - let index = 0; - let lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, match => { - if (match === '%%') { - return; - } - index++; - if (match === '%c') { - // We only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - args.splice(lastC, 0, c); -} - -/** - * Invokes `console.debug()` when available. - * No-op when `console.debug` is not a "function". - * If `console.debug` is not available, falls back - * to `console.log`. - * - * @api public - */ -exports.log = console.debug || console.log || (() => {}); - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ -function save(namespaces) { - try { - if (namespaces) { - exports.storage.setItem('debug', namespaces); - } else { - exports.storage.removeItem('debug'); - } - } catch (error) { - // Swallow - // XXX (@Qix-) should we be logging these? - } -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ -function load() { - let r; - try { - r = exports.storage.getItem('debug'); - } catch (error) { - // Swallow - // XXX (@Qix-) should we be logging these? - } - - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } - return r; -} - -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - -function localstorage() { - try { - // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context - // The Browser also has localStorage in the global context. - return localStorage; - } catch (error) { - // Swallow - // XXX (@Qix-) should we be logging these? - } -} -module.exports = __webpack_require__(/*! ./common */ "./node_modules/debug/src/common.js")(exports); -const { - formatters -} = module.exports; - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -formatters.j = function (v) { - try { - return JSON.stringify(v); - } catch (error) { - return '[UnexpectedJSONParseError]: ' + error.message; - } -}; - -/***/ }), - -/***/ "./node_modules/debug/src/common.js": -/*!******************************************!*\ - !*** ./node_modules/debug/src/common.js ***! - \******************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - */ - -function setup(env) { - createDebug.debug = createDebug; - createDebug.default = createDebug; - createDebug.coerce = coerce; - createDebug.disable = disable; - createDebug.enable = enable; - createDebug.enabled = enabled; - createDebug.humanize = __webpack_require__(/*! ms */ "./node_modules/ms/index.js"); - createDebug.destroy = destroy; - Object.keys(env).forEach(key => { - createDebug[key] = env[key]; - }); - - /** - * The currently active debug mode names, and names to skip. - */ - - createDebug.names = []; - createDebug.skips = []; - - /** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ - createDebug.formatters = {}; - - /** - * Selects a color for a debug namespace - * @param {String} namespace The namespace string for the debug instance to be colored - * @return {Number|String} An ANSI color code for the given namespace - * @api private - */ - function selectColor(namespace) { - let hash = 0; - for (let i = 0; i < namespace.length; i++) { - hash = (hash << 5) - hash + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - - return createDebug.colors[Math.abs(hash) % createDebug.colors.length]; - } - createDebug.selectColor = selectColor; - - /** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - function createDebug(namespace) { - let prevTime; - let enableOverride = null; - let namespacesCache; - let enabledCache; - function debug(...args) { - // Disabled? - if (!debug.enabled) { - return; - } - const self = debug; - - // Set `diff` timestamp - const curr = Number(new Date()); - const ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - args[0] = createDebug.coerce(args[0]); - if (typeof args[0] !== 'string') { - // Anything else let's inspect with %O - args.unshift('%O'); - } - - // Apply any `formatters` transformations - let index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => { - // If we encounter an escaped % then don't increase the array index - if (match === '%%') { - return '%'; - } - index++; - const formatter = createDebug.formatters[format]; - if (typeof formatter === 'function') { - const val = args[index]; - match = formatter.call(self, val); - - // Now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - // Apply env-specific formatting (colors, etc.) - createDebug.formatArgs.call(self, args); - const logFn = self.log || createDebug.log; - logFn.apply(self, args); - } - debug.namespace = namespace; - debug.useColors = createDebug.useColors(); - debug.color = createDebug.selectColor(namespace); - debug.extend = extend; - debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release. - - Object.defineProperty(debug, 'enabled', { - enumerable: true, - configurable: false, - get: () => { - if (enableOverride !== null) { - return enableOverride; - } - if (namespacesCache !== createDebug.namespaces) { - namespacesCache = createDebug.namespaces; - enabledCache = createDebug.enabled(namespace); - } - return enabledCache; - }, - set: v => { - enableOverride = v; - } - }); - - // Env-specific initialization logic for debug instances - if (typeof createDebug.init === 'function') { - createDebug.init(debug); - } - return debug; - } - function extend(namespace, delimiter) { - const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace); - newDebug.log = this.log; - return newDebug; - } - - /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - function enable(namespaces) { - createDebug.save(namespaces); - createDebug.namespaces = namespaces; - createDebug.names = []; - createDebug.skips = []; - let i; - const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - const len = split.length; - for (i = 0; i < len; i++) { - if (!split[i]) { - // ignore empty strings - continue; - } - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$')); - } else { - createDebug.names.push(new RegExp('^' + namespaces + '$')); - } - } - } - - /** - * Disable debug output. - * - * @return {String} namespaces - * @api public - */ - function disable() { - const namespaces = [...createDebug.names.map(toNamespace), ...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)].join(','); - createDebug.enable(''); - return namespaces; - } - - /** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - let i; - let len; - for (i = 0, len = createDebug.skips.length; i < len; i++) { - if (createDebug.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = createDebug.names.length; i < len; i++) { - if (createDebug.names[i].test(name)) { - return true; - } - } - return false; - } - - /** - * Convert regexp to namespace - * - * @param {RegExp} regxep - * @return {String} namespace - * @api private - */ - function toNamespace(regexp) { - return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*'); - } - - /** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - function coerce(val) { - if (val instanceof Error) { - return val.stack || val.message; - } - return val; - } - - /** - * XXX DO NOT USE. This is a temporary stub function. - * XXX It WILL be removed in the next major release. - */ - function destroy() { - console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'); - } - createDebug.enable(createDebug.load()); - return createDebug; -} -module.exports = setup; - -/***/ }), - -/***/ "./node_modules/deep-assign/index.js": -/*!*******************************************!*\ - !*** ./node_modules/deep-assign/index.js ***! - \*******************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var isObj = __webpack_require__(/*! is-obj */ "./node_modules/is-obj/index.js"); -var hasOwnProperty = Object.prototype.hasOwnProperty; -var propIsEnumerable = Object.prototype.propertyIsEnumerable; -function toObject(val) { - if (val === null || val === undefined) { - throw new TypeError('Sources cannot be null or undefined'); - } - return Object(val); -} -function assignKey(to, from, key) { - var val = from[key]; - if (val === undefined || val === null) { - return; - } - if (hasOwnProperty.call(to, key)) { - if (to[key] === undefined || to[key] === null) { - throw new TypeError('Cannot convert undefined or null to object (' + key + ')'); - } - } - if (!hasOwnProperty.call(to, key) || !isObj(val)) { - to[key] = val; - } else { - to[key] = assign(Object(to[key]), from[key]); - } -} -function assign(to, from) { - if (to === from) { - return to; - } - from = Object(from); - for (var key in from) { - if (hasOwnProperty.call(from, key)) { - assignKey(to, from, key); - } - } - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(from); - for (var i = 0; i < symbols.length; i++) { - if (propIsEnumerable.call(from, symbols[i])) { - assignKey(to, from, symbols[i]); - } - } - } - return to; -} -module.exports = function deepAssign(target) { - target = toObject(target); - for (var s = 1; s < arguments.length; s++) { - assign(target, arguments[s]); - } - return target; -}; - -/***/ }), - -/***/ "./node_modules/dtype/index.js": -/*!*************************************!*\ - !*** ./node_modules/dtype/index.js ***! - \*************************************/ -/***/ ((module) => { - -module.exports = function (dtype) { - switch (dtype) { - case 'int8': - return Int8Array; - case 'int16': - return Int16Array; - case 'int32': - return Int32Array; - case 'uint8': - return Uint8Array; - case 'uint16': - return Uint16Array; - case 'uint32': - return Uint32Array; - case 'float32': - return Float32Array; - case 'float64': - return Float64Array; - case 'array': - return Array; - case 'uint8_clamped': - return Uint8ClampedArray; - } -}; - -/***/ }), - -/***/ "./node_modules/global/window.js": -/*!***************************************!*\ - !*** ./node_modules/global/window.js ***! - \***************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var win; -if (typeof window !== "undefined") { - win = window; -} else if (typeof __webpack_require__.g !== "undefined") { - win = __webpack_require__.g; -} else if (typeof self !== "undefined") { - win = self; -} else { - win = {}; -} -module.exports = win; - -/***/ }), - -/***/ "./node_modules/ieee754/index.js": -/*!***************************************!*\ - !*** ./node_modules/ieee754/index.js ***! - \***************************************/ -/***/ ((__unused_webpack_module, exports) => { - -/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ -exports.read = function (buffer, offset, isLE, mLen, nBytes) { - var e, m; - var eLen = nBytes * 8 - mLen - 1; - var eMax = (1 << eLen) - 1; - var eBias = eMax >> 1; - var nBits = -7; - var i = isLE ? nBytes - 1 : 0; - var d = isLE ? -1 : 1; - var s = buffer[offset + i]; - i += d; - e = s & (1 << -nBits) - 1; - s >>= -nBits; - nBits += eLen; - for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} - m = e & (1 << -nBits) - 1; - e >>= -nBits; - nBits += mLen; - for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} - if (e === 0) { - e = 1 - eBias; - } else if (e === eMax) { - return m ? NaN : (s ? -1 : 1) * Infinity; - } else { - m = m + Math.pow(2, mLen); - e = e - eBias; - } - return (s ? -1 : 1) * m * Math.pow(2, e - mLen); -}; -exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { - var e, m, c; - var eLen = nBytes * 8 - mLen - 1; - var eMax = (1 << eLen) - 1; - var eBias = eMax >> 1; - var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0; - var i = isLE ? 0 : nBytes - 1; - var d = isLE ? 1 : -1; - var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0; - value = Math.abs(value); - if (isNaN(value) || value === Infinity) { - m = isNaN(value) ? 1 : 0; - e = eMax; - } else { - e = Math.floor(Math.log(value) / Math.LN2); - if (value * (c = Math.pow(2, -e)) < 1) { - e--; - c *= 2; - } - if (e + eBias >= 1) { - value += rt / c; - } else { - value += rt * Math.pow(2, 1 - eBias); - } - if (value * c >= 2) { - e++; - c /= 2; - } - if (e + eBias >= eMax) { - m = 0; - e = eMax; - } else if (e + eBias >= 1) { - m = (value * c - 1) * Math.pow(2, mLen); - e = e + eBias; - } else { - m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); - e = 0; - } - } - for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} - e = e << mLen | m; - eLen += mLen; - for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} - buffer[offset + i - d] |= s * 128; -}; - -/***/ }), - -/***/ "./node_modules/is-buffer/index.js": -/*!*****************************************!*\ - !*** ./node_modules/is-buffer/index.js ***! - \*****************************************/ -/***/ ((module) => { - -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh - * @license MIT - */ - -// The _isBuffer check is for Safari 5-7 support, because it's missing -// Object.prototype.constructor. Remove this eventually -module.exports = function (obj) { - return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer); -}; -function isBuffer(obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj); -} - -// For Node v0.10 support. Remove this eventually. -function isSlowBuffer(obj) { - return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)); -} - -/***/ }), - -/***/ "./node_modules/is-function/index.js": -/*!*******************************************!*\ - !*** ./node_modules/is-function/index.js ***! - \*******************************************/ -/***/ ((module) => { - -module.exports = isFunction; -var toString = Object.prototype.toString; -function isFunction(fn) { - if (!fn) { - return false; - } - var string = toString.call(fn); - return string === '[object Function]' || typeof fn === 'function' && string !== '[object RegExp]' || typeof window !== 'undefined' && ( - // IE8 and below - fn === window.setTimeout || fn === window.alert || fn === window.confirm || fn === window.prompt); -} -; - -/***/ }), - -/***/ "./node_modules/is-obj/index.js": -/*!**************************************!*\ - !*** ./node_modules/is-obj/index.js ***! - \**************************************/ -/***/ ((module) => { - -"use strict"; - - -module.exports = function (x) { - var type = typeof x; - return x !== null && (type === 'object' || type === 'function'); -}; - -/***/ }), - -/***/ "./node_modules/layout-bmfont-text/index.js": -/*!**************************************************!*\ - !*** ./node_modules/layout-bmfont-text/index.js ***! - \**************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var wordWrap = __webpack_require__(/*! word-wrapper */ "./node_modules/word-wrapper/index.js"); -var xtend = __webpack_require__(/*! xtend */ "./node_modules/xtend/immutable.js"); -var number = __webpack_require__(/*! as-number */ "./node_modules/as-number/index.js"); -var X_HEIGHTS = ['x', 'e', 'a', 'o', 'n', 's', 'r', 'c', 'u', 'm', 'v', 'w', 'z']; -var M_WIDTHS = ['m', 'w']; -var CAP_HEIGHTS = ['H', 'I', 'N', 'E', 'F', 'K', 'L', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; -var TAB_ID = '\t'.charCodeAt(0); -var SPACE_ID = ' '.charCodeAt(0); -var ALIGN_LEFT = 0, - ALIGN_CENTER = 1, - ALIGN_RIGHT = 2; -module.exports = function createLayout(opt) { - return new TextLayout(opt); -}; -function TextLayout(opt) { - this.glyphs = []; - this._measure = this.computeMetrics.bind(this); - this.update(opt); -} -TextLayout.prototype.update = function (opt) { - opt = xtend({ - measure: this._measure - }, opt); - this._opt = opt; - this._opt.tabSize = number(this._opt.tabSize, 4); - if (!opt.font) throw new Error('must provide a valid bitmap font'); - var glyphs = this.glyphs; - var text = opt.text || ''; - var font = opt.font; - this._setupSpaceGlyphs(font); - var lines = wordWrap.lines(text, opt); - var minWidth = opt.width || 0; - - //clear glyphs - glyphs.length = 0; - - //get max line width - var maxLineWidth = lines.reduce(function (prev, line) { - return Math.max(prev, line.width, minWidth); - }, 0); - - //the pen position - var x = 0; - var y = 0; - var lineHeight = number(opt.lineHeight, font.common.lineHeight); - var baseline = font.common.base; - var descender = lineHeight - baseline; - var letterSpacing = opt.letterSpacing || 0; - var height = lineHeight * lines.length - descender; - var align = getAlignType(this._opt.align); - - //draw text along baseline - y -= height; - - //the metrics for this text layout - this._width = maxLineWidth; - this._height = height; - this._descender = lineHeight - baseline; - this._baseline = baseline; - this._xHeight = getXHeight(font); - this._capHeight = getCapHeight(font); - this._lineHeight = lineHeight; - this._ascender = lineHeight - descender - this._xHeight; - - //layout each glyph - var self = this; - lines.forEach(function (line, lineIndex) { - var start = line.start; - var end = line.end; - var lineWidth = line.width; - var lastGlyph; - - //for each glyph in that line... - for (var i = start; i < end; i++) { - var id = text.charCodeAt(i); - var glyph = self.getGlyph(font, id); - if (glyph) { - if (lastGlyph) x += getKerning(font, lastGlyph.id, glyph.id); - var tx = x; - if (align === ALIGN_CENTER) tx += (maxLineWidth - lineWidth) / 2;else if (align === ALIGN_RIGHT) tx += maxLineWidth - lineWidth; - glyphs.push({ - position: [tx, y], - data: glyph, - index: i, - line: lineIndex - }); - - //move pen forward - x += glyph.xadvance + letterSpacing; - lastGlyph = glyph; - } - } - - //next line down - y += lineHeight; - x = 0; - }); - this._linesTotal = lines.length; -}; -TextLayout.prototype._setupSpaceGlyphs = function (font) { - //These are fallbacks, when the font doesn't include - //' ' or '\t' glyphs - this._fallbackSpaceGlyph = null; - this._fallbackTabGlyph = null; - if (!font.chars || font.chars.length === 0) return; - - //try to get space glyph - //then fall back to the 'm' or 'w' glyphs - //then fall back to the first glyph available - var space = getGlyphById(font, SPACE_ID) || getMGlyph(font) || font.chars[0]; - - //and create a fallback for tab - var tabWidth = this._opt.tabSize * space.xadvance; - this._fallbackSpaceGlyph = space; - this._fallbackTabGlyph = xtend(space, { - x: 0, - y: 0, - xadvance: tabWidth, - id: TAB_ID, - xoffset: 0, - yoffset: 0, - width: 0, - height: 0 - }); -}; -TextLayout.prototype.getGlyph = function (font, id) { - var glyph = getGlyphById(font, id); - if (glyph) return glyph;else if (id === TAB_ID) return this._fallbackTabGlyph;else if (id === SPACE_ID) return this._fallbackSpaceGlyph; - return null; -}; -TextLayout.prototype.computeMetrics = function (text, start, end, width) { - var letterSpacing = this._opt.letterSpacing || 0; - var font = this._opt.font; - var curPen = 0; - var curWidth = 0; - var count = 0; - var glyph; - var lastGlyph; - if (!font.chars || font.chars.length === 0) { - return { - start: start, - end: start, - width: 0 - }; - } - end = Math.min(text.length, end); - for (var i = start; i < end; i++) { - var id = text.charCodeAt(i); - var glyph = this.getGlyph(font, id); - if (glyph) { - //move pen forward - var xoff = glyph.xoffset; - var kern = lastGlyph ? getKerning(font, lastGlyph.id, glyph.id) : 0; - curPen += kern; - var nextPen = curPen + glyph.xadvance + letterSpacing; - var nextWidth = curPen + glyph.width; - - //we've hit our limit; we can't move onto the next glyph - if (nextWidth >= width || nextPen >= width) break; - - //otherwise continue along our line - curPen = nextPen; - curWidth = nextWidth; - lastGlyph = glyph; - } - count++; - } - - //make sure rightmost edge lines up with rendered glyphs - if (lastGlyph) curWidth += lastGlyph.xoffset; - return { - start: start, - end: start + count, - width: curWidth - }; -} - -//getters for the private vars -; -['width', 'height', 'descender', 'ascender', 'xHeight', 'baseline', 'capHeight', 'lineHeight'].forEach(addGetter); -function addGetter(name) { - Object.defineProperty(TextLayout.prototype, name, { - get: wrapper(name), - configurable: true - }); -} - -//create lookups for private vars -function wrapper(name) { - return new Function(['return function ' + name + '() {', ' return this._' + name, '}'].join('\n'))(); -} -function getGlyphById(font, id) { - if (!font.chars || font.chars.length === 0) return null; - var glyphIdx = findChar(font.chars, id); - if (glyphIdx >= 0) return font.chars[glyphIdx]; - return null; -} -function getXHeight(font) { - for (var i = 0; i < X_HEIGHTS.length; i++) { - var id = X_HEIGHTS[i].charCodeAt(0); - var idx = findChar(font.chars, id); - if (idx >= 0) return font.chars[idx].height; - } - return 0; -} -function getMGlyph(font) { - for (var i = 0; i < M_WIDTHS.length; i++) { - var id = M_WIDTHS[i].charCodeAt(0); - var idx = findChar(font.chars, id); - if (idx >= 0) return font.chars[idx]; - } - return 0; -} -function getCapHeight(font) { - for (var i = 0; i < CAP_HEIGHTS.length; i++) { - var id = CAP_HEIGHTS[i].charCodeAt(0); - var idx = findChar(font.chars, id); - if (idx >= 0) return font.chars[idx].height; - } - return 0; -} -function getKerning(font, left, right) { - if (!font.kernings || font.kernings.length === 0) return 0; - var table = font.kernings; - for (var i = 0; i < table.length; i++) { - var kern = table[i]; - if (kern.first === left && kern.second === right) return kern.amount; - } - return 0; -} -function getAlignType(align) { - if (align === 'center') return ALIGN_CENTER;else if (align === 'right') return ALIGN_RIGHT; - return ALIGN_LEFT; -} -function findChar(array, value, start) { - start = start || 0; - for (var i = start; i < array.length; i++) { - if (array[i].id === value) { - return i; - } - } - return -1; -} - -/***/ }), - -/***/ "./node_modules/load-bmfont/browser.js": -/*!*********************************************!*\ - !*** ./node_modules/load-bmfont/browser.js ***! - \*********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* provided dependency */ var Buffer = __webpack_require__(/*! buffer */ "./node_modules/buffer/index.js")["Buffer"]; -var xhr = __webpack_require__(/*! xhr */ "./node_modules/xhr/index.js"); -var noop = function () {}; -var parseASCII = __webpack_require__(/*! parse-bmfont-ascii */ "./node_modules/parse-bmfont-ascii/index.js"); -var parseXML = __webpack_require__(/*! parse-bmfont-xml */ "./node_modules/parse-bmfont-xml/lib/browser.js"); -var readBinary = __webpack_require__(/*! parse-bmfont-binary */ "./node_modules/parse-bmfont-binary/index.js"); -var isBinaryFormat = __webpack_require__(/*! ./lib/is-binary */ "./node_modules/load-bmfont/lib/is-binary.js"); -var xtend = __webpack_require__(/*! xtend */ "./node_modules/xtend/immutable.js"); -var xml2 = function hasXML2() { - return self.XMLHttpRequest && "withCredentials" in new XMLHttpRequest(); -}(); -module.exports = function (opt, cb) { - cb = typeof cb === 'function' ? cb : noop; - if (typeof opt === 'string') opt = { - uri: opt - };else if (!opt) opt = {}; - var expectBinary = opt.binary; - if (expectBinary) opt = getBinaryOpts(opt); - xhr(opt, function (err, res, body) { - if (err) return cb(err); - if (!/^2/.test(res.statusCode)) return cb(new Error('http status code: ' + res.statusCode)); - if (!body) return cb(new Error('no body result')); - var binary = false; - - //if the response type is an array buffer, - //we need to convert it into a regular Buffer object - if (isArrayBuffer(body)) { - var array = new Uint8Array(body); - body = Buffer.from(array, 'binary'); - } - - //now check the string/Buffer response - //and see if it has a binary BMF header - if (isBinaryFormat(body)) { - binary = true; - //if we have a string, turn it into a Buffer - if (typeof body === 'string') body = Buffer.from(body, 'binary'); - } - - //we are not parsing a binary format, just ASCII/XML/etc - if (!binary) { - //might still be a buffer if responseType is 'arraybuffer' - if (Buffer.isBuffer(body)) body = body.toString(opt.encoding); - body = body.trim(); - } - var result; - try { - var type = res.headers['content-type']; - if (binary) result = readBinary(body);else if (/json/.test(type) || body.charAt(0) === '{') result = JSON.parse(body);else if (/xml/.test(type) || body.charAt(0) === '<') result = parseXML(body);else result = parseASCII(body); - } catch (e) { - cb(new Error('error parsing font ' + e.message)); - cb = noop; - } - cb(null, result); - }); -}; -function isArrayBuffer(arr) { - var str = Object.prototype.toString; - return str.call(arr) === '[object ArrayBuffer]'; -} -function getBinaryOpts(opt) { - //IE10+ and other modern browsers support array buffers - if (xml2) return xtend(opt, { - responseType: 'arraybuffer' - }); - if (typeof self.XMLHttpRequest === 'undefined') throw new Error('your browser does not support XHR loading'); - - //IE9 and XML1 browsers could still use an override - var req = new self.XMLHttpRequest(); - req.overrideMimeType('text/plain; charset=x-user-defined'); - return xtend({ - xhr: req - }, opt); -} - -/***/ }), - -/***/ "./node_modules/load-bmfont/lib/is-binary.js": -/*!***************************************************!*\ - !*** ./node_modules/load-bmfont/lib/is-binary.js ***! - \***************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* provided dependency */ var Buffer = __webpack_require__(/*! buffer */ "./node_modules/buffer/index.js")["Buffer"]; -var equal = __webpack_require__(/*! buffer-equal */ "./node_modules/buffer-equal/index.js"); -var HEADER = Buffer.from([66, 77, 70, 3]); -module.exports = function (buf) { - if (typeof buf === 'string') return buf.substring(0, 3) === 'BMF'; - return buf.length > 4 && equal(buf.slice(0, 4), HEADER); -}; - -/***/ }), - -/***/ "./node_modules/ms/index.js": -/*!**********************************!*\ - !*** ./node_modules/ms/index.js ***! - \**********************************/ -/***/ ((module) => { - -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var w = d * 7; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ - -module.exports = function (val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isFinite(val)) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val)); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'weeks': - case 'week': - case 'w': - return n * w; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtShort(ms) { - var msAbs = Math.abs(ms); - if (msAbs >= d) { - return Math.round(ms / d) + 'd'; - } - if (msAbs >= h) { - return Math.round(ms / h) + 'h'; - } - if (msAbs >= m) { - return Math.round(ms / m) + 'm'; - } - if (msAbs >= s) { - return Math.round(ms / s) + 's'; - } - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtLong(ms) { - var msAbs = Math.abs(ms); - if (msAbs >= d) { - return plural(ms, msAbs, d, 'day'); - } - if (msAbs >= h) { - return plural(ms, msAbs, h, 'hour'); - } - if (msAbs >= m) { - return plural(ms, msAbs, m, 'minute'); - } - if (msAbs >= s) { - return plural(ms, msAbs, s, 'second'); - } - return ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, msAbs, n, name) { - var isPlural = msAbs >= n * 1.5; - return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : ''); -} - -/***/ }), - -/***/ "./node_modules/parse-bmfont-ascii/index.js": -/*!**************************************************!*\ - !*** ./node_modules/parse-bmfont-ascii/index.js ***! - \**************************************************/ -/***/ ((module) => { - -module.exports = function parseBMFontAscii(data) { - if (!data) throw new Error('no data provided'); - data = data.toString().trim(); - var output = { - pages: [], - chars: [], - kernings: [] - }; - var lines = data.split(/\r\n?|\n/g); - if (lines.length === 0) throw new Error('no data in BMFont file'); - for (var i = 0; i < lines.length; i++) { - var lineData = splitLine(lines[i], i); - if (!lineData) - //skip empty lines - continue; - if (lineData.key === 'page') { - if (typeof lineData.data.id !== 'number') throw new Error('malformed file at line ' + i + ' -- needs page id=N'); - if (typeof lineData.data.file !== 'string') throw new Error('malformed file at line ' + i + ' -- needs page file="path"'); - output.pages[lineData.data.id] = lineData.data.file; - } else if (lineData.key === 'chars' || lineData.key === 'kernings') { - //... do nothing for these two ... - } else if (lineData.key === 'char') { - output.chars.push(lineData.data); - } else if (lineData.key === 'kerning') { - output.kernings.push(lineData.data); - } else { - output[lineData.key] = lineData.data; - } - } - return output; -}; -function splitLine(line, idx) { - line = line.replace(/\t+/g, ' ').trim(); - if (!line) return null; - var space = line.indexOf(' '); - if (space === -1) throw new Error("no named row at line " + idx); - var key = line.substring(0, space); - line = line.substring(space + 1); - //clear "letter" field as it is non-standard and - //requires additional complexity to parse " / = symbols - line = line.replace(/letter=[\'\"]\S+[\'\"]/gi, ''); - line = line.split("="); - line = line.map(function (str) { - return str.trim().match(/(".*?"|[^"\s]+)+(?=\s*|\s*$)/g); - }); - var data = []; - for (var i = 0; i < line.length; i++) { - var dt = line[i]; - if (i === 0) { - data.push({ - key: dt[0], - data: "" - }); - } else if (i === line.length - 1) { - data[data.length - 1].data = parseData(dt[0]); - } else { - data[data.length - 1].data = parseData(dt[0]); - data.push({ - key: dt[1], - data: "" - }); - } - } - var out = { - key: key, - data: {} - }; - data.forEach(function (v) { - out.data[v.key] = v.data; - }); - return out; -} -function parseData(data) { - if (!data || data.length === 0) return ""; - if (data.indexOf('"') === 0 || data.indexOf("'") === 0) return data.substring(1, data.length - 1); - if (data.indexOf(',') !== -1) return parseIntList(data); - return parseInt(data, 10); -} -function parseIntList(data) { - return data.split(',').map(function (val) { - return parseInt(val, 10); - }); -} - -/***/ }), - -/***/ "./node_modules/parse-bmfont-binary/index.js": -/*!***************************************************!*\ - !*** ./node_modules/parse-bmfont-binary/index.js ***! - \***************************************************/ -/***/ ((module) => { - -var HEADER = [66, 77, 70]; -module.exports = function readBMFontBinary(buf) { - if (buf.length < 6) throw new Error('invalid buffer length for BMFont'); - var header = HEADER.every(function (byte, i) { - return buf.readUInt8(i) === byte; - }); - if (!header) throw new Error('BMFont missing BMF byte header'); - var i = 3; - var vers = buf.readUInt8(i++); - if (vers > 3) throw new Error('Only supports BMFont Binary v3 (BMFont App v1.10)'); - var target = { - kernings: [], - chars: [] - }; - for (var b = 0; b < 5; b++) i += readBlock(target, buf, i); - return target; -}; -function readBlock(target, buf, i) { - if (i > buf.length - 1) return 0; - var blockID = buf.readUInt8(i++); - var blockSize = buf.readInt32LE(i); - i += 4; - switch (blockID) { - case 1: - target.info = readInfo(buf, i); - break; - case 2: - target.common = readCommon(buf, i); - break; - case 3: - target.pages = readPages(buf, i, blockSize); - break; - case 4: - target.chars = readChars(buf, i, blockSize); - break; - case 5: - target.kernings = readKernings(buf, i, blockSize); - break; - } - return 5 + blockSize; -} -function readInfo(buf, i) { - var info = {}; - info.size = buf.readInt16LE(i); - var bitField = buf.readUInt8(i + 2); - info.smooth = bitField >> 7 & 1; - info.unicode = bitField >> 6 & 1; - info.italic = bitField >> 5 & 1; - info.bold = bitField >> 4 & 1; - - //fixedHeight is only mentioned in binary spec - if (bitField >> 3 & 1) info.fixedHeight = 1; - info.charset = buf.readUInt8(i + 3) || ''; - info.stretchH = buf.readUInt16LE(i + 4); - info.aa = buf.readUInt8(i + 6); - info.padding = [buf.readInt8(i + 7), buf.readInt8(i + 8), buf.readInt8(i + 9), buf.readInt8(i + 10)]; - info.spacing = [buf.readInt8(i + 11), buf.readInt8(i + 12)]; - info.outline = buf.readUInt8(i + 13); - info.face = readStringNT(buf, i + 14); - return info; -} -function readCommon(buf, i) { - var common = {}; - common.lineHeight = buf.readUInt16LE(i); - common.base = buf.readUInt16LE(i + 2); - common.scaleW = buf.readUInt16LE(i + 4); - common.scaleH = buf.readUInt16LE(i + 6); - common.pages = buf.readUInt16LE(i + 8); - var bitField = buf.readUInt8(i + 10); - common.packed = 0; - common.alphaChnl = buf.readUInt8(i + 11); - common.redChnl = buf.readUInt8(i + 12); - common.greenChnl = buf.readUInt8(i + 13); - common.blueChnl = buf.readUInt8(i + 14); - return common; -} -function readPages(buf, i, size) { - var pages = []; - var text = readNameNT(buf, i); - var len = text.length + 1; - var count = size / len; - for (var c = 0; c < count; c++) { - pages[c] = buf.slice(i, i + text.length).toString('utf8'); - i += len; - } - return pages; -} -function readChars(buf, i, blockSize) { - var chars = []; - var count = blockSize / 20; - for (var c = 0; c < count; c++) { - var char = {}; - var off = c * 20; - char.id = buf.readUInt32LE(i + 0 + off); - char.x = buf.readUInt16LE(i + 4 + off); - char.y = buf.readUInt16LE(i + 6 + off); - char.width = buf.readUInt16LE(i + 8 + off); - char.height = buf.readUInt16LE(i + 10 + off); - char.xoffset = buf.readInt16LE(i + 12 + off); - char.yoffset = buf.readInt16LE(i + 14 + off); - char.xadvance = buf.readInt16LE(i + 16 + off); - char.page = buf.readUInt8(i + 18 + off); - char.chnl = buf.readUInt8(i + 19 + off); - chars[c] = char; - } - return chars; -} -function readKernings(buf, i, blockSize) { - var kernings = []; - var count = blockSize / 10; - for (var c = 0; c < count; c++) { - var kern = {}; - var off = c * 10; - kern.first = buf.readUInt32LE(i + 0 + off); - kern.second = buf.readUInt32LE(i + 4 + off); - kern.amount = buf.readInt16LE(i + 8 + off); - kernings[c] = kern; - } - return kernings; -} -function readNameNT(buf, offset) { - var pos = offset; - for (; pos < buf.length; pos++) { - if (buf[pos] === 0x00) break; - } - return buf.slice(offset, pos); -} -function readStringNT(buf, offset) { - return readNameNT(buf, offset).toString('utf8'); -} - -/***/ }), - -/***/ "./node_modules/parse-bmfont-xml/lib/browser.js": -/*!******************************************************!*\ - !*** ./node_modules/parse-bmfont-xml/lib/browser.js ***! - \******************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var parseAttributes = __webpack_require__(/*! ./parse-attribs */ "./node_modules/parse-bmfont-xml/lib/parse-attribs.js"); -var parseFromString = __webpack_require__(/*! xml-parse-from-string */ "./node_modules/xml-parse-from-string/index.js"); - -//In some cases element.attribute.nodeName can return -//all lowercase values.. so we need to map them to the correct -//case -var NAME_MAP = { - scaleh: 'scaleH', - scalew: 'scaleW', - stretchh: 'stretchH', - lineheight: 'lineHeight', - alphachnl: 'alphaChnl', - redchnl: 'redChnl', - greenchnl: 'greenChnl', - bluechnl: 'blueChnl' -}; -module.exports = function parse(data) { - data = data.toString(); - var xmlRoot = parseFromString(data); - var output = { - pages: [], - chars: [], - kernings: [] - } - - //get config settings - ; - ['info', 'common'].forEach(function (key) { - var element = xmlRoot.getElementsByTagName(key)[0]; - if (element) output[key] = parseAttributes(getAttribs(element)); - }); - - //get page info - var pageRoot = xmlRoot.getElementsByTagName('pages')[0]; - if (!pageRoot) throw new Error('malformed file -- no element'); - var pages = pageRoot.getElementsByTagName('page'); - for (var i = 0; i < pages.length; i++) { - var p = pages[i]; - var id = parseInt(p.getAttribute('id'), 10); - var file = p.getAttribute('file'); - if (isNaN(id)) throw new Error('malformed file -- page "id" attribute is NaN'); - if (!file) throw new Error('malformed file -- needs page "file" attribute'); - output.pages[parseInt(id, 10)] = file; - } - - //get kernings / chars - ; - ['chars', 'kernings'].forEach(function (key) { - var element = xmlRoot.getElementsByTagName(key)[0]; - if (!element) return; - var childTag = key.substring(0, key.length - 1); - var children = element.getElementsByTagName(childTag); - for (var i = 0; i < children.length; i++) { - var child = children[i]; - output[key].push(parseAttributes(getAttribs(child))); - } - }); - return output; -}; -function getAttribs(element) { - var attribs = getAttribList(element); - return attribs.reduce(function (dict, attrib) { - var key = mapName(attrib.nodeName); - dict[key] = attrib.nodeValue; - return dict; - }, {}); -} -function getAttribList(element) { - //IE8+ and modern browsers - var attribs = []; - for (var i = 0; i < element.attributes.length; i++) attribs.push(element.attributes[i]); - return attribs; -} -function mapName(nodeName) { - return NAME_MAP[nodeName.toLowerCase()] || nodeName; -} - -/***/ }), - -/***/ "./node_modules/parse-bmfont-xml/lib/parse-attribs.js": -/*!************************************************************!*\ - !*** ./node_modules/parse-bmfont-xml/lib/parse-attribs.js ***! - \************************************************************/ -/***/ ((module) => { - -//Some versions of GlyphDesigner have a typo -//that causes some bugs with parsing. -//Need to confirm with recent version of the software -//to see whether this is still an issue or not. -var GLYPH_DESIGNER_ERROR = 'chasrset'; -module.exports = function parseAttributes(obj) { - if (GLYPH_DESIGNER_ERROR in obj) { - obj['charset'] = obj[GLYPH_DESIGNER_ERROR]; - delete obj[GLYPH_DESIGNER_ERROR]; - } - for (var k in obj) { - if (k === 'face' || k === 'charset') continue;else if (k === 'padding' || k === 'spacing') obj[k] = parseIntList(obj[k]);else obj[k] = parseInt(obj[k], 10); - } - return obj; -}; -function parseIntList(data) { - return data.split(',').map(function (val) { - return parseInt(val, 10); - }); -} - -/***/ }), - -/***/ "./node_modules/parse-headers/parse-headers.js": -/*!*****************************************************!*\ - !*** ./node_modules/parse-headers/parse-headers.js ***! - \*****************************************************/ -/***/ ((module) => { - -var trim = function (string) { - return string.replace(/^\s+|\s+$/g, ''); - }, - isArray = function (arg) { - return Object.prototype.toString.call(arg) === '[object Array]'; - }; -module.exports = function (headers) { - if (!headers) return {}; - var result = {}; - var headersArr = trim(headers).split('\n'); - for (var i = 0; i < headersArr.length; i++) { - var row = headersArr[i]; - var index = row.indexOf(':'), - key = trim(row.slice(0, index)).toLowerCase(), - value = trim(row.slice(index + 1)); - if (typeof result[key] === 'undefined') { - result[key] = value; - } else if (isArray(result[key])) { - result[key].push(value); - } else { - result[key] = [result[key], value]; - } - } - return result; -}; - -/***/ }), - -/***/ "./node_modules/quad-indices/index.js": -/*!********************************************!*\ - !*** ./node_modules/quad-indices/index.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var dtype = __webpack_require__(/*! dtype */ "./node_modules/dtype/index.js"); -var anArray = __webpack_require__(/*! an-array */ "./node_modules/an-array/index.js"); -var isBuffer = __webpack_require__(/*! is-buffer */ "./node_modules/is-buffer/index.js"); -var CW = [0, 2, 3]; -var CCW = [2, 1, 3]; -module.exports = function createQuadElements(array, opt) { - //if user didn't specify an output array - if (!array || !(anArray(array) || isBuffer(array))) { - opt = array || {}; - array = null; - } - if (typeof opt === 'number') - //backwards-compatible - opt = { - count: opt - };else opt = opt || {}; - var type = typeof opt.type === 'string' ? opt.type : 'uint16'; - var count = typeof opt.count === 'number' ? opt.count : 1; - var start = opt.start || 0; - var dir = opt.clockwise !== false ? CW : CCW, - a = dir[0], - b = dir[1], - c = dir[2]; - var numIndices = count * 6; - var indices = array || new (dtype(type))(numIndices); - for (var i = 0, j = 0; i < numIndices; i += 6, j += 4) { - var x = i + start; - indices[x + 0] = j + 0; - indices[x + 1] = j + 1; - indices[x + 2] = j + 2; - indices[x + 3] = j + a; - indices[x + 4] = j + b; - indices[x + 5] = j + c; - } - return indices; -}; - -/***/ }), - -/***/ "./node_modules/super-animejs/lib/anime.es.js": -/*!****************************************************!*\ - !*** ./node_modules/super-animejs/lib/anime.es.js ***! - \****************************************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* - * anime.js v3.0.0 - * (c) 2019 Julian Garnier - * Released under the MIT license - * animejs.com - */ - -// Defaults - -var defaultInstanceSettings = { - update: null, - begin: null, - loopBegin: null, - changeBegin: null, - change: null, - changeComplete: null, - loopComplete: null, - complete: null, - loop: 1, - direction: 'normal', - autoplay: true, - timelineOffset: 0 -}; -var defaultTweenSettings = { - duration: 1000, - delay: 0, - endDelay: 0, - easing: 'easeOutElastic(1, .5)', - round: 0 -}; -var validTransforms = ['translateX', 'translateY', 'translateZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'perspective']; - -// Caching - -var cache = { - CSS: {}, - springs: {} -}; - -// Utils - -function minMax(val, min, max) { - return Math.min(Math.max(val, min), max); -} -function stringContains(str, text) { - return str.indexOf(text) > -1; -} -function applyArguments(func, args) { - return func.apply(null, args); -} -var hexRegex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i; -var rgbPrefixRegex = /^rgb/; -var hslRegex = /^hsl/; -var is = { - arr: function (a) { - return Array.isArray(a); - }, - obj: function (a) { - return stringContains(Object.prototype.toString.call(a), 'Object'); - }, - pth: function (a) { - return is.obj(a) && a.hasOwnProperty('totalLength'); - }, - svg: function (a) { - return a instanceof SVGElement; - }, - inp: function (a) { - return a instanceof HTMLInputElement; - }, - dom: function (a) { - return a.nodeType || is.svg(a); - }, - str: function (a) { - return typeof a === 'string'; - }, - fnc: function (a) { - return typeof a === 'function'; - }, - und: function (a) { - return typeof a === 'undefined'; - }, - hex: function (a) { - return hexRegex.test(a); - }, - rgb: function (a) { - return rgbPrefixRegex.test(a); - }, - hsl: function (a) { - return hslRegex.test(a); - }, - col: function (a) { - return is.hex(a) || is.rgb(a) || is.hsl(a); - }, - key: function (a) { - return !defaultInstanceSettings.hasOwnProperty(a) && !defaultTweenSettings.hasOwnProperty(a) && a !== 'targets' && a !== 'keyframes'; - } -}; - -// Easings - -var easingFunctionRegex = /\(([^)]+)\)/; -function parseEasingParameters(string) { - var match = easingFunctionRegex.exec(string); - return match ? match[1].split(',').map(function (p) { - return parseFloat(p); - }) : []; -} - -// Spring solver inspired by Webkit Copyright © 2016 Apple Inc. All rights reserved. https://webkit.org/demos/spring/spring.js - -function spring(string, duration) { - var params = parseEasingParameters(string); - var mass = minMax(is.und(params[0]) ? 1 : params[0], .1, 100); - var stiffness = minMax(is.und(params[1]) ? 100 : params[1], .1, 100); - var damping = minMax(is.und(params[2]) ? 10 : params[2], .1, 100); - var velocity = minMax(is.und(params[3]) ? 0 : params[3], .1, 100); - var w0 = Math.sqrt(stiffness / mass); - var zeta = damping / (2 * Math.sqrt(stiffness * mass)); - var wd = zeta < 1 ? w0 * Math.sqrt(1 - zeta * zeta) : 0; - var a = 1; - var b = zeta < 1 ? (zeta * w0 + -velocity) / wd : -velocity + w0; - function solver(t) { - var progress = duration ? duration * t / 1000 : t; - if (zeta < 1) { - progress = Math.exp(-progress * zeta * w0) * (a * Math.cos(wd * progress) + b * Math.sin(wd * progress)); - } else { - progress = (a + b * progress) * Math.exp(-progress * w0); - } - if (t === 0 || t === 1) { - return t; - } - return 1 - progress; - } - function getDuration() { - var cached = cache.springs[string]; - if (cached) { - return cached; - } - var frame = 1 / 6; - var elapsed = 0; - var rest = 0; - while (true) { - elapsed += frame; - if (solver(elapsed) === 1) { - rest++; - if (rest >= 16) { - break; - } - } else { - rest = 0; - } - } - var duration = elapsed * frame * 1000; - cache.springs[string] = duration; - return duration; - } - return duration ? solver : getDuration; -} - -// Elastic easing adapted from jQueryUI http://api.jqueryui.com/easings/ - -function elastic(amplitude, period) { - if (amplitude === void 0) amplitude = 1; - if (period === void 0) period = .5; - var a = minMax(amplitude, 1, 10); - var p = minMax(period, .1, 2); - return function (t) { - return t === 0 || t === 1 ? t : -a * Math.pow(2, 10 * (t - 1)) * Math.sin((t - 1 - p / (Math.PI * 2) * Math.asin(1 / a)) * (Math.PI * 2) / p); - }; -} - -// Basic steps easing implementation https://developer.mozilla.org/fr/docs/Web/CSS/transition-timing-function - -function steps(steps) { - if (steps === void 0) steps = 10; - return function (t) { - return Math.round(t * steps) * (1 / steps); - }; -} - -// BezierEasing https://github.com/gre/bezier-easing - -var bezier = function () { - var kSplineTableSize = 11; - var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); - function A(aA1, aA2) { - return 1.0 - 3.0 * aA2 + 3.0 * aA1; - } - function B(aA1, aA2) { - return 3.0 * aA2 - 6.0 * aA1; - } - function C(aA1) { - return 3.0 * aA1; - } - function calcBezier(aT, aA1, aA2) { - return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; - } - function getSlope(aT, aA1, aA2) { - return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); - } - function binarySubdivide(aX, aA, aB, mX1, mX2) { - var currentX, - currentT, - i = 0; - do { - currentT = aA + (aB - aA) / 2.0; - currentX = calcBezier(currentT, mX1, mX2) - aX; - if (currentX > 0.0) { - aB = currentT; - } else { - aA = currentT; - } - } while (Math.abs(currentX) > 0.0000001 && ++i < 10); - return currentT; - } - function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) { - for (var i = 0; i < 4; ++i) { - var currentSlope = getSlope(aGuessT, mX1, mX2); - if (currentSlope === 0.0) { - return aGuessT; - } - var currentX = calcBezier(aGuessT, mX1, mX2) - aX; - aGuessT -= currentX / currentSlope; - } - return aGuessT; - } - function bezier(mX1, mY1, mX2, mY2) { - if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { - return; - } - var sampleValues = new Float32Array(kSplineTableSize); - if (mX1 !== mY1 || mX2 !== mY2) { - for (var i = 0; i < kSplineTableSize; ++i) { - sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); - } - } - function getTForX(aX) { - var intervalStart = 0; - var currentSample = 1; - var lastSample = kSplineTableSize - 1; - for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { - intervalStart += kSampleStepSize; - } - --currentSample; - var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); - var guessForT = intervalStart + dist * kSampleStepSize; - var initialSlope = getSlope(guessForT, mX1, mX2); - if (initialSlope >= 0.001) { - return newtonRaphsonIterate(aX, guessForT, mX1, mX2); - } else if (initialSlope === 0.0) { - return guessForT; - } else { - return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); - } - } - return function (x) { - if (mX1 === mY1 && mX2 === mY2) { - return x; - } - if (x === 0 || x === 1) { - return x; - } - return calcBezier(getTForX(x), mY1, mY2); - }; - } - return bezier; -}(); -var penner = function () { - var names = ['Quad', 'Cubic', 'Quart', 'Quint', 'Sine', 'Expo', 'Circ', 'Back', 'Elastic']; - - // Approximated Penner equations http://matthewlein.com/ceaser/ - - var curves = { - In: [[0.550, 0.085, 0.680, 0.530], /* inQuad */ - [0.550, 0.055, 0.675, 0.190], /* inCubic */ - [0.895, 0.030, 0.685, 0.220], /* inQuart */ - [0.755, 0.050, 0.855, 0.060], /* inQuint */ - [0.470, 0.000, 0.745, 0.715], /* inSine */ - [0.950, 0.050, 0.795, 0.035], /* inExpo */ - [0.600, 0.040, 0.980, 0.335], /* inCirc */ - [0.600, -0.280, 0.735, 0.045], /* inBack */ - elastic /* inElastic */], - - Out: [[0.250, 0.460, 0.450, 0.940], /* outQuad */ - [0.215, 0.610, 0.355, 1.000], /* outCubic */ - [0.165, 0.840, 0.440, 1.000], /* outQuart */ - [0.230, 1.000, 0.320, 1.000], /* outQuint */ - [0.390, 0.575, 0.565, 1.000], /* outSine */ - [0.190, 1.000, 0.220, 1.000], /* outExpo */ - [0.075, 0.820, 0.165, 1.000], /* outCirc */ - [0.175, 0.885, 0.320, 1.275], /* outBack */ - function (a, p) { - return function (t) { - return 1 - elastic(a, p)(1 - t); - }; - } /* outElastic */], - - InOut: [[0.455, 0.030, 0.515, 0.955], /* inOutQuad */ - [0.645, 0.045, 0.355, 1.000], /* inOutCubic */ - [0.770, 0.000, 0.175, 1.000], /* inOutQuart */ - [0.860, 0.000, 0.070, 1.000], /* inOutQuint */ - [0.445, 0.050, 0.550, 0.950], /* inOutSine */ - [1.000, 0.000, 0.000, 1.000], /* inOutExpo */ - [0.785, 0.135, 0.150, 0.860], /* inOutCirc */ - [0.680, -0.550, 0.265, 1.550], /* inOutBack */ - function (a, p) { - return function (t) { - return t < .5 ? elastic(a, p)(t * 2) / 2 : 1 - elastic(a, p)(t * -2 + 2) / 2; - }; - } /* inOutElastic */] - }; - - var eases = { - linear: [0.250, 0.250, 0.750, 0.750] - }; - for (var coords in curves) { - for (var i = 0, len = curves[coords].length; i < len; i++) { - eases['ease' + coords + names[i]] = curves[coords][i]; - } - } - return eases; -}(); -function parseEasings(easing, duration) { - if (is.fnc(easing)) { - return easing; - } - var name = easing.split('(')[0]; - var ease = penner[name]; - var args = parseEasingParameters(easing); - switch (name) { - case 'spring': - return spring(easing, duration); - case 'cubicBezier': - return applyArguments(bezier, args); - case 'steps': - return applyArguments(steps, args); - default: - return is.fnc(ease) ? applyArguments(ease, args) : applyArguments(bezier, ease); - } -} - -// Strings - -function selectString(str) { - try { - var nodes = document.querySelectorAll(str); - return nodes; - } catch (e) { - return; - } -} - -// Arrays - -var auxArrayFilter = []; -function filterArray(arr, callback) { - var result = auxArrayFilter; - var len = arr.length; - var thisArg = arguments.length >= 2 ? arguments[1] : void 0; - for (var i = 0; i < len; i++) { - if (i in arr) { - var val = arr[i]; - if (callback.call(thisArg, val, i, arr)) { - result.push(val); - } - } - } - - // arr turns into the auxArray and we return the previously aux array. - auxArrayFilter = arr; - auxArrayFilter.length = 0; - return result; -} -function flattenArray(arr, result) { - if (!result) { - result = []; - } - for (var i = 0, length = arr.length; i < length; i++) { - var value = arr[i]; - if (Array.isArray(value)) { - flattenArray(value, result); - } else { - result.push(value); - } - } - return result; -} -function toArray(o) { - if (is.arr(o)) { - return o; - } - if (is.str(o)) { - o = selectString(o) || o; - } - if (o instanceof NodeList || o instanceof HTMLCollection) { - return [].slice.call(o); - } - return [o]; -} -function arrayContains(arr, val) { - return arr.some(function (a) { - return a === val; - }); -} - -// Objects - -function cloneObject(o) { - var clone = {}; - for (var p in o) { - clone[p] = o[p]; - } - return clone; -} -function replaceObjectProps(o1, o2) { - var o = cloneObject(o1); - for (var p in o1) { - o[p] = o2.hasOwnProperty(p) ? o2[p] : o1[p]; - } - return o; -} -function mergeObjects(o1, o2) { - var o = cloneObject(o1); - for (var p in o2) { - o[p] = is.und(o1[p]) ? o2[p] : o1[p]; - } - return o; -} - -// Colors - -var rgbRegex = /rgb\((\d+,\s*[\d]+,\s*[\d]+)\)/g; -function rgbToRgba(rgbValue) { - var rgb = rgbRegex.exec(rgbValue); - return rgb ? "rgba(" + rgb[1] + ",1)" : rgbValue; -} -var hexToRgbaHexRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; -var hexToRgbaRgbRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; -function hexToRgba(hexValue) { - var hex = hexValue.replace(hexToRgbaHexRegex, function (m, r, g, b) { - return r + r + g + g + b + b; - }); - var rgb = hexToRgbaRgbRegex.exec(hex); - var r = parseInt(rgb[1], 16); - var g = parseInt(rgb[2], 16); - var b = parseInt(rgb[3], 16); - return "rgba(" + r + "," + g + "," + b + ",1)"; -} -var hslToRgbaHsl1Regex = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g; -var hslToRgbaHsl2Regex = /hsla\((\d+),\s*([\d.]+)%,\s*([\d.]+)%,\s*([\d.]+)\)/g; -function hslToRgba(hslValue) { - var hsl = hslToRgbaHsl1Regex.exec(hslValue) || hslToRgbaHsl2Regex.exec(hslValue); - var h = parseInt(hsl[1], 10) / 360; - var s = parseInt(hsl[2], 10) / 100; - var l = parseInt(hsl[3], 10) / 100; - var a = hsl[4] || 1; - function hue2rgb(p, q, t) { - if (t < 0) { - t += 1; - } - if (t > 1) { - t -= 1; - } - if (t < 1 / 6) { - return p + (q - p) * 6 * t; - } - if (t < 1 / 2) { - return q; - } - if (t < 2 / 3) { - return p + (q - p) * (2 / 3 - t) * 6; - } - return p; - } - var r, g, b; - if (s == 0) { - r = g = b = l; - } else { - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1 / 3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1 / 3); - } - return "rgba(" + r * 255 + "," + g * 255 + "," + b * 255 + "," + a + ")"; -} -function colorToRgb(val) { - if (is.rgb(val)) { - return rgbToRgba(val); - } - if (is.hex(val)) { - return hexToRgba(val); - } - if (is.hsl(val)) { - return hslToRgba(val); - } -} - -// Units - -var unitRegex = /([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/; -function getUnit(val) { - var split = unitRegex.exec(val); - if (split) { - return split[2]; - } -} -function getTransformUnit(propName) { - if (stringContains(propName, 'translate') || propName === 'perspective') { - return 'px'; - } - if (stringContains(propName, 'rotate') || stringContains(propName, 'skew')) { - return 'deg'; - } -} - -// Values - -function getFunctionValue(val, animatable) { - if (!is.fnc(val)) { - return val; - } - return val(animatable.target, animatable.id, animatable.total); -} -function getAttribute(el, prop) { - return el.getAttribute(prop); -} -function convertPxToUnit(el, value, unit) { - var valueUnit = getUnit(value); - if (arrayContains([unit, 'deg', 'rad', 'turn'], valueUnit)) { - return value; - } - var cached = cache.CSS[value + unit]; - if (!is.und(cached)) { - return cached; - } - var baseline = 100; - var tempEl = document.createElement(el.tagName); - var parentEl = el.parentNode && el.parentNode !== document ? el.parentNode : document.body; - parentEl.appendChild(tempEl); - tempEl.style.position = 'absolute'; - tempEl.style.width = baseline + unit; - var factor = baseline / tempEl.offsetWidth; - parentEl.removeChild(tempEl); - var convertedUnit = factor * parseFloat(value); - cache.CSS[value + unit] = convertedUnit; - return convertedUnit; -} -function getCSSValue(el, prop, unit) { - if (prop in el.style) { - var uppercasePropName = prop.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); - var value = el.style[prop] || getComputedStyle(el).getPropertyValue(uppercasePropName) || '0'; - return unit ? convertPxToUnit(el, value, unit) : value; - } -} -function getAnimationType(el, prop) { - if (is.dom(el) && !is.inp(el) && (getAttribute(el, prop) || is.svg(el) && el[prop])) { - return 'attribute'; - } - if (is.dom(el) && arrayContains(validTransforms, prop)) { - return 'transform'; - } - if (is.dom(el) && prop !== 'transform' && getCSSValue(el, prop)) { - return 'css'; - } - if (el[prop] != null) { - return 'object'; - } -} -var transformRegex = /(\w+)\(([^)]*)\)/g; -function getElementTransforms(el) { - if (!is.dom(el)) { - return; - } - var str = el.style.transform || ''; - var transforms = new Map(); - var m; - while (m = transformRegex.exec(str)) { - transforms.set(m[1], m[2]); - } - return transforms; -} -function getTransformValue(el, propName, animatable, unit) { - var defaultVal = stringContains(propName, 'scale') ? 1 : 0 + getTransformUnit(propName); - var value = getElementTransforms(el).get(propName) || defaultVal; - if (animatable) { - animatable.transforms.list.set(propName, value); - animatable.transforms['last'] = propName; - } - return unit ? convertPxToUnit(el, value, unit) : value; -} -function getOriginalTargetValue(target, propName, unit, animatable) { - switch (getAnimationType(target, propName)) { - case 'transform': - return getTransformValue(target, propName, animatable, unit); - case 'css': - return getCSSValue(target, propName, unit); - case 'attribute': - return getAttribute(target, propName); - default: - return target[propName] || 0; - } -} -var operatorRegex = /^(\*=|\+=|-=)/; -function getRelativeValue(to, from) { - var operator = operatorRegex.exec(to); - if (!operator) { - return to; - } - var u = getUnit(to) || 0; - var x = parseFloat(from); - var y = parseFloat(to.replace(operator[0], '')); - switch (operator[0][0]) { - case '+': - return x + y + u; - case '-': - return x - y + u; - case '*': - return x * y + u; - } -} -var whitespaceRegex = /\s/g; -function validateValue(val, unit) { - if (is.col(val)) { - return colorToRgb(val); - } - var originalUnit = getUnit(val); - var unitLess = originalUnit ? val.substr(0, val.length - originalUnit.length) : val; - return unit && !whitespaceRegex.test(val) ? unitLess + unit : unitLess; -} - -// getTotalLength() equivalent for circle, rect, polyline, polygon and line shapes -// adapted from https://gist.github.com/SebLambla/3e0550c496c236709744 - -function getDistance(p1, p2) { - return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); -} -function getCircleLength(el) { - return Math.PI * 2 * getAttribute(el, 'r'); -} -function getRectLength(el) { - return getAttribute(el, 'width') * 2 + getAttribute(el, 'height') * 2; -} -function getLineLength(el) { - return getDistance({ - x: getAttribute(el, 'x1'), - y: getAttribute(el, 'y1') - }, { - x: getAttribute(el, 'x2'), - y: getAttribute(el, 'y2') - }); -} -function getPolylineLength(el) { - var points = el.points; - var totalLength = 0; - var previousPos; - for (var i = 0; i < points.numberOfItems; i++) { - var currentPos = points.getItem(i); - if (i > 0) { - totalLength += getDistance(previousPos, currentPos); - } - previousPos = currentPos; - } - return totalLength; -} -function getPolygonLength(el) { - var points = el.points; - return getPolylineLength(el) + getDistance(points.getItem(points.numberOfItems - 1), points.getItem(0)); -} - -// Path animation - -function getTotalLength(el) { - if (el.getTotalLength) { - return el.getTotalLength(); - } - switch (el.tagName.toLowerCase()) { - case 'circle': - return getCircleLength(el); - case 'rect': - return getRectLength(el); - case 'line': - return getLineLength(el); - case 'polyline': - return getPolylineLength(el); - case 'polygon': - return getPolygonLength(el); - } -} -function setDashoffset(el) { - var pathLength = getTotalLength(el); - el.setAttribute('stroke-dasharray', pathLength); - return pathLength; -} - -// Motion path - -function getParentSvgEl(el) { - var parentEl = el.parentNode; - while (is.svg(parentEl)) { - parentEl = parentEl.parentNode; - if (!is.svg(parentEl.parentNode)) { - break; - } - } - return parentEl; -} -function getParentSvg(pathEl, svgData) { - var svg = svgData || {}; - var parentSvgEl = svg.el || getParentSvgEl(pathEl); - var rect = parentSvgEl.getBoundingClientRect(); - var viewBoxAttr = getAttribute(parentSvgEl, 'viewBox'); - var width = rect.width; - var height = rect.height; - var viewBox = svg.viewBox || (viewBoxAttr ? viewBoxAttr.split(' ') : [0, 0, width, height]); - return { - el: parentSvgEl, - viewBox: viewBox, - x: viewBox[0] / 1, - y: viewBox[1] / 1, - w: width / viewBox[2], - h: height / viewBox[3] - }; -} -function getPath(path, percent) { - var pathEl = is.str(path) ? selectString(path)[0] : path; - var p = percent || 100; - return function (property) { - return { - property: property, - el: pathEl, - svg: getParentSvg(pathEl), - totalLength: getTotalLength(pathEl) * (p / 100) - }; - }; -} -function getPathProgress(path, progress) { - function point(offset) { - if (offset === void 0) offset = 0; - var l = progress + offset >= 1 ? progress + offset : 0; - return path.el.getPointAtLength(l); - } - var svg = getParentSvg(path.el, path.svg); - var p = point(); - var p0 = point(-1); - var p1 = point(+1); - switch (path.property) { - case 'x': - return (p.x - svg.x) * svg.w; - case 'y': - return (p.y - svg.y) * svg.h; - case 'angle': - return Math.atan2(p1.y - p0.y, p1.x - p0.x) * 180 / Math.PI; - } -} - -// Decompose value - -var valueRegex = /-?\d*\.?\d+/g; -function decomposeValue(val, unit) { - var value = validateValue(is.pth(val) ? val.totalLength : val, unit) + ''; - return { - original: value, - numbers: value.match(valueRegex) ? value.match(valueRegex).map(Number) : [0], - strings: is.str(val) || unit ? value.split(valueRegex) : [] - }; -} - -// Animatables - -function parseTargets(targets) { - var targetsArray = targets ? flattenArray(is.arr(targets) ? targets.map(toArray) : toArray(targets)) : []; - return filterArray(targetsArray, function (item, pos, self) { - return self.indexOf(item) === pos; - }); -} -function getAnimatables(targets) { - var parsed = parseTargets(targets); - return parsed.map(function (t, i) { - return { - target: t, - id: i, - total: parsed.length, - transforms: { - list: getElementTransforms(t) - } - }; - }); -} - -// Properties - -var springRegex = /^spring/; -function normalizePropertyTweens(prop, tweenSettings) { - var settings = cloneObject(tweenSettings); - // Override duration if easing is a spring - if (springRegex.test(settings.easing)) { - settings.duration = spring(settings.easing); - } - if (is.arr(prop)) { - var l = prop.length; - var isFromTo = l === 2 && !is.obj(prop[0]); - if (!isFromTo) { - // Duration divided by the number of tweens - if (!is.fnc(tweenSettings.duration)) { - settings.duration = tweenSettings.duration / l; - } - } else { - // Transform [from, to] values shorthand to a valid tween value - prop = { - value: prop - }; - } - } - var propArray = is.arr(prop) ? prop : [prop]; - return propArray.map(function (v, i) { - var obj = is.obj(v) && !is.pth(v) ? v : { - value: v - }; - // Default delay value should only be applied to the first tween - if (is.und(obj.delay)) { - obj.delay = !i ? tweenSettings.delay : 0; - } - // Default endDelay value should only be applied to the last tween - if (is.und(obj.endDelay)) { - obj.endDelay = i === propArray.length - 1 ? tweenSettings.endDelay : 0; - } - return obj; - }).map(function (k) { - return mergeObjects(k, settings); - }); -} -function flattenKeyframes(keyframes) { - var propertyNames = filterArray(flattenArray(keyframes.map(function (key) { - return Object.keys(key); - })), function (p) { - return is.key(p); - }).reduce(function (a, b) { - if (a.indexOf(b) < 0) { - a.push(b); - } - return a; - }, []); - var properties = {}; - var loop = function (i) { - var propName = propertyNames[i]; - properties[propName] = keyframes.map(function (key) { - var newKey = {}; - for (var p in key) { - if (is.key(p)) { - if (p == propName) { - newKey.value = key[p]; - } - } else { - newKey[p] = key[p]; - } - } - return newKey; - }); - }; - for (var i = 0; i < propertyNames.length; i++) loop(i); - return properties; -} -function getProperties(tweenSettings, params) { - var properties = []; - var keyframes = params.keyframes; - if (keyframes) { - params = mergeObjects(flattenKeyframes(keyframes), params); - } - for (var p in params) { - if (is.key(p)) { - properties.push({ - name: p, - tweens: normalizePropertyTweens(params[p], tweenSettings) - }); - } - } - return properties; -} - -// Tweens - -function normalizeTweenValues(tween, animatable) { - var t = {}; - for (var p in tween) { - var value = getFunctionValue(tween[p], animatable); - if (is.arr(value)) { - value = value.map(function (v) { - return getFunctionValue(v, animatable); - }); - if (value.length === 1) { - value = value[0]; - } - } - t[p] = value; - } - t.duration = parseFloat(t.duration); - t.delay = parseFloat(t.delay); - return t; -} -function normalizeTweens(prop, animatable) { - var previousTween; - return prop.tweens.map(function (t) { - var tween = normalizeTweenValues(t, animatable); - var tweenValue = tween.value; - var to = is.arr(tweenValue) ? tweenValue[1] : tweenValue; - var toUnit = getUnit(to); - var originalValue = getOriginalTargetValue(animatable.target, prop.name, toUnit, animatable); - var previousValue = previousTween ? previousTween.to.original : originalValue; - var from = is.arr(tweenValue) ? tweenValue[0] : previousValue; - var fromUnit = getUnit(from) || getUnit(originalValue); - var unit = toUnit || fromUnit; - if (is.und(to)) { - to = previousValue; - } - tween.from = decomposeValue(from, unit); - tween.to = decomposeValue(getRelativeValue(to, from), unit); - tween.start = previousTween ? previousTween.end : 0; - tween.end = tween.start + tween.delay + tween.duration + tween.endDelay; - tween.easing = parseEasings(tween.easing, tween.duration); - tween.isPath = is.pth(tweenValue); - tween.isColor = is.col(tween.from.original); - if (tween.isColor) { - tween.round = 1; - } - previousTween = tween; - return tween; - }); -} - -// Tween progress - -var setProgressValue = { - css: function (t, p, v) { - return t.style[p] = v; - }, - attribute: function (t, p, v) { - return t.setAttribute(p, v); - }, - object: function (t, p, v) { - return t[p] = v; - }, - transform: function (t, p, v, transforms, manual) { - transforms.list.set(p, v); - if (p === transforms.last || manual) { - var str = ''; - transforms.list.forEach(function (value, prop) { - str += prop + "(" + value + ") "; - }); - t.style.transform = str; - } - } -}; - -// Set Value helper - -function setTargetsValue(targets, properties) { - var animatables = getAnimatables(targets); - for (var i = 0, len = animatables.length; i < len; i++) { - var animatable = animatables[i]; - for (var property in properties) { - var value = getFunctionValue(properties[property], animatable); - var target = animatable.target; - var valueUnit = getUnit(value); - var originalValue = getOriginalTargetValue(target, property, valueUnit, animatable); - var unit = valueUnit || getUnit(originalValue); - var to = getRelativeValue(validateValue(value, unit), originalValue); - var animType = getAnimationType(target, property); - setProgressValue[animType](target, property, to, animatable.transforms, true); - } - } -} - -// Animations - -function createAnimation(animatable, prop) { - var animType = getAnimationType(animatable.target, prop.name); - if (animType) { - var tweens = normalizeTweens(prop, animatable); - var lastTween = tweens[tweens.length - 1]; - return { - type: animType, - property: prop.name, - animatable: animatable, - tweens: tweens, - duration: lastTween.end, - delay: tweens[0].delay, - endDelay: lastTween.endDelay - }; - } -} -function getAnimations(animatables, properties) { - return filterArray(flattenArray(animatables.map(function (animatable) { - return properties.map(function (prop) { - return createAnimation(animatable, prop); - }); - })), function (a) { - return !is.und(a); - }); -} - -// Create Instance - -function getInstanceTimings(animations, tweenSettings) { - var animLength = animations.length; - var getTlOffset = function (anim) { - return anim.timelineOffset ? anim.timelineOffset : 0; - }; - var timings = {}; - timings.duration = animLength ? Math.max.apply(Math, animations.map(function (anim) { - return getTlOffset(anim) + anim.duration; - })) : tweenSettings.duration; - timings.delay = animLength ? Math.min.apply(Math, animations.map(function (anim) { - return getTlOffset(anim) + anim.delay; - })) : tweenSettings.delay; - timings.endDelay = animLength ? timings.duration - Math.max.apply(Math, animations.map(function (anim) { - return getTlOffset(anim) + anim.duration - anim.endDelay; - })) : tweenSettings.endDelay; - return timings; -} -var instanceID = 0; -function createNewInstance(params) { - var instanceSettings = replaceObjectProps(defaultInstanceSettings, params); - var tweenSettings = replaceObjectProps(defaultTweenSettings, params); - var properties = getProperties(tweenSettings, params); - var animatables = getAnimatables(params.targets); - var animations = getAnimations(animatables, properties); - var timings = getInstanceTimings(animations, tweenSettings); - var id = instanceID; - instanceID++; - return mergeObjects(instanceSettings, { - id: id, - children: [], - animatables: animatables, - animations: animations, - duration: timings.duration, - delay: timings.delay, - endDelay: timings.endDelay - }); -} - -// Core - -var activeInstances = []; -var pausedInstances = []; -var raf; -var engine = function () { - function play() { - raf = requestAnimationFrame(step); - } - function step(t) { - var activeInstancesLength = activeInstances.length; - if (activeInstancesLength) { - var i = 0; - while (i < activeInstancesLength) { - var activeInstance = activeInstances[i]; - if (!activeInstance.paused) { - activeInstance.tick(t); - } else { - var instanceIndex = activeInstances.indexOf(activeInstance); - if (instanceIndex > -1) { - activeInstances.splice(instanceIndex, 1); - activeInstancesLength = activeInstances.length; - } - } - i++; - } - play(); - } else { - raf = cancelAnimationFrame(raf); - } - } - return play; -}(); -function handleVisibilityChange() { - if (document.hidden) { - for (var i = 0, len = activeInstances.length; i < len; i++) { - activeInstance[i].pause(); - } - pausedInstances = activeInstances.slice(0); - activeInstances = []; - } else { - for (var i$1 = 0, len$1 = pausedInstances.length; i$1 < len$1; i$1++) { - pausedInstances[i$1].play(); - } - } -} -document.addEventListener('visibilitychange', handleVisibilityChange); - -// Public Instance - -function anime(params) { - if (params === void 0) params = {}; - var startTime = 0, - lastTime = 0, - now = 0; - var children, - childrenLength = 0; - var resolve = null; - function makePromise() { - return window.Promise && new Promise(function (_resolve) { - return resolve = _resolve; - }); - } - var promise = makePromise(); - var instance = createNewInstance(params); - function toggleInstanceDirection() { - instance.reversed = !instance.reversed; - for (var i = 0, len = children.length; i < len; i++) { - children[i].reversed = instance.reversed; - } - } - function adjustTime(time) { - return instance.reversed ? instance.duration - time : time; - } - function resetTime() { - startTime = 0; - lastTime = adjustTime(instance.currentTime) * (1 / anime.speed); - } - function seekCild(time, child) { - if (child) { - child.seek(time - child.timelineOffset); - } - } - function syncInstanceChildren(time) { - if (!instance.reversePlayback) { - for (var i = 0; i < childrenLength; i++) { - seekCild(time, children[i]); - } - } else { - for (var i$1 = childrenLength; i$1--;) { - seekCild(time, children[i$1]); - } - } - } - function setAnimationsProgress(insTime) { - var i = 0; - var animations = instance.animations; - var animationsLength = animations.length; - while (i < animationsLength) { - var anim = animations[i]; - var animatable = anim.animatable; - var tweens = anim.tweens; - var tweenLength = tweens.length - 1; - var tween = tweens[tweenLength]; - // Only check for keyframes if there is more than one tween - if (tweenLength) { - tween = filterArray(tweens, function (t) { - return insTime < t.end; - })[0] || tween; - } - var elapsed = minMax(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration; - var eased = isNaN(elapsed) ? 1 : tween.easing(elapsed); - var strings = tween.to.strings; - var round = tween.round; - var numbers = []; - var toNumbersLength = tween.to.numbers.length; - var progress = void 0; - for (var n = 0; n < toNumbersLength; n++) { - var value = void 0; - var toNumber = tween.to.numbers[n]; - var fromNumber = tween.from.numbers[n] || 0; - if (!tween.isPath) { - value = fromNumber + eased * (toNumber - fromNumber); - } else { - value = getPathProgress(tween.value, eased * toNumber); - } - if (round) { - if (!(tween.isColor && n > 2)) { - value = Math.round(value * round) / round; - } - } - numbers.push(value); - } - // Manual Array.reduce for better performances - var stringsLength = strings.length; - if (!stringsLength) { - progress = numbers[0]; - } else { - progress = strings[0]; - for (var s = 0; s < stringsLength; s++) { - var a = strings[s]; - var b = strings[s + 1]; - var n$1 = numbers[s]; - if (!isNaN(n$1)) { - if (!b) { - progress += n$1 + ' '; - } else { - progress += n$1 + b; - } - } - } - } - setProgressValue[anim.type](animatable.target, anim.property, progress, animatable.transforms); - anim.currentValue = progress; - i++; - } - } - function setCallback(cb) { - if (instance[cb] && !instance.passThrough) { - instance[cb](instance); - } - } - function countIteration() { - if (instance.remaining && instance.remaining !== true) { - instance.remaining--; - } - } - function setInstanceProgress(engineTime) { - var insDuration = instance.duration; - var insDelay = instance.delay; - var insEndDelay = insDuration - instance.endDelay; - var insTime = adjustTime(engineTime); - instance.progress = minMax(insTime / insDuration * 100, 0, 100); - instance.reversePlayback = insTime < instance.currentTime; - if (children) { - syncInstanceChildren(insTime); - } - if (!instance.began && instance.currentTime > 0) { - instance.began = true; - setCallback('begin'); - setCallback('loopBegin'); - } - if (insTime <= insDelay && instance.currentTime !== 0) { - setAnimationsProgress(0); - } - if (insTime >= insEndDelay && instance.currentTime !== insDuration || !insDuration) { - setAnimationsProgress(insDuration); - } - if (insTime > insDelay && insTime < insEndDelay) { - if (!instance.changeBegan) { - instance.changeBegan = true; - instance.changeCompleted = false; - setCallback('changeBegin'); - } - setCallback('change'); - setAnimationsProgress(insTime); - } else { - if (instance.changeBegan) { - instance.changeCompleted = true; - instance.changeBegan = false; - setCallback('changeComplete'); - } - } - instance.currentTime = minMax(insTime, 0, insDuration); - if (instance.began) { - setCallback('update'); - } - if (engineTime >= insDuration) { - lastTime = 0; - countIteration(); - if (instance.remaining) { - startTime = now; - setCallback('loopComplete'); - setCallback('loopBegin'); - if (instance.direction === 'alternate') { - toggleInstanceDirection(); - } - } else { - instance.paused = true; - if (!instance.completed) { - instance.completed = true; - setCallback('loopComplete'); - setCallback('complete'); - if ('Promise' in window) { - resolve(); - promise = makePromise(); - } - } - } - } - } - instance.reset = function () { - var direction = instance.direction; - instance.passThrough = false; - instance.currentTime = 0; - instance.progress = 0; - instance.paused = true; - instance.began = false; - instance.changeBegan = false; - instance.completed = false; - instance.changeCompleted = false; - instance.reversePlayback = false; - instance.reversed = direction === 'reverse'; - instance.remaining = instance.loop; - children = instance.children; - childrenLength = children.length; - for (var i = childrenLength; i--;) { - instance.children[i].reset(); - } - if (instance.reversed && instance.loop !== true || direction === 'alternate' && instance.loop === 1) { - instance.remaining++; - } - setAnimationsProgress(0); - }; - - // Set Value helper - - instance.set = function (targets, properties) { - setTargetsValue(targets, properties); - return instance; - }; - instance.tick = function (t) { - now = t; - if (!startTime) { - startTime = now; - } - setInstanceProgress((now + (lastTime - startTime)) * anime.speed); - }; - instance.seek = function (time) { - setInstanceProgress(adjustTime(time)); - }; - instance.pause = function () { - instance.paused = true; - resetTime(); - }; - instance.play = function () { - if (!instance.paused) { - return; - } - instance.paused = false; - activeInstances.push(instance); - resetTime(); - if (!raf) { - engine(); - } - }; - instance.reverse = function () { - toggleInstanceDirection(); - resetTime(); - }; - instance.restart = function () { - instance.reset(); - instance.play(); - }; - instance.finished = promise; - instance.reset(); - if (instance.autoplay) { - instance.play(); - } - return instance; -} - -// Remove targets from animation - -function removeTargetsFromAnimations(targetsArray, animations) { - for (var a = animations.length; a--;) { - if (arrayContains(targetsArray, animations[a].animatable.target)) { - animations.splice(a, 1); - } - } -} -function removeTargets(targets) { - var targetsArray = parseTargets(targets); - for (var i = activeInstances.length; i--;) { - var instance = activeInstances[i]; - var animations = instance.animations; - var children = instance.children; - removeTargetsFromAnimations(targetsArray, animations); - for (var c = children.length; c--;) { - var child = children[c]; - var childAnimations = child.animations; - removeTargetsFromAnimations(targetsArray, childAnimations); - if (!childAnimations.length && !child.children.length) { - children.splice(c, 1); - } - } - if (!animations.length && !children.length) { - instance.pause(); - } - } -} - -// Stagger helpers - -function stagger(val, params) { - if (params === void 0) params = {}; - var direction = params.direction || 'normal'; - var easing = params.easing ? parseEasings(params.easing) : null; - var grid = params.grid; - var axis = params.axis; - var fromIndex = params.from || 0; - var fromFirst = fromIndex === 'first'; - var fromCenter = fromIndex === 'center'; - var fromLast = fromIndex === 'last'; - var isRange = is.arr(val); - var val1 = isRange ? parseFloat(val[0]) : parseFloat(val); - var val2 = isRange ? parseFloat(val[1]) : 0; - var unit = getUnit(isRange ? val[1] : val) || 0; - var start = params.start || 0 + (isRange ? val1 : 0); - var values = []; - var maxValue = 0; - return function (el, i, t) { - if (fromFirst) { - fromIndex = 0; - } - if (fromCenter) { - fromIndex = (t - 1) / 2; - } - if (fromLast) { - fromIndex = t - 1; - } - if (!values.length) { - for (var index = 0; index < t; index++) { - if (!grid) { - values.push(Math.abs(fromIndex - index)); - } else { - var fromX = !fromCenter ? fromIndex % grid[0] : (grid[0] - 1) / 2; - var fromY = !fromCenter ? Math.floor(fromIndex / grid[0]) : (grid[1] - 1) / 2; - var toX = index % grid[0]; - var toY = Math.floor(index / grid[0]); - var distanceX = fromX - toX; - var distanceY = fromY - toY; - var value = Math.sqrt(distanceX * distanceX + distanceY * distanceY); - if (axis === 'x') { - value = -distanceX; - } - if (axis === 'y') { - value = -distanceY; - } - values.push(value); - } - maxValue = Math.max.apply(Math, values); - } - if (easing) { - values = values.map(function (val) { - return easing(val / maxValue) * maxValue; - }); - } - if (direction === 'reverse') { - values = values.map(function (val) { - return axis ? val < 0 ? val * -1 : -val : Math.abs(maxValue - val); - }); - } - } - var spacing = isRange ? (val2 - val1) / maxValue : val1; - return start + spacing * (Math.round(values[i] * 100) / 100) + unit; - }; -} - -// Timeline - -function timeline(params) { - if (params === void 0) params = {}; - var tl = anime(params); - tl.duration = 0; - tl.add = function (instanceParams, timelineOffset) { - var tlIndex = activeInstances.indexOf(tl); - var children = tl.children; - if (tlIndex > -1) { - activeInstances.splice(tlIndex, 1); - } - function passThrough(ins) { - ins.passThrough = true; - } - for (var i = 0; i < children.length; i++) { - passThrough(children[i]); - } - var insParams = mergeObjects(instanceParams, replaceObjectProps(defaultTweenSettings, params)); - insParams.targets = insParams.targets || params.targets; - var tlDuration = tl.duration; - insParams.autoplay = false; - insParams.direction = tl.direction; - insParams.timelineOffset = is.und(timelineOffset) ? tlDuration : getRelativeValue(timelineOffset, tlDuration); - passThrough(tl); - tl.seek(insParams.timelineOffset); - var ins = anime(insParams); - passThrough(ins); - children.push(ins); - var timings = getInstanceTimings(children, params); - tl.delay = timings.delay; - tl.endDelay = timings.endDelay; - tl.duration = timings.duration; - tl.seek(0); - tl.reset(); - if (tl.autoplay) { - tl.play(); - } - return tl; - }; - return tl; -} -anime.version = '3.0.0'; -anime.speed = 1; -anime.running = activeInstances; -anime.remove = removeTargets; -anime.get = getOriginalTargetValue; -anime.set = setTargetsValue; -anime.convertPx = convertPxToUnit; -anime.path = getPath; -anime.setDashoffset = setDashoffset; -anime.stagger = stagger; -anime.timeline = timeline; -anime.easing = parseEasings; -anime.penner = penner; -anime.random = function (min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -}; -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (anime); - -/***/ }), - -/***/ "./node_modules/three-bmfont-text/index.js": -/*!*************************************************!*\ - !*** ./node_modules/three-bmfont-text/index.js ***! - \*************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var createLayout = __webpack_require__(/*! layout-bmfont-text */ "./node_modules/layout-bmfont-text/index.js"); -var createIndices = __webpack_require__(/*! quad-indices */ "./node_modules/quad-indices/index.js"); -var vertices = __webpack_require__(/*! ./lib/vertices */ "./node_modules/three-bmfont-text/lib/vertices.js"); -var utils = __webpack_require__(/*! ./lib/utils */ "./node_modules/three-bmfont-text/lib/utils.js"); -module.exports = function createTextGeometry(opt) { - return new TextGeometry(opt); -}; -class TextGeometry extends THREE.BufferGeometry { - constructor(opt) { - super(); - if (typeof opt === 'string') { - opt = { - text: opt - }; - } - - // use these as default values for any subsequent - // calls to update() - this._opt = Object.assign({}, opt); - - // also do an initial setup... - if (opt) this.update(opt); - } - update(opt) { - if (typeof opt === 'string') { - opt = { - text: opt - }; - } - - // use constructor defaults - opt = Object.assign({}, this._opt, opt); - if (!opt.font) { - throw new TypeError('must specify a { font } in options'); - } - this.layout = createLayout(opt); - - // get vec2 texcoords - var flipY = opt.flipY !== false; - - // the desired BMFont data - var font = opt.font; - - // determine texture size from font file - var texWidth = font.common.scaleW; - var texHeight = font.common.scaleH; - - // get visible glyphs - var glyphs = this.layout.glyphs.filter(function (glyph) { - var bitmap = glyph.data; - return bitmap.width * bitmap.height > 0; - }); - - // provide visible glyphs for convenience - this.visibleGlyphs = glyphs; - - // get common vertex data - var positions = vertices.positions(glyphs); - var uvs = vertices.uvs(glyphs, texWidth, texHeight, flipY); - var indices = createIndices([], { - clockwise: true, - type: 'uint16', - count: glyphs.length - }); - - // update vertex data - this.setIndex(indices); - this.setAttribute('position', new THREE.BufferAttribute(positions, 2)); - this.setAttribute('uv', new THREE.BufferAttribute(uvs, 2)); - - // update multipage data - if (!opt.multipage && 'page' in this.attributes) { - // disable multipage rendering - this.removeAttribute('page'); - } else if (opt.multipage) { - // enable multipage rendering - var pages = vertices.pages(glyphs); - this.setAttribute('page', new THREE.BufferAttribute(pages, 1)); - } - - // recompute bounding box and sphere, if present - if (this.boundingBox !== null) { - this.computeBoundingBox(); - } - if (this.boundingSphere !== null) { - this.computeBoundingSphere(); - } - } - computeBoundingSphere() { - if (this.boundingSphere === null) { - this.boundingSphere = new THREE.Sphere(); - } - var positions = this.attributes.position.array; - var itemSize = this.attributes.position.itemSize; - if (!positions || !itemSize || positions.length < 2) { - this.boundingSphere.radius = 0; - this.boundingSphere.center.set(0, 0, 0); - return; - } - utils.computeSphere(positions, this.boundingSphere); - if (isNaN(this.boundingSphere.radius)) { - console.error('THREE.BufferGeometry.computeBoundingSphere(): ' + 'Computed radius is NaN. The ' + '"position" attribute is likely to have NaN values.'); - } - } - computeBoundingBox() { - if (this.boundingBox === null) { - this.boundingBox = new THREE.Box3(); - } - var bbox = this.boundingBox; - var positions = this.attributes.position.array; - var itemSize = this.attributes.position.itemSize; - if (!positions || !itemSize || positions.length < 2) { - bbox.makeEmpty(); - return; - } - utils.computeBox(positions, bbox); - } -} - -/***/ }), - -/***/ "./node_modules/three-bmfont-text/lib/utils.js": -/*!*****************************************************!*\ - !*** ./node_modules/three-bmfont-text/lib/utils.js ***! - \*****************************************************/ -/***/ ((module) => { - -var itemSize = 2; -var box = { - min: [0, 0], - max: [0, 0] -}; -function bounds(positions) { - var count = positions.length / itemSize; - box.min[0] = positions[0]; - box.min[1] = positions[1]; - box.max[0] = positions[0]; - box.max[1] = positions[1]; - for (var i = 0; i < count; i++) { - var x = positions[i * itemSize + 0]; - var y = positions[i * itemSize + 1]; - box.min[0] = Math.min(x, box.min[0]); - box.min[1] = Math.min(y, box.min[1]); - box.max[0] = Math.max(x, box.max[0]); - box.max[1] = Math.max(y, box.max[1]); - } -} -module.exports.computeBox = function (positions, output) { - bounds(positions); - output.min.set(box.min[0], box.min[1], 0); - output.max.set(box.max[0], box.max[1], 0); -}; -module.exports.computeSphere = function (positions, output) { - bounds(positions); - var minX = box.min[0]; - var minY = box.min[1]; - var maxX = box.max[0]; - var maxY = box.max[1]; - var width = maxX - minX; - var height = maxY - minY; - var length = Math.sqrt(width * width + height * height); - output.center.set(minX + width / 2, minY + height / 2, 0); - output.radius = length / 2; -}; - -/***/ }), - -/***/ "./node_modules/three-bmfont-text/lib/vertices.js": -/*!********************************************************!*\ - !*** ./node_modules/three-bmfont-text/lib/vertices.js ***! - \********************************************************/ -/***/ ((module) => { - -module.exports.pages = function pages(glyphs) { - var pages = new Float32Array(glyphs.length * 4 * 1); - var i = 0; - glyphs.forEach(function (glyph) { - var id = glyph.data.page || 0; - pages[i++] = id; - pages[i++] = id; - pages[i++] = id; - pages[i++] = id; - }); - return pages; -}; -module.exports.uvs = function uvs(glyphs, texWidth, texHeight, flipY) { - var uvs = new Float32Array(glyphs.length * 4 * 2); - var i = 0; - glyphs.forEach(function (glyph) { - var bitmap = glyph.data; - var bw = bitmap.x + bitmap.width; - var bh = bitmap.y + bitmap.height; - - // top left position - var u0 = bitmap.x / texWidth; - var v1 = bitmap.y / texHeight; - var u1 = bw / texWidth; - var v0 = bh / texHeight; - if (flipY) { - v1 = (texHeight - bitmap.y) / texHeight; - v0 = (texHeight - bh) / texHeight; - } - - // BL - uvs[i++] = u0; - uvs[i++] = v1; - // TL - uvs[i++] = u0; - uvs[i++] = v0; - // TR - uvs[i++] = u1; - uvs[i++] = v0; - // BR - uvs[i++] = u1; - uvs[i++] = v1; - }); - return uvs; -}; -module.exports.positions = function positions(glyphs) { - var positions = new Float32Array(glyphs.length * 4 * 2); - var i = 0; - glyphs.forEach(function (glyph) { - var bitmap = glyph.data; - - // bottom left position - var x = glyph.position[0] + bitmap.xoffset; - var y = glyph.position[1] + bitmap.yoffset; - - // quad size - var w = bitmap.width; - var h = bitmap.height; - - // BL - positions[i++] = x; - positions[i++] = y; - // TL - positions[i++] = x; - positions[i++] = y + h; - // TR - positions[i++] = x + w; - positions[i++] = y + h; - // BR - positions[i++] = x + w; - positions[i++] = y; - }); - return positions; -}; - -/***/ }), - -/***/ "./node_modules/webvr-polyfill/build/webvr-polyfill.js": -/*!*************************************************************!*\ - !*** ./node_modules/webvr-polyfill/build/webvr-polyfill.js ***! - \*************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -/** - * @license - * webvr-polyfill - * Copyright (c) 2015-2017 Google - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @license - * cardboard-vr-display - * Copyright (c) 2015-2017 Google - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @license - * webvr-polyfill-dpdb - * Copyright (c) 2017 Google - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @license - * wglu-preserve-state - * Copyright (c) 2016, Brandon Jones. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * @license - * nosleep.js - * Copyright (c) 2017, Rich Tibbett - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -(function (global, factory) { - true ? module.exports = factory() : 0; -})(this, function () { - 'use strict'; - - var commonjsGlobal = typeof window !== 'undefined' ? window : typeof __webpack_require__.g !== 'undefined' ? __webpack_require__.g : typeof self !== 'undefined' ? self : {}; - function unwrapExports(x) { - return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; - } - function createCommonjsModule(fn, module) { - return module = { - exports: {} - }, fn(module, module.exports), module.exports; - } - var isMobile = function isMobile() { - return /Android/i.test(navigator.userAgent) || /iPhone|iPad|iPod/i.test(navigator.userAgent); - }; - var copyArray = function copyArray(source, dest) { - for (var i = 0, n = source.length; i < n; i++) { - dest[i] = source[i]; - } - }; - var extend = function extend(dest, src) { - for (var key in src) { - if (src.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - return dest; - }; - var cardboardVrDisplay = createCommonjsModule(function (module, exports) { - /** - * @license - * cardboard-vr-display - * Copyright (c) 2015-2017 Google - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /** - * @license - * gl-preserve-state - * Copyright (c) 2016, Brandon Jones. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - /** - * @license - * webvr-polyfill-dpdb - * Copyright (c) 2015-2017 Google - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /** - * @license - * nosleep.js - * Copyright (c) 2017, Rich Tibbett - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - (function (global, factory) { - module.exports = factory(); - })(commonjsGlobal, function () { - var classCallCheck = function (instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - }; - var createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - var slicedToArray = function () { - function sliceIterator(arr, i) { - var _arr = []; - var _n = true; - var _d = false; - var _e = undefined; - try { - for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"]) _i["return"](); - } finally { - if (_d) throw _e; - } - } - return _arr; - } - return function (arr, i) { - if (Array.isArray(arr)) { - return arr; - } else if (Symbol.iterator in Object(arr)) { - return sliceIterator(arr, i); - } else { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); - } - }; - }(); - var MIN_TIMESTEP = 0.001; - var MAX_TIMESTEP = 1; - var dataUri = function dataUri(mimeType, svg) { - return 'data:' + mimeType + ',' + encodeURIComponent(svg); - }; - var lerp = function lerp(a, b, t) { - return a + (b - a) * t; - }; - var isIOS = function () { - var isIOS = /iPad|iPhone|iPod/.test(navigator.platform); - return function () { - return isIOS; - }; - }(); - var isWebViewAndroid = function () { - var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && navigator.userAgent.indexOf('Android') !== -1 && navigator.userAgent.indexOf('Chrome') !== -1; - return function () { - return isWebViewAndroid; - }; - }(); - var isSafari = function () { - var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - return function () { - return isSafari; - }; - }(); - var isFirefoxAndroid = function () { - var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1; - return function () { - return isFirefoxAndroid; - }; - }(); - var getChromeVersion = function () { - var match = navigator.userAgent.match(/.*Chrome\/([0-9]+)/); - var value = match ? parseInt(match[1], 10) : null; - return function () { - return value; - }; - }(); - var isSafariWithoutDeviceMotion = function () { - var value = false; - value = isIOS() && isSafari() && navigator.userAgent.indexOf('13_4') !== -1; - return function () { - return value; - }; - }(); - var isChromeWithoutDeviceMotion = function () { - var value = false; - if (getChromeVersion() === 65) { - var match = navigator.userAgent.match(/.*Chrome\/([0-9\.]*)/); - if (match) { - var _match$1$split = match[1].split('.'), - _match$1$split2 = slicedToArray(_match$1$split, 4), - major = _match$1$split2[0], - minor = _match$1$split2[1], - branch = _match$1$split2[2], - build = _match$1$split2[3]; - value = parseInt(branch, 10) === 3325 && parseInt(build, 10) < 148; - } - } - return function () { - return value; - }; - }(); - var isR7 = function () { - var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1; - return function () { - return isR7; - }; - }(); - var isLandscapeMode = function isLandscapeMode() { - var rtn = window.orientation == 90 || window.orientation == -90; - return isR7() ? !rtn : rtn; - }; - var isTimestampDeltaValid = function isTimestampDeltaValid(timestampDeltaS) { - if (isNaN(timestampDeltaS)) { - return false; - } - if (timestampDeltaS <= MIN_TIMESTEP) { - return false; - } - if (timestampDeltaS > MAX_TIMESTEP) { - return false; - } - return true; - }; - var getScreenWidth = function getScreenWidth() { - return Math.max(window.screen.width, window.screen.height) * window.devicePixelRatio; - }; - var getScreenHeight = function getScreenHeight() { - return Math.min(window.screen.width, window.screen.height) * window.devicePixelRatio; - }; - var requestFullscreen = function requestFullscreen(element) { - if (isWebViewAndroid()) { - return false; - } - if (element.requestFullscreen) { - element.requestFullscreen(); - } else if (element.webkitRequestFullscreen) { - element.webkitRequestFullscreen(); - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(); - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(); - } else { - return false; - } - return true; - }; - var exitFullscreen = function exitFullscreen() { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } else { - return false; - } - return true; - }; - var getFullscreenElement = function getFullscreenElement() { - return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; - }; - var linkProgram = function linkProgram(gl, vertexSource, fragmentSource, attribLocationMap) { - var vertexShader = gl.createShader(gl.VERTEX_SHADER); - gl.shaderSource(vertexShader, vertexSource); - gl.compileShader(vertexShader); - var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); - gl.shaderSource(fragmentShader, fragmentSource); - gl.compileShader(fragmentShader); - var program = gl.createProgram(); - gl.attachShader(program, vertexShader); - gl.attachShader(program, fragmentShader); - for (var attribName in attribLocationMap) { - gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); - } - gl.linkProgram(program); - gl.deleteShader(vertexShader); - gl.deleteShader(fragmentShader); - return program; - }; - var getProgramUniforms = function getProgramUniforms(gl, program) { - var uniforms = {}; - var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); - var uniformName = ''; - for (var i = 0; i < uniformCount; i++) { - var uniformInfo = gl.getActiveUniform(program, i); - uniformName = uniformInfo.name.replace('[0]', ''); - uniforms[uniformName] = gl.getUniformLocation(program, uniformName); - } - return uniforms; - }; - var orthoMatrix = function orthoMatrix(out, left, right, bottom, top, near, far) { - var lr = 1 / (left - right), - bt = 1 / (bottom - top), - nf = 1 / (near - far); - out[0] = -2 * lr; - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[5] = -2 * bt; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[10] = 2 * nf; - out[11] = 0; - out[12] = (left + right) * lr; - out[13] = (top + bottom) * bt; - out[14] = (far + near) * nf; - out[15] = 1; - return out; - }; - var isMobile = function isMobile() { - var check = false; - (function (a) { - if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; - })(navigator.userAgent || navigator.vendor || window.opera); - return check; - }; - var extend = function extend(dest, src) { - for (var key in src) { - if (src.hasOwnProperty(key)) { - dest[key] = src[key]; - } - } - return dest; - }; - var safariCssSizeWorkaround = function safariCssSizeWorkaround(canvas) { - if (isIOS()) { - var width = canvas.style.width; - var height = canvas.style.height; - canvas.style.width = parseInt(width) + 1 + 'px'; - canvas.style.height = parseInt(height) + 'px'; - setTimeout(function () { - canvas.style.width = width; - canvas.style.height = height; - }, 100); - } - window.canvas = canvas; - }; - var frameDataFromPose = function () { - var piOver180 = Math.PI / 180.0; - var rad45 = Math.PI * 0.25; - function mat4_perspectiveFromFieldOfView(out, fov, near, far) { - var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45), - downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45), - leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45), - rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45), - xScale = 2.0 / (leftTan + rightTan), - yScale = 2.0 / (upTan + downTan); - out[0] = xScale; - out[1] = 0.0; - out[2] = 0.0; - out[3] = 0.0; - out[4] = 0.0; - out[5] = yScale; - out[6] = 0.0; - out[7] = 0.0; - out[8] = -((leftTan - rightTan) * xScale * 0.5); - out[9] = (upTan - downTan) * yScale * 0.5; - out[10] = far / (near - far); - out[11] = -1.0; - out[12] = 0.0; - out[13] = 0.0; - out[14] = far * near / (near - far); - out[15] = 0.0; - return out; - } - function mat4_fromRotationTranslation(out, q, v) { - var x = q[0], - y = q[1], - z = q[2], - w = q[3], - x2 = x + x, - y2 = y + y, - z2 = z + z, - xx = x * x2, - xy = x * y2, - xz = x * z2, - yy = y * y2, - yz = y * z2, - zz = z * z2, - wx = w * x2, - wy = w * y2, - wz = w * z2; - out[0] = 1 - (yy + zz); - out[1] = xy + wz; - out[2] = xz - wy; - out[3] = 0; - out[4] = xy - wz; - out[5] = 1 - (xx + zz); - out[6] = yz + wx; - out[7] = 0; - out[8] = xz + wy; - out[9] = yz - wx; - out[10] = 1 - (xx + yy); - out[11] = 0; - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - out[15] = 1; - return out; - } - function mat4_translate(out, a, v) { - var x = v[0], - y = v[1], - z = v[2], - a00, - a01, - a02, - a03, - a10, - a11, - a12, - a13, - a20, - a21, - a22, - a23; - if (a === out) { - out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; - out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; - out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; - out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; - } else { - a00 = a[0]; - a01 = a[1]; - a02 = a[2]; - a03 = a[3]; - a10 = a[4]; - a11 = a[5]; - a12 = a[6]; - a13 = a[7]; - a20 = a[8]; - a21 = a[9]; - a22 = a[10]; - a23 = a[11]; - out[0] = a00; - out[1] = a01; - out[2] = a02; - out[3] = a03; - out[4] = a10; - out[5] = a11; - out[6] = a12; - out[7] = a13; - out[8] = a20; - out[9] = a21; - out[10] = a22; - out[11] = a23; - out[12] = a00 * x + a10 * y + a20 * z + a[12]; - out[13] = a01 * x + a11 * y + a21 * z + a[13]; - out[14] = a02 * x + a12 * y + a22 * z + a[14]; - out[15] = a03 * x + a13 * y + a23 * z + a[15]; - } - return out; - } - function mat4_invert(out, a) { - var a00 = a[0], - a01 = a[1], - a02 = a[2], - a03 = a[3], - a10 = a[4], - a11 = a[5], - a12 = a[6], - a13 = a[7], - a20 = a[8], - a21 = a[9], - a22 = a[10], - a23 = a[11], - a30 = a[12], - a31 = a[13], - a32 = a[14], - a33 = a[15], - b00 = a00 * a11 - a01 * a10, - b01 = a00 * a12 - a02 * a10, - b02 = a00 * a13 - a03 * a10, - b03 = a01 * a12 - a02 * a11, - b04 = a01 * a13 - a03 * a11, - b05 = a02 * a13 - a03 * a12, - b06 = a20 * a31 - a21 * a30, - b07 = a20 * a32 - a22 * a30, - b08 = a20 * a33 - a23 * a30, - b09 = a21 * a32 - a22 * a31, - b10 = a21 * a33 - a23 * a31, - b11 = a22 * a33 - a23 * a32, - det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - if (!det) { - return null; - } - det = 1.0 / det; - out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; - out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; - out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; - out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; - out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; - out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; - out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; - out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; - out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; - out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; - out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; - out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; - out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; - out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; - out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; - out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; - return out; - } - var defaultOrientation = new Float32Array([0, 0, 0, 1]); - var defaultPosition = new Float32Array([0, 0, 0]); - function updateEyeMatrices(projection, view, pose, fov, offset, vrDisplay) { - mat4_perspectiveFromFieldOfView(projection, fov || null, vrDisplay.depthNear, vrDisplay.depthFar); - var orientation = pose.orientation || defaultOrientation; - var position = pose.position || defaultPosition; - mat4_fromRotationTranslation(view, orientation, position); - if (offset) mat4_translate(view, view, offset); - mat4_invert(view, view); - } - return function (frameData, pose, vrDisplay) { - if (!frameData || !pose) return false; - frameData.pose = pose; - frameData.timestamp = pose.timestamp; - updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay._getFieldOfView("left"), vrDisplay._getEyeOffset("left"), vrDisplay); - updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay._getFieldOfView("right"), vrDisplay._getEyeOffset("right"), vrDisplay); - return true; - }; - }(); - var isInsideCrossOriginIFrame = function isInsideCrossOriginIFrame() { - var isFramed = window.self !== window.top; - var refOrigin = getOriginFromUrl(document.referrer); - var thisOrigin = getOriginFromUrl(window.location.href); - return isFramed && refOrigin !== thisOrigin; - }; - var getOriginFromUrl = function getOriginFromUrl(url) { - var domainIdx; - var protoSepIdx = url.indexOf("://"); - if (protoSepIdx !== -1) { - domainIdx = protoSepIdx + 3; - } else { - domainIdx = 0; - } - var domainEndIdx = url.indexOf('/', domainIdx); - if (domainEndIdx === -1) { - domainEndIdx = url.length; - } - return url.substring(0, domainEndIdx); - }; - var getQuaternionAngle = function getQuaternionAngle(quat) { - if (quat.w > 1) { - console.warn('getQuaternionAngle: w > 1'); - return 0; - } - var angle = 2 * Math.acos(quat.w); - return angle; - }; - var warnOnce = function () { - var observedWarnings = {}; - return function (key, message) { - if (observedWarnings[key] === undefined) { - console.warn('webvr-polyfill: ' + message); - observedWarnings[key] = true; - } - }; - }(); - var deprecateWarning = function deprecateWarning(deprecated, suggested) { - var alternative = suggested ? 'Please use ' + suggested + ' instead.' : ''; - warnOnce(deprecated, deprecated + ' has been deprecated. ' + 'This may not work on native WebVR displays. ' + alternative); - }; - function WGLUPreserveGLState(gl, bindings, callback) { - if (!bindings) { - callback(gl); - return; - } - var boundValues = []; - var activeTexture = null; - for (var i = 0; i < bindings.length; ++i) { - var binding = bindings[i]; - switch (binding) { - case gl.TEXTURE_BINDING_2D: - case gl.TEXTURE_BINDING_CUBE_MAP: - var textureUnit = bindings[++i]; - if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) { - console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"); - boundValues.push(null, null); - break; - } - if (!activeTexture) { - activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); - } - gl.activeTexture(textureUnit); - boundValues.push(gl.getParameter(binding), null); - break; - case gl.ACTIVE_TEXTURE: - activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); - boundValues.push(null); - break; - default: - boundValues.push(gl.getParameter(binding)); - break; - } - } - callback(gl); - for (var i = 0; i < bindings.length; ++i) { - var binding = bindings[i]; - var boundValue = boundValues[i]; - switch (binding) { - case gl.ACTIVE_TEXTURE: - break; - case gl.ARRAY_BUFFER_BINDING: - gl.bindBuffer(gl.ARRAY_BUFFER, boundValue); - break; - case gl.COLOR_CLEAR_VALUE: - gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); - break; - case gl.COLOR_WRITEMASK: - gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); - break; - case gl.CURRENT_PROGRAM: - gl.useProgram(boundValue); - break; - case gl.ELEMENT_ARRAY_BUFFER_BINDING: - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue); - break; - case gl.FRAMEBUFFER_BINDING: - gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue); - break; - case gl.RENDERBUFFER_BINDING: - gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue); - break; - case gl.TEXTURE_BINDING_2D: - var textureUnit = bindings[++i]; - if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) break; - gl.activeTexture(textureUnit); - gl.bindTexture(gl.TEXTURE_2D, boundValue); - break; - case gl.TEXTURE_BINDING_CUBE_MAP: - var textureUnit = bindings[++i]; - if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) break; - gl.activeTexture(textureUnit); - gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue); - break; - case gl.VIEWPORT: - gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); - break; - case gl.BLEND: - case gl.CULL_FACE: - case gl.DEPTH_TEST: - case gl.SCISSOR_TEST: - case gl.STENCIL_TEST: - if (boundValue) { - gl.enable(binding); - } else { - gl.disable(binding); - } - break; - default: - console.log("No GL restore behavior for 0x" + binding.toString(16)); - break; - } - if (activeTexture) { - gl.activeTexture(activeTexture); - } - } - } - var glPreserveState = WGLUPreserveGLState; - var distortionVS = ['attribute vec2 position;', 'attribute vec3 texCoord;', 'varying vec2 vTexCoord;', 'uniform vec4 viewportOffsetScale[2];', 'void main() {', ' vec4 viewport = viewportOffsetScale[int(texCoord.z)];', ' vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;', ' gl_Position = vec4( position, 1.0, 1.0 );', '}'].join('\n'); - var distortionFS = ['precision mediump float;', 'uniform sampler2D diffuse;', 'varying vec2 vTexCoord;', 'void main() {', ' gl_FragColor = texture2D(diffuse, vTexCoord);', '}'].join('\n'); - function CardboardDistorter(gl, cardboardUI, bufferScale, dirtySubmitFrameBindings) { - this.gl = gl; - this.cardboardUI = cardboardUI; - this.bufferScale = bufferScale; - this.dirtySubmitFrameBindings = dirtySubmitFrameBindings; - this.ctxAttribs = gl.getContextAttributes(); - this.instanceExt = gl.getExtension('ANGLE_instanced_arrays'); - this.meshWidth = 20; - this.meshHeight = 20; - this.bufferWidth = gl.drawingBufferWidth; - this.bufferHeight = gl.drawingBufferHeight; - this.realBindFramebuffer = gl.bindFramebuffer; - this.realEnable = gl.enable; - this.realDisable = gl.disable; - this.realColorMask = gl.colorMask; - this.realClearColor = gl.clearColor; - this.realViewport = gl.viewport; - if (!isIOS()) { - this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width'); - this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height'); - } - this.isPatched = false; - this.lastBoundFramebuffer = null; - this.cullFace = false; - this.depthTest = false; - this.blend = false; - this.scissorTest = false; - this.stencilTest = false; - this.viewport = [0, 0, 0, 0]; - this.colorMask = [true, true, true, true]; - this.clearColor = [0, 0, 0, 0]; - this.attribs = { - position: 0, - texCoord: 1 - }; - this.program = linkProgram(gl, distortionVS, distortionFS, this.attribs); - this.uniforms = getProgramUniforms(gl, this.program); - this.viewportOffsetScale = new Float32Array(8); - this.setTextureBounds(); - this.vertexBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - this.indexCount = 0; - this.renderTarget = gl.createTexture(); - this.framebuffer = gl.createFramebuffer(); - this.depthStencilBuffer = null; - this.depthBuffer = null; - this.stencilBuffer = null; - if (this.ctxAttribs.depth && this.ctxAttribs.stencil) { - this.depthStencilBuffer = gl.createRenderbuffer(); - } else if (this.ctxAttribs.depth) { - this.depthBuffer = gl.createRenderbuffer(); - } else if (this.ctxAttribs.stencil) { - this.stencilBuffer = gl.createRenderbuffer(); - } - this.patch(); - this.onResize(); - } - CardboardDistorter.prototype.destroy = function () { - var gl = this.gl; - this.unpatch(); - gl.deleteProgram(this.program); - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.indexBuffer); - gl.deleteTexture(this.renderTarget); - gl.deleteFramebuffer(this.framebuffer); - if (this.depthStencilBuffer) { - gl.deleteRenderbuffer(this.depthStencilBuffer); - } - if (this.depthBuffer) { - gl.deleteRenderbuffer(this.depthBuffer); - } - if (this.stencilBuffer) { - gl.deleteRenderbuffer(this.stencilBuffer); - } - if (this.cardboardUI) { - this.cardboardUI.destroy(); - } - }; - CardboardDistorter.prototype.onResize = function () { - var gl = this.gl; - var self = this; - var glState = [gl.RENDERBUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0]; - glPreserveState(gl, glState, function (gl) { - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); - if (self.scissorTest) { - self.realDisable.call(gl, gl.SCISSOR_TEST); - } - self.realColorMask.call(gl, true, true, true, true); - self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - self.realClearColor.call(gl, 0, 0, 0, 1); - gl.clear(gl.COLOR_BUFFER_BIT); - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer); - gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); - gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, self.bufferWidth, self.bufferHeight, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0); - if (self.ctxAttribs.depth && self.ctxAttribs.stencil) { - gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, self.bufferWidth, self.bufferHeight); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.depthStencilBuffer); - } else if (self.ctxAttribs.depth) { - gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, self.bufferWidth, self.bufferHeight); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, self.depthBuffer); - } else if (self.ctxAttribs.stencil) { - gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, self.bufferWidth, self.bufferHeight); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, self.stencilBuffer); - } - if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) { - console.error('Framebuffer incomplete!'); - } - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); - if (self.scissorTest) { - self.realEnable.call(gl, gl.SCISSOR_TEST); - } - self.realColorMask.apply(gl, self.colorMask); - self.realViewport.apply(gl, self.viewport); - self.realClearColor.apply(gl, self.clearColor); - }); - if (this.cardboardUI) { - this.cardboardUI.onResize(); - } - }; - CardboardDistorter.prototype.patch = function () { - if (this.isPatched) { - return; - } - var self = this; - var canvas = this.gl.canvas; - var gl = this.gl; - if (!isIOS()) { - canvas.width = getScreenWidth() * this.bufferScale; - canvas.height = getScreenHeight() * this.bufferScale; - Object.defineProperty(canvas, 'width', { - configurable: true, - enumerable: true, - get: function get() { - return self.bufferWidth; - }, - set: function set(value) { - self.bufferWidth = value; - self.realCanvasWidth.set.call(canvas, value); - self.onResize(); - } - }); - Object.defineProperty(canvas, 'height', { - configurable: true, - enumerable: true, - get: function get() { - return self.bufferHeight; - }, - set: function set(value) { - self.bufferHeight = value; - self.realCanvasHeight.set.call(canvas, value); - self.onResize(); - } - }); - } - this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); - if (this.lastBoundFramebuffer == null) { - this.lastBoundFramebuffer = this.framebuffer; - this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); - } - this.gl.bindFramebuffer = function (target, framebuffer) { - self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer; - self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer); - }; - this.cullFace = gl.getParameter(gl.CULL_FACE); - this.depthTest = gl.getParameter(gl.DEPTH_TEST); - this.blend = gl.getParameter(gl.BLEND); - this.scissorTest = gl.getParameter(gl.SCISSOR_TEST); - this.stencilTest = gl.getParameter(gl.STENCIL_TEST); - gl.enable = function (pname) { - switch (pname) { - case gl.CULL_FACE: - self.cullFace = true; - break; - case gl.DEPTH_TEST: - self.depthTest = true; - break; - case gl.BLEND: - self.blend = true; - break; - case gl.SCISSOR_TEST: - self.scissorTest = true; - break; - case gl.STENCIL_TEST: - self.stencilTest = true; - break; - } - self.realEnable.call(gl, pname); - }; - gl.disable = function (pname) { - switch (pname) { - case gl.CULL_FACE: - self.cullFace = false; - break; - case gl.DEPTH_TEST: - self.depthTest = false; - break; - case gl.BLEND: - self.blend = false; - break; - case gl.SCISSOR_TEST: - self.scissorTest = false; - break; - case gl.STENCIL_TEST: - self.stencilTest = false; - break; - } - self.realDisable.call(gl, pname); - }; - this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK); - gl.colorMask = function (r, g, b, a) { - self.colorMask[0] = r; - self.colorMask[1] = g; - self.colorMask[2] = b; - self.colorMask[3] = a; - self.realColorMask.call(gl, r, g, b, a); - }; - this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); - gl.clearColor = function (r, g, b, a) { - self.clearColor[0] = r; - self.clearColor[1] = g; - self.clearColor[2] = b; - self.clearColor[3] = a; - self.realClearColor.call(gl, r, g, b, a); - }; - this.viewport = gl.getParameter(gl.VIEWPORT); - gl.viewport = function (x, y, w, h) { - self.viewport[0] = x; - self.viewport[1] = y; - self.viewport[2] = w; - self.viewport[3] = h; - self.realViewport.call(gl, x, y, w, h); - }; - this.isPatched = true; - safariCssSizeWorkaround(canvas); - }; - CardboardDistorter.prototype.unpatch = function () { - if (!this.isPatched) { - return; - } - var gl = this.gl; - var canvas = this.gl.canvas; - if (!isIOS()) { - Object.defineProperty(canvas, 'width', this.realCanvasWidth); - Object.defineProperty(canvas, 'height', this.realCanvasHeight); - } - canvas.width = this.bufferWidth; - canvas.height = this.bufferHeight; - gl.bindFramebuffer = this.realBindFramebuffer; - gl.enable = this.realEnable; - gl.disable = this.realDisable; - gl.colorMask = this.realColorMask; - gl.clearColor = this.realClearColor; - gl.viewport = this.realViewport; - if (this.lastBoundFramebuffer == this.framebuffer) { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - } - this.isPatched = false; - setTimeout(function () { - safariCssSizeWorkaround(canvas); - }, 1); - }; - CardboardDistorter.prototype.setTextureBounds = function (leftBounds, rightBounds) { - if (!leftBounds) { - leftBounds = [0, 0, 0.5, 1]; - } - if (!rightBounds) { - rightBounds = [0.5, 0, 0.5, 1]; - } - this.viewportOffsetScale[0] = leftBounds[0]; - this.viewportOffsetScale[1] = leftBounds[1]; - this.viewportOffsetScale[2] = leftBounds[2]; - this.viewportOffsetScale[3] = leftBounds[3]; - this.viewportOffsetScale[4] = rightBounds[0]; - this.viewportOffsetScale[5] = rightBounds[1]; - this.viewportOffsetScale[6] = rightBounds[2]; - this.viewportOffsetScale[7] = rightBounds[3]; - }; - CardboardDistorter.prototype.submitFrame = function () { - var gl = this.gl; - var self = this; - var glState = []; - if (!this.dirtySubmitFrameBindings) { - glState.push(gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, gl.TEXTURE_BINDING_2D, gl.TEXTURE0); - } - glPreserveState(gl, glState, function (gl) { - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null); - var positionDivisor = 0; - var texCoordDivisor = 0; - if (self.instanceExt) { - positionDivisor = gl.getVertexAttrib(self.attribs.position, self.instanceExt.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); - texCoordDivisor = gl.getVertexAttrib(self.attribs.texCoord, self.instanceExt.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); - } - if (self.cullFace) { - self.realDisable.call(gl, gl.CULL_FACE); - } - if (self.depthTest) { - self.realDisable.call(gl, gl.DEPTH_TEST); - } - if (self.blend) { - self.realDisable.call(gl, gl.BLEND); - } - if (self.scissorTest) { - self.realDisable.call(gl, gl.SCISSOR_TEST); - } - if (self.stencilTest) { - self.realDisable.call(gl, gl.STENCIL_TEST); - } - self.realColorMask.call(gl, true, true, true, true); - self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - if (self.ctxAttribs.alpha || isIOS()) { - self.realClearColor.call(gl, 0, 0, 0, 1); - gl.clear(gl.COLOR_BUFFER_BIT); - } - gl.useProgram(self.program); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); - gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); - gl.enableVertexAttribArray(self.attribs.position); - gl.enableVertexAttribArray(self.attribs.texCoord); - gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0); - gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8); - if (self.instanceExt) { - if (positionDivisor != 0) { - self.instanceExt.vertexAttribDivisorANGLE(self.attribs.position, 0); - } - if (texCoordDivisor != 0) { - self.instanceExt.vertexAttribDivisorANGLE(self.attribs.texCoord, 0); - } - } - gl.activeTexture(gl.TEXTURE0); - gl.uniform1i(self.uniforms.diffuse, 0); - gl.bindTexture(gl.TEXTURE_2D, self.renderTarget); - gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale); - gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0); - if (self.cardboardUI) { - self.cardboardUI.renderNoState(); - } - self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer); - if (!self.ctxAttribs.preserveDrawingBuffer) { - self.realClearColor.call(gl, 0, 0, 0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - } - if (!self.dirtySubmitFrameBindings) { - self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer); - } - if (self.cullFace) { - self.realEnable.call(gl, gl.CULL_FACE); - } - if (self.depthTest) { - self.realEnable.call(gl, gl.DEPTH_TEST); - } - if (self.blend) { - self.realEnable.call(gl, gl.BLEND); - } - if (self.scissorTest) { - self.realEnable.call(gl, gl.SCISSOR_TEST); - } - if (self.stencilTest) { - self.realEnable.call(gl, gl.STENCIL_TEST); - } - self.realColorMask.apply(gl, self.colorMask); - self.realViewport.apply(gl, self.viewport); - if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) { - self.realClearColor.apply(gl, self.clearColor); - } - if (self.instanceExt) { - if (positionDivisor != 0) { - self.instanceExt.vertexAttribDivisorANGLE(self.attribs.position, positionDivisor); - } - if (texCoordDivisor != 0) { - self.instanceExt.vertexAttribDivisorANGLE(self.attribs.texCoord, texCoordDivisor); - } - } - }); - if (isIOS()) { - var canvas = gl.canvas; - if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) { - self.bufferWidth = canvas.width; - self.bufferHeight = canvas.height; - self.onResize(); - } - } - }; - CardboardDistorter.prototype.updateDeviceInfo = function (deviceInfo) { - var gl = this.gl; - var self = this; - var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING]; - glPreserveState(gl, glState, function (gl) { - var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo); - gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); - if (!self.indexCount) { - var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); - self.indexCount = indices.length; - } - }); - }; - CardboardDistorter.prototype.computeMeshVertices_ = function (width, height, deviceInfo) { - var vertices = new Float32Array(2 * width * height * 5); - var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); - var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); - var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); - var vidx = 0; - for (var e = 0; e < 2; e++) { - for (var j = 0; j < height; j++) { - for (var i = 0; i < width; i++, vidx++) { - var u = i / (width - 1); - var v = j / (height - 1); - var s = u; - var t = v; - var x = lerp(lensFrustum[0], lensFrustum[2], u); - var y = lerp(lensFrustum[3], lensFrustum[1], v); - var d = Math.sqrt(x * x + y * y); - var r = deviceInfo.distortion.distortInverse(d); - var p = x * r / d; - var q = y * r / d; - u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); - v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); - u = (viewport.x + u * viewport.width - 0.5) * 2.0; - v = (viewport.y + v * viewport.height - 0.5) * 2.0; - vertices[vidx * 5 + 0] = u; - vertices[vidx * 5 + 1] = v; - vertices[vidx * 5 + 2] = s; - vertices[vidx * 5 + 3] = t; - vertices[vidx * 5 + 4] = e; - } - } - var w = lensFrustum[2] - lensFrustum[0]; - lensFrustum[0] = -(w + lensFrustum[0]); - lensFrustum[2] = w - lensFrustum[2]; - w = noLensFrustum[2] - noLensFrustum[0]; - noLensFrustum[0] = -(w + noLensFrustum[0]); - noLensFrustum[2] = w - noLensFrustum[2]; - viewport.x = 1 - (viewport.x + viewport.width); - } - return vertices; - }; - CardboardDistorter.prototype.computeMeshIndices_ = function (width, height) { - var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); - var halfwidth = width / 2; - var halfheight = height / 2; - var vidx = 0; - var iidx = 0; - for (var e = 0; e < 2; e++) { - for (var j = 0; j < height; j++) { - for (var i = 0; i < width; i++, vidx++) { - if (i == 0 || j == 0) continue; - if (i <= halfwidth == j <= halfheight) { - indices[iidx++] = vidx; - indices[iidx++] = vidx - width - 1; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx - width - 1; - indices[iidx++] = vidx; - indices[iidx++] = vidx - 1; - } else { - indices[iidx++] = vidx - 1; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx - 1; - indices[iidx++] = vidx - width - 1; - } - } - } - } - return indices; - }; - CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function (proto, attrName) { - var descriptor = Object.getOwnPropertyDescriptor(proto, attrName); - if (descriptor.get === undefined || descriptor.set === undefined) { - descriptor.configurable = true; - descriptor.enumerable = true; - descriptor.get = function () { - return this.getAttribute(attrName); - }; - descriptor.set = function (val) { - this.setAttribute(attrName, val); - }; - } - return descriptor; - }; - var uiVS = ['attribute vec2 position;', 'uniform mat4 projectionMat;', 'void main() {', ' gl_Position = projectionMat * vec4( position, -1.0, 1.0 );', '}'].join('\n'); - var uiFS = ['precision mediump float;', 'uniform vec4 color;', 'void main() {', ' gl_FragColor = color;', '}'].join('\n'); - var DEG2RAD = Math.PI / 180.0; - var kAnglePerGearSection = 60; - var kOuterRimEndAngle = 12; - var kInnerRimBeginAngle = 20; - var kOuterRadius = 1; - var kMiddleRadius = 0.75; - var kInnerRadius = 0.3125; - var kCenterLineThicknessDp = 4; - var kButtonWidthDp = 28; - var kTouchSlopFactor = 1.5; - function CardboardUI(gl) { - this.gl = gl; - this.attribs = { - position: 0 - }; - this.program = linkProgram(gl, uiVS, uiFS, this.attribs); - this.uniforms = getProgramUniforms(gl, this.program); - this.vertexBuffer = gl.createBuffer(); - this.gearOffset = 0; - this.gearVertexCount = 0; - this.arrowOffset = 0; - this.arrowVertexCount = 0; - this.projMat = new Float32Array(16); - this.listener = null; - this.onResize(); - } - CardboardUI.prototype.destroy = function () { - var gl = this.gl; - if (this.listener) { - gl.canvas.removeEventListener('click', this.listener, false); - } - gl.deleteProgram(this.program); - gl.deleteBuffer(this.vertexBuffer); - }; - CardboardUI.prototype.listen = function (optionsCallback, backCallback) { - var canvas = this.gl.canvas; - this.listener = function (event) { - var midline = canvas.clientWidth / 2; - var buttonSize = kButtonWidthDp * kTouchSlopFactor; - if (event.clientX > midline - buttonSize && event.clientX < midline + buttonSize && event.clientY > canvas.clientHeight - buttonSize) { - optionsCallback(event); - } else if (event.clientX < buttonSize && event.clientY < buttonSize) { - backCallback(event); - } - }; - canvas.addEventListener('click', this.listener, false); - }; - CardboardUI.prototype.onResize = function () { - var gl = this.gl; - var self = this; - var glState = [gl.ARRAY_BUFFER_BINDING]; - glPreserveState(gl, glState, function (gl) { - var vertices = []; - var midline = gl.drawingBufferWidth / 2; - var physicalPixels = Math.max(screen.width, screen.height) * window.devicePixelRatio; - var scalingRatio = gl.drawingBufferWidth / physicalPixels; - var dps = scalingRatio * window.devicePixelRatio; - var lineWidth = kCenterLineThicknessDp * dps / 2; - var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps; - var buttonScale = kButtonWidthDp * dps / 2; - var buttonBorder = (kButtonWidthDp * kTouchSlopFactor - kButtonWidthDp) * dps; - vertices.push(midline - lineWidth, buttonSize); - vertices.push(midline - lineWidth, gl.drawingBufferHeight); - vertices.push(midline + lineWidth, buttonSize); - vertices.push(midline + lineWidth, gl.drawingBufferHeight); - self.gearOffset = vertices.length / 2; - function addGearSegment(theta, r) { - var angle = (90 - theta) * DEG2RAD; - var x = Math.cos(angle); - var y = Math.sin(angle); - vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale); - vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale); - } - for (var i = 0; i <= 6; i++) { - var segmentTheta = i * kAnglePerGearSection; - addGearSegment(segmentTheta, kOuterRadius); - addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius); - addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius); - addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius); - addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius); - } - self.gearVertexCount = vertices.length / 2 - self.gearOffset; - self.arrowOffset = vertices.length / 2; - function addArrowVertex(x, y) { - vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y); - } - var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD); - addArrowVertex(0, buttonScale); - addArrowVertex(buttonScale, 0); - addArrowVertex(buttonScale + angledLineWidth, angledLineWidth); - addArrowVertex(angledLineWidth, buttonScale + angledLineWidth); - addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); - addArrowVertex(0, buttonScale); - addArrowVertex(buttonScale, buttonScale * 2); - addArrowVertex(buttonScale + angledLineWidth, buttonScale * 2 - angledLineWidth); - addArrowVertex(angledLineWidth, buttonScale - angledLineWidth); - addArrowVertex(0, buttonScale); - addArrowVertex(angledLineWidth, buttonScale - lineWidth); - addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth); - addArrowVertex(angledLineWidth, buttonScale + lineWidth); - addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth); - self.arrowVertexCount = vertices.length / 2 - self.arrowOffset; - gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); - }); - }; - CardboardUI.prototype.render = function () { - var gl = this.gl; - var self = this; - var glState = [gl.CULL_FACE, gl.DEPTH_TEST, gl.BLEND, gl.SCISSOR_TEST, gl.STENCIL_TEST, gl.COLOR_WRITEMASK, gl.VIEWPORT, gl.CURRENT_PROGRAM, gl.ARRAY_BUFFER_BINDING]; - glPreserveState(gl, glState, function (gl) { - gl.disable(gl.CULL_FACE); - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - gl.disable(gl.SCISSOR_TEST); - gl.disable(gl.STENCIL_TEST); - gl.colorMask(true, true, true, true); - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - self.renderNoState(); - }); - }; - CardboardUI.prototype.renderNoState = function () { - var gl = this.gl; - gl.useProgram(this.program); - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.enableVertexAttribArray(this.attribs.position); - gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0); - gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0); - orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0); - gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat); - gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); - gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount); - gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount); - }; - function Distortion(coefficients) { - this.coefficients = coefficients; - } - Distortion.prototype.distortInverse = function (radius) { - var r0 = 0; - var r1 = 1; - var dr0 = radius - this.distort(r0); - while (Math.abs(r1 - r0) > 0.0001) { - var dr1 = radius - this.distort(r1); - var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0)); - r0 = r1; - r1 = r2; - dr0 = dr1; - } - return r1; - }; - Distortion.prototype.distort = function (radius) { - var r2 = radius * radius; - var ret = 0; - for (var i = 0; i < this.coefficients.length; i++) { - ret = r2 * (ret + this.coefficients[i]); - } - return (ret + 1) * radius; - }; - var degToRad = Math.PI / 180; - var radToDeg = 180 / Math.PI; - var Vector3 = function Vector3(x, y, z) { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - }; - Vector3.prototype = { - constructor: Vector3, - set: function set(x, y, z) { - this.x = x; - this.y = y; - this.z = z; - return this; - }, - copy: function copy(v) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - return this; - }, - length: function length() { - return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); - }, - normalize: function normalize() { - var scalar = this.length(); - if (scalar !== 0) { - var invScalar = 1 / scalar; - this.multiplyScalar(invScalar); - } else { - this.x = 0; - this.y = 0; - this.z = 0; - } - return this; - }, - multiplyScalar: function multiplyScalar(scalar) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - }, - applyQuaternion: function applyQuaternion(q) { - var x = this.x; - var y = this.y; - var z = this.z; - var qx = q.x; - var qy = q.y; - var qz = q.z; - var qw = q.w; - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = -qx * x - qy * y - qz * z; - this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; - this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; - this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; - return this; - }, - dot: function dot(v) { - return this.x * v.x + this.y * v.y + this.z * v.z; - }, - crossVectors: function crossVectors(a, b) { - var ax = a.x, - ay = a.y, - az = a.z; - var bx = b.x, - by = b.y, - bz = b.z; - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - return this; - } - }; - var Quaternion = function Quaternion(x, y, z, w) { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = w !== undefined ? w : 1; - }; - Quaternion.prototype = { - constructor: Quaternion, - set: function set(x, y, z, w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - return this; - }, - copy: function copy(quaternion) { - this.x = quaternion.x; - this.y = quaternion.y; - this.z = quaternion.z; - this.w = quaternion.w; - return this; - }, - setFromEulerXYZ: function setFromEulerXYZ(x, y, z) { - var c1 = Math.cos(x / 2); - var c2 = Math.cos(y / 2); - var c3 = Math.cos(z / 2); - var s1 = Math.sin(x / 2); - var s2 = Math.sin(y / 2); - var s3 = Math.sin(z / 2); - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - return this; - }, - setFromEulerYXZ: function setFromEulerYXZ(x, y, z) { - var c1 = Math.cos(x / 2); - var c2 = Math.cos(y / 2); - var c3 = Math.cos(z / 2); - var s1 = Math.sin(x / 2); - var s2 = Math.sin(y / 2); - var s3 = Math.sin(z / 2); - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - return this; - }, - setFromAxisAngle: function setFromAxisAngle(axis, angle) { - var halfAngle = angle / 2, - s = Math.sin(halfAngle); - this.x = axis.x * s; - this.y = axis.y * s; - this.z = axis.z * s; - this.w = Math.cos(halfAngle); - return this; - }, - multiply: function multiply(q) { - return this.multiplyQuaternions(this, q); - }, - multiplyQuaternions: function multiplyQuaternions(a, b) { - var qax = a.x, - qay = a.y, - qaz = a.z, - qaw = a.w; - var qbx = b.x, - qby = b.y, - qbz = b.z, - qbw = b.w; - this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - return this; - }, - inverse: function inverse() { - this.x *= -1; - this.y *= -1; - this.z *= -1; - this.normalize(); - return this; - }, - normalize: function normalize() { - var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); - if (l === 0) { - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 1; - } else { - l = 1 / l; - this.x = this.x * l; - this.y = this.y * l; - this.z = this.z * l; - this.w = this.w * l; - } - return this; - }, - slerp: function slerp(qb, t) { - if (t === 0) return this; - if (t === 1) return this.copy(qb); - var x = this.x, - y = this.y, - z = this.z, - w = this.w; - var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z; - if (cosHalfTheta < 0) { - this.w = -qb.w; - this.x = -qb.x; - this.y = -qb.y; - this.z = -qb.z; - cosHalfTheta = -cosHalfTheta; - } else { - this.copy(qb); - } - if (cosHalfTheta >= 1.0) { - this.w = w; - this.x = x; - this.y = y; - this.z = z; - return this; - } - var halfTheta = Math.acos(cosHalfTheta); - var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); - if (Math.abs(sinHalfTheta) < 0.001) { - this.w = 0.5 * (w + this.w); - this.x = 0.5 * (x + this.x); - this.y = 0.5 * (y + this.y); - this.z = 0.5 * (z + this.z); - return this; - } - var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, - ratioB = Math.sin(t * halfTheta) / sinHalfTheta; - this.w = w * ratioA + this.w * ratioB; - this.x = x * ratioA + this.x * ratioB; - this.y = y * ratioA + this.y * ratioB; - this.z = z * ratioA + this.z * ratioB; - return this; - }, - setFromUnitVectors: function () { - var v1, r; - var EPS = 0.000001; - return function (vFrom, vTo) { - if (v1 === undefined) v1 = new Vector3(); - r = vFrom.dot(vTo) + 1; - if (r < EPS) { - r = 0; - if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { - v1.set(-vFrom.y, vFrom.x, 0); - } else { - v1.set(0, -vFrom.z, vFrom.y); - } - } else { - v1.crossVectors(vFrom, vTo); - } - this.x = v1.x; - this.y = v1.y; - this.z = v1.z; - this.w = r; - this.normalize(); - return this; - }; - }() - }; - function Device(params) { - this.width = params.width || getScreenWidth(); - this.height = params.height || getScreenHeight(); - this.widthMeters = params.widthMeters; - this.heightMeters = params.heightMeters; - this.bevelMeters = params.bevelMeters; - } - var DEFAULT_ANDROID = new Device({ - widthMeters: 0.110, - heightMeters: 0.062, - bevelMeters: 0.004 - }); - var DEFAULT_IOS = new Device({ - widthMeters: 0.1038, - heightMeters: 0.0584, - bevelMeters: 0.004 - }); - var Viewers = { - CardboardV1: new CardboardViewer({ - id: 'CardboardV1', - label: 'Cardboard I/O 2014', - fov: 40, - interLensDistance: 0.060, - baselineLensDistance: 0.035, - screenLensDistance: 0.042, - distortionCoefficients: [0.441, 0.156], - inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139, -0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841, 0.0651772, -0.01488963, 0.001559834] - }), - CardboardV2: new CardboardViewer({ - id: 'CardboardV2', - label: 'Cardboard I/O 2015', - fov: 60, - interLensDistance: 0.064, - baselineLensDistance: 0.035, - screenLensDistance: 0.039, - distortionCoefficients: [0.34, 0.55], - inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051, 1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956, -9.904169E-4, 6.183535E-5, -1.6981803E-6] - }) - }; - function DeviceInfo(deviceParams, additionalViewers) { - this.viewer = Viewers.CardboardV2; - this.updateDeviceParams(deviceParams); - this.distortion = new Distortion(this.viewer.distortionCoefficients); - for (var i = 0; i < additionalViewers.length; i++) { - var viewer = additionalViewers[i]; - Viewers[viewer.id] = new CardboardViewer(viewer); - } - } - DeviceInfo.prototype.updateDeviceParams = function (deviceParams) { - this.device = this.determineDevice_(deviceParams) || this.device; - }; - DeviceInfo.prototype.getDevice = function () { - return this.device; - }; - DeviceInfo.prototype.setViewer = function (viewer) { - this.viewer = viewer; - this.distortion = new Distortion(this.viewer.distortionCoefficients); - }; - DeviceInfo.prototype.determineDevice_ = function (deviceParams) { - if (!deviceParams) { - if (isIOS()) { - console.warn('Using fallback iOS device measurements.'); - return DEFAULT_IOS; - } else { - console.warn('Using fallback Android device measurements.'); - return DEFAULT_ANDROID; - } - } - var METERS_PER_INCH = 0.0254; - var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi; - var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi; - var width = getScreenWidth(); - var height = getScreenHeight(); - return new Device({ - widthMeters: metersPerPixelX * width, - heightMeters: metersPerPixelY * height, - bevelMeters: deviceParams.bevelMm * 0.001 - }); - }; - DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function () { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - var eyeToScreenDistance = viewer.screenLensDistance; - var outerDist = (device.widthMeters - viewer.interLensDistance) / 2; - var innerDist = viewer.interLensDistance / 2; - var bottomDist = viewer.baselineLensDistance - device.bevelMeters; - var topDist = device.heightMeters - bottomDist; - var outerAngle = radToDeg * Math.atan(distortion.distort(outerDist / eyeToScreenDistance)); - var innerAngle = radToDeg * Math.atan(distortion.distort(innerDist / eyeToScreenDistance)); - var bottomAngle = radToDeg * Math.atan(distortion.distort(bottomDist / eyeToScreenDistance)); - var topAngle = radToDeg * Math.atan(distortion.distort(topDist / eyeToScreenDistance)); - return { - leftDegrees: Math.min(outerAngle, viewer.fov), - rightDegrees: Math.min(innerAngle, viewer.fov), - downDegrees: Math.min(bottomAngle, viewer.fov), - upDegrees: Math.min(topAngle, viewer.fov) - }; - }; - DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function () { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - var fovLeft = Math.tan(-degToRad * viewer.fov); - var fovTop = Math.tan(degToRad * viewer.fov); - var fovRight = Math.tan(degToRad * viewer.fov); - var fovBottom = Math.tan(-degToRad * viewer.fov); - var halfWidth = device.widthMeters / 4; - var halfHeight = device.heightMeters / 2; - var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight; - var centerX = viewer.interLensDistance / 2 - halfWidth; - var centerY = -verticalLensOffset; - var centerZ = viewer.screenLensDistance; - var screenLeft = distortion.distort((centerX - halfWidth) / centerZ); - var screenTop = distortion.distort((centerY + halfHeight) / centerZ); - var screenRight = distortion.distort((centerX + halfWidth) / centerZ); - var screenBottom = distortion.distort((centerY - halfHeight) / centerZ); - var result = new Float32Array(4); - result[0] = Math.max(fovLeft, screenLeft); - result[1] = Math.min(fovTop, screenTop); - result[2] = Math.min(fovRight, screenRight); - result[3] = Math.max(fovBottom, screenBottom); - return result; - }; - DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function () { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - var result = new Float32Array(4); - var fovLeft = distortion.distortInverse(Math.tan(-degToRad * viewer.fov)); - var fovTop = distortion.distortInverse(Math.tan(degToRad * viewer.fov)); - var fovRight = distortion.distortInverse(Math.tan(degToRad * viewer.fov)); - var fovBottom = distortion.distortInverse(Math.tan(-degToRad * viewer.fov)); - var halfWidth = device.widthMeters / 4; - var halfHeight = device.heightMeters / 2; - var verticalLensOffset = viewer.baselineLensDistance - device.bevelMeters - halfHeight; - var centerX = viewer.interLensDistance / 2 - halfWidth; - var centerY = -verticalLensOffset; - var centerZ = viewer.screenLensDistance; - var screenLeft = (centerX - halfWidth) / centerZ; - var screenTop = (centerY + halfHeight) / centerZ; - var screenRight = (centerX + halfWidth) / centerZ; - var screenBottom = (centerY - halfHeight) / centerZ; - result[0] = Math.max(fovLeft, screenLeft); - result[1] = Math.min(fovTop, screenTop); - result[2] = Math.min(fovRight, screenRight); - result[3] = Math.max(fovBottom, screenBottom); - return result; - }; - DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function (undistortedFrustum) { - var viewer = this.viewer; - var device = this.device; - var dist = viewer.screenLensDistance; - var eyeX = (device.widthMeters - viewer.interLensDistance) / 2; - var eyeY = viewer.baselineLensDistance - device.bevelMeters; - var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters; - var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters; - var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters; - var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters; - return { - x: left, - y: bottom, - width: right - left, - height: top - bottom - }; - }; - DeviceInfo.prototype.getFieldOfViewLeftEye = function (opt_isUndistorted) { - return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() : this.getDistortedFieldOfViewLeftEye(); - }; - DeviceInfo.prototype.getFieldOfViewRightEye = function (opt_isUndistorted) { - var fov = this.getFieldOfViewLeftEye(opt_isUndistorted); - return { - leftDegrees: fov.rightDegrees, - rightDegrees: fov.leftDegrees, - upDegrees: fov.upDegrees, - downDegrees: fov.downDegrees - }; - }; - DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function () { - var p = this.getUndistortedParams_(); - return { - leftDegrees: radToDeg * Math.atan(p.outerDist), - rightDegrees: radToDeg * Math.atan(p.innerDist), - downDegrees: radToDeg * Math.atan(p.bottomDist), - upDegrees: radToDeg * Math.atan(p.topDist) - }; - }; - DeviceInfo.prototype.getUndistortedViewportLeftEye = function () { - var p = this.getUndistortedParams_(); - var viewer = this.viewer; - var device = this.device; - var eyeToScreenDistance = viewer.screenLensDistance; - var screenWidth = device.widthMeters / eyeToScreenDistance; - var screenHeight = device.heightMeters / eyeToScreenDistance; - var xPxPerTanAngle = device.width / screenWidth; - var yPxPerTanAngle = device.height / screenHeight; - var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle); - var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle); - return { - x: x, - y: y, - width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x, - height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y - }; - }; - DeviceInfo.prototype.getUndistortedParams_ = function () { - var viewer = this.viewer; - var device = this.device; - var distortion = this.distortion; - var eyeToScreenDistance = viewer.screenLensDistance; - var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance; - var screenWidth = device.widthMeters / eyeToScreenDistance; - var screenHeight = device.heightMeters / eyeToScreenDistance; - var eyePosX = screenWidth / 2 - halfLensDistance; - var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance; - var maxFov = viewer.fov; - var viewerMax = distortion.distortInverse(Math.tan(degToRad * maxFov)); - var outerDist = Math.min(eyePosX, viewerMax); - var innerDist = Math.min(halfLensDistance, viewerMax); - var bottomDist = Math.min(eyePosY, viewerMax); - var topDist = Math.min(screenHeight - eyePosY, viewerMax); - return { - outerDist: outerDist, - innerDist: innerDist, - topDist: topDist, - bottomDist: bottomDist, - eyePosX: eyePosX, - eyePosY: eyePosY - }; - }; - function CardboardViewer(params) { - this.id = params.id; - this.label = params.label; - this.fov = params.fov; - this.interLensDistance = params.interLensDistance; - this.baselineLensDistance = params.baselineLensDistance; - this.screenLensDistance = params.screenLensDistance; - this.distortionCoefficients = params.distortionCoefficients; - this.inverseCoefficients = params.inverseCoefficients; - } - DeviceInfo.Viewers = Viewers; - var format = 1; - var last_updated = "2019-11-09T17:36:14Z"; - var devices = [{ - "type": "android", - "rules": [{ - "mdmh": "asus/*/Nexus 7/*" - }, { - "ua": "Nexus 7" - }], - "dpi": [320.8, 323], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "asus/*/ASUS_X00PD/*" - }, { - "ua": "ASUS_X00PD" - }], - "dpi": 245, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "asus/*/ASUS_X008D/*" - }, { - "ua": "ASUS_X008D" - }], - "dpi": 282, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "asus/*/ASUS_Z00AD/*" - }, { - "ua": "ASUS_Z00AD" - }], - "dpi": [403, 404.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Google/*/Pixel 2 XL/*" - }, { - "ua": "Pixel 2 XL" - }], - "dpi": 537.9, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Google/*/Pixel 3 XL/*" - }, { - "ua": "Pixel 3 XL" - }], - "dpi": [558.5, 553.8], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Google/*/Pixel XL/*" - }, { - "ua": "Pixel XL" - }], - "dpi": [537.9, 533], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Google/*/Pixel 3/*" - }, { - "ua": "Pixel 3" - }], - "dpi": 442.4, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Google/*/Pixel 2/*" - }, { - "ua": "Pixel 2" - }], - "dpi": 441, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "Google/*/Pixel/*" - }, { - "ua": "Pixel" - }], - "dpi": [432.6, 436.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "HTC/*/HTC6435LVW/*" - }, { - "ua": "HTC6435LVW" - }], - "dpi": [449.7, 443.3], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "HTC/*/HTC One XL/*" - }, { - "ua": "HTC One XL" - }], - "dpi": [315.3, 314.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "htc/*/Nexus 9/*" - }, { - "ua": "Nexus 9" - }], - "dpi": 289, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "HTC/*/HTC One M9/*" - }, { - "ua": "HTC One M9" - }], - "dpi": [442.5, 443.3], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "HTC/*/HTC One_M8/*" - }, { - "ua": "HTC One_M8" - }], - "dpi": [449.7, 447.4], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "HTC/*/HTC One/*" - }, { - "ua": "HTC One" - }], - "dpi": 472.8, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Huawei/*/Nexus 6P/*" - }, { - "ua": "Nexus 6P" - }], - "dpi": [515.1, 518], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Huawei/*/BLN-L24/*" - }, { - "ua": "HONORBLN-L24" - }], - "dpi": 480, - "bw": 4, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "Huawei/*/BKL-L09/*" - }, { - "ua": "BKL-L09" - }], - "dpi": 403, - "bw": 3.47, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "LENOVO/*/Lenovo PB2-690Y/*" - }, { - "ua": "Lenovo PB2-690Y" - }], - "dpi": [457.2, 454.713], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/Nexus 5X/*" - }, { - "ua": "Nexus 5X" - }], - "dpi": [422, 419.9], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/LGMS345/*" - }, { - "ua": "LGMS345" - }], - "dpi": [221.7, 219.1], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/LG-D800/*" - }, { - "ua": "LG-D800" - }], - "dpi": [422, 424.1], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/LG-D850/*" - }, { - "ua": "LG-D850" - }], - "dpi": [537.9, 541.9], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/VS985 4G/*" - }, { - "ua": "VS985 4G" - }], - "dpi": [537.9, 535.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/Nexus 5/*" - }, { - "ua": "Nexus 5 B" - }], - "dpi": [442.4, 444.8], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/Nexus 4/*" - }, { - "ua": "Nexus 4" - }], - "dpi": [319.8, 318.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/LG-P769/*" - }, { - "ua": "LG-P769" - }], - "dpi": [240.6, 247.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/LGMS323/*" - }, { - "ua": "LGMS323" - }], - "dpi": [206.6, 204.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "LGE/*/LGLS996/*" - }, { - "ua": "LGLS996" - }], - "dpi": [403.4, 401.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Micromax/*/4560MMX/*" - }, { - "ua": "4560MMX" - }], - "dpi": [240, 219.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Micromax/*/A250/*" - }, { - "ua": "Micromax A250" - }], - "dpi": [480, 446.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Micromax/*/Micromax AQ4501/*" - }, { - "ua": "Micromax AQ4501" - }], - "dpi": 240, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/G5/*" - }, { - "ua": "Moto G (5) Plus" - }], - "dpi": [403.4, 403], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/DROID RAZR/*" - }, { - "ua": "DROID RAZR" - }], - "dpi": [368.1, 256.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT830C/*" - }, { - "ua": "XT830C" - }], - "dpi": [254, 255.9], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1021/*" - }, { - "ua": "XT1021" - }], - "dpi": [254, 256.7], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1023/*" - }, { - "ua": "XT1023" - }], - "dpi": [254, 256.7], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1028/*" - }, { - "ua": "XT1028" - }], - "dpi": [326.6, 327.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1034/*" - }, { - "ua": "XT1034" - }], - "dpi": [326.6, 328.4], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1053/*" - }, { - "ua": "XT1053" - }], - "dpi": [315.3, 316.1], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1562/*" - }, { - "ua": "XT1562" - }], - "dpi": [403.4, 402.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/Nexus 6/*" - }, { - "ua": "Nexus 6 B" - }], - "dpi": [494.3, 489.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1063/*" - }, { - "ua": "XT1063" - }], - "dpi": [295, 296.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1064/*" - }, { - "ua": "XT1064" - }], - "dpi": [295, 295.6], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1092/*" - }, { - "ua": "XT1092" - }], - "dpi": [422, 424.1], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/XT1095/*" - }, { - "ua": "XT1095" - }], - "dpi": [422, 423.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "motorola/*/G4/*" - }, { - "ua": "Moto G (4)" - }], - "dpi": 401, - "bw": 4, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/A0001/*" - }, { - "ua": "A0001" - }], - "dpi": [403.4, 401], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE E1001/*" - }, { - "ua": "ONE E1001" - }], - "dpi": [442.4, 441.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE E1003/*" - }, { - "ua": "ONE E1003" - }], - "dpi": [442.4, 441.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE E1005/*" - }, { - "ua": "ONE E1005" - }], - "dpi": [442.4, 441.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE A2001/*" - }, { - "ua": "ONE A2001" - }], - "dpi": [391.9, 405.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE A2003/*" - }, { - "ua": "ONE A2003" - }], - "dpi": [391.9, 405.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE A2005/*" - }, { - "ua": "ONE A2005" - }], - "dpi": [391.9, 405.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A3000/*" - }, { - "ua": "ONEPLUS A3000" - }], - "dpi": 401, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A3003/*" - }, { - "ua": "ONEPLUS A3003" - }], - "dpi": 401, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A3010/*" - }, { - "ua": "ONEPLUS A3010" - }], - "dpi": 401, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A5000/*" - }, { - "ua": "ONEPLUS A5000 " - }], - "dpi": [403.411, 399.737], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONE A5010/*" - }, { - "ua": "ONEPLUS A5010" - }], - "dpi": [403, 400], - "bw": 2, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A6000/*" - }, { - "ua": "ONEPLUS A6000" - }], - "dpi": 401, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A6003/*" - }, { - "ua": "ONEPLUS A6003" - }], - "dpi": 401, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A6010/*" - }, { - "ua": "ONEPLUS A6010" - }], - "dpi": 401, - "bw": 2, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OnePlus/*/ONEPLUS A6013/*" - }, { - "ua": "ONEPLUS A6013" - }], - "dpi": 401, - "bw": 2, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "OPPO/*/X909/*" - }, { - "ua": "X909" - }], - "dpi": [442.4, 444.1], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9082/*" - }, { - "ua": "GT-I9082" - }], - "dpi": [184.7, 185.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G360P/*" - }, { - "ua": "SM-G360P" - }], - "dpi": [196.7, 205.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/Nexus S/*" - }, { - "ua": "Nexus S" - }], - "dpi": [234.5, 229.8], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9300/*" - }, { - "ua": "GT-I9300" - }], - "dpi": [304.8, 303.9], - "bw": 5, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-T230NU/*" - }, { - "ua": "SM-T230NU" - }], - "dpi": 216, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SGH-T399/*" - }, { - "ua": "SGH-T399" - }], - "dpi": [217.7, 231.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SGH-M919/*" - }, { - "ua": "SGH-M919" - }], - "dpi": [440.8, 437.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-N9005/*" - }, { - "ua": "SM-N9005" - }], - "dpi": [386.4, 387], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" - }, { - "ua": "SAMSUNG-SM-N900A" - }], - "dpi": [386.4, 387.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9500/*" - }, { - "ua": "GT-I9500" - }], - "dpi": [442.5, 443.3], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9505/*" - }, { - "ua": "GT-I9505" - }], - "dpi": 439.4, - "bw": 4, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G900F/*" - }, { - "ua": "SM-G900F" - }], - "dpi": [415.6, 431.6], - "bw": 5, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G900M/*" - }, { - "ua": "SM-G900M" - }], - "dpi": [415.6, 431.6], - "bw": 5, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G800F/*" - }, { - "ua": "SM-G800F" - }], - "dpi": 326.8, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G906S/*" - }, { - "ua": "SM-G906S" - }], - "dpi": [562.7, 572.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9300/*" - }, { - "ua": "GT-I9300" - }], - "dpi": [306.7, 304.8], - "bw": 5, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-T535/*" - }, { - "ua": "SM-T535" - }], - "dpi": [142.6, 136.4], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-N920C/*" - }, { - "ua": "SM-N920C" - }], - "dpi": [515.1, 518.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-N920P/*" - }, { - "ua": "SM-N920P" - }], - "dpi": [386.3655, 390.144], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-N920W8/*" - }, { - "ua": "SM-N920W8" - }], - "dpi": [515.1, 518.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9300I/*" - }, { - "ua": "GT-I9300I" - }], - "dpi": [304.8, 305.8], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-I9195/*" - }, { - "ua": "GT-I9195" - }], - "dpi": [249.4, 256.7], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SPH-L520/*" - }, { - "ua": "SPH-L520" - }], - "dpi": [249.4, 255.9], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" - }, { - "ua": "SAMSUNG-SGH-I717" - }], - "dpi": 285.8, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SPH-D710/*" - }, { - "ua": "SPH-D710" - }], - "dpi": [217.7, 204.2], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/GT-N7100/*" - }, { - "ua": "GT-N7100" - }], - "dpi": 265.1, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SCH-I605/*" - }, { - "ua": "SCH-I605" - }], - "dpi": 265.1, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/Galaxy Nexus/*" - }, { - "ua": "Galaxy Nexus" - }], - "dpi": [315.3, 314.2], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-N910H/*" - }, { - "ua": "SM-N910H" - }], - "dpi": [515.1, 518], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-N910C/*" - }, { - "ua": "SM-N910C" - }], - "dpi": [515.2, 520.2], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G130M/*" - }, { - "ua": "SM-G130M" - }], - "dpi": [165.9, 164.8], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G928I/*" - }, { - "ua": "SM-G928I" - }], - "dpi": [515.1, 518.4], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G920F/*" - }, { - "ua": "SM-G920F" - }], - "dpi": 580.6, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G920P/*" - }, { - "ua": "SM-G920P" - }], - "dpi": [522.5, 577], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G925F/*" - }, { - "ua": "SM-G925F" - }], - "dpi": 580.6, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G925V/*" - }, { - "ua": "SM-G925V" - }], - "dpi": [522.5, 576.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G930F/*" - }, { - "ua": "SM-G930F" - }], - "dpi": 576.6, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G935F/*" - }, { - "ua": "SM-G935F" - }], - "dpi": 533, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G950F/*" - }, { - "ua": "SM-G950F" - }], - "dpi": [562.707, 565.293], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G955U/*" - }, { - "ua": "SM-G955U" - }], - "dpi": [522.514, 525.762], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G955F/*" - }, { - "ua": "SM-G955F" - }], - "dpi": [522.514, 525.762], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G960F/*" - }, { - "ua": "SM-G960F" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G9600/*" - }, { - "ua": "SM-G9600" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G960T/*" - }, { - "ua": "SM-G960T" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G960N/*" - }, { - "ua": "SM-G960N" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G960U/*" - }, { - "ua": "SM-G960U" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G9608/*" - }, { - "ua": "SM-G9608" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G960FD/*" - }, { - "ua": "SM-G960FD" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G960W/*" - }, { - "ua": "SM-G960W" - }], - "dpi": [569.575, 571.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G965F/*" - }, { - "ua": "SM-G965F" - }], - "dpi": 529, - "bw": 2, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Sony/*/C6903/*" - }, { - "ua": "C6903" - }], - "dpi": [442.5, 443.3], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "Sony/*/D6653/*" - }, { - "ua": "D6653" - }], - "dpi": [428.6, 427.6], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Sony/*/E6653/*" - }, { - "ua": "E6653" - }], - "dpi": [428.6, 425.7], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Sony/*/E6853/*" - }, { - "ua": "E6853" - }], - "dpi": [403.4, 401.9], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Sony/*/SGP321/*" - }, { - "ua": "SGP321" - }], - "dpi": [224.7, 224.1], - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" - }, { - "ua": "ALCATEL ONE TOUCH Fierce" - }], - "dpi": [240, 247.5], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "THL/*/thl 5000/*" - }, { - "ua": "thl 5000" - }], - "dpi": [480, 443.3], - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Fly/*/IQ4412/*" - }, { - "ua": "IQ4412" - }], - "dpi": 307.9, - "bw": 3, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "ZTE/*/ZTE Blade L2/*" - }, { - "ua": "ZTE Blade L2" - }], - "dpi": 240, - "bw": 3, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "BENEVE/*/VR518/*" - }, { - "ua": "VR518" - }], - "dpi": 480, - "bw": 3, - "ac": 500 - }, { - "type": "ios", - "rules": [{ - "res": [640, 960] - }], - "dpi": [325.1, 328.4], - "bw": 4, - "ac": 1000 - }, { - "type": "ios", - "rules": [{ - "res": [640, 1136] - }], - "dpi": [317.1, 320.2], - "bw": 3, - "ac": 1000 - }, { - "type": "ios", - "rules": [{ - "res": [750, 1334] - }], - "dpi": 326.4, - "bw": 4, - "ac": 1000 - }, { - "type": "ios", - "rules": [{ - "res": [1242, 2208] - }], - "dpi": [453.6, 458.4], - "bw": 4, - "ac": 1000 - }, { - "type": "ios", - "rules": [{ - "res": [1125, 2001] - }], - "dpi": [410.9, 415.4], - "bw": 4, - "ac": 1000 - }, { - "type": "ios", - "rules": [{ - "res": [1125, 2436] - }], - "dpi": 458, - "bw": 4, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "Huawei/*/EML-L29/*" - }, { - "ua": "EML-L29" - }], - "dpi": 428, - "bw": 3.45, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "Nokia/*/Nokia 7.1/*" - }, { - "ua": "Nokia 7.1" - }], - "dpi": [432, 431.9], - "bw": 3, - "ac": 500 - }, { - "type": "ios", - "rules": [{ - "res": [1242, 2688] - }], - "dpi": 458, - "bw": 4, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G570M/*" - }, { - "ua": "SM-G570M" - }], - "dpi": 320, - "bw": 3.684, - "ac": 1000 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G970F/*" - }, { - "ua": "SM-G970F" - }], - "dpi": 438, - "bw": 2.281, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G973F/*" - }, { - "ua": "SM-G973F" - }], - "dpi": 550, - "bw": 2.002, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G975F/*" - }, { - "ua": "SM-G975F" - }], - "dpi": 522, - "bw": 2.054, - "ac": 500 - }, { - "type": "android", - "rules": [{ - "mdmh": "samsung/*/SM-G977F/*" - }, { - "ua": "SM-G977F" - }], - "dpi": 505, - "bw": 2.334, - "ac": 500 - }, { - "type": "ios", - "rules": [{ - "res": [828, 1792] - }], - "dpi": 326, - "bw": 5, - "ac": 500 - }]; - var DPDB_CACHE = { - format: format, - last_updated: last_updated, - devices: devices - }; - function Dpdb(url, onDeviceParamsUpdated) { - this.dpdb = DPDB_CACHE; - this.recalculateDeviceParams_(); - if (url) { - this.onDeviceParamsUpdated = onDeviceParamsUpdated; - var xhr = new XMLHttpRequest(); - var obj = this; - xhr.open('GET', url, true); - xhr.addEventListener('load', function () { - obj.loading = false; - if (xhr.status >= 200 && xhr.status <= 299) { - obj.dpdb = JSON.parse(xhr.response); - obj.recalculateDeviceParams_(); - } else { - console.error('Error loading online DPDB!'); - } - }); - xhr.send(); - } - } - Dpdb.prototype.getDeviceParams = function () { - return this.deviceParams; - }; - Dpdb.prototype.recalculateDeviceParams_ = function () { - var newDeviceParams = this.calcDeviceParams_(); - if (newDeviceParams) { - this.deviceParams = newDeviceParams; - if (this.onDeviceParamsUpdated) { - this.onDeviceParamsUpdated(this.deviceParams); - } - } else { - console.error('Failed to recalculate device parameters.'); - } - }; - Dpdb.prototype.calcDeviceParams_ = function () { - var db = this.dpdb; - if (!db) { - console.error('DPDB not available.'); - return null; - } - if (db.format != 1) { - console.error('DPDB has unexpected format version.'); - return null; - } - if (!db.devices || !db.devices.length) { - console.error('DPDB does not have a devices section.'); - return null; - } - var userAgent = navigator.userAgent || navigator.vendor || window.opera; - var width = getScreenWidth(); - var height = getScreenHeight(); - if (!db.devices) { - console.error('DPDB has no devices section.'); - return null; - } - for (var i = 0; i < db.devices.length; i++) { - var device = db.devices[i]; - if (!device.rules) { - console.warn('Device[' + i + '] has no rules section.'); - continue; - } - if (device.type != 'ios' && device.type != 'android') { - console.warn('Device[' + i + '] has invalid type.'); - continue; - } - if (isIOS() != (device.type == 'ios')) continue; - var matched = false; - for (var j = 0; j < device.rules.length; j++) { - var rule = device.rules[j]; - if (this.ruleMatches_(rule, userAgent, width, height)) { - matched = true; - break; - } - } - if (!matched) continue; - var xdpi = device.dpi[0] || device.dpi; - var ydpi = device.dpi[1] || device.dpi; - return new DeviceParams({ - xdpi: xdpi, - ydpi: ydpi, - bevelMm: device.bw - }); - } - console.warn('No DPDB device match.'); - return null; - }; - Dpdb.prototype.ruleMatches_ = function (rule, ua, screenWidth, screenHeight) { - if (!rule.ua && !rule.res) return false; - if (rule.ua && rule.ua.substring(0, 2) === 'SM') rule.ua = rule.ua.substring(0, 7); - if (rule.ua && ua.indexOf(rule.ua) < 0) return false; - if (rule.res) { - if (!rule.res[0] || !rule.res[1]) return false; - var resX = rule.res[0]; - var resY = rule.res[1]; - if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) || Math.max(screenWidth, screenHeight) != Math.max(resX, resY)) { - return false; - } - } - return true; - }; - function DeviceParams(params) { - this.xdpi = params.xdpi; - this.ydpi = params.ydpi; - this.bevelMm = params.bevelMm; - } - function SensorSample(sample, timestampS) { - this.set(sample, timestampS); - } - SensorSample.prototype.set = function (sample, timestampS) { - this.sample = sample; - this.timestampS = timestampS; - }; - SensorSample.prototype.copy = function (sensorSample) { - this.set(sensorSample.sample, sensorSample.timestampS); - }; - function ComplementaryFilter(kFilter, isDebug) { - this.kFilter = kFilter; - this.isDebug = isDebug; - this.currentAccelMeasurement = new SensorSample(); - this.currentGyroMeasurement = new SensorSample(); - this.previousGyroMeasurement = new SensorSample(); - if (isIOS()) { - this.filterQ = new Quaternion(-1, 0, 0, 1); - } else { - this.filterQ = new Quaternion(1, 0, 0, 1); - } - this.previousFilterQ = new Quaternion(); - this.previousFilterQ.copy(this.filterQ); - this.accelQ = new Quaternion(); - this.isOrientationInitialized = false; - this.estimatedGravity = new Vector3(); - this.measuredGravity = new Vector3(); - this.gyroIntegralQ = new Quaternion(); - } - ComplementaryFilter.prototype.addAccelMeasurement = function (vector, timestampS) { - this.currentAccelMeasurement.set(vector, timestampS); - }; - ComplementaryFilter.prototype.addGyroMeasurement = function (vector, timestampS) { - this.currentGyroMeasurement.set(vector, timestampS); - var deltaT = timestampS - this.previousGyroMeasurement.timestampS; - if (isTimestampDeltaValid(deltaT)) { - this.run_(); - } - this.previousGyroMeasurement.copy(this.currentGyroMeasurement); - }; - ComplementaryFilter.prototype.run_ = function () { - if (!this.isOrientationInitialized) { - this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample); - this.previousFilterQ.copy(this.accelQ); - this.isOrientationInitialized = true; - return; - } - var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS; - var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT); - this.gyroIntegralQ.multiply(gyroDeltaQ); - this.filterQ.copy(this.previousFilterQ); - this.filterQ.multiply(gyroDeltaQ); - var invFilterQ = new Quaternion(); - invFilterQ.copy(this.filterQ); - invFilterQ.inverse(); - this.estimatedGravity.set(0, 0, -1); - this.estimatedGravity.applyQuaternion(invFilterQ); - this.estimatedGravity.normalize(); - this.measuredGravity.copy(this.currentAccelMeasurement.sample); - this.measuredGravity.normalize(); - var deltaQ = new Quaternion(); - deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity); - deltaQ.inverse(); - if (this.isDebug) { - console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)', radToDeg * getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1)); - } - var targetQ = new Quaternion(); - targetQ.copy(this.filterQ); - targetQ.multiply(deltaQ); - this.filterQ.slerp(targetQ, 1 - this.kFilter); - this.previousFilterQ.copy(this.filterQ); - }; - ComplementaryFilter.prototype.getOrientation = function () { - return this.filterQ; - }; - ComplementaryFilter.prototype.accelToQuaternion_ = function (accel) { - var normAccel = new Vector3(); - normAccel.copy(accel); - normAccel.normalize(); - var quat = new Quaternion(); - quat.setFromUnitVectors(new Vector3(0, 0, -1), normAccel); - quat.inverse(); - return quat; - }; - ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function (gyro, dt) { - var quat = new Quaternion(); - var axis = new Vector3(); - axis.copy(gyro); - axis.normalize(); - quat.setFromAxisAngle(axis, gyro.length() * dt); - return quat; - }; - function PosePredictor(predictionTimeS, isDebug) { - this.predictionTimeS = predictionTimeS; - this.isDebug = isDebug; - this.previousQ = new Quaternion(); - this.previousTimestampS = null; - this.deltaQ = new Quaternion(); - this.outQ = new Quaternion(); - } - PosePredictor.prototype.getPrediction = function (currentQ, gyro, timestampS) { - if (!this.previousTimestampS) { - this.previousQ.copy(currentQ); - this.previousTimestampS = timestampS; - return currentQ; - } - var axis = new Vector3(); - axis.copy(gyro); - axis.normalize(); - var angularSpeed = gyro.length(); - if (angularSpeed < degToRad * 20) { - if (this.isDebug) { - console.log('Moving slowly, at %s deg/s: no prediction', (radToDeg * angularSpeed).toFixed(1)); - } - this.outQ.copy(currentQ); - this.previousQ.copy(currentQ); - return this.outQ; - } - var predictAngle = angularSpeed * this.predictionTimeS; - this.deltaQ.setFromAxisAngle(axis, predictAngle); - this.outQ.copy(this.previousQ); - this.outQ.multiply(this.deltaQ); - this.previousQ.copy(currentQ); - this.previousTimestampS = timestampS; - return this.outQ; - }; - function FusionPoseSensor(kFilter, predictionTime, yawOnly, isDebug) { - this.yawOnly = yawOnly; - this.accelerometer = new Vector3(); - this.gyroscope = new Vector3(); - this.filter = new ComplementaryFilter(kFilter, isDebug); - this.posePredictor = new PosePredictor(predictionTime, isDebug); - this.isFirefoxAndroid = isFirefoxAndroid(); - this.isIOS = isIOS(); - var chromeVersion = getChromeVersion(); - this.isDeviceMotionInRadians = !this.isIOS && chromeVersion && chromeVersion < 66; - this.isWithoutDeviceMotion = isChromeWithoutDeviceMotion() || isSafariWithoutDeviceMotion(); - this.filterToWorldQ = new Quaternion(); - if (isIOS()) { - this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); - } else { - this.filterToWorldQ.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); - } - this.inverseWorldToScreenQ = new Quaternion(); - this.worldToScreenQ = new Quaternion(); - this.originalPoseAdjustQ = new Quaternion(); - this.originalPoseAdjustQ.setFromAxisAngle(new Vector3(0, 0, 1), -window.orientation * Math.PI / 180); - this.setScreenTransform_(); - if (isLandscapeMode()) { - this.filterToWorldQ.multiply(this.inverseWorldToScreenQ); - } - this.resetQ = new Quaternion(); - this.orientationOut_ = new Float32Array(4); - this.start(); - } - FusionPoseSensor.prototype.getPosition = function () { - return null; - }; - FusionPoseSensor.prototype.getOrientation = function () { - var orientation = void 0; - if (this.isWithoutDeviceMotion && this._deviceOrientationQ) { - this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () { - var z = new Quaternion().setFromAxisAngle(new Vector3(0, 0, -1), 0); - var y = new Quaternion(); - if (window.orientation === -90) { - y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / -2); - } else { - y.setFromAxisAngle(new Vector3(0, 1, 0), Math.PI / 2); - } - return z.multiply(y); - }(); - this.deviceOrientationFilterToWorldQ = this.deviceOrientationFilterToWorldQ || function () { - var q = new Quaternion(); - q.setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); - return q; - }(); - orientation = this._deviceOrientationQ; - var out = new Quaternion(); - out.copy(orientation); - out.multiply(this.deviceOrientationFilterToWorldQ); - out.multiply(this.resetQ); - out.multiply(this.worldToScreenQ); - out.multiplyQuaternions(this.deviceOrientationFixQ, out); - if (this.yawOnly) { - out.x = 0; - out.z = 0; - out.normalize(); - } - this.orientationOut_[0] = out.x; - this.orientationOut_[1] = out.y; - this.orientationOut_[2] = out.z; - this.orientationOut_[3] = out.w; - return this.orientationOut_; - } else { - var filterOrientation = this.filter.getOrientation(); - orientation = this.posePredictor.getPrediction(filterOrientation, this.gyroscope, this.previousTimestampS); - } - var out = new Quaternion(); - out.copy(this.filterToWorldQ); - out.multiply(this.resetQ); - out.multiply(orientation); - out.multiply(this.worldToScreenQ); - if (this.yawOnly) { - out.x = 0; - out.z = 0; - out.normalize(); - } - this.orientationOut_[0] = out.x; - this.orientationOut_[1] = out.y; - this.orientationOut_[2] = out.z; - this.orientationOut_[3] = out.w; - return this.orientationOut_; - }; - FusionPoseSensor.prototype.resetPose = function () { - this.resetQ.copy(this.filter.getOrientation()); - this.resetQ.x = 0; - this.resetQ.y = 0; - this.resetQ.z *= -1; - this.resetQ.normalize(); - if (isLandscapeMode()) { - this.resetQ.multiply(this.inverseWorldToScreenQ); - } - this.resetQ.multiply(this.originalPoseAdjustQ); - }; - FusionPoseSensor.prototype.onDeviceOrientation_ = function (e) { - this._deviceOrientationQ = this._deviceOrientationQ || new Quaternion(); - var alpha = e.alpha, - beta = e.beta, - gamma = e.gamma; - alpha = (alpha || 0) * Math.PI / 180; - beta = (beta || 0) * Math.PI / 180; - gamma = (gamma || 0) * Math.PI / 180; - this._deviceOrientationQ.setFromEulerYXZ(beta, alpha, -gamma); - }; - FusionPoseSensor.prototype.onDeviceMotion_ = function (deviceMotion) { - this.updateDeviceMotion_(deviceMotion); - }; - FusionPoseSensor.prototype.updateDeviceMotion_ = function (deviceMotion) { - var accGravity = deviceMotion.accelerationIncludingGravity; - var rotRate = deviceMotion.rotationRate; - var timestampS = deviceMotion.timeStamp / 1000; - var deltaS = timestampS - this.previousTimestampS; - if (deltaS < 0) { - warnOnce('fusion-pose-sensor:invalid:non-monotonic', 'Invalid timestamps detected: non-monotonic timestamp from devicemotion'); - this.previousTimestampS = timestampS; - return; - } else if (deltaS <= MIN_TIMESTEP || deltaS > MAX_TIMESTEP) { - warnOnce('fusion-pose-sensor:invalid:outside-threshold', 'Invalid timestamps detected: Timestamp from devicemotion outside expected range.'); - this.previousTimestampS = timestampS; - return; - } - this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z); - if (rotRate) { - if (isR7()) { - this.gyroscope.set(-rotRate.beta, rotRate.alpha, rotRate.gamma); - } else { - this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); - } - if (!this.isDeviceMotionInRadians) { - this.gyroscope.multiplyScalar(Math.PI / 180); - } - this.filter.addGyroMeasurement(this.gyroscope, timestampS); - } - this.filter.addAccelMeasurement(this.accelerometer, timestampS); - this.previousTimestampS = timestampS; - }; - FusionPoseSensor.prototype.onOrientationChange_ = function (screenOrientation) { - this.setScreenTransform_(); - }; - FusionPoseSensor.prototype.onMessage_ = function (event) { - var message = event.data; - if (!message || !message.type) { - return; - } - var type = message.type.toLowerCase(); - if (type !== 'devicemotion') { - return; - } - this.updateDeviceMotion_(message.deviceMotionEvent); - }; - FusionPoseSensor.prototype.setScreenTransform_ = function () { - this.worldToScreenQ.set(0, 0, 0, 1); - switch (window.orientation) { - case 0: - break; - case 90: - this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), -Math.PI / 2); - break; - case -90: - this.worldToScreenQ.setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2); - break; - case 180: - break; - } - this.inverseWorldToScreenQ.copy(this.worldToScreenQ); - this.inverseWorldToScreenQ.inverse(); - }; - FusionPoseSensor.prototype.start = function () { - this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this); - this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this); - this.onMessageCallback_ = this.onMessage_.bind(this); - this.onDeviceOrientationCallback_ = this.onDeviceOrientation_.bind(this); - if (isIOS() && isInsideCrossOriginIFrame()) { - window.addEventListener('message', this.onMessageCallback_); - } - window.addEventListener('orientationchange', this.onOrientationChangeCallback_); - if (this.isWithoutDeviceMotion) { - window.addEventListener('deviceorientation', this.onDeviceOrientationCallback_); - } else { - window.addEventListener('devicemotion', this.onDeviceMotionCallback_); - } - }; - FusionPoseSensor.prototype.stop = function () { - window.removeEventListener('devicemotion', this.onDeviceMotionCallback_); - window.removeEventListener('deviceorientation', this.onDeviceOrientationCallback_); - window.removeEventListener('orientationchange', this.onOrientationChangeCallback_); - window.removeEventListener('message', this.onMessageCallback_); - }; - var SENSOR_FREQUENCY = 60; - var X_AXIS = new Vector3(1, 0, 0); - var Z_AXIS = new Vector3(0, 0, 1); - var SENSOR_TO_VR = new Quaternion(); - SENSOR_TO_VR.setFromAxisAngle(X_AXIS, -Math.PI / 2); - SENSOR_TO_VR.multiply(new Quaternion().setFromAxisAngle(Z_AXIS, Math.PI / 2)); - var PoseSensor = function () { - function PoseSensor(config) { - classCallCheck(this, PoseSensor); - this.config = config; - this.sensor = null; - this.fusionSensor = null; - this._out = new Float32Array(4); - this.api = null; - this.errors = []; - this._sensorQ = new Quaternion(); - this._outQ = new Quaternion(); - this._onSensorRead = this._onSensorRead.bind(this); - this._onSensorError = this._onSensorError.bind(this); - this.init(); - } - createClass(PoseSensor, [{ - key: 'init', - value: function init() { - var sensor = null; - try { - sensor = new RelativeOrientationSensor({ - frequency: SENSOR_FREQUENCY, - referenceFrame: 'screen' - }); - sensor.addEventListener('error', this._onSensorError); - } catch (error) { - this.errors.push(error); - if (error.name === 'SecurityError') { - console.error('Cannot construct sensors due to the Feature Policy'); - console.warn('Attempting to fall back using "devicemotion"; however this will ' + 'fail in the future without correct permissions.'); - this.useDeviceMotion(); - } else if (error.name === 'ReferenceError') { - this.useDeviceMotion(); - } else { - console.error(error); - } - } - if (sensor) { - this.api = 'sensor'; - this.sensor = sensor; - this.sensor.addEventListener('reading', this._onSensorRead); - this.sensor.start(); - } - } - }, { - key: 'useDeviceMotion', - value: function useDeviceMotion() { - this.api = 'devicemotion'; - this.fusionSensor = new FusionPoseSensor(this.config.K_FILTER, this.config.PREDICTION_TIME_S, this.config.YAW_ONLY, this.config.DEBUG); - if (this.sensor) { - this.sensor.removeEventListener('reading', this._onSensorRead); - this.sensor.removeEventListener('error', this._onSensorError); - this.sensor = null; - } - } - }, { - key: 'getOrientation', - value: function getOrientation() { - if (this.fusionSensor) { - return this.fusionSensor.getOrientation(); - } - if (!this.sensor || !this.sensor.quaternion) { - this._out[0] = this._out[1] = this._out[2] = 0; - this._out[3] = 1; - return this._out; - } - var q = this.sensor.quaternion; - this._sensorQ.set(q[0], q[1], q[2], q[3]); - var out = this._outQ; - out.copy(SENSOR_TO_VR); - out.multiply(this._sensorQ); - if (this.config.YAW_ONLY) { - out.x = out.z = 0; - out.normalize(); - } - this._out[0] = out.x; - this._out[1] = out.y; - this._out[2] = out.z; - this._out[3] = out.w; - return this._out; - } - }, { - key: '_onSensorError', - value: function _onSensorError(event) { - this.errors.push(event.error); - if (event.error.name === 'NotAllowedError') { - console.error('Permission to access sensor was denied'); - } else if (event.error.name === 'NotReadableError') { - console.error('Sensor could not be read'); - } else { - console.error(event.error); - } - this.useDeviceMotion(); - } - }, { - key: '_onSensorRead', - value: function _onSensorRead() {} - }]); - return PoseSensor; - }(); - var rotateInstructionsAsset = ""; - function RotateInstructions() { - this.loadIcon_(); - var overlay = document.createElement('div'); - var s = overlay.style; - s.position = 'fixed'; - s.top = 0; - s.right = 0; - s.bottom = 0; - s.left = 0; - s.backgroundColor = 'gray'; - s.fontFamily = 'sans-serif'; - s.zIndex = 1000000; - var img = document.createElement('img'); - img.src = this.icon; - var s = img.style; - s.marginLeft = '25%'; - s.marginTop = '25%'; - s.width = '50%'; - overlay.appendChild(img); - var text = document.createElement('div'); - var s = text.style; - s.textAlign = 'center'; - s.fontSize = '16px'; - s.lineHeight = '24px'; - s.margin = '24px 25%'; - s.width = '50%'; - text.innerHTML = 'Place your phone into your Cardboard viewer.'; - overlay.appendChild(text); - var snackbar = document.createElement('div'); - var s = snackbar.style; - s.backgroundColor = '#CFD8DC'; - s.position = 'fixed'; - s.bottom = 0; - s.width = '100%'; - s.height = '48px'; - s.padding = '14px 24px'; - s.boxSizing = 'border-box'; - s.color = '#656A6B'; - overlay.appendChild(snackbar); - var snackbarText = document.createElement('div'); - snackbarText.style.float = 'left'; - snackbarText.innerHTML = 'No Cardboard viewer?'; - var snackbarButton = document.createElement('a'); - snackbarButton.href = 'https://www.google.com/get/cardboard/get-cardboard/'; - snackbarButton.innerHTML = 'get one'; - snackbarButton.target = '_blank'; - var s = snackbarButton.style; - s.float = 'right'; - s.fontWeight = 600; - s.textTransform = 'uppercase'; - s.borderLeft = '1px solid gray'; - s.paddingLeft = '24px'; - s.textDecoration = 'none'; - s.color = '#656A6B'; - snackbar.appendChild(snackbarText); - snackbar.appendChild(snackbarButton); - this.overlay = overlay; - this.text = text; - this.hide(); - } - RotateInstructions.prototype.show = function (parent) { - if (!parent && !this.overlay.parentElement) { - document.body.appendChild(this.overlay); - } else if (parent) { - if (this.overlay.parentElement && this.overlay.parentElement != parent) this.overlay.parentElement.removeChild(this.overlay); - parent.appendChild(this.overlay); - } - this.overlay.style.display = 'block'; - var img = this.overlay.querySelector('img'); - var s = img.style; - if (isLandscapeMode()) { - s.width = '20%'; - s.marginLeft = '40%'; - s.marginTop = '3%'; - } else { - s.width = '50%'; - s.marginLeft = '25%'; - s.marginTop = '25%'; - } - }; - RotateInstructions.prototype.hide = function () { - this.overlay.style.display = 'none'; - }; - RotateInstructions.prototype.showTemporarily = function (ms, parent) { - this.show(parent); - this.timer = setTimeout(this.hide.bind(this), ms); - }; - RotateInstructions.prototype.disableShowTemporarily = function () { - clearTimeout(this.timer); - }; - RotateInstructions.prototype.update = function () { - this.disableShowTemporarily(); - if (!isLandscapeMode() && isMobile()) { - this.show(); - } else { - this.hide(); - } - }; - RotateInstructions.prototype.loadIcon_ = function () { - this.icon = dataUri('image/svg+xml', rotateInstructionsAsset); - }; - var DEFAULT_VIEWER = 'CardboardV1'; - var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER'; - var CLASS_NAME = 'webvr-polyfill-viewer-selector'; - function ViewerSelector(defaultViewer) { - try { - this.selectedKey = localStorage.getItem(VIEWER_KEY); - } catch (error) { - console.error('Failed to load viewer profile: %s', error); - } - if (!this.selectedKey) { - this.selectedKey = defaultViewer || DEFAULT_VIEWER; - } - this.dialog = this.createDialog_(DeviceInfo.Viewers); - this.root = null; - this.onChangeCallbacks_ = []; - } - ViewerSelector.prototype.show = function (root) { - this.root = root; - root.appendChild(this.dialog); - var selected = this.dialog.querySelector('#' + this.selectedKey); - selected.checked = true; - this.dialog.style.display = 'block'; - }; - ViewerSelector.prototype.hide = function () { - if (this.root && this.root.contains(this.dialog)) { - this.root.removeChild(this.dialog); - } - this.dialog.style.display = 'none'; - }; - ViewerSelector.prototype.getCurrentViewer = function () { - return DeviceInfo.Viewers[this.selectedKey]; - }; - ViewerSelector.prototype.getSelectedKey_ = function () { - var input = this.dialog.querySelector('input[name=field]:checked'); - if (input) { - return input.id; - } - return null; - }; - ViewerSelector.prototype.onChange = function (cb) { - this.onChangeCallbacks_.push(cb); - }; - ViewerSelector.prototype.fireOnChange_ = function (viewer) { - for (var i = 0; i < this.onChangeCallbacks_.length; i++) { - this.onChangeCallbacks_[i](viewer); - } - }; - ViewerSelector.prototype.onSave_ = function () { - this.selectedKey = this.getSelectedKey_(); - if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) { - console.error('ViewerSelector.onSave_: this should never happen!'); - return; - } - this.fireOnChange_(DeviceInfo.Viewers[this.selectedKey]); - try { - localStorage.setItem(VIEWER_KEY, this.selectedKey); - } catch (error) { - console.error('Failed to save viewer profile: %s', error); - } - this.hide(); - }; - ViewerSelector.prototype.createDialog_ = function (options) { - var container = document.createElement('div'); - container.classList.add(CLASS_NAME); - container.style.display = 'none'; - var overlay = document.createElement('div'); - var s = overlay.style; - s.position = 'fixed'; - s.left = 0; - s.top = 0; - s.width = '100%'; - s.height = '100%'; - s.background = 'rgba(0, 0, 0, 0.3)'; - overlay.addEventListener('click', this.hide.bind(this)); - var width = 280; - var dialog = document.createElement('div'); - var s = dialog.style; - s.boxSizing = 'border-box'; - s.position = 'fixed'; - s.top = '24px'; - s.left = '50%'; - s.marginLeft = -width / 2 + 'px'; - s.width = width + 'px'; - s.padding = '24px'; - s.overflow = 'hidden'; - s.background = '#fafafa'; - s.fontFamily = "'Roboto', sans-serif"; - s.boxShadow = '0px 5px 20px #666'; - dialog.appendChild(this.createH1_('Select your viewer')); - for (var id in options) { - dialog.appendChild(this.createChoice_(id, options[id].label)); - } - dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this))); - container.appendChild(overlay); - container.appendChild(dialog); - return container; - }; - ViewerSelector.prototype.createH1_ = function (name) { - var h1 = document.createElement('h1'); - var s = h1.style; - s.color = 'black'; - s.fontSize = '20px'; - s.fontWeight = 'bold'; - s.marginTop = 0; - s.marginBottom = '24px'; - h1.innerHTML = name; - return h1; - }; - ViewerSelector.prototype.createChoice_ = function (id, name) { - var div = document.createElement('div'); - div.style.marginTop = '8px'; - div.style.color = 'black'; - var input = document.createElement('input'); - input.style.fontSize = '30px'; - input.setAttribute('id', id); - input.setAttribute('type', 'radio'); - input.setAttribute('value', id); - input.setAttribute('name', 'field'); - var label = document.createElement('label'); - label.style.marginLeft = '4px'; - label.setAttribute('for', id); - label.innerHTML = name; - div.appendChild(input); - div.appendChild(label); - return div; - }; - ViewerSelector.prototype.createButton_ = function (label, onclick) { - var button = document.createElement('button'); - button.innerHTML = label; - var s = button.style; - s.float = 'right'; - s.textTransform = 'uppercase'; - s.color = '#1094f7'; - s.fontSize = '14px'; - s.letterSpacing = 0; - s.border = 0; - s.background = 'none'; - s.marginTop = '16px'; - button.addEventListener('click', onclick); - return button; - }; - var commonjsGlobal$$1 = typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : typeof self !== 'undefined' ? self : {}; - function unwrapExports$$1(x) { - return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; - } - function createCommonjsModule$$1(fn, module) { - return module = { - exports: {} - }, fn(module, module.exports), module.exports; - } - var NoSleep = createCommonjsModule$$1(function (module, exports) { - (function webpackUniversalModuleDefinition(root, factory) { - module.exports = factory(); - })(commonjsGlobal$$1, function () { - return function (modules) { - var installedModules = {}; - function __nested_webpack_require_167216__(moduleId) { - if (installedModules[moduleId]) { - return installedModules[moduleId].exports; - } - var module = installedModules[moduleId] = { - i: moduleId, - l: false, - exports: {} - }; - modules[moduleId].call(module.exports, module, module.exports, __nested_webpack_require_167216__); - module.l = true; - return module.exports; - } - __nested_webpack_require_167216__.m = modules; - __nested_webpack_require_167216__.c = installedModules; - __nested_webpack_require_167216__.d = function (exports, name, getter) { - if (!__nested_webpack_require_167216__.o(exports, name)) { - Object.defineProperty(exports, name, { - configurable: false, - enumerable: true, - get: getter - }); - } - }; - __nested_webpack_require_167216__.n = function (module) { - var getter = module && module.__esModule ? function getDefault() { - return module['default']; - } : function getModuleExports() { - return module; - }; - __nested_webpack_require_167216__.d(getter, 'a', getter); - return getter; - }; - __nested_webpack_require_167216__.o = function (object, property) { - return Object.prototype.hasOwnProperty.call(object, property); - }; - __nested_webpack_require_167216__.p = ""; - return __nested_webpack_require_167216__(__nested_webpack_require_167216__.s = 0); - }([function (module, exports, __nested_webpack_require_168841__) { - var _createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - var mediaFile = __nested_webpack_require_168841__(1); - var oldIOS = typeof navigator !== 'undefined' && parseFloat(('' + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0, ''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', '')) < 10 && !window.MSStream; - var NoSleep = function () { - function NoSleep() { - _classCallCheck(this, NoSleep); - if (oldIOS) { - this.noSleepTimer = null; - } else { - this.noSleepVideo = document.createElement('video'); - this.noSleepVideo.setAttribute('playsinline', ''); - this.noSleepVideo.setAttribute('src', mediaFile); - this.noSleepVideo.addEventListener('timeupdate', function (e) { - if (this.noSleepVideo.currentTime > 0.5) { - this.noSleepVideo.currentTime = Math.random(); - } - }.bind(this)); - } - } - _createClass(NoSleep, [{ - key: 'enable', - value: function enable() { - if (oldIOS) { - this.disable(); - this.noSleepTimer = window.setInterval(function () { - window.location.href = '/'; - window.setTimeout(window.stop, 0); - }, 15000); - } else { - this.noSleepVideo.play(); - } - } - }, { - key: 'disable', - value: function disable() { - if (oldIOS) { - if (this.noSleepTimer) { - window.clearInterval(this.noSleepTimer); - this.noSleepTimer = null; - } - } else { - this.noSleepVideo.pause(); - } - } - }]); - return NoSleep; - }(); - module.exports = NoSleep; - }, function (module, exports, __webpack_require__) { - module.exports = 'data:video/mp4;base64,AAAAIGZ0eXBtcDQyAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAACKBtZGF0AAAC8wYF///v3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE0MiByMjQ3OSBkZDc5YTYxIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxNCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTEgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9MiBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0wIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MCA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0wIHRocmVhZHM9NiBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTMgYl9weXJhbWlkPTIgYl9hZGFwdD0xIGJfYmlhcz0wIGRpcmVjdD0xIHdlaWdodGI9MSBvcGVuX2dvcD0wIHdlaWdodHA9MSBrZXlpbnQ9MzAwIGtleWludF9taW49MzAgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD0xMCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIwLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IHZidl9tYXhyYXRlPTIwMDAwIHZidl9idWZzaXplPTI1MDAwIGNyZl9tYXg9MC4wIG5hbF9ocmQ9bm9uZSBmaWxsZXI9MCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAOWWIhAA3//p+C7v8tDDSTjf97w55i3SbRPO4ZY+hkjD5hbkAkL3zpJ6h/LR1CAABzgB1kqqzUorlhQAAAAxBmiQYhn/+qZYADLgAAAAJQZ5CQhX/AAj5IQADQGgcIQADQGgcAAAACQGeYUQn/wALKCEAA0BoHAAAAAkBnmNEJ/8ACykhAANAaBwhAANAaBwAAAANQZpoNExDP/6plgAMuSEAA0BoHAAAAAtBnoZFESwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBnqVEJ/8ACykhAANAaBwAAAAJAZ6nRCf/AAsoIQADQGgcIQADQGgcAAAADUGarDRMQz/+qZYADLghAANAaBwAAAALQZ7KRRUsK/8ACPkhAANAaBwAAAAJAZ7pRCf/AAsoIQADQGgcIQADQGgcAAAACQGe60Qn/wALKCEAA0BoHAAAAA1BmvA0TEM//qmWAAy5IQADQGgcIQADQGgcAAAAC0GfDkUVLCv/AAj5IQADQGgcAAAACQGfLUQn/wALKSEAA0BoHCEAA0BoHAAAAAkBny9EJ/8ACyghAANAaBwAAAANQZs0NExDP/6plgAMuCEAA0BoHAAAAAtBn1JFFSwr/wAI+SEAA0BoHCEAA0BoHAAAAAkBn3FEJ/8ACyghAANAaBwAAAAJAZ9zRCf/AAsoIQADQGgcIQADQGgcAAAADUGbeDRMQz/+qZYADLkhAANAaBwAAAALQZ+WRRUsK/8ACPghAANAaBwhAANAaBwAAAAJAZ+1RCf/AAspIQADQGgcAAAACQGft0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bm7w0TEM//qmWAAy4IQADQGgcAAAAC0Gf2kUVLCv/AAj5IQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHAAAAAkBn/tEJ/8ACykhAANAaBwAAAANQZvgNExDP/6plgAMuSEAA0BoHCEAA0BoHAAAAAtBnh5FFSwr/wAI+CEAA0BoHAAAAAkBnj1EJ/8ACyghAANAaBwhAANAaBwAAAAJAZ4/RCf/AAspIQADQGgcAAAADUGaJDRMQz/+qZYADLghAANAaBwAAAALQZ5CRRUsK/8ACPkhAANAaBwhAANAaBwAAAAJAZ5hRCf/AAsoIQADQGgcAAAACQGeY0Qn/wALKSEAA0BoHCEAA0BoHAAAAA1Bmmg0TEM//qmWAAy5IQADQGgcAAAAC0GehkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGepUQn/wALKSEAA0BoHAAAAAkBnqdEJ/8ACyghAANAaBwAAAANQZqsNExDP/6plgAMuCEAA0BoHCEAA0BoHAAAAAtBnspFFSwr/wAI+SEAA0BoHAAAAAkBnulEJ/8ACyghAANAaBwhAANAaBwAAAAJAZ7rRCf/AAsoIQADQGgcAAAADUGa8DRMQz/+qZYADLkhAANAaBwhAANAaBwAAAALQZ8ORRUsK/8ACPkhAANAaBwAAAAJAZ8tRCf/AAspIQADQGgcIQADQGgcAAAACQGfL0Qn/wALKCEAA0BoHAAAAA1BmzQ0TEM//qmWAAy4IQADQGgcAAAAC0GfUkUVLCv/AAj5IQADQGgcIQADQGgcAAAACQGfcUQn/wALKCEAA0BoHAAAAAkBn3NEJ/8ACyghAANAaBwhAANAaBwAAAANQZt4NExC//6plgAMuSEAA0BoHAAAAAtBn5ZFFSwr/wAI+CEAA0BoHCEAA0BoHAAAAAkBn7VEJ/8ACykhAANAaBwAAAAJAZ+3RCf/AAspIQADQGgcAAAADUGbuzRMQn/+nhAAYsAhAANAaBwhAANAaBwAAAAJQZ/aQhP/AAspIQADQGgcAAAACQGf+UQn/wALKCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHCEAA0BoHAAACiFtb292AAAAbG12aGQAAAAA1YCCX9WAgl8AAAPoAAAH/AABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAGGlvZHMAAAAAEICAgAcAT////v7/AAAF+XRyYWsAAABcdGtoZAAAAAPVgIJf1YCCXwAAAAEAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAygAAAMoAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAB9AAABdwAAEAAAAABXFtZGlhAAAAIG1kaGQAAAAA1YCCX9WAgl8AAV+QAAK/IFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAUcbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAAE3HN0YmwAAACYc3RzZAAAAAAAAAABAAAAiGF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAygDKAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAyYXZjQwFNQCj/4QAbZ01AKOyho3ySTUBAQFAAAAMAEAAr8gDxgxlgAQAEaO+G8gAAABhzdHRzAAAAAAAAAAEAAAA8AAALuAAAABRzdHNzAAAAAAAAAAEAAAABAAAB8GN0dHMAAAAAAAAAPAAAAAEAABdwAAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAADqYAAAAAQAAF3AAAAABAAAAAAAAAAEAAAu4AAAAAQAAOpgAAAABAAAXcAAAAAEAAAAAAAAAAQAAC7gAAAABAAA6mAAAAAEAABdwAAAAAQAAAAAAAAABAAALuAAAAAEAAC7gAAAAAQAAF3AAAAABAAAAAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAEEc3RzegAAAAAAAAAAAAAAPAAAAzQAAAAQAAAADQAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAAPAAAADQAAAA0AAAARAAAADwAAAA0AAAANAAAAEQAAAA8AAAANAAAADQAAABEAAAANAAAADQAAAQBzdGNvAAAAAAAAADwAAAAwAAADZAAAA3QAAAONAAADoAAAA7kAAAPQAAAD6wAAA/4AAAQXAAAELgAABEMAAARcAAAEbwAABIwAAAShAAAEugAABM0AAATkAAAE/wAABRIAAAUrAAAFQgAABV0AAAVwAAAFiQAABaAAAAW1AAAFzgAABeEAAAX+AAAGEwAABiwAAAY/AAAGVgAABnEAAAaEAAAGnQAABrQAAAbPAAAG4gAABvUAAAcSAAAHJwAAB0AAAAdTAAAHcAAAB4UAAAeeAAAHsQAAB8gAAAfjAAAH9gAACA8AAAgmAAAIQQAACFQAAAhnAAAIhAAACJcAAAMsdHJhawAAAFx0a2hkAAAAA9WAgl/VgIJfAAAAAgAAAAAAAAf8AAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAACsm1kaWEAAAAgbWRoZAAAAADVgIJf1YCCXwAArEQAAWAAVcQAAAAAACdoZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU3RlcmVvAAAAAmNtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAidzdGJsAAAAZ3N0c2QAAAAAAAAAAQAAAFdtcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAArEQAAAAAADNlc2RzAAAAAAOAgIAiAAIABICAgBRAFQAAAAADDUAAAAAABYCAgAISEAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAABYAAAEAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAAGAAAAWAAAAXBzdGNvAAAAAAAAAFgAAAOBAAADhwAAA5oAAAOtAAADswAAA8oAAAPfAAAD5QAAA/gAAAQLAAAEEQAABCgAAAQ9AAAEUAAABFYAAARpAAAEgAAABIYAAASbAAAErgAABLQAAATHAAAE3gAABPMAAAT5AAAFDAAABR8AAAUlAAAFPAAABVEAAAVXAAAFagAABX0AAAWDAAAFmgAABa8AAAXCAAAFyAAABdsAAAXyAAAF+AAABg0AAAYgAAAGJgAABjkAAAZQAAAGZQAABmsAAAZ+AAAGkQAABpcAAAauAAAGwwAABskAAAbcAAAG7wAABwYAAAcMAAAHIQAABzQAAAc6AAAHTQAAB2QAAAdqAAAHfwAAB5IAAAeYAAAHqwAAB8IAAAfXAAAH3QAAB/AAAAgDAAAICQAACCAAAAg1AAAIOwAACE4AAAhhAAAIeAAACH4AAAiRAAAIpAAACKoAAAiwAAAItgAACLwAAAjCAAAAFnVkdGEAAAAObmFtZVN0ZXJlbwAAAHB1ZHRhAAAAaG1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAAO2lsc3QAAAAzqXRvbwAAACtkYXRhAAAAAQAAAABIYW5kQnJha2UgMC4xMC4yIDIwMTUwNjExMDA='; - }]); - }); - }); - var NoSleep$1 = unwrapExports$$1(NoSleep); - var nextDisplayId = 1000; - var defaultLeftBounds = [0, 0, 0.5, 1]; - var defaultRightBounds = [0.5, 0, 0.5, 1]; - var raf = window.requestAnimationFrame; - var caf = window.cancelAnimationFrame; - function VRFrameData() { - this.leftProjectionMatrix = new Float32Array(16); - this.leftViewMatrix = new Float32Array(16); - this.rightProjectionMatrix = new Float32Array(16); - this.rightViewMatrix = new Float32Array(16); - this.pose = null; - } - function VRDisplayCapabilities(config) { - Object.defineProperties(this, { - hasPosition: { - writable: false, - enumerable: true, - value: config.hasPosition - }, - hasExternalDisplay: { - writable: false, - enumerable: true, - value: config.hasExternalDisplay - }, - canPresent: { - writable: false, - enumerable: true, - value: config.canPresent - }, - maxLayers: { - writable: false, - enumerable: true, - value: config.maxLayers - }, - hasOrientation: { - enumerable: true, - get: function get() { - deprecateWarning('VRDisplayCapabilities.prototype.hasOrientation', 'VRDisplay.prototype.getFrameData'); - return config.hasOrientation; - } - } - }); - } - function VRDisplay(config) { - config = config || {}; - var USE_WAKELOCK = 'wakelock' in config ? config.wakelock : true; - this.isPolyfilled = true; - this.displayId = nextDisplayId++; - this.displayName = ''; - this.depthNear = 0.01; - this.depthFar = 10000.0; - this.isPresenting = false; - Object.defineProperty(this, 'isConnected', { - get: function get() { - deprecateWarning('VRDisplay.prototype.isConnected', 'VRDisplayCapabilities.prototype.hasExternalDisplay'); - return false; - } - }); - this.capabilities = new VRDisplayCapabilities({ - hasPosition: false, - hasOrientation: false, - hasExternalDisplay: false, - canPresent: false, - maxLayers: 1 - }); - this.stageParameters = null; - this.waitingForPresent_ = false; - this.layer_ = null; - this.originalParent_ = null; - this.fullscreenElement_ = null; - this.fullscreenWrapper_ = null; - this.fullscreenElementCachedStyle_ = null; - this.fullscreenEventTarget_ = null; - this.fullscreenChangeHandler_ = null; - this.fullscreenErrorHandler_ = null; - if (USE_WAKELOCK && isMobile()) { - this.wakelock_ = new NoSleep$1(); - } - } - VRDisplay.prototype.getFrameData = function (frameData) { - return frameDataFromPose(frameData, this._getPose(), this); - }; - VRDisplay.prototype.getPose = function () { - deprecateWarning('VRDisplay.prototype.getPose', 'VRDisplay.prototype.getFrameData'); - return this._getPose(); - }; - VRDisplay.prototype.resetPose = function () { - deprecateWarning('VRDisplay.prototype.resetPose'); - return this._resetPose(); - }; - VRDisplay.prototype.getImmediatePose = function () { - deprecateWarning('VRDisplay.prototype.getImmediatePose', 'VRDisplay.prototype.getFrameData'); - return this._getPose(); - }; - VRDisplay.prototype.requestAnimationFrame = function (callback) { - return raf(callback); - }; - VRDisplay.prototype.cancelAnimationFrame = function (id) { - return caf(id); - }; - VRDisplay.prototype.wrapForFullscreen = function (element) { - if (isIOS()) { - return element; - } - if (!this.fullscreenWrapper_) { - this.fullscreenWrapper_ = document.createElement('div'); - var cssProperties = ['height: ' + Math.min(screen.height, screen.width) + 'px !important', 'top: 0 !important', 'left: 0 !important', 'right: 0 !important', 'border: 0', 'margin: 0', 'padding: 0', 'z-index: 999999 !important', 'position: fixed']; - this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';'); - this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper'); - } - if (this.fullscreenElement_ == element) { - return this.fullscreenWrapper_; - } - if (this.fullscreenElement_) { - if (this.originalParent_) { - this.originalParent_.appendChild(this.fullscreenElement_); - } else { - this.fullscreenElement_.parentElement.removeChild(this.fullscreenElement_); - } - } - this.fullscreenElement_ = element; - this.originalParent_ = element.parentElement; - if (!this.originalParent_) { - document.body.appendChild(element); - } - if (!this.fullscreenWrapper_.parentElement) { - var parent = this.fullscreenElement_.parentElement; - parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_); - parent.removeChild(this.fullscreenElement_); - } - this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild); - this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style'); - var self = this; - function applyFullscreenElementStyle() { - if (!self.fullscreenElement_) { - return; - } - var cssProperties = ['position: absolute', 'top: 0', 'left: 0', 'width: ' + Math.max(screen.width, screen.height) + 'px', 'height: ' + Math.min(screen.height, screen.width) + 'px', 'border: 0', 'margin: 0', 'padding: 0']; - self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';'); - } - applyFullscreenElementStyle(); - return this.fullscreenWrapper_; - }; - VRDisplay.prototype.removeFullscreenWrapper = function () { - if (!this.fullscreenElement_) { - return; - } - var element = this.fullscreenElement_; - if (this.fullscreenElementCachedStyle_) { - element.setAttribute('style', this.fullscreenElementCachedStyle_); - } else { - element.removeAttribute('style'); - } - this.fullscreenElement_ = null; - this.fullscreenElementCachedStyle_ = null; - var parent = this.fullscreenWrapper_.parentElement; - this.fullscreenWrapper_.removeChild(element); - if (this.originalParent_ === parent) { - parent.insertBefore(element, this.fullscreenWrapper_); - } else if (this.originalParent_) { - this.originalParent_.appendChild(element); - } - parent.removeChild(this.fullscreenWrapper_); - return element; - }; - VRDisplay.prototype.requestPresent = function (layers) { - var wasPresenting = this.isPresenting; - var self = this; - if (!(layers instanceof Array)) { - deprecateWarning('VRDisplay.prototype.requestPresent with non-array argument', 'an array of VRLayers as the first argument'); - layers = [layers]; - } - return new Promise(function (resolve, reject) { - if (!self.capabilities.canPresent) { - reject(new Error('VRDisplay is not capable of presenting.')); - return; - } - if (layers.length == 0 || layers.length > self.capabilities.maxLayers) { - reject(new Error('Invalid number of layers.')); - return; - } - var incomingLayer = layers[0]; - if (!incomingLayer.source) { - resolve(); - return; - } - var leftBounds = incomingLayer.leftBounds || defaultLeftBounds; - var rightBounds = incomingLayer.rightBounds || defaultRightBounds; - if (wasPresenting) { - var layer = self.layer_; - if (layer.source !== incomingLayer.source) { - layer.source = incomingLayer.source; - } - for (var i = 0; i < 4; i++) { - layer.leftBounds[i] = leftBounds[i]; - layer.rightBounds[i] = rightBounds[i]; - } - self.wrapForFullscreen(self.layer_.source); - self.updatePresent_(); - resolve(); - return; - } - self.layer_ = { - predistorted: incomingLayer.predistorted, - source: incomingLayer.source, - leftBounds: leftBounds.slice(0), - rightBounds: rightBounds.slice(0) - }; - self.waitingForPresent_ = false; - if (self.layer_ && self.layer_.source) { - var fullscreenElement = self.wrapForFullscreen(self.layer_.source); - var onFullscreenChange = function onFullscreenChange() { - var actualFullscreenElement = getFullscreenElement(); - self.isPresenting = fullscreenElement === actualFullscreenElement; - if (self.isPresenting) { - if (screen.orientation && screen.orientation.lock) { - screen.orientation.lock('landscape-primary').catch(function (error) { - console.error('screen.orientation.lock() failed due to', error.message); - }); - } - self.waitingForPresent_ = false; - self.beginPresent_(); - resolve(); - } else { - if (screen.orientation && screen.orientation.unlock) { - screen.orientation.unlock(); - } - self.removeFullscreenWrapper(); - self.disableWakeLock(); - self.endPresent_(); - self.removeFullscreenListeners_(); - } - self.fireVRDisplayPresentChange_(); - }; - var onFullscreenError = function onFullscreenError() { - if (!self.waitingForPresent_) { - return; - } - self.removeFullscreenWrapper(); - self.removeFullscreenListeners_(); - self.disableWakeLock(); - self.waitingForPresent_ = false; - self.isPresenting = false; - reject(new Error('Unable to present.')); - }; - self.addFullscreenListeners_(fullscreenElement, onFullscreenChange, onFullscreenError); - if (requestFullscreen(fullscreenElement)) { - self.enableWakeLock(); - self.waitingForPresent_ = true; - } else if (isIOS() || isWebViewAndroid()) { - self.enableWakeLock(); - self.isPresenting = true; - self.beginPresent_(); - self.fireVRDisplayPresentChange_(); - resolve(); - } - } - if (!self.waitingForPresent_ && !isIOS()) { - exitFullscreen(); - reject(new Error('Unable to present.')); - } - }); - }; - VRDisplay.prototype.exitPresent = function () { - var wasPresenting = this.isPresenting; - var self = this; - this.isPresenting = false; - this.layer_ = null; - this.disableWakeLock(); - return new Promise(function (resolve, reject) { - if (wasPresenting) { - if (!exitFullscreen() && isIOS()) { - self.endPresent_(); - self.fireVRDisplayPresentChange_(); - } - if (isWebViewAndroid()) { - self.removeFullscreenWrapper(); - self.removeFullscreenListeners_(); - self.endPresent_(); - self.fireVRDisplayPresentChange_(); - } - resolve(); - } else { - reject(new Error('Was not presenting to VRDisplay.')); - } - }); - }; - VRDisplay.prototype.getLayers = function () { - if (this.layer_) { - return [this.layer_]; - } - return []; - }; - VRDisplay.prototype.fireVRDisplayPresentChange_ = function () { - var event = new CustomEvent('vrdisplaypresentchange', { - detail: { - display: this - } - }); - window.dispatchEvent(event); - }; - VRDisplay.prototype.fireVRDisplayConnect_ = function () { - var event = new CustomEvent('vrdisplayconnect', { - detail: { - display: this - } - }); - window.dispatchEvent(event); - }; - VRDisplay.prototype.addFullscreenListeners_ = function (element, changeHandler, errorHandler) { - this.removeFullscreenListeners_(); - this.fullscreenEventTarget_ = element; - this.fullscreenChangeHandler_ = changeHandler; - this.fullscreenErrorHandler_ = errorHandler; - if (changeHandler) { - if (document.fullscreenEnabled) { - element.addEventListener('fullscreenchange', changeHandler, false); - } else if (document.webkitFullscreenEnabled) { - element.addEventListener('webkitfullscreenchange', changeHandler, false); - } else if (document.mozFullScreenEnabled) { - document.addEventListener('mozfullscreenchange', changeHandler, false); - } else if (document.msFullscreenEnabled) { - element.addEventListener('msfullscreenchange', changeHandler, false); - } - } - if (errorHandler) { - if (document.fullscreenEnabled) { - element.addEventListener('fullscreenerror', errorHandler, false); - } else if (document.webkitFullscreenEnabled) { - element.addEventListener('webkitfullscreenerror', errorHandler, false); - } else if (document.mozFullScreenEnabled) { - document.addEventListener('mozfullscreenerror', errorHandler, false); - } else if (document.msFullscreenEnabled) { - element.addEventListener('msfullscreenerror', errorHandler, false); - } - } - }; - VRDisplay.prototype.removeFullscreenListeners_ = function () { - if (!this.fullscreenEventTarget_) return; - var element = this.fullscreenEventTarget_; - if (this.fullscreenChangeHandler_) { - var changeHandler = this.fullscreenChangeHandler_; - element.removeEventListener('fullscreenchange', changeHandler, false); - element.removeEventListener('webkitfullscreenchange', changeHandler, false); - document.removeEventListener('mozfullscreenchange', changeHandler, false); - element.removeEventListener('msfullscreenchange', changeHandler, false); - } - if (this.fullscreenErrorHandler_) { - var errorHandler = this.fullscreenErrorHandler_; - element.removeEventListener('fullscreenerror', errorHandler, false); - element.removeEventListener('webkitfullscreenerror', errorHandler, false); - document.removeEventListener('mozfullscreenerror', errorHandler, false); - element.removeEventListener('msfullscreenerror', errorHandler, false); - } - this.fullscreenEventTarget_ = null; - this.fullscreenChangeHandler_ = null; - this.fullscreenErrorHandler_ = null; - }; - VRDisplay.prototype.enableWakeLock = function () { - if (this.wakelock_) { - this.wakelock_.enable(); - } - }; - VRDisplay.prototype.disableWakeLock = function () { - if (this.wakelock_) { - this.wakelock_.disable(); - } - }; - VRDisplay.prototype.beginPresent_ = function () {}; - VRDisplay.prototype.endPresent_ = function () {}; - VRDisplay.prototype.submitFrame = function (pose) {}; - VRDisplay.prototype.getEyeParameters = function (whichEye) { - return null; - }; - var config = { - ADDITIONAL_VIEWERS: [], - DEFAULT_VIEWER: '', - MOBILE_WAKE_LOCK: true, - DEBUG: false, - DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json', - K_FILTER: 0.98, - PREDICTION_TIME_S: 0.040, - CARDBOARD_UI_DISABLED: false, - ROTATE_INSTRUCTIONS_DISABLED: false, - YAW_ONLY: false, - BUFFER_SCALE: 0.5, - DIRTY_SUBMIT_FRAME_BINDINGS: false - }; - var Eye = { - LEFT: 'left', - RIGHT: 'right' - }; - function CardboardVRDisplay(config$$1) { - var defaults = extend({}, config); - config$$1 = extend(defaults, config$$1 || {}); - VRDisplay.call(this, { - wakelock: config$$1.MOBILE_WAKE_LOCK - }); - this.config = config$$1; - this.displayName = 'Cardboard VRDisplay'; - this.capabilities = new VRDisplayCapabilities({ - hasPosition: false, - hasOrientation: true, - hasExternalDisplay: false, - canPresent: true, - maxLayers: 1 - }); - this.stageParameters = null; - this.bufferScale_ = this.config.BUFFER_SCALE; - this.poseSensor_ = new PoseSensor(this.config); - this.distorter_ = null; - this.cardboardUI_ = null; - this.dpdb_ = new Dpdb(this.config.DPDB_URL, this.onDeviceParamsUpdated_.bind(this)); - this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams(), config$$1.ADDITIONAL_VIEWERS); - this.viewerSelector_ = new ViewerSelector(config$$1.DEFAULT_VIEWER); - this.viewerSelector_.onChange(this.onViewerChanged_.bind(this)); - this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer()); - if (!this.config.ROTATE_INSTRUCTIONS_DISABLED) { - this.rotateInstructions_ = new RotateInstructions(); - } - if (isIOS()) { - window.addEventListener('resize', this.onResize_.bind(this)); - } - } - CardboardVRDisplay.prototype = Object.create(VRDisplay.prototype); - CardboardVRDisplay.prototype._getPose = function () { - return { - position: null, - orientation: this.poseSensor_.getOrientation(), - linearVelocity: null, - linearAcceleration: null, - angularVelocity: null, - angularAcceleration: null - }; - }; - CardboardVRDisplay.prototype._resetPose = function () { - if (this.poseSensor_.resetPose) { - this.poseSensor_.resetPose(); - } - }; - CardboardVRDisplay.prototype._getFieldOfView = function (whichEye) { - var fieldOfView; - if (whichEye == Eye.LEFT) { - fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye(); - } else if (whichEye == Eye.RIGHT) { - fieldOfView = this.deviceInfo_.getFieldOfViewRightEye(); - } else { - console.error('Invalid eye provided: %s', whichEye); - return null; - } - return fieldOfView; - }; - CardboardVRDisplay.prototype._getEyeOffset = function (whichEye) { - var offset; - if (whichEye == Eye.LEFT) { - offset = [-this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; - } else if (whichEye == Eye.RIGHT) { - offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0]; - } else { - console.error('Invalid eye provided: %s', whichEye); - return null; - } - return offset; - }; - CardboardVRDisplay.prototype.getEyeParameters = function (whichEye) { - var offset = this._getEyeOffset(whichEye); - var fieldOfView = this._getFieldOfView(whichEye); - var eyeParams = { - offset: offset, - renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_, - renderHeight: this.deviceInfo_.device.height * this.bufferScale_ - }; - Object.defineProperty(eyeParams, 'fieldOfView', { - enumerable: true, - get: function get() { - deprecateWarning('VRFieldOfView', 'VRFrameData\'s projection matrices'); - return fieldOfView; - } - }); - return eyeParams; - }; - CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function (newParams) { - if (this.config.DEBUG) { - console.log('DPDB reported that device params were updated.'); - } - this.deviceInfo_.updateDeviceParams(newParams); - if (this.distorter_) { - this.distorter_.updateDeviceInfo(this.deviceInfo_); - } - }; - CardboardVRDisplay.prototype.updateBounds_ = function () { - if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) { - this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds); - } - }; - CardboardVRDisplay.prototype.beginPresent_ = function () { - var gl = this.layer_.source.getContext('webgl'); - if (!gl) gl = this.layer_.source.getContext('experimental-webgl'); - if (!gl) gl = this.layer_.source.getContext('webgl2'); - if (!gl) return; - if (this.layer_.predistorted) { - if (!this.config.CARDBOARD_UI_DISABLED) { - gl.canvas.width = getScreenWidth() * this.bufferScale_; - gl.canvas.height = getScreenHeight() * this.bufferScale_; - this.cardboardUI_ = new CardboardUI(gl); - } - } else { - if (!this.config.CARDBOARD_UI_DISABLED) { - this.cardboardUI_ = new CardboardUI(gl); - } - this.distorter_ = new CardboardDistorter(gl, this.cardboardUI_, this.config.BUFFER_SCALE, this.config.DIRTY_SUBMIT_FRAME_BINDINGS); - this.distorter_.updateDeviceInfo(this.deviceInfo_); - } - if (this.cardboardUI_) { - this.cardboardUI_.listen(function (e) { - this.viewerSelector_.show(this.layer_.source.parentElement); - e.stopPropagation(); - e.preventDefault(); - }.bind(this), function (e) { - this.exitPresent(); - e.stopPropagation(); - e.preventDefault(); - }.bind(this)); - } - if (this.rotateInstructions_) { - if (isLandscapeMode() && isMobile()) { - this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement); - } else { - this.rotateInstructions_.update(); - } - } - this.orientationHandler = this.onOrientationChange_.bind(this); - window.addEventListener('orientationchange', this.orientationHandler); - this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this); - window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); - this.fireVRDisplayDeviceParamsChange_(); - }; - CardboardVRDisplay.prototype.endPresent_ = function () { - if (this.distorter_) { - this.distorter_.destroy(); - this.distorter_ = null; - } - if (this.cardboardUI_) { - this.cardboardUI_.destroy(); - this.cardboardUI_ = null; - } - if (this.rotateInstructions_) { - this.rotateInstructions_.hide(); - } - this.viewerSelector_.hide(); - window.removeEventListener('orientationchange', this.orientationHandler); - window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler); - }; - CardboardVRDisplay.prototype.updatePresent_ = function () { - this.endPresent_(); - this.beginPresent_(); - }; - CardboardVRDisplay.prototype.submitFrame = function (pose) { - if (this.distorter_) { - this.updateBounds_(); - this.distorter_.submitFrame(); - } else if (this.cardboardUI_ && this.layer_) { - var gl = this.layer_.source.getContext('webgl'); - if (!gl) gl = this.layer_.source.getContext('experimental-webgl'); - if (!gl) gl = this.layer_.source.getContext('webgl2'); - var canvas = gl.canvas; - if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) { - this.cardboardUI_.onResize(); - } - this.lastWidth = canvas.width; - this.lastHeight = canvas.height; - this.cardboardUI_.render(); - } - }; - CardboardVRDisplay.prototype.onOrientationChange_ = function (e) { - this.viewerSelector_.hide(); - if (this.rotateInstructions_) { - this.rotateInstructions_.update(); - } - this.onResize_(); - }; - CardboardVRDisplay.prototype.onResize_ = function (e) { - if (this.layer_) { - var gl = this.layer_.source.getContext('webgl'); - if (!gl) gl = this.layer_.source.getContext('experimental-webgl'); - if (!gl) gl = this.layer_.source.getContext('webgl2'); - var cssProperties = ['position: absolute', 'top: 0', 'left: 0', 'width: 100vw', 'height: 100vh', 'border: 0', 'margin: 0', 'padding: 0px', 'box-sizing: content-box']; - gl.canvas.setAttribute('style', cssProperties.join('; ') + ';'); - safariCssSizeWorkaround(gl.canvas); - } - }; - CardboardVRDisplay.prototype.onViewerChanged_ = function (viewer) { - this.deviceInfo_.setViewer(viewer); - if (this.distorter_) { - this.distorter_.updateDeviceInfo(this.deviceInfo_); - } - this.fireVRDisplayDeviceParamsChange_(); - }; - CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function () { - var event = new CustomEvent('vrdisplaydeviceparamschange', { - detail: { - vrdisplay: this, - deviceInfo: this.deviceInfo_ - } - }); - window.dispatchEvent(event); - }; - CardboardVRDisplay.VRFrameData = VRFrameData; - CardboardVRDisplay.VRDisplay = VRDisplay; - return CardboardVRDisplay; - }); - }); - var CardboardVRDisplay = unwrapExports(cardboardVrDisplay); - var version = "0.10.12"; - var DefaultConfig = { - ADDITIONAL_VIEWERS: [], - DEFAULT_VIEWER: '', - PROVIDE_MOBILE_VRDISPLAY: true, - MOBILE_WAKE_LOCK: true, - DEBUG: false, - DPDB_URL: 'https://dpdb.webvr.rocks/dpdb.json', - K_FILTER: 0.98, - PREDICTION_TIME_S: 0.040, - CARDBOARD_UI_DISABLED: false, - ROTATE_INSTRUCTIONS_DISABLED: false, - YAW_ONLY: false, - BUFFER_SCALE: 0.5, - DIRTY_SUBMIT_FRAME_BINDINGS: false - }; - function WebVRPolyfill(config) { - this.config = extend(extend({}, DefaultConfig), config); - this.polyfillDisplays = []; - this.enabled = false; - this.hasNative = 'getVRDisplays' in navigator; - this.native = {}; - this.native.getVRDisplays = navigator.getVRDisplays; - this.native.VRFrameData = window.VRFrameData; - this.native.VRDisplay = window.VRDisplay; - if (!this.hasNative || this.config.PROVIDE_MOBILE_VRDISPLAY && isMobile()) { - this.enable(); - this.getVRDisplays().then(function (displays) { - if (displays && displays[0] && displays[0].fireVRDisplayConnect_) { - displays[0].fireVRDisplayConnect_(); - } - }); - } - } - WebVRPolyfill.prototype.getPolyfillDisplays = function () { - if (this._polyfillDisplaysPopulated) { - return this.polyfillDisplays; - } - if (isMobile()) { - var vrDisplay = new CardboardVRDisplay({ - ADDITIONAL_VIEWERS: this.config.ADDITIONAL_VIEWERS, - DEFAULT_VIEWER: this.config.DEFAULT_VIEWER, - MOBILE_WAKE_LOCK: this.config.MOBILE_WAKE_LOCK, - DEBUG: this.config.DEBUG, - DPDB_URL: this.config.DPDB_URL, - CARDBOARD_UI_DISABLED: this.config.CARDBOARD_UI_DISABLED, - K_FILTER: this.config.K_FILTER, - PREDICTION_TIME_S: this.config.PREDICTION_TIME_S, - ROTATE_INSTRUCTIONS_DISABLED: this.config.ROTATE_INSTRUCTIONS_DISABLED, - YAW_ONLY: this.config.YAW_ONLY, - BUFFER_SCALE: this.config.BUFFER_SCALE, - DIRTY_SUBMIT_FRAME_BINDINGS: this.config.DIRTY_SUBMIT_FRAME_BINDINGS - }); - this.polyfillDisplays.push(vrDisplay); - } - this._polyfillDisplaysPopulated = true; - return this.polyfillDisplays; - }; - WebVRPolyfill.prototype.enable = function () { - this.enabled = true; - if (this.hasNative && this.native.VRFrameData) { - var NativeVRFrameData = this.native.VRFrameData; - var nativeFrameData = new this.native.VRFrameData(); - var nativeGetFrameData = this.native.VRDisplay.prototype.getFrameData; - window.VRDisplay.prototype.getFrameData = function (frameData) { - if (frameData instanceof NativeVRFrameData) { - nativeGetFrameData.call(this, frameData); - return; - } - nativeGetFrameData.call(this, nativeFrameData); - frameData.pose = nativeFrameData.pose; - copyArray(nativeFrameData.leftProjectionMatrix, frameData.leftProjectionMatrix); - copyArray(nativeFrameData.rightProjectionMatrix, frameData.rightProjectionMatrix); - copyArray(nativeFrameData.leftViewMatrix, frameData.leftViewMatrix); - copyArray(nativeFrameData.rightViewMatrix, frameData.rightViewMatrix); - }; - } - navigator.getVRDisplays = this.getVRDisplays.bind(this); - window.VRDisplay = CardboardVRDisplay.VRDisplay; - window.VRFrameData = CardboardVRDisplay.VRFrameData; - }; - WebVRPolyfill.prototype.getVRDisplays = function () { - var _this = this; - var config = this.config; - if (!this.hasNative) { - return Promise.resolve(this.getPolyfillDisplays()); - } - return this.native.getVRDisplays.call(navigator).then(function (nativeDisplays) { - return nativeDisplays.length > 0 ? nativeDisplays : _this.getPolyfillDisplays(); - }); - }; - WebVRPolyfill.version = version; - WebVRPolyfill.VRFrameData = CardboardVRDisplay.VRFrameData; - WebVRPolyfill.VRDisplay = CardboardVRDisplay.VRDisplay; - var webvrPolyfill = Object.freeze({ - default: WebVRPolyfill - }); - var require$$0 = webvrPolyfill && WebVRPolyfill || webvrPolyfill; - if (typeof commonjsGlobal !== 'undefined' && commonjsGlobal.window) { - if (!commonjsGlobal.document) { - commonjsGlobal.document = commonjsGlobal.window.document; - } - if (!commonjsGlobal.navigator) { - commonjsGlobal.navigator = commonjsGlobal.window.navigator; - } - } - var src = require$$0; - return src; -}); - -/***/ }), - -/***/ "./node_modules/word-wrapper/index.js": -/*!********************************************!*\ - !*** ./node_modules/word-wrapper/index.js ***! - \********************************************/ -/***/ ((module) => { - -var newline = /\n/; -var newlineChar = '\n'; -var whitespace = /\s/; -module.exports = function (text, opt) { - var lines = module.exports.lines(text, opt); - return lines.map(function (line) { - return text.substring(line.start, line.end); - }).join('\n'); -}; -module.exports.lines = function wordwrap(text, opt) { - opt = opt || {}; - - //zero width results in nothing visible - if (opt.width === 0 && opt.mode !== 'nowrap') return []; - text = text || ''; - var width = typeof opt.width === 'number' ? opt.width : Number.MAX_VALUE; - var start = Math.max(0, opt.start || 0); - var end = typeof opt.end === 'number' ? opt.end : text.length; - var mode = opt.mode; - var measure = opt.measure || monospace; - if (mode === 'pre') return pre(measure, text, start, end, width);else return greedy(measure, text, start, end, width, mode); -}; -function idxOf(text, chr, start, end) { - var idx = text.indexOf(chr, start); - if (idx === -1 || idx > end) return end; - return idx; -} -function isWhitespace(chr) { - return whitespace.test(chr); -} -function pre(measure, text, start, end, width) { - var lines = []; - var lineStart = start; - for (var i = start; i < end && i < text.length; i++) { - var chr = text.charAt(i); - var isNewline = newline.test(chr); - - //If we've reached a newline, then step down a line - //Or if we've reached the EOF - if (isNewline || i === end - 1) { - var lineEnd = isNewline ? i : i + 1; - var measured = measure(text, lineStart, lineEnd, width); - lines.push(measured); - lineStart = i + 1; - } - } - return lines; -} -function greedy(measure, text, start, end, width, mode) { - //A greedy word wrapper based on LibGDX algorithm - //https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/BitmapFontCache.java - var lines = []; - var testWidth = width; - //if 'nowrap' is specified, we only wrap on newline chars - if (mode === 'nowrap') testWidth = Number.MAX_VALUE; - while (start < end && start < text.length) { - //get next newline position - var newLine = idxOf(text, newlineChar, start, end); - - //eat whitespace at start of line - while (start < newLine) { - if (!isWhitespace(text.charAt(start))) break; - start++; - } - - //determine visible # of glyphs for the available width - var measured = measure(text, start, newLine, testWidth); - var lineEnd = start + (measured.end - measured.start); - var nextStart = lineEnd + newlineChar.length; - - //if we had to cut the line before the next newline... - if (lineEnd < newLine) { - //find char to break on - while (lineEnd > start) { - if (isWhitespace(text.charAt(lineEnd))) break; - lineEnd--; - } - if (lineEnd === start) { - if (nextStart > start + newlineChar.length) nextStart--; - lineEnd = nextStart; // If no characters to break, show all. - } else { - nextStart = lineEnd; - //eat whitespace at end of line - while (lineEnd > start) { - if (!isWhitespace(text.charAt(lineEnd - newlineChar.length))) break; - lineEnd--; - } - } - } - if (lineEnd >= start) { - var result = measure(text, start, lineEnd, testWidth); - lines.push(result); - } - start = nextStart; - } - return lines; -} - -//determines the visible number of glyphs within a given width -function monospace(text, start, end, width) { - var glyphs = Math.min(width, end - start); - return { - start: start, - end: start + glyphs - }; -} - -/***/ }), - -/***/ "./node_modules/xhr/index.js": -/*!***********************************!*\ - !*** ./node_modules/xhr/index.js ***! - \***********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var window = __webpack_require__(/*! global/window */ "./node_modules/global/window.js"); -var isFunction = __webpack_require__(/*! is-function */ "./node_modules/is-function/index.js"); -var parseHeaders = __webpack_require__(/*! parse-headers */ "./node_modules/parse-headers/parse-headers.js"); -var xtend = __webpack_require__(/*! xtend */ "./node_modules/xtend/immutable.js"); -module.exports = createXHR; -// Allow use of default import syntax in TypeScript -module.exports["default"] = createXHR; -createXHR.XMLHttpRequest = window.XMLHttpRequest || noop; -createXHR.XDomainRequest = "withCredentials" in new createXHR.XMLHttpRequest() ? createXHR.XMLHttpRequest : window.XDomainRequest; -forEachArray(["get", "put", "post", "patch", "head", "delete"], function (method) { - createXHR[method === "delete" ? "del" : method] = function (uri, options, callback) { - options = initParams(uri, options, callback); - options.method = method.toUpperCase(); - return _createXHR(options); - }; -}); -function forEachArray(array, iterator) { - for (var i = 0; i < array.length; i++) { - iterator(array[i]); - } -} -function isEmpty(obj) { - for (var i in obj) { - if (obj.hasOwnProperty(i)) return false; - } - return true; -} -function initParams(uri, options, callback) { - var params = uri; - if (isFunction(options)) { - callback = options; - if (typeof uri === "string") { - params = { - uri: uri - }; - } - } else { - params = xtend(options, { - uri: uri - }); - } - params.callback = callback; - return params; -} -function createXHR(uri, options, callback) { - options = initParams(uri, options, callback); - return _createXHR(options); -} -function _createXHR(options) { - if (typeof options.callback === "undefined") { - throw new Error("callback argument missing"); - } - var called = false; - var callback = function cbOnce(err, response, body) { - if (!called) { - called = true; - options.callback(err, response, body); - } - }; - function readystatechange() { - if (xhr.readyState === 4) { - setTimeout(loadFunc, 0); - } - } - function getBody() { - // Chrome with requestType=blob throws errors arround when even testing access to responseText - var body = undefined; - if (xhr.response) { - body = xhr.response; - } else { - body = xhr.responseText || getXml(xhr); - } - if (isJson) { - try { - body = JSON.parse(body); - } catch (e) {} - } - return body; - } - function errorFunc(evt) { - clearTimeout(timeoutTimer); - if (!(evt instanceof Error)) { - evt = new Error("" + (evt || "Unknown XMLHttpRequest Error")); - } - evt.statusCode = 0; - return callback(evt, failureResponse); - } - - // will load the data & process the response in a special response object - function loadFunc() { - if (aborted) return; - var status; - clearTimeout(timeoutTimer); - if (options.useXDR && xhr.status === undefined) { - //IE8 CORS GET successful response doesn't have a status field, but body is fine - status = 200; - } else { - status = xhr.status === 1223 ? 204 : xhr.status; - } - var response = failureResponse; - var err = null; - if (status !== 0) { - response = { - body: getBody(), - statusCode: status, - method: method, - headers: {}, - url: uri, - rawRequest: xhr - }; - if (xhr.getAllResponseHeaders) { - //remember xhr can in fact be XDR for CORS in IE - response.headers = parseHeaders(xhr.getAllResponseHeaders()); - } - } else { - err = new Error("Internal XMLHttpRequest Error"); - } - return callback(err, response, response.body); - } - var xhr = options.xhr || null; - if (!xhr) { - if (options.cors || options.useXDR) { - xhr = new createXHR.XDomainRequest(); - } else { - xhr = new createXHR.XMLHttpRequest(); - } - } - var key; - var aborted; - var uri = xhr.url = options.uri || options.url; - var method = xhr.method = options.method || "GET"; - var body = options.body || options.data; - var headers = xhr.headers = options.headers || {}; - var sync = !!options.sync; - var isJson = false; - var timeoutTimer; - var failureResponse = { - body: undefined, - headers: {}, - statusCode: 0, - method: method, - url: uri, - rawRequest: xhr - }; - if ("json" in options && options.json !== false) { - isJson = true; - headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user - if (method !== "GET" && method !== "HEAD") { - headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user - body = JSON.stringify(options.json === true ? body : options.json); - } - } - xhr.onreadystatechange = readystatechange; - xhr.onload = loadFunc; - xhr.onerror = errorFunc; - // IE9 must have onprogress be set to a unique function. - xhr.onprogress = function () { - // IE must die - }; - xhr.onabort = function () { - aborted = true; - }; - xhr.ontimeout = errorFunc; - xhr.open(method, uri, !sync, options.username, options.password); - //has to be after open - if (!sync) { - xhr.withCredentials = !!options.withCredentials; - } - // Cannot set timeout with sync request - // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly - // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent - if (!sync && options.timeout > 0) { - timeoutTimer = setTimeout(function () { - if (aborted) return; - aborted = true; //IE9 may still call readystatechange - xhr.abort("timeout"); - var e = new Error("XMLHttpRequest timeout"); - e.code = "ETIMEDOUT"; - errorFunc(e); - }, options.timeout); - } - if (xhr.setRequestHeader) { - for (key in headers) { - if (headers.hasOwnProperty(key)) { - xhr.setRequestHeader(key, headers[key]); - } - } - } else if (options.headers && !isEmpty(options.headers)) { - throw new Error("Headers cannot be set on an XDomainRequest object"); - } - if ("responseType" in options) { - xhr.responseType = options.responseType; - } - if ("beforeSend" in options && typeof options.beforeSend === "function") { - options.beforeSend(xhr); - } - - // Microsoft Edge browser sends "undefined" when send is called with undefined value. - // XMLHttpRequest spec says to pass null as body to indicate no body - // See https://github.com/naugtur/xhr/issues/100. - xhr.send(body || null); - return xhr; -} -function getXml(xhr) { - // xhr.responseXML will throw Exception "InvalidStateError" or "DOMException" - // See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseXML. - try { - if (xhr.responseType === "document") { - return xhr.responseXML; - } - var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"; - if (xhr.responseType === "" && !firefoxBugTakenEffect) { - return xhr.responseXML; - } - } catch (e) {} - return null; -} -function noop() {} - -/***/ }), - -/***/ "./node_modules/xml-parse-from-string/index.js": -/*!*****************************************************!*\ - !*** ./node_modules/xml-parse-from-string/index.js ***! - \*****************************************************/ -/***/ ((module) => { - -module.exports = function xmlparser() { - //common browsers - if (typeof self.DOMParser !== 'undefined') { - return function (str) { - var parser = new self.DOMParser(); - return parser.parseFromString(str, 'application/xml'); - }; - } - - //IE8 fallback - if (typeof self.ActiveXObject !== 'undefined' && new self.ActiveXObject('Microsoft.XMLDOM')) { - return function (str) { - var xmlDoc = new self.ActiveXObject("Microsoft.XMLDOM"); - xmlDoc.async = "false"; - xmlDoc.loadXML(str); - return xmlDoc; - }; - } - - //last resort fallback - return function (str) { - var div = document.createElement('div'); - div.innerHTML = str; - return div; - }; -}(); - -/***/ }), - -/***/ "./node_modules/xtend/immutable.js": -/*!*****************************************!*\ - !*** ./node_modules/xtend/immutable.js ***! - \*****************************************/ -/***/ ((module) => { - -module.exports = extend; -var hasOwnProperty = Object.prototype.hasOwnProperty; -function extend() { - var target = {}; - for (var i = 0; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - return target; -} - -/***/ }), - -/***/ "./src/components/anchored.js": -/*!************************************!*\ - !*** ./src/components/anchored.js ***! - \************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE, XRRigidTransform, localStorage */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var warn = utils.debug('components:anchored:warn'); - -/** - * Anchored component. - * Feature only available in browsers that implement the WebXR anchors module. - * Once anchored the entity remains to a fixed position in real-world space. - * If the anchor is persistent, the anchor positioned remains across sessions or until the browser data is cleared. - */ -module.exports.Component = registerComponent('anchored', { - schema: { - persistent: { - default: false - } - }, - init: function () { - var sceneEl = this.el.sceneEl; - var webxrData = sceneEl.getAttribute('webxr'); - var optionalFeaturesArray = webxrData.optionalFeatures; - if (optionalFeaturesArray.indexOf('anchors') === -1) { - optionalFeaturesArray.push('anchors'); - this.el.sceneEl.setAttribute('webxr', webxrData); - } - this.auxQuaternion = new THREE.Quaternion(); - this.onEnterVR = this.onEnterVR.bind(this); - this.el.sceneEl.addEventListener('enter-vr', this.onEnterVR); - }, - onEnterVR: function () { - this.anchor = undefined; - this.requestPersistentAnchorPending = this.data.persistent; - this.requestAnchorPending = !this.data.persistent; - }, - tick: function () { - var sceneEl = this.el.sceneEl; - var xrManager = sceneEl.renderer.xr; - var frame; - var refSpace; - var pose; - var object3D = this.el.object3D; - if (!sceneEl.is('ar-mode') && !sceneEl.is('vr-mode')) { - return; - } - if (!this.anchor && this.requestPersistentAnchorPending) { - this.restorePersistentAnchor(); - } - if (!this.anchor && this.requestAnchorPending) { - this.createAnchor(); - } - if (!this.anchor) { - return; - } - frame = sceneEl.frame; - refSpace = xrManager.getReferenceSpace(); - pose = frame.getPose(this.anchor.anchorSpace, refSpace); - object3D.matrix.elements = pose.transform.matrix; - object3D.matrix.decompose(object3D.position, object3D.rotation, object3D.scale); - }, - createAnchor: async function createAnchor(position, quaternion) { - var sceneEl = this.el.sceneEl; - var xrManager = sceneEl.renderer.xr; - var frame; - var referenceSpace; - var anchorPose; - var anchor; - var object3D = this.el.object3D; - position = position || object3D.position; - quaternion = quaternion || this.auxQuaternion.setFromEuler(object3D.rotation); - if (!anchorsSupported(sceneEl)) { - warn('This browser doesn\'t support the WebXR anchors module'); - return; - } - if (this.anchor) { - this.deleteAnchor(); - } - frame = sceneEl.frame; - referenceSpace = xrManager.getReferenceSpace(); - anchorPose = new XRRigidTransform({ - x: position.x, - y: position.y, - z: position.z - }, { - x: quaternion.x, - y: quaternion.y, - z: quaternion.z, - w: quaternion.w - }); - this.requestAnchorPending = false; - anchor = await frame.createAnchor(anchorPose, referenceSpace); - if (this.data.persistent) { - if (this.el.id) { - this.persistentHandle = await anchor.requestPersistentHandle(); - localStorage.setItem(this.el.id, this.persistentHandle); - } else { - warn('The anchor won\'t be persisted because the entity has no assigned id.'); - } - } - sceneEl.object3D.attach(this.el.object3D); - this.anchor = anchor; - }, - restorePersistentAnchor: async function restorePersistentAnchor() { - var xrManager = this.el.sceneEl.renderer.xr; - var session = xrManager.getSession(); - var persistentAnchors = session.persistentAnchors; - var storedPersistentHandle; - this.requestPersistentAnchorPending = false; - if (!this.el.id) { - warn('The entity associated to the persistent anchor cannot be retrieved because it doesn\'t have an assigned id.'); - this.requestAnchorPending = true; - return; - } - if (persistentAnchors) { - storedPersistentHandle = localStorage.getItem(this.el.id); - for (var i = 0; i < persistentAnchors.length; ++i) { - if (storedPersistentHandle !== persistentAnchors[i]) { - continue; - } - this.anchor = await session.restorePersistentAnchor(persistentAnchors[i]); - if (this.anchor) { - this.persistentHandle = persistentAnchors[i]; - } - break; - } - if (!this.anchor) { - this.requestAnchorPending = true; - } - } else { - this.requestPersistentAnchorPending = true; - } - }, - deleteAnchor: function () { - var xrManager; - var session; - var anchor = this.anchor; - if (!anchor) { - return; - } - xrManager = this.el.sceneEl.renderer.xr; - session = xrManager.getSession(); - anchor.delete(); - this.el.sceneEl.object3D.add(this.el.object3D); - if (this.persistentHandle) { - session.deletePersistentAnchor(this.persistentHandle); - } - this.anchor = undefined; - } -}); -function anchorsSupported(sceneEl) { - var xrManager = sceneEl.renderer.xr; - var session = xrManager.getSession(); - return session && session.restorePersistentAnchor; -} - -/***/ }), - -/***/ "./src/components/animation.js": -/*!*************************************!*\ - !*** ./src/components/animation.js ***! - \*************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var anime = (__webpack_require__(/*! super-animejs */ "./node_modules/super-animejs/lib/anime.es.js")["default"]); -var components = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").components); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var utils = __webpack_require__(/*! ../utils */ "./src/utils/index.js"); -var colorHelperFrom = new THREE.Color(); -var colorHelperTo = new THREE.Color(); -var getComponentProperty = utils.entity.getComponentProperty; -var setComponentProperty = utils.entity.setComponentProperty; -var splitCache = {}; -var TYPE_COLOR = 'color'; -var PROP_POSITION = 'position'; -var PROP_ROTATION = 'rotation'; -var PROP_SCALE = 'scale'; -var STRING_COMPONENTS = 'components'; -var STRING_OBJECT3D = 'object3D'; - -/** - * Animation component for A-Frame using anime.js. - * - * The component manually controls the tick by setting `autoplay: false` on anime.js and - * manually * calling `animation.tick()` in the tick handler. To pause or resume, we toggle a - * boolean * flag * `isAnimationPlaying`. - * - * anime.js animation config for tweenining Javascript objects and values works as: - * - * config = { - * targets: {foo: 0.0, bar: '#000'}, - * foo: 1.0, - * bar: '#FFF' - * } - * - * The above will tween each property in `targets`. The `to` values are set in the root of - * the config. - * - * @member {object} animation - anime.js instance. - * @member {boolean} animationIsPlaying - Control if animation is playing. - */ -module.exports.Component = registerComponent('animation', { - schema: { - autoplay: { - default: true - }, - delay: { - default: 0 - }, - dir: { - default: '' - }, - dur: { - default: 1000 - }, - easing: { - default: 'easeInQuad' - }, - elasticity: { - default: 400 - }, - enabled: { - default: true - }, - from: { - default: '' - }, - loop: { - default: 0, - parse: function (value) { - // Boolean or integer. - if (value === true || value === 'true') { - return true; - } - if (value === false || value === 'false') { - return false; - } - return parseInt(value, 10); - } - }, - property: { - default: '' - }, - startEvents: { - type: 'array' - }, - pauseEvents: { - type: 'array' - }, - resumeEvents: { - type: 'array' - }, - round: { - default: false - }, - to: { - default: '' - }, - type: { - default: '' - }, - isRawProperty: { - default: false - } - }, - multiple: true, - init: function () { - var self = this; - this.eventDetail = { - name: this.attrName - }; - this.time = 0; - this.animation = null; - this.animationIsPlaying = false; - this.onStartEvent = this.onStartEvent.bind(this); - this.beginAnimation = this.beginAnimation.bind(this); - this.pauseAnimation = this.pauseAnimation.bind(this); - this.resumeAnimation = this.resumeAnimation.bind(this); - this.fromColor = {}; - this.toColor = {}; - this.targets = {}; - this.targetsArray = []; - this.updateConfigForDefault = this.updateConfigForDefault.bind(this); - this.updateConfigForRawColor = this.updateConfigForRawColor.bind(this); - this.config = { - complete: function () { - self.animationIsPlaying = false; - self.el.emit('animationcomplete', self.eventDetail, false); - if (self.id) { - self.el.emit('animationcomplete__' + self.id, self.eventDetail, false); - } - } - }; - }, - update: function (oldData) { - var config = this.config; - var data = this.data; - this.animationIsPlaying = false; - if (!this.data.enabled) { - return; - } - if (!data.property) { - return; - } - - // Base config. - config.autoplay = false; - config.direction = data.dir; - config.duration = data.dur; - config.easing = data.easing; - config.elasticity = data.elasticity; - config.loop = data.loop; - config.round = data.round; - - // Start new animation. - this.createAndStartAnimation(); - }, - tick: function (t, dt) { - if (!this.animationIsPlaying) { - return; - } - this.time += dt; - this.animation.tick(this.time); - }, - remove: function () { - this.pauseAnimation(); - this.removeEventListeners(); - }, - pause: function () { - this.paused = true; - this.pausedWasPlaying = this.animationIsPlaying; - this.pauseAnimation(); - this.removeEventListeners(); - }, - /** - * `play` handler only for resuming scene. - */ - play: function () { - if (!this.paused) { - return; - } - this.paused = false; - this.addEventListeners(); - if (this.pausedWasPlaying) { - this.resumeAnimation(); - this.pausedWasPlaying = false; - } - }, - /** - * Start animation from scratch. - */ - createAndStartAnimation: function () { - var data = this.data; - this.updateConfig(); - this.animationIsPlaying = false; - this.animation = anime(this.config); - this.animation.began = true; - this.removeEventListeners(); - this.addEventListeners(); - - // Wait for start events for animation. - if (!data.autoplay || data.startEvents && data.startEvents.length) { - return; - } - - // Delay animation. - if (data.delay) { - setTimeout(this.beginAnimation, data.delay); - return; - } - - // Play animation. - this.beginAnimation(); - }, - /** - * This is before animation start (including from startEvents). - * Set to initial state (config.from, time = 0, seekTime = 0). - */ - beginAnimation: function () { - this.updateConfig(); - this.animation.began = true; - this.time = 0; - this.animationIsPlaying = true; - this.stopRelatedAnimations(); - this.el.emit('animationbegin', this.eventDetail, false); - }, - pauseAnimation: function () { - this.animationIsPlaying = false; - }, - resumeAnimation: function () { - this.animationIsPlaying = true; - }, - /** - * startEvents callback. - */ - onStartEvent: function () { - if (!this.data.enabled) { - return; - } - this.updateConfig(); - if (this.animation) { - this.animation.pause(); - } - this.animation = anime(this.config); - - // Include the delay before each start event. - if (this.data.delay) { - setTimeout(this.beginAnimation, this.data.delay); - return; - } - this.beginAnimation(); - }, - /** - * rawProperty: true and type: color; - */ - updateConfigForRawColor: function () { - var config = this.config; - var data = this.data; - var el = this.el; - var from; - var key; - var to; - if (this.waitComponentInitRawProperty(this.updateConfigForRawColor)) { - return; - } - from = data.from === '' ? getRawProperty(el, data.property) : data.from; - to = data.to; - - // Use r/g/b vector for color type. - this.setColorConfig(from, to); - from = this.fromColor; - to = this.toColor; - this.targetsArray.length = 0; - this.targetsArray.push(from); - config.targets = this.targetsArray; - for (key in to) { - config[key] = to[key]; - } - config.update = function () { - var lastValue = {}; - return function (anim) { - var value; - value = anim.animatables[0].target; - // For animation timeline. - if (value.r === lastValue.r && value.g === lastValue.g && value.b === lastValue.b) { - return; - } - setRawProperty(el, data.property, value, data.type); - }; - }(); - }, - /** - * Stuff property into generic `property` key. - */ - updateConfigForDefault: function () { - var config = this.config; - var data = this.data; - var el = this.el; - var from; - var isBoolean; - var isNumber; - var to; - if (this.waitComponentInitRawProperty(this.updateConfigForDefault)) { - return; - } - if (data.from === '') { - // Infer from. - from = isRawProperty(data) ? getRawProperty(el, data.property) : getComponentProperty(el, data.property); - } else { - // Explicit from. - from = data.from; - } - to = data.to; - isNumber = !isNaN(from || to); - if (isNumber) { - from = parseFloat(from); - to = parseFloat(to); - } else { - from = from ? from.toString() : from; - to = to ? to.toString() : to; - } - - // Convert booleans to integer to allow boolean flipping. - isBoolean = data.to === 'true' || data.to === 'false' || data.to === true || data.to === false; - if (isBoolean) { - from = data.from === 'true' || data.from === true ? 1 : 0; - to = data.to === 'true' || data.to === true ? 1 : 0; - } - this.targets.aframeProperty = from; - config.targets = this.targets; - config.aframeProperty = to; - config.update = function () { - var lastValue; - return function (anim) { - var value; - value = anim.animatables[0].target.aframeProperty; - - // Need to do a last value check for animation timeline since all the tweening - // begins simultaneously even if the value has not changed. Also better for perf - // anyway. - if (value === lastValue) { - return; - } - lastValue = value; - if (isBoolean) { - value = value >= 1; - } - if (isRawProperty(data)) { - setRawProperty(el, data.property, value, data.type); - } else { - setComponentProperty(el, data.property, value); - } - }; - }(); - }, - /** - * Extend x/y/z/w onto the config. - * Update vector by modifying object3D. - */ - updateConfigForVector: function () { - var config = this.config; - var data = this.data; - var el = this.el; - var key; - var from; - var to; - - // Parse coordinates. - from = data.from !== '' ? utils.coordinates.parse(data.from) // If data.from defined, use that. - : getComponentProperty(el, data.property); // If data.from not defined, get on the fly. - to = utils.coordinates.parse(data.to); - if (data.property === PROP_ROTATION) { - toRadians(from); - toRadians(to); - } - - // Set to and from. - this.targetsArray.length = 0; - this.targetsArray.push(from); - config.targets = this.targetsArray; - for (key in to) { - config[key] = to[key]; - } - - // If animating object3D transformation, run more optimized updater. - if (data.property === PROP_POSITION || data.property === PROP_ROTATION || data.property === PROP_SCALE) { - config.update = function () { - var lastValue = {}; - return function (anim) { - var value = anim.animatables[0].target; - - // For animation timeline. - if (value.x === lastValue.x && value.y === lastValue.y && value.z === lastValue.z) { - return; - } - lastValue.x = value.x; - lastValue.y = value.y; - lastValue.z = value.z; - el.object3D[data.property].set(value.x, value.y, value.z); - }; - }(); - return; - } - - // Animating some vector. - config.update = function () { - var lastValue = {}; - return function (anim) { - var value = anim.animatables[0].target; - - // Animate rotation through radians. - // For animation timeline. - if (value.x === lastValue.x && value.y === lastValue.y && value.z === lastValue.z) { - return; - } - lastValue.x = value.x; - lastValue.y = value.y; - lastValue.z = value.z; - setComponentProperty(el, data.property, value); - }; - }(); - }, - /** - * Update the config before each run. - */ - updateConfig: function () { - var propType; - - // Route config type. - propType = getPropertyType(this.el, this.data.property); - if (isRawProperty(this.data) && this.data.type === TYPE_COLOR) { - this.updateConfigForRawColor(); - } else if (propType === 'vec2' || propType === 'vec3' || propType === 'vec4') { - this.updateConfigForVector(); - } else { - this.updateConfigForDefault(); - } - }, - /** - * Wait for component to initialize. - */ - waitComponentInitRawProperty: function (cb) { - var componentName; - var data = this.data; - var el = this.el; - var self = this; - if (data.from !== '') { - return false; - } - if (!data.property.startsWith(STRING_COMPONENTS)) { - return false; - } - componentName = splitDot(data.property)[1]; - if (el.components[componentName]) { - return false; - } - el.addEventListener('componentinitialized', function wait(evt) { - if (evt.detail.name !== componentName) { - return; - } - cb(); - // Since the config was created async, create the animation now since we missed it - // earlier. - self.animation = anime(self.config); - el.removeEventListener('componentinitialized', wait); - }); - return true; - }, - /** - * Make sure two animations on the same property don't fight each other. - * e.g., animation__mouseenter="property: material.opacity" - * animation__mouseleave="property: material.opacity" - */ - stopRelatedAnimations: function () { - var component; - var componentName; - for (componentName in this.el.components) { - component = this.el.components[componentName]; - if (componentName === this.attrName) { - continue; - } - if (component.name !== 'animation') { - continue; - } - if (!component.animationIsPlaying) { - continue; - } - if (component.data.property !== this.data.property) { - continue; - } - component.animationIsPlaying = false; - } - }, - addEventListeners: function () { - var data = this.data; - var el = this.el; - addEventListeners(el, data.startEvents, this.onStartEvent); - addEventListeners(el, data.pauseEvents, this.pauseAnimation); - addEventListeners(el, data.resumeEvents, this.resumeAnimation); - }, - removeEventListeners: function () { - var data = this.data; - var el = this.el; - removeEventListeners(el, data.startEvents, this.onStartEvent); - removeEventListeners(el, data.pauseEvents, this.pauseAnimation); - removeEventListeners(el, data.resumeEvents, this.resumeAnimation); - }, - setColorConfig: function (from, to) { - colorHelperFrom.set(from); - colorHelperTo.set(to); - from = this.fromColor; - to = this.toColor; - from.r = colorHelperFrom.r; - from.g = colorHelperFrom.g; - from.b = colorHelperFrom.b; - to.r = colorHelperTo.r; - to.g = colorHelperTo.g; - to.b = colorHelperTo.b; - } -}); - -/** - * Given property name, check schema to see what type we are animating. - * We just care whether the property is a vector. - */ -function getPropertyType(el, property) { - var component; - var componentName; - var split; - var propertyName; - split = property.split('.'); - componentName = split[0]; - propertyName = split[1]; - component = el.components[componentName] || components[componentName]; - - // Primitives. - if (!component) { - return null; - } - - // Dynamic schema. We only care about vectors anyways. - if (propertyName && !component.schema[propertyName]) { - return null; - } - - // Multi-prop. - if (propertyName) { - return component.schema[propertyName].type; - } - - // Single-prop. - return component.schema.type; -} - -/** - * Convert object to radians. - */ -function toRadians(obj) { - obj.x = THREE.MathUtils.degToRad(obj.x); - obj.y = THREE.MathUtils.degToRad(obj.y); - obj.z = THREE.MathUtils.degToRad(obj.z); -} -function addEventListeners(el, eventNames, handler) { - var i; - for (i = 0; i < eventNames.length; i++) { - el.addEventListener(eventNames[i], handler); - } -} -function removeEventListeners(el, eventNames, handler) { - var i; - for (i = 0; i < eventNames.length; i++) { - el.removeEventListener(eventNames[i], handler); - } -} -function getRawProperty(el, path) { - var i; - var split; - var value; - split = splitDot(path); - value = el; - for (i = 0; i < split.length; i++) { - value = value[split[i]]; - } - if (value === undefined) { - console.log(el); - throw new Error('[animation] property (' + path + ') could not be found'); - } - return value; -} -function setRawProperty(el, path, value, type) { - var i; - var split; - var propertyName; - var targetValue; - if (path.startsWith('object3D.rotation')) { - value = THREE.MathUtils.degToRad(value); - } - - // Walk. - split = splitDot(path); - targetValue = el; - for (i = 0; i < split.length - 1; i++) { - targetValue = targetValue[split[i]]; - } - propertyName = split[split.length - 1]; - - // Raw color. - if (type === TYPE_COLOR) { - if ('r' in targetValue[propertyName]) { - targetValue[propertyName].r = value.r; - targetValue[propertyName].g = value.g; - targetValue[propertyName].b = value.b; - } else { - targetValue[propertyName].x = value.r; - targetValue[propertyName].y = value.g; - targetValue[propertyName].z = value.b; - } - return; - } - targetValue[propertyName] = value; -} -function splitDot(path) { - if (path in splitCache) { - return splitCache[path]; - } - splitCache[path] = path.split('.'); - return splitCache[path]; -} -function isRawProperty(data) { - return data.isRawProperty || data.property.startsWith(STRING_COMPONENTS) || data.property.startsWith(STRING_OBJECT3D); -} - -/***/ }), - -/***/ "./src/components/camera.js": -/*!**********************************!*\ - !*** ./src/components/camera.js ***! - \**********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); - -/** - * Camera component. - * Pairs along with camera system to handle tracking the active camera. - */ -module.exports.Component = registerComponent('camera', { - schema: { - active: { - default: true - }, - far: { - default: 10000 - }, - fov: { - default: 80, - min: 0 - }, - near: { - default: 0.005, - min: 0 - }, - spectator: { - default: false - }, - zoom: { - default: 1, - min: 0 - } - }, - /** - * Initialize three.js camera and add it to the entity. - * Add reference from scene to this entity as the camera. - */ - init: function () { - var camera; - var el = this.el; - - // Create camera. - camera = this.camera = new THREE.PerspectiveCamera(); - el.setObject3D('camera', camera); - }, - /** - * Update three.js camera. - */ - update: function (oldData) { - var data = this.data; - var camera = this.camera; - - // Update properties. - camera.aspect = data.aspect || window.innerWidth / window.innerHeight; - camera.far = data.far; - camera.fov = data.fov; - camera.near = data.near; - camera.zoom = data.zoom; - camera.updateProjectionMatrix(); - this.updateActiveCamera(oldData); - this.updateSpectatorCamera(oldData); - }, - updateActiveCamera: function (oldData) { - var data = this.data; - var el = this.el; - var system = this.system; - // Active property did not change. - if (oldData && oldData.active === data.active || data.spectator) { - return; - } - - // If `active` property changes, or first update, handle active camera with system. - if (data.active && system.activeCameraEl !== el) { - // Camera enabled. Set camera to this camera. - system.setActiveCamera(el); - } else if (!data.active && system.activeCameraEl === el) { - // Camera disabled. Set camera to another camera. - system.disableActiveCamera(); - } - }, - updateSpectatorCamera: function (oldData) { - var data = this.data; - var el = this.el; - var system = this.system; - // spectator property did not change. - if (oldData && oldData.spectator === data.spectator) { - return; - } - - // If `spectator` property changes, or first update, handle spectator camera with system. - if (data.spectator && system.spectatorCameraEl !== el) { - // Camera enabled. Set camera to this camera. - system.setSpectatorCamera(el); - } else if (!data.spectator && system.spectatorCameraEl === el) { - // Camera disabled. Set camera to another camera. - system.disableSpectatorCamera(); - } - }, - /** - * Remove camera on remove (callback). - */ - remove: function () { - this.el.removeObject3D('camera'); - } -}); - -/***/ }), - -/***/ "./src/components/cursor.js": -/*!**********************************!*\ - !*** ./src/components/cursor.js ***! - \**********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE, MouseEvent, TouchEvent */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var EVENTS = { - CLICK: 'click', - FUSING: 'fusing', - MOUSEENTER: 'mouseenter', - MOUSEDOWN: 'mousedown', - MOUSELEAVE: 'mouseleave', - MOUSEUP: 'mouseup' -}; -var STATES = { - FUSING: 'cursor-fusing', - HOVERING: 'cursor-hovering', - HOVERED: 'cursor-hovered' -}; -var CANVAS_EVENTS = { - DOWN: ['mousedown', 'touchstart'], - UP: ['mouseup', 'touchend'] -}; -var WEBXR_EVENTS = { - DOWN: ['selectstart'], - UP: ['selectend'] -}; -var CANVAS_HOVER_CLASS = 'a-mouse-cursor-hover'; - -/** - * Cursor component. Applies the raycaster component specifically for starting the raycaster - * from the camera and pointing from camera's facing direction, and then only returning the - * closest intersection. Cursor can be fine-tuned by setting raycaster properties. - * - * @member {object} fuseTimeout - Timeout to trigger fuse-click. - * @member {Element} cursorDownEl - Entity that was last mousedowned during current click. - * @member {object} intersection - Attributes of the current intersection event, including - * 3D- and 2D-space coordinates. See: http://threejs.org/docs/api/core/Raycaster.html - * @member {Element} intersectedEl - Currently-intersected entity. Used to keep track to - * emit events when unintersecting. - */ -module.exports.Component = registerComponent('cursor', { - dependencies: ['raycaster'], - schema: { - downEvents: { - default: [] - }, - fuse: { - default: utils.device.isMobile() - }, - fuseTimeout: { - default: 1500, - min: 0 - }, - mouseCursorStylesEnabled: { - default: true - }, - upEvents: { - default: [] - }, - rayOrigin: { - default: 'entity', - oneOf: ['mouse', 'entity', 'xrselect'] - } - }, - after: ['tracked-controls'], - multiple: true, - init: function () { - var self = this; - this.fuseTimeout = undefined; - this.cursorDownEl = null; - this.intersectedEl = null; - this.canvasBounds = document.body.getBoundingClientRect(); - this.isCursorDown = false; - this.activeXRInput = null; - - // Debounce. - this.updateCanvasBounds = utils.debounce(function updateCanvasBounds() { - self.canvasBounds = self.el.sceneEl.canvas.getBoundingClientRect(); - }, 500); - this.eventDetail = {}; - this.intersectedEventDetail = { - cursorEl: this.el - }; - - // Bind methods. - this.onCursorDown = this.onCursorDown.bind(this); - this.onCursorUp = this.onCursorUp.bind(this); - this.onIntersection = this.onIntersection.bind(this); - this.onIntersectionCleared = this.onIntersectionCleared.bind(this); - this.onMouseMove = this.onMouseMove.bind(this); - this.onEnterVR = this.onEnterVR.bind(this); - }, - update: function (oldData) { - if (this.data.rayOrigin === oldData.rayOrigin) { - return; - } - this.updateMouseEventListeners(); - }, - tick: function () { - // Update on frame to allow someone to select and mousemove - var frame = this.el.sceneEl.frame; - var inputSource = this.activeXRInput; - if (this.data.rayOrigin === 'xrselect' && frame && inputSource) { - this.onMouseMove({ - frame: frame, - inputSource: inputSource, - type: 'fakeselectevent' - }); - } - }, - play: function () { - this.addEventListeners(); - }, - pause: function () { - this.removeEventListeners(); - }, - remove: function () { - var el = this.el; - el.removeState(STATES.HOVERING); - el.removeState(STATES.FUSING); - clearTimeout(this.fuseTimeout); - if (this.intersectedEl) { - this.intersectedEl.removeState(STATES.HOVERED); - } - this.removeEventListeners(); - }, - addEventListeners: function () { - var canvas; - var data = this.data; - var el = this.el; - var self = this; - function addCanvasListeners() { - canvas = el.sceneEl.canvas; - if (data.downEvents.length || data.upEvents.length) { - return; - } - CANVAS_EVENTS.DOWN.forEach(function (downEvent) { - canvas.addEventListener(downEvent, self.onCursorDown); - }); - CANVAS_EVENTS.UP.forEach(function (upEvent) { - canvas.addEventListener(upEvent, self.onCursorUp); - }); - } - canvas = el.sceneEl.canvas; - if (canvas) { - addCanvasListeners(); - } else { - el.sceneEl.addEventListener('render-target-loaded', addCanvasListeners); - } - data.downEvents.forEach(function (downEvent) { - el.addEventListener(downEvent, self.onCursorDown); - }); - data.upEvents.forEach(function (upEvent) { - el.addEventListener(upEvent, self.onCursorUp); - }); - el.addEventListener('raycaster-intersection', this.onIntersection); - el.addEventListener('raycaster-closest-entity-changed', this.onIntersection); - el.addEventListener('raycaster-intersection-cleared', this.onIntersectionCleared); - el.sceneEl.addEventListener('rendererresize', this.updateCanvasBounds); - el.sceneEl.addEventListener('enter-vr', this.onEnterVR); - window.addEventListener('resize', this.updateCanvasBounds); - window.addEventListener('scroll', this.updateCanvasBounds); - this.updateMouseEventListeners(); - }, - removeEventListeners: function () { - var canvas; - var data = this.data; - var el = this.el; - var self = this; - canvas = el.sceneEl.canvas; - if (canvas && !data.downEvents.length && !data.upEvents.length) { - CANVAS_EVENTS.DOWN.forEach(function (downEvent) { - canvas.removeEventListener(downEvent, self.onCursorDown); - }); - CANVAS_EVENTS.UP.forEach(function (upEvent) { - canvas.removeEventListener(upEvent, self.onCursorUp); - }); - } - data.downEvents.forEach(function (downEvent) { - el.removeEventListener(downEvent, self.onCursorDown); - }); - data.upEvents.forEach(function (upEvent) { - el.removeEventListener(upEvent, self.onCursorUp); - }); - el.removeEventListener('raycaster-intersection', this.onIntersection); - el.removeEventListener('raycaster-intersection-cleared', this.onIntersectionCleared); - canvas.removeEventListener('mousemove', this.onMouseMove); - canvas.removeEventListener('touchstart', this.onMouseMove); - canvas.removeEventListener('touchmove', this.onMouseMove); - el.sceneEl.removeEventListener('rendererresize', this.updateCanvasBounds); - el.sceneEl.removeEventListener('enter-vr', this.onEnterVR); - window.removeEventListener('resize', this.updateCanvasBounds); - window.removeEventListener('scroll', this.updateCanvasBounds); - }, - updateMouseEventListeners: function () { - var canvas; - var el = this.el; - canvas = el.sceneEl.canvas; - canvas.removeEventListener('mousemove', this.onMouseMove); - canvas.removeEventListener('touchmove', this.onMouseMove); - el.setAttribute('raycaster', 'useWorldCoordinates', false); - if (this.data.rayOrigin !== 'mouse') { - return; - } - canvas.addEventListener('mousemove', this.onMouseMove, false); - canvas.addEventListener('touchmove', this.onMouseMove, false); - el.setAttribute('raycaster', 'useWorldCoordinates', true); - this.updateCanvasBounds(); - }, - onMouseMove: function () { - var direction = new THREE.Vector3(); - var mouse = new THREE.Vector2(); - var origin = new THREE.Vector3(); - var rayCasterConfig = { - origin: origin, - direction: direction - }; - return function (evt) { - var bounds = this.canvasBounds; - var camera = this.el.sceneEl.camera; - var left; - var point; - var top; - var frame; - var inputSource; - var referenceSpace; - var pose; - var transform; - camera.parent.updateMatrixWorld(); - - // Calculate mouse position based on the canvas element - if (evt.type === 'touchmove' || evt.type === 'touchstart') { - // Track the first touch for simplicity. - point = evt.touches.item(0); - } else { - point = evt; - } - left = point.clientX - bounds.left; - top = point.clientY - bounds.top; - mouse.x = left / bounds.width * 2 - 1; - mouse.y = -(top / bounds.height) * 2 + 1; - if (this.data.rayOrigin === 'xrselect' && (evt.type === 'selectstart' || evt.type === 'fakeselectevent')) { - frame = evt.frame; - inputSource = evt.inputSource; - referenceSpace = this.el.renderer.xr.getReferenceSpace(); - pose = frame.getPose(inputSource.targetRaySpace, referenceSpace); - transform = pose.transform; - direction.set(0, 0, -1); - direction.applyQuaternion(transform.orientation); - origin.copy(transform.position); - } else if (evt.type === 'fakeselectout') { - direction.set(0, 1, 0); - origin.set(0, 9999, 0); - } else if (camera && camera.isPerspectiveCamera) { - origin.setFromMatrixPosition(camera.matrixWorld); - direction.set(mouse.x, mouse.y, 0.5).unproject(camera).sub(origin).normalize(); - } else if (camera && camera.isOrthographicCamera) { - origin.set(mouse.x, mouse.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera - direction.set(0, 0, -1).transformDirection(camera.matrixWorld); - } else { - console.error('AFRAME.Raycaster: Unsupported camera type: ' + camera.type); - } - this.el.setAttribute('raycaster', rayCasterConfig); - if (evt.type === 'touchmove') { - evt.preventDefault(); - } - }; - }(), - /** - * Trigger mousedown and keep track of the mousedowned entity. - */ - onCursorDown: function (evt) { - this.isCursorDown = true; - // Raycast again for touch. - if (this.data.rayOrigin === 'mouse' && evt.type === 'touchstart') { - this.onMouseMove(evt); - this.el.components.raycaster.checkIntersections(); - evt.preventDefault(); - } - if (this.data.rayOrigin === 'xrselect' && evt.type === 'selectstart') { - this.activeXRInput = evt.inputSource; - this.onMouseMove(evt); - this.el.components.raycaster.checkIntersections(); - - // if something was tapped on don't do ar-hit-test things - if (this.el.components.raycaster.intersectedEls.length && this.el.sceneEl.components['ar-hit-test'] !== undefined && this.el.sceneEl.getAttribute('ar-hit-test').enabled) { - // Cancel the ar-hit-test behaviours and disable the ar-hit-test - this.el.sceneEl.setAttribute('ar-hit-test', 'enabled', false); - this.reenableARHitTest = true; - } - } - this.twoWayEmit(EVENTS.MOUSEDOWN, evt); - this.cursorDownEl = this.intersectedEl; - }, - /** - * Trigger mouseup if: - * - Not fusing (mobile has no mouse). - * - Currently intersecting an entity. - * - Currently-intersected entity is the same as the one when mousedown was triggered, - * in case user mousedowned one entity, dragged to another, and mouseupped. - */ - onCursorUp: function (evt) { - if (!this.isCursorDown) { - return; - } - this.isCursorDown = false; - var data = this.data; - this.twoWayEmit(EVENTS.MOUSEUP, evt); - if (this.reenableARHitTest === true) { - this.el.sceneEl.setAttribute('ar-hit-test', 'enabled', true); - this.reenableARHitTest = undefined; - } - - // If intersected entity has changed since the cursorDown, still emit mouseUp on the - // previously cursorUp entity. - if (this.cursorDownEl && this.cursorDownEl !== this.intersectedEl) { - this.intersectedEventDetail.intersection = null; - this.cursorDownEl.emit(EVENTS.MOUSEUP, this.intersectedEventDetail); - } - if ((!data.fuse || data.rayOrigin === 'mouse' || data.rayOrigin === 'xrselect') && this.intersectedEl && this.cursorDownEl === this.intersectedEl) { - this.twoWayEmit(EVENTS.CLICK, evt); - } - - // if the current xr input stops selecting then make the ray caster point somewhere else - if (data.rayOrigin === 'xrselect' && this.activeXRInput === evt.inputSource) { - this.onMouseMove({ - type: 'fakeselectout' - }); - } - this.activeXRInput = null; - this.cursorDownEl = null; - if (evt.type === 'touchend') { - evt.preventDefault(); - } - }, - /** - * Handle intersection. - */ - onIntersection: function (evt) { - var currentIntersection; - var cursorEl = this.el; - var index; - var intersectedEl; - var intersection; - - // Select closest object, excluding the cursor. - index = evt.detail.els[0] === cursorEl ? 1 : 0; - intersection = evt.detail.intersections[index]; - intersectedEl = evt.detail.els[index]; - - // If cursor is the only intersected object, ignore the event. - if (!intersectedEl) { - return; - } - - // Already intersecting this entity. - if (this.intersectedEl === intersectedEl) { - return; - } - - // Ignore events further away than active intersection. - if (this.intersectedEl) { - currentIntersection = this.el.components.raycaster.getIntersection(this.intersectedEl); - if (currentIntersection && currentIntersection.distance <= intersection.distance) { - return; - } - } - - // Unset current intersection. - this.clearCurrentIntersection(true); - this.setIntersection(intersectedEl, intersection); - }, - /** - * Handle intersection cleared. - */ - onIntersectionCleared: function (evt) { - var clearedEls = evt.detail.clearedEls; - // Check if the current intersection has ended - if (clearedEls.indexOf(this.intersectedEl) === -1) { - return; - } - this.clearCurrentIntersection(); - }, - onEnterVR: function () { - this.clearCurrentIntersection(true); - var xrSession = this.el.sceneEl.xrSession; - var self = this; - if (!xrSession) { - return; - } - if (this.data.rayOrigin === 'mouse') { - return; - } - WEBXR_EVENTS.DOWN.forEach(function (downEvent) { - xrSession.addEventListener(downEvent, self.onCursorDown); - }); - WEBXR_EVENTS.UP.forEach(function (upEvent) { - xrSession.addEventListener(upEvent, self.onCursorUp); - }); - }, - setIntersection: function (intersectedEl, intersection) { - var cursorEl = this.el; - var data = this.data; - var self = this; - - // Already intersecting. - if (this.intersectedEl === intersectedEl) { - return; - } - - // Set new intersection. - this.intersectedEl = intersectedEl; - - // Hovering. - cursorEl.addState(STATES.HOVERING); - intersectedEl.addState(STATES.HOVERED); - this.twoWayEmit(EVENTS.MOUSEENTER); - if (this.data.mouseCursorStylesEnabled && this.data.rayOrigin === 'mouse') { - this.el.sceneEl.canvas.classList.add(CANVAS_HOVER_CLASS); - } - - // Begin fuse if necessary. - if (data.fuseTimeout === 0 || !data.fuse || data.rayOrigin === 'xrselect' || data.rayOrigin === 'mouse') { - return; - } - cursorEl.addState(STATES.FUSING); - this.twoWayEmit(EVENTS.FUSING); - this.fuseTimeout = setTimeout(function fuse() { - cursorEl.removeState(STATES.FUSING); - self.twoWayEmit(EVENTS.CLICK); - }, data.fuseTimeout); - }, - clearCurrentIntersection: function (ignoreRemaining) { - var index; - var intersection; - var intersections; - var cursorEl = this.el; - - // Nothing to be cleared. - if (!this.intersectedEl) { - return; - } - - // No longer hovering (or fusing). - this.intersectedEl.removeState(STATES.HOVERED); - cursorEl.removeState(STATES.HOVERING); - cursorEl.removeState(STATES.FUSING); - this.twoWayEmit(EVENTS.MOUSELEAVE); - if (this.data.mouseCursorStylesEnabled && this.data.rayOrigin === 'mouse') { - this.el.sceneEl.canvas.classList.remove(CANVAS_HOVER_CLASS); - } - - // Unset intersected entity (after emitting the event). - this.intersectedEl = null; - - // Clear fuseTimeout. - clearTimeout(this.fuseTimeout); - - // Set intersection to another raycast element if any. - if (ignoreRemaining === true) { - return; - } - intersections = this.el.components.raycaster.intersections; - if (intersections.length === 0) { - return; - } - // Exclude the cursor. - index = intersections[0].object.el === cursorEl ? 1 : 0; - intersection = intersections[index]; - if (!intersection) { - return; - } - this.setIntersection(intersection.object.el, intersection); - }, - /** - * Helper to emit on both the cursor and the intersected entity (if exists). - */ - twoWayEmit: function (evtName, originalEvent) { - var el = this.el; - var intersectedEl = this.intersectedEl; - var intersection; - function addOriginalEvent(detail, evt) { - if (originalEvent instanceof MouseEvent) { - detail.mouseEvent = originalEvent; - } else if (typeof TouchEvent !== 'undefined' && originalEvent instanceof TouchEvent) { - detail.touchEvent = originalEvent; - } - } - intersection = this.el.components.raycaster.getIntersection(intersectedEl); - this.eventDetail.intersectedEl = intersectedEl; - this.eventDetail.intersection = intersection; - addOriginalEvent(this.eventDetail, originalEvent); - el.emit(evtName, this.eventDetail); - if (!intersectedEl) { - return; - } - this.intersectedEventDetail.intersection = intersection; - addOriginalEvent(this.intersectedEventDetail, originalEvent); - intersectedEl.emit(evtName, this.intersectedEventDetail); - } -}); - -/***/ }), - -/***/ "./src/components/generic-tracked-controller-controls.js": -/*!***************************************************************!*\ - !*** ./src/components/generic-tracked-controller-controls.js ***! - \***************************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var GAMEPAD_ID_PREFIX = 'generic'; - -/** - * Button indices: - * 0 - trigger - * 1 - squeeze - * 2 - touchpad - * 3 - thumbstick - * - * Axis: - * 0 - touchpad - * 1 - thumbstick - * - */ -var INPUT_MAPPING = { - axes: { - touchpad: [0, 1], - thumbstick: [2, 3] - }, - buttons: ['trigger', 'squeeze', 'touchpad', 'thumbstick'] -}; - -/** - * Oculus Go controls. - * Interface with Oculus Go controller and map Gamepad events to - * controller buttons: trackpad, trigger - * Load a controller model and highlight the pressed buttons. - */ -module.exports.Component = registerComponent('generic-tracked-controller-controls', { - schema: { - hand: { - default: '' - }, - // This informs the degenerate arm model. - defaultModel: { - default: true - }, - defaultModelColor: { - default: 'gray' - }, - orientationOffset: { - type: 'vec3' - }, - disabled: { - default: false - } - }, - after: ['tracked-controls'], - /** - * Button IDs: - * 0 - trackpad - * 1 - trigger - */ - mapping: INPUT_MAPPING, - bindMethods: function () { - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - init: function () { - var self = this; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.controllerPresent = false; - this.wasControllerConnected = false; - this.lastControllerCheck = 0; - this.bindMethods(); - - // generic-tracked-controller-controls has the lowest precedence. - // Disable this component if there are more specialized controls components. - this.el.addEventListener('controllerconnected', function (evt) { - if (evt.detail.name === self.name) { - return; - } - self.wasControllerConnected = true; - self.removeEventListeners(); - self.removeControllersUpdateListener(); - }); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - var data = this.data; - var hand = data.hand ? data.hand : undefined; - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, { - hand: hand, - iterateControllerProfiles: true - }); - }, - play: function () { - if (this.wasControllerConnected) { - return; - } - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - - // Do nothing if tracked-controls already set. - // Generic controls have the lowest precedence. - if (this.el.components['tracked-controls']) { - this.removeEventListeners(); - return; - } - el.setAttribute('tracked-controls', { - hand: data.hand, - idPrefix: GAMEPAD_ID_PREFIX, - orientationOffset: data.orientationOffset, - iterateControllerProfiles: true - }); - if (!this.data.defaultModel) { - return; - } - this.initDefaultModel(); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - if (!this.wasControllerConnected) { - return; - } - this.checkIfControllerPresent(); - }, - onButtonChanged: function (evt) { - var button = this.mapping.buttons[evt.detail.id]; - if (!button) return; - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - initDefaultModel: function () { - var modelEl = this.modelEl = document.createElement('a-entity'); - modelEl.setAttribute('geometry', { - primitive: 'sphere', - radius: 0.03 - }); - modelEl.setAttribute('material', { - color: this.data.color - }); - this.el.appendChild(modelEl); - this.el.emit('controllermodelready', { - name: 'generic-tracked-controller-controls', - model: this.modelEl, - rayOrigin: { - origin: { - x: 0, - y: 0, - z: -0.01 - }, - direction: { - x: 0, - y: 0, - z: -1 - } - } - }); - } -}); - -/***/ }), - -/***/ "./src/components/geometry.js": -/*!************************************!*\ - !*** ./src/components/geometry.js ***! - \************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var geometries = (__webpack_require__(/*! ../core/geometry */ "./src/core/geometry.js").geometries); -var geometryNames = (__webpack_require__(/*! ../core/geometry */ "./src/core/geometry.js").geometryNames); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var dummyGeometry = new THREE.BufferGeometry(); - -/** - * Geometry component. Combined with material component to make a mesh in 3D object. - * Extended with registered geometries. - */ -module.exports.Component = registerComponent('geometry', { - schema: { - buffer: { - default: true - }, - primitive: { - default: 'box', - oneOf: geometryNames, - schemaChange: true - }, - skipCache: { - default: false - } - }, - init: function () { - this.geometry = null; - }, - /** - * Talk to geometry system to get or create geometry. - */ - update: function (previousData) { - var data = this.data; - var el = this.el; - var mesh; - var system = this.system; - - // Dispose old geometry if we created one. - if (this.geometry) { - system.unuseGeometry(previousData); - this.geometry = null; - } - - // Create new geometry. - this.geometry = system.getOrCreateGeometry(data); - - // Set on mesh. If mesh does not exist, create it. - mesh = el.getObject3D('mesh'); - if (mesh) { - mesh.geometry = this.geometry; - } else { - mesh = new THREE.Mesh(); - mesh.geometry = this.geometry; - // Default material if not defined on the entity. - if (!this.el.getAttribute('material')) { - mesh.material = new THREE.MeshStandardMaterial({ - color: Math.random() * 0xFFFFFF, - metalness: 0, - roughness: 0.5 - }); - } - el.setObject3D('mesh', mesh); - } - }, - /** - * Tell geometry system that entity is no longer using the geometry. - * Unset the geometry on the mesh - */ - remove: function () { - this.system.unuseGeometry(this.data); - this.el.getObject3D('mesh').geometry = dummyGeometry; - this.geometry = null; - }, - /** - * Update geometry component schema based on geometry type. - */ - updateSchema: function (data) { - var currentGeometryType = this.oldData && this.oldData.primitive; - var newGeometryType = data.primitive; - var schema = geometries[newGeometryType] && geometries[newGeometryType].schema; - - // Geometry has no schema. - if (!schema) { - throw new Error('Unknown geometry schema `' + newGeometryType + '`'); - } - // Nothing has changed. - if (currentGeometryType && currentGeometryType === newGeometryType) { - return; - } - this.extendSchema(schema); - } -}); - -/***/ }), - -/***/ "./src/components/gltf-model.js": -/*!**************************************!*\ - !*** ./src/components/gltf-model.js ***! - \**************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var warn = utils.debug('components:gltf-model:warn'); - -/** - * glTF model loader. - */ -module.exports.Component = registerComponent('gltf-model', { - schema: { - type: 'model' - }, - init: function () { - var self = this; - var dracoLoader = this.system.getDRACOLoader(); - var meshoptDecoder = this.system.getMeshoptDecoder(); - var ktxLoader = this.system.getKTX2Loader(); - this.model = null; - this.loader = new THREE.GLTFLoader(); - if (dracoLoader) { - this.loader.setDRACOLoader(dracoLoader); - } - if (meshoptDecoder) { - this.ready = meshoptDecoder.then(function (meshoptDecoder) { - self.loader.setMeshoptDecoder(meshoptDecoder); - }); - } else { - this.ready = Promise.resolve(); - } - if (ktxLoader) { - this.loader.setKTX2Loader(ktxLoader); - } - }, - update: function () { - var self = this; - var el = this.el; - var src = this.data; - if (!src) { - return; - } - this.remove(); - this.ready.then(function () { - self.loader.load(src, function gltfLoaded(gltfModel) { - self.model = gltfModel.scene || gltfModel.scenes[0]; - self.model.animations = gltfModel.animations; - el.setObject3D('mesh', self.model); - el.emit('model-loaded', { - format: 'gltf', - model: self.model - }); - }, undefined /* onProgress */, function gltfFailed(error) { - var message = error && error.message ? error.message : 'Failed to load glTF model'; - warn(message); - el.emit('model-error', { - format: 'gltf', - src: src - }); - }); - }); - }, - remove: function () { - if (!this.model) { - return; - } - this.el.removeObject3D('mesh'); - } -}); - -/***/ }), - -/***/ "./src/components/grabbable.js": -/*!*************************************!*\ - !*** ./src/components/grabbable.js ***! - \*************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -registerComponent('grabbable', { - init: function () { - this.el.setAttribute('obb-collider', 'centerModel: true'); - } -}); - -/***/ }), - -/***/ "./src/components/hand-controls.js": -/*!*****************************************!*\ - !*** ./src/components/hand-controls.js ***! - \*****************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -// Found at https://github.com/aframevr/assets. -var MODEL_URLS = { - toonLeft: AFRAME_CDN_ROOT + 'controllers/hands/leftHand.glb', - toonRight: AFRAME_CDN_ROOT + 'controllers/hands/rightHand.glb', - lowPolyLeft: AFRAME_CDN_ROOT + 'controllers/hands/leftHandLow.glb', - lowPolyRight: AFRAME_CDN_ROOT + 'controllers/hands/rightHandLow.glb', - highPolyLeft: AFRAME_CDN_ROOT + 'controllers/hands/leftHandHigh.glb', - highPolyRight: AFRAME_CDN_ROOT + 'controllers/hands/rightHandHigh.glb' -}; - -// Poses. -var ANIMATIONS = { - open: 'Open', - // point: grip active, trackpad surface active, trigger inactive. - point: 'Point', - // pointThumb: grip active, trigger inactive, trackpad surface inactive. - pointThumb: 'Point + Thumb', - // fist: grip active, trigger active, trackpad surface active. - fist: 'Fist', - // hold: trigger active, grip inactive. - hold: 'Hold', - // thumbUp: grip active, trigger active, trackpad surface inactive. - thumbUp: 'Thumb Up' -}; - -// Map animation to public events for the API. -var EVENTS = {}; -EVENTS[ANIMATIONS.fist] = 'grip'; -EVENTS[ANIMATIONS.thumbUp] = 'pistol'; -EVENTS[ANIMATIONS.point] = 'pointing'; - -/** - * Hand controls component that abstracts 6DoF controls: - * oculus-touch-controls, vive-controls, windows-motion-controls. - * - * Originally meant to be a sample implementation of applications-specific controls that - * abstracts multiple types of controllers. - * - * Auto-detect appropriate controller. - * Handle common events coming from the detected vendor-specific controls. - * Translate button events to semantic hand-related event names: - * (gripclose, gripopen, thumbup, thumbdown, pointup, pointdown) - * Load hand model with gestures that are applied based on the button pressed. - * - * @property {string} Hand mapping (`left`, `right`). - */ -module.exports.Component = registerComponent('hand-controls', { - schema: { - color: { - default: 'white', - type: 'color' - }, - hand: { - default: 'left' - }, - handModelStyle: { - default: 'lowPoly', - oneOf: ['lowPoly', 'highPoly', 'toon'] - } - }, - after: ['tracked-controls'], - init: function () { - var self = this; - var el = this.el; - // Active buttons populated by events provided by the attached controls. - this.pressedButtons = {}; - this.touchedButtons = {}; - this.loader = new THREE.GLTFLoader(); - this.loader.setCrossOrigin('anonymous'); - this.onGripDown = function () { - self.handleButton('grip', 'down'); - }; - this.onGripUp = function () { - self.handleButton('grip', 'up'); - }; - this.onTrackpadDown = function () { - self.handleButton('trackpad', 'down'); - }; - this.onTrackpadUp = function () { - self.handleButton('trackpad', 'up'); - }; - this.onTrackpadTouchStart = function () { - self.handleButton('trackpad', 'touchstart'); - }; - this.onTrackpadTouchEnd = function () { - self.handleButton('trackpad', 'touchend'); - }; - this.onTriggerDown = function () { - self.handleButton('trigger', 'down'); - }; - this.onTriggerUp = function () { - self.handleButton('trigger', 'up'); - }; - this.onTriggerTouchStart = function () { - self.handleButton('trigger', 'touchstart'); - }; - this.onTriggerTouchEnd = function () { - self.handleButton('trigger', 'touchend'); - }; - this.onGripTouchStart = function () { - self.handleButton('grip', 'touchstart'); - }; - this.onGripTouchEnd = function () { - self.handleButton('grip', 'touchend'); - }; - this.onThumbstickDown = function () { - self.handleButton('thumbstick', 'down'); - }; - this.onThumbstickUp = function () { - self.handleButton('thumbstick', 'up'); - }; - this.onAorXTouchStart = function () { - self.handleButton('AorX', 'touchstart'); - }; - this.onAorXTouchEnd = function () { - self.handleButton('AorX', 'touchend'); - }; - this.onBorYTouchStart = function () { - self.handleButton('BorY', 'touchstart'); - }; - this.onBorYTouchEnd = function () { - self.handleButton('BorY', 'touchend'); - }; - this.onSurfaceTouchStart = function () { - self.handleButton('surface', 'touchstart'); - }; - this.onSurfaceTouchEnd = function () { - self.handleButton('surface', 'touchend'); - }; - this.onControllerConnected = this.onControllerConnected.bind(this); - this.onControllerDisconnected = this.onControllerDisconnected.bind(this); - el.addEventListener('controllerconnected', this.onControllerConnected); - el.addEventListener('controllerdisconnected', this.onControllerDisconnected); - - // Hidden by default. - el.object3D.visible = false; - }, - play: function () { - this.addEventListeners(); - }, - pause: function () { - this.removeEventListeners(); - }, - tick: function (time, delta) { - var mesh = this.el.getObject3D('mesh'); - if (!mesh || !mesh.mixer) { - return; - } - mesh.mixer.update(delta / 1000); - }, - onControllerConnected: function () { - this.el.object3D.visible = true; - }, - onControllerDisconnected: function () { - this.el.object3D.visible = false; - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('gripdown', this.onGripDown); - el.addEventListener('gripup', this.onGripUp); - el.addEventListener('trackpaddown', this.onTrackpadDown); - el.addEventListener('trackpadup', this.onTrackpadUp); - el.addEventListener('trackpadtouchstart', this.onTrackpadTouchStart); - el.addEventListener('trackpadtouchend', this.onTrackpadTouchEnd); - el.addEventListener('triggerdown', this.onTriggerDown); - el.addEventListener('triggerup', this.onTriggerUp); - el.addEventListener('triggertouchstart', this.onTriggerTouchStart); - el.addEventListener('triggertouchend', this.onTriggerTouchEnd); - el.addEventListener('griptouchstart', this.onGripTouchStart); - el.addEventListener('griptouchend', this.onGripTouchEnd); - el.addEventListener('thumbstickdown', this.onThumbstickDown); - el.addEventListener('thumbstickup', this.onThumbstickUp); - el.addEventListener('abuttontouchstart', this.onAorXTouchStart); - el.addEventListener('abuttontouchend', this.onAorXTouchEnd); - el.addEventListener('bbuttontouchstart', this.onBorYTouchStart); - el.addEventListener('bbuttontouchend', this.onBorYTouchEnd); - el.addEventListener('xbuttontouchstart', this.onAorXTouchStart); - el.addEventListener('xbuttontouchend', this.onAorXTouchEnd); - el.addEventListener('ybuttontouchstart', this.onBorYTouchStart); - el.addEventListener('ybuttontouchend', this.onBorYTouchEnd); - el.addEventListener('surfacetouchstart', this.onSurfaceTouchStart); - el.addEventListener('surfacetouchend', this.onSurfaceTouchEnd); - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('gripdown', this.onGripDown); - el.removeEventListener('gripup', this.onGripUp); - el.removeEventListener('trackpaddown', this.onTrackpadDown); - el.removeEventListener('trackpadup', this.onTrackpadUp); - el.removeEventListener('trackpadtouchstart', this.onTrackpadTouchStart); - el.removeEventListener('trackpadtouchend', this.onTrackpadTouchEnd); - el.removeEventListener('triggerdown', this.onTriggerDown); - el.removeEventListener('triggerup', this.onTriggerUp); - el.removeEventListener('triggertouchstart', this.onTriggerTouchStart); - el.removeEventListener('triggertouchend', this.onTriggerTouchEnd); - el.removeEventListener('griptouchstart', this.onGripTouchStart); - el.removeEventListener('griptouchend', this.onGripTouchEnd); - el.removeEventListener('thumbstickdown', this.onThumbstickDown); - el.removeEventListener('thumbstickup', this.onThumbstickUp); - el.removeEventListener('abuttontouchstart', this.onAorXTouchStart); - el.removeEventListener('abuttontouchend', this.onAorXTouchEnd); - el.removeEventListener('bbuttontouchstart', this.onBorYTouchStart); - el.removeEventListener('bbuttontouchend', this.onBorYTouchEnd); - el.removeEventListener('xbuttontouchstart', this.onAorXTouchStart); - el.removeEventListener('xbuttontouchend', this.onAorXTouchEnd); - el.removeEventListener('ybuttontouchstart', this.onBorYTouchStart); - el.removeEventListener('ybuttontouchend', this.onBorYTouchEnd); - el.removeEventListener('surfacetouchstart', this.onSurfaceTouchStart); - el.removeEventListener('surfacetouchend', this.onSurfaceTouchEnd); - }, - /** - * Update handler. More like the `init` handler since the only property is the hand, and - * that won't be changing much. - */ - update: function (previousHand) { - var controlConfiguration; - var el = this.el; - var hand = this.data.hand; - var handModelStyle = this.data.handModelStyle; - var handColor = this.data.color; - var self = this; - - // Get common configuration to abstract different vendor controls. - controlConfiguration = { - hand: hand, - model: false - }; - - // Set model. - if (hand !== previousHand) { - var handmodelUrl = MODEL_URLS[handModelStyle + hand.charAt(0).toUpperCase() + hand.slice(1)]; - this.loader.load(handmodelUrl, function (gltf) { - var mesh = gltf.scene.children[0]; - var handModelOrientationZ = hand === 'left' ? Math.PI / 2 : -Math.PI / 2; - // The WebXR standard defines the grip space such that a cylinder held in a closed hand points - // along the Z axis. The models currently have such a cylinder point along the X-Axis. - var handModelOrientationX = el.sceneEl.hasWebXR ? -Math.PI / 2 : 0; - mesh.mixer = new THREE.AnimationMixer(mesh); - self.clips = gltf.animations; - el.setObject3D('mesh', mesh); - mesh.traverse(function (object) { - if (!object.isMesh) { - return; - } - object.material.color = new THREE.Color(handColor); - }); - mesh.position.set(0, 0, 0); - mesh.rotation.set(handModelOrientationX, 0, handModelOrientationZ); - el.setAttribute('magicleap-controls', controlConfiguration); - el.setAttribute('vive-controls', controlConfiguration); - el.setAttribute('oculus-touch-controls', controlConfiguration); - el.setAttribute('pico-controls', controlConfiguration); - el.setAttribute('windows-motion-controls', controlConfiguration); - el.setAttribute('hp-mixed-reality-controls', controlConfiguration); - }); - } - }, - remove: function () { - this.el.removeObject3D('mesh'); - }, - /** - * Play model animation, based on which button was pressed and which kind of event. - * - * 1. Process buttons. - * 2. Determine gesture (this.determineGesture()). - * 3. Animation gesture (this.animationGesture()). - * 4. Emit gesture events (this.emitGestureEvents()). - * - * @param {string} button - Name of the button. - * @param {string} evt - Type of event for the button (i.e., down/up/touchstart/touchend). - */ - handleButton: function (button, evt) { - var lastGesture; - var isPressed = evt === 'down'; - var isTouched = evt === 'touchstart'; - - // Update objects. - if (evt.indexOf('touch') === 0) { - // Update touch object. - if (isTouched === this.touchedButtons[button]) { - return; - } - this.touchedButtons[button] = isTouched; - } else { - // Update button object. - if (isPressed === this.pressedButtons[button]) { - return; - } - this.pressedButtons[button] = isPressed; - } - - // Determine the gesture. - lastGesture = this.gesture; - this.gesture = this.determineGesture(); - - // Same gesture. - if (this.gesture === lastGesture) { - return; - } - // Animate gesture. - this.animateGesture(this.gesture, lastGesture); - - // Emit events. - this.emitGestureEvents(this.gesture, lastGesture); - }, - /** - * Determine which pose hand should be in considering active and touched buttons. - */ - determineGesture: function () { - var gesture; - var isGripActive = this.pressedButtons.grip; - var isSurfaceActive = this.pressedButtons.surface || this.touchedButtons.surface; - var isTrackpadActive = this.pressedButtons.trackpad || this.touchedButtons.trackpad; - var isTriggerActive = this.pressedButtons.trigger || this.touchedButtons.trigger; - var isABXYActive = this.touchedButtons.AorX || this.touchedButtons.BorY; - var isVive = isViveController(this.el.components['tracked-controls']); - - // Works well with Oculus Touch and Windows Motion Controls, but Vive needs tweaks. - if (isVive) { - if (isGripActive || isTriggerActive) { - gesture = ANIMATIONS.fist; - } else if (isTrackpadActive) { - gesture = ANIMATIONS.point; - } - } else { - if (isGripActive) { - if (isSurfaceActive || isABXYActive || isTrackpadActive) { - gesture = isTriggerActive ? ANIMATIONS.fist : ANIMATIONS.point; - } else { - gesture = isTriggerActive ? ANIMATIONS.thumbUp : ANIMATIONS.pointThumb; - } - } else if (isTriggerActive) { - gesture = ANIMATIONS.hold; - } - } - return gesture; - }, - /** - * Play corresponding clip to a gesture - */ - getClip: function (gesture) { - var clip; - var i; - for (i = 0; i < this.clips.length; i++) { - clip = this.clips[i]; - if (clip.name !== gesture) { - continue; - } - return clip; - } - }, - /** - * Play gesture animation. - * - * @param {string} gesture - Which pose to animate to. If absent, then animate to open. - * @param {string} lastGesture - Previous gesture, to reverse back to open if needed. - */ - animateGesture: function (gesture, lastGesture) { - if (gesture) { - this.playAnimation(gesture || ANIMATIONS.open, lastGesture, false); - return; - } - - // If no gesture, then reverse the current gesture back to open pose. - this.playAnimation(lastGesture, lastGesture, true); - }, - /** - * Emit `hand-controls`-specific events. - */ - emitGestureEvents: function (gesture, lastGesture) { - var el = this.el; - var eventName; - if (lastGesture === gesture) { - return; - } - - // Emit event for lastGesture not inactive. - eventName = getGestureEventName(lastGesture, false); - if (eventName) { - el.emit(eventName); - } - - // Emit event for current gesture now active. - eventName = getGestureEventName(gesture, true); - if (eventName) { - el.emit(eventName); - } - }, - /** - * Play hand animation based on button state. - * - * @param {string} gesture - Name of the animation as specified by the model. - * @param {string} lastGesture - Previous pose. - * @param {boolean} reverse - Whether animation should play in reverse. - */ - playAnimation: function (gesture, lastGesture, reverse) { - var clip; - var fromAction; - var mesh = this.el.getObject3D('mesh'); - var toAction; - if (!mesh) { - return; - } - - // Grab clip action. - clip = this.getClip(gesture); - toAction = mesh.mixer.clipAction(clip); - - // Reverse from gesture to no gesture. - if (reverse) { - toAction.paused = false; - toAction.timeScale = -1; - return; - } - toAction.clampWhenFinished = true; - toAction.loop = THREE.LoopOnce; - toAction.repetitions = 0; - toAction.timeScale = 1; - toAction.time = 0; - toAction.weight = 1; - - // No gesture to gesture. - if (!lastGesture) { - // Play animation. - mesh.mixer.stopAllAction(); - toAction.play(); - return; - } - - // Animate or crossfade from gesture to gesture. - clip = this.getClip(lastGesture); - toAction.reset(); - toAction.play(); - fromAction = mesh.mixer.clipAction(clip); - fromAction.crossFadeTo(toAction, 0.15, true); - } -}); - -/** - * Suffix gestures based on toggle state (e.g., open/close, up/down, start/end). - * - * @param {string} gesture - * @param {boolean} active - */ -function getGestureEventName(gesture, active) { - var eventName; - if (!gesture) { - return; - } - eventName = EVENTS[gesture]; - if (eventName === 'grip') { - return eventName + (active ? 'close' : 'open'); - } - if (eventName === 'point') { - return eventName + (active ? 'up' : 'down'); - } - if (eventName === 'pointing' || eventName === 'pistol') { - return eventName + (active ? 'start' : 'end'); - } -} -function isViveController(trackedControls) { - var controller = trackedControls && trackedControls.controller; - var isVive = controller && (controller.id && controller.id.indexOf('OpenVR ') === 0 || controller.profiles && controller.profiles[0] && controller.profiles[0] === 'htc-vive'); - return isVive; -} - -/***/ }), - -/***/ "./src/components/hand-tracking-controls.js": -/*!**************************************************!*\ - !*** ./src/components/hand-tracking-controls.js ***! - \**************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE, XRHand */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var AEntity = (__webpack_require__(/*! ../core/a-entity */ "./src/core/a-entity.js").AEntity); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var LEFT_HAND_MODEL_URL = AFRAME_CDN_ROOT + 'controllers/oculus-hands/v4/left.glb'; -var RIGHT_HAND_MODEL_URL = AFRAME_CDN_ROOT + 'controllers/oculus-hands/v4/right.glb'; -var JOINTS = ['wrist', 'thumb-metacarpal', 'thumb-phalanx-proximal', 'thumb-phalanx-distal', 'thumb-tip', 'index-finger-metacarpal', 'index-finger-phalanx-proximal', 'index-finger-phalanx-intermediate', 'index-finger-phalanx-distal', 'index-finger-tip', 'middle-finger-metacarpal', 'middle-finger-phalanx-proximal', 'middle-finger-phalanx-intermediate', 'middle-finger-phalanx-distal', 'middle-finger-tip', 'ring-finger-metacarpal', 'ring-finger-phalanx-proximal', 'ring-finger-phalanx-intermediate', 'ring-finger-phalanx-distal', 'ring-finger-tip', 'pinky-finger-metacarpal', 'pinky-finger-phalanx-proximal', 'pinky-finger-phalanx-intermediate', 'pinky-finger-phalanx-distal', 'pinky-finger-tip']; -var WRIST_INDEX = 0; -var THUMB_TIP_INDEX = 4; -var INDEX_TIP_INDEX = 9; -var PINCH_START_DISTANCE = 0.015; -var PINCH_END_PERCENTAGE = 0.1; - -/** - * Controls for hand tracking - */ -module.exports.Component = registerComponent('hand-tracking-controls', { - schema: { - hand: { - default: 'right', - oneOf: ['left', 'right'] - }, - modelStyle: { - default: 'mesh', - oneOf: ['dots', 'mesh'] - }, - modelColor: { - default: 'white' - }, - modelOpacity: { - default: 1.0 - } - }, - after: ['tracked-controls'], - bindMethods: function () { - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - }, - addEventListeners: function () { - this.el.addEventListener('model-loaded', this.onModelLoaded); - for (var i = 0; i < this.jointEls.length; ++i) { - this.jointEls[i].object3D.visible = true; - } - }, - removeEventListeners: function () { - this.el.removeEventListener('model-loaded', this.onModelLoaded); - for (var i = 0; i < this.jointEls.length; ++i) { - this.jointEls[i].object3D.visible = false; - } - }, - init: function () { - var sceneEl = this.el.sceneEl; - var webxrData = sceneEl.getAttribute('webxr'); - var optionalFeaturesArray = webxrData.optionalFeatures; - if (optionalFeaturesArray.indexOf('hand-tracking') === -1) { - optionalFeaturesArray.push('hand-tracking'); - sceneEl.setAttribute('webxr', webxrData); - } - this.wristObject3D = new THREE.Object3D(); - this.el.sceneEl.object3D.add(this.wristObject3D); - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onChildAttached = this.onChildAttached.bind(this); - this.jointEls = []; - this.controllerPresent = false; - this.isPinched = false; - this.pinchEventDetail = { - position: new THREE.Vector3(), - wristRotation: new THREE.Quaternion() - }; - this.indexTipPosition = new THREE.Vector3(); - this.hasPoses = false; - this.jointPoses = new Float32Array(16 * JOINTS.length); - this.jointRadii = new Float32Array(JOINTS.length); - this.bindMethods(); - this.updateReferenceSpace = this.updateReferenceSpace.bind(this); - this.el.sceneEl.addEventListener('enter-vr', this.updateReferenceSpace); - this.el.sceneEl.addEventListener('exit-vr', this.updateReferenceSpace); - this.el.addEventListener('child-attached', this.onChildAttached); - this.el.object3D.visible = false; - this.wristObject3D.visible = false; - }, - onChildAttached: function (evt) { - this.addChildEntity(evt.detail.el); - }, - update: function () { - this.updateModelMaterial(); - }, - updateModelMaterial: function () { - var jointEls = this.jointEls; - var skinnedMesh = this.skinnedMesh; - var transparent = !(this.data.modelOpacity === 1.0); - if (skinnedMesh) { - this.skinnedMesh.material.color.set(this.data.modelColor); - this.skinnedMesh.material.transparent = transparent; - this.skinnedMesh.material.opacity = this.data.modelOpacity; - } - for (var i = 0; i < jointEls.length; i++) { - jointEls[i].setAttribute('material', { - color: this.data.modelColor, - transparent: transparent, - opacity: this.data.modelOpacity - }); - } - }, - updateReferenceSpace: function () { - var self = this; - var xrSession = this.el.sceneEl.xrSession; - this.referenceSpace = undefined; - if (!xrSession) { - return; - } - var referenceSpaceType = self.el.sceneEl.systems.webxr.sessionReferenceSpaceType; - xrSession.requestReferenceSpace(referenceSpaceType).then(function (referenceSpace) { - self.referenceSpace = referenceSpace; - }).catch(function (error) { - self.el.sceneEl.systems.webxr.warnIfFeatureNotRequested(referenceSpaceType, 'tracked-controls-webxr uses reference space ' + referenceSpaceType); - throw error; - }); - }, - checkIfControllerPresent: function () { - var data = this.data; - var hand = data.hand ? data.hand : undefined; - checkControllerPresentAndSetup(this, '', { - hand: hand, - iterateControllerProfiles: true, - handTracking: true - }); - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - tick: function () { - var sceneEl = this.el.sceneEl; - var controller = this.el.components['tracked-controls'] && this.el.components['tracked-controls'].controller; - var frame = sceneEl.frame; - var trackedControlsWebXR = this.el.components['tracked-controls-webxr']; - var referenceSpace = this.referenceSpace; - if (!controller || !frame || !referenceSpace || !trackedControlsWebXR) { - return; - } - this.hasPoses = false; - if (controller.hand) { - this.el.object3D.position.set(0, 0, 0); - this.el.object3D.rotation.set(0, 0, 0); - this.hasPoses = frame.fillPoses(controller.hand.values(), referenceSpace, this.jointPoses) && frame.fillJointRadii(controller.hand.values(), this.jointRadii); - this.updateHandModel(); - this.detectGesture(); - this.updateWristObject(); - } - }, - updateWristObject: function () { - var jointPose = new THREE.Matrix4(); - return function () { - var wristObject3D = this.wristObject3D; - if (!wristObject3D || !this.hasPoses) { - return; - } - jointPose.fromArray(this.jointPoses, WRIST_INDEX * 16); - wristObject3D.position.setFromMatrixPosition(jointPose); - wristObject3D.quaternion.setFromRotationMatrix(jointPose); - }; - }(), - updateHandModel: function () { - if (this.data.modelStyle === 'dots') { - this.updateHandDotsModel(); - } - if (this.data.modelStyle === 'mesh') { - this.updateHandMeshModel(); - } - }, - getBone: function (name) { - var bones = this.bones; - for (var i = 0; i < bones.length; i++) { - if (bones[i].name === name) { - return bones[i]; - } - } - return null; - }, - updateHandMeshModel: function () { - var jointPose = new THREE.Matrix4(); - return function () { - var i = 0; - var jointPoses = this.jointPoses; - var controller = this.el.components['tracked-controls'] && this.el.components['tracked-controls'].controller; - if (!controller || !this.mesh) { - return; - } - this.mesh.visible = false; - if (!this.hasPoses) { - return; - } - for (var inputjoint of controller.hand.values()) { - var bone = this.getBone(inputjoint.jointName); - if (bone != null) { - this.mesh.visible = true; - jointPose.fromArray(jointPoses, i * 16); - bone.position.setFromMatrixPosition(jointPose); - bone.quaternion.setFromRotationMatrix(jointPose); - } - i++; - } - }; - }(), - updateHandDotsModel: function () { - var jointPoses = this.jointPoses; - var jointRadii = this.jointRadii; - var controller = this.el.components['tracked-controls'] && this.el.components['tracked-controls'].controller; - var jointEl; - var object3D; - for (var i = 0; i < controller.hand.size; i++) { - jointEl = this.jointEls[i]; - object3D = jointEl.object3D; - jointEl.object3D.visible = this.hasPoses; - if (!this.hasPoses) { - continue; - } - object3D.matrix.fromArray(jointPoses, i * 16); - object3D.matrix.decompose(object3D.position, object3D.rotation, object3D.scale); - jointEl.setAttribute('scale', { - x: jointRadii[i], - y: jointRadii[i], - z: jointRadii[i] - }); - } - }, - detectGesture: function () { - this.detectPinch(); - }, - detectPinch: function () { - var thumbTipPosition = new THREE.Vector3(); - var jointPose = new THREE.Matrix4(); - return function () { - var indexTipPosition = this.indexTipPosition; - var pinchEventDetail = this.pinchEventDetail; - if (!this.hasPoses) { - return; - } - thumbTipPosition.setFromMatrixPosition(jointPose.fromArray(this.jointPoses, THUMB_TIP_INDEX * 16)); - indexTipPosition.setFromMatrixPosition(jointPose.fromArray(this.jointPoses, INDEX_TIP_INDEX * 16)); - pinchEventDetail.wristRotation.setFromRotationMatrix(jointPose.fromArray(this.jointPoses, WRIST_INDEX * 16)); - var distance = indexTipPosition.distanceTo(thumbTipPosition); - if (distance < PINCH_START_DISTANCE && this.isPinched === false) { - this.isPinched = true; - this.pinchDistance = distance; - pinchEventDetail.position.copy(indexTipPosition).add(thumbTipPosition).multiplyScalar(0.5); - this.el.emit('pinchstarted', pinchEventDetail); - } - if (distance > this.pinchDistance + this.pinchDistance * PINCH_END_PERCENTAGE && this.isPinched === true) { - this.isPinched = false; - pinchEventDetail.position.copy(indexTipPosition).add(thumbTipPosition).multiplyScalar(0.5); - this.el.emit('pinchended', pinchEventDetail); - } - if (this.isPinched) { - pinchEventDetail.position.copy(indexTipPosition).add(thumbTipPosition).multiplyScalar(0.5); - this.el.emit('pinchmoved', pinchEventDetail); - } - }; - }(), - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - el.setAttribute('tracked-controls', { - id: '', - hand: data.hand, - iterateControllerProfiles: true, - handTrackingEnabled: true - }); - if (this.mesh) { - if (this.mesh !== el.getObject3D('mesh')) { - el.setObject3D('mesh', this.mesh); - } - return; - } - this.initDefaultModel(); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - var el = this.el; - var controller; - this.checkIfControllerPresent(); - controller = el.components['tracked-controls'] && el.components['tracked-controls'].controller; - if (!this.mesh) { - return; - } - if (controller && controller.hand && controller.hand instanceof XRHand) { - el.setObject3D('mesh', this.mesh); - } - }, - initDefaultModel: function () { - var data = this.data; - if (data.modelStyle === 'dots') { - this.initDotsModel(); - } - if (data.modelStyle === 'mesh') { - this.initMeshHandModel(); - } - this.el.object3D.visible = true; - this.wristObject3D.visible = true; - }, - initDotsModel: function () { - // Add models just once. - if (this.jointEls.length !== 0) { - return; - } - for (var i = 0; i < JOINTS.length; ++i) { - var jointEl = this.jointEl = document.createElement('a-entity'); - jointEl.setAttribute('geometry', { - primitive: 'sphere', - radius: 1.0 - }); - jointEl.object3D.visible = false; - this.el.appendChild(jointEl); - this.jointEls.push(jointEl); - } - this.updateModelMaterial(); - }, - initMeshHandModel: function () { - var modelURL = this.data.hand === 'left' ? LEFT_HAND_MODEL_URL : RIGHT_HAND_MODEL_URL; - this.el.setAttribute('gltf-model', modelURL); - }, - onModelLoaded: function () { - var mesh = this.mesh = this.el.getObject3D('mesh').children[0]; - var skinnedMesh = this.skinnedMesh = mesh.getObjectByProperty('type', 'SkinnedMesh'); - if (!this.skinnedMesh) { - return; - } - this.bones = skinnedMesh.skeleton.bones; - this.el.removeObject3D('mesh'); - mesh.position.set(0, 0, 0); - mesh.rotation.set(0, 0, 0); - skinnedMesh.frustumCulled = false; - skinnedMesh.material = new THREE.MeshStandardMaterial(); - this.updateModelMaterial(); - this.setupChildrenEntities(); - this.el.setObject3D('mesh', mesh); - }, - setupChildrenEntities: function () { - var childrenEls = this.el.children; - for (var i = 0; i < childrenEls.length; ++i) { - if (!(childrenEls[i] instanceof AEntity)) { - continue; - } - this.addChildEntity(childrenEls[i]); - } - }, - addChildEntity: function (childEl) { - if (!(childEl instanceof AEntity)) { - return; - } - this.wristObject3D.add(childEl.object3D); - } -}); - -/***/ }), - -/***/ "./src/components/hand-tracking-grab-controls.js": -/*!*******************************************************!*\ - !*** ./src/components/hand-tracking-grab-controls.js ***! - \*******************************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -registerComponent('hand-tracking-grab-controls', { - schema: { - hand: { - default: 'right', - oneOf: ['left', 'right'] - }, - color: { - type: 'color', - default: 'white' - }, - hoverColor: { - type: 'color', - default: '#538df1' - }, - hoverEnabled: { - default: false - } - }, - init: function () { - var el = this.el; - var data = this.data; - var trackedObject3DVariable; - if (data.hand === 'right') { - trackedObject3DVariable = 'components.hand-tracking-controls.bones.3'; - } else { - trackedObject3DVariable = 'components.hand-tracking-controls.bones.21'; - } - el.setAttribute('hand-tracking-controls', { - hand: data.hand - }); - el.setAttribute('obb-collider', { - trackedObject3D: trackedObject3DVariable, - size: 0.04 - }); - this.auxMatrix = new THREE.Matrix4(); - this.auxQuaternion = new THREE.Quaternion(); - this.auxQuaternion2 = new THREE.Quaternion(); - this.auxVector = new THREE.Vector3(); - this.auxVector2 = new THREE.Vector3(); - this.grabbingObjectPosition = new THREE.Vector3(); - this.grabbedObjectPosition = new THREE.Vector3(); - this.grabbedObjectPositionDelta = new THREE.Vector3(); - this.grabDeltaPosition = new THREE.Vector3(); - this.grabInitialRotation = new THREE.Quaternion(); - this.onCollisionStarted = this.onCollisionStarted.bind(this); - this.el.addEventListener('obbcollisionstarted', this.onCollisionStarted); - this.onCollisionEnded = this.onCollisionEnded.bind(this); - this.el.addEventListener('obbcollisionended', this.onCollisionEnded); - this.onPinchStarted = this.onPinchStarted.bind(this); - this.el.addEventListener('pinchstarted', this.onPinchStarted); - this.onPinchEnded = this.onPinchEnded.bind(this); - this.el.addEventListener('pinchended', this.onPinchEnded); - this.onPinchMoved = this.onPinchMoved.bind(this); - this.el.addEventListener('pinchmoved', this.onPinchMoved); - }, - transferEntityOwnership: function () { - var grabbingElComponent; - var grabbingEls = this.el.sceneEl.querySelectorAll('[hand-tracking-grab-controls]'); - for (var i = 0; i < grabbingEls.length; ++i) { - grabbingElComponent = grabbingEls[i].components['hand-tracking-grab-controls']; - if (grabbingElComponent === this) { - continue; - } - if (this.grabbedEl && this.grabbedEl === grabbingElComponent.grabbedEl) { - grabbingElComponent.releaseGrabbedEntity(); - } - } - return false; - }, - onCollisionStarted: function (evt) { - var withEl = evt.detail.withEl; - if (this.collidedEl) { - return; - } - if (!withEl.getAttribute('grabbable')) { - return; - } - this.collidedEl = withEl; - this.grabbingObject3D = evt.detail.trackedObject3D; - if (this.data.hoverEnabled) { - this.el.setAttribute('hand-tracking-controls', 'modelColor', this.data.hoverColor); - } - }, - onCollisionEnded: function () { - this.collidedEl = undefined; - if (this.grabbedEl) { - return; - } - this.grabbingObject3D = undefined; - if (this.data.hoverEnabled) { - this.el.setAttribute('hand-tracking-controls', 'modelColor', this.data.color); - } - }, - onPinchStarted: function (evt) { - if (!this.collidedEl) { - return; - } - this.pinchPosition = evt.detail.position; - this.wristRotation = evt.detail.wristRotation; - this.grabbedEl = this.collidedEl; - this.transferEntityOwnership(); - this.grab(); - }, - onPinchEnded: function () { - this.releaseGrabbedEntity(); - }, - onPinchMoved: function (evt) { - this.wristRotation = evt.detail.wristRotation; - }, - releaseGrabbedEntity: function () { - var grabbedEl = this.grabbedEl; - if (!grabbedEl) { - return; - } - grabbedEl.object3D.updateMatrixWorld = this.originalUpdateMatrixWorld; - grabbedEl.object3D.matrixAutoUpdate = true; - grabbedEl.object3D.matrixWorldAutoUpdate = true; - grabbedEl.object3D.matrixWorld.decompose(this.auxVector, this.auxQuaternion, this.auxVector2); - grabbedEl.object3D.position.copy(this.auxVector); - grabbedEl.object3D.quaternion.copy(this.auxQuaternion); - this.el.emit('grabended', { - grabbedEl: grabbedEl - }); - this.grabbedEl = undefined; - }, - grab: function () { - var grabbedEl = this.grabbedEl; - var grabbedObjectWorldPosition; - grabbedObjectWorldPosition = grabbedEl.object3D.getWorldPosition(this.grabbedObjectPosition); - this.grabDeltaPosition.copy(grabbedObjectWorldPosition).sub(this.pinchPosition); - this.grabInitialRotation.copy(this.auxQuaternion.copy(this.wristRotation).invert()); - this.originalUpdateMatrixWorld = grabbedEl.object3D.updateMatrixWorld; - grabbedEl.object3D.updateMatrixWorld = function () {/* no op */}; - grabbedEl.object3D.updateMatrixWorldChildren = function (force) { - var children = this.children; - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i]; - if (child.matrixWorldAutoUpdate === true || force === true) { - child.updateMatrixWorld(true); - } - } - }; - grabbedEl.object3D.matrixAutoUpdate = false; - grabbedEl.object3D.matrixWorldAutoUpdate = false; - this.el.emit('grabstarted', { - grabbedEl: grabbedEl - }); - }, - tock: function () { - var auxMatrix = this.auxMatrix; - var auxQuaternion = this.auxQuaternion; - var auxQuaternion2 = this.auxQuaternion2; - var grabbedObject3D; - var grabbedEl = this.grabbedEl; - if (!grabbedEl) { - return; - } - - // We have to compose 4 transformations. - // Both grabbing and grabbed entities position and rotation. - - // 1. Move grabbed entity to the pinch position (middle point between index and thumb) - // 2. Apply the rotation delta (subtract initial rotation) of the grabbing entity position (wrist). - // 3. Translate grabbed entity to the original position: distance between grabbed and grabbing entities at collision time. - // 4. Apply grabbed entity rotation. - // 5. Preserve original scale. - - // Store grabbed entity local rotation. - grabbedObject3D = grabbedEl.object3D; - grabbedObject3D.getWorldQuaternion(auxQuaternion2); - - // Reset grabbed entity matrix. - grabbedObject3D.matrixWorld.identity(); - - // 1. - auxMatrix.identity(); - auxMatrix.makeTranslation(this.pinchPosition); - grabbedObject3D.matrixWorld.multiply(auxMatrix); - - // 2. - auxMatrix.identity(); - auxMatrix.makeRotationFromQuaternion(auxQuaternion.copy(this.wristRotation).multiply(this.grabInitialRotation)); - grabbedObject3D.matrixWorld.multiply(auxMatrix); - - // 3. - auxMatrix.identity(); - auxMatrix.makeTranslation(this.grabDeltaPosition); - grabbedObject3D.matrixWorld.multiply(auxMatrix); - - // 4. - auxMatrix.identity(); - auxMatrix.makeRotationFromQuaternion(auxQuaternion2); - grabbedObject3D.matrixWorld.multiply(auxMatrix); - - // 5. - auxMatrix.makeScale(grabbedEl.object3D.scale.x, grabbedEl.object3D.scale.y, grabbedEl.object3D.scale.z); - grabbedObject3D.matrixWorld.multiply(auxMatrix); - grabbedObject3D.updateMatrixWorldChildren(); - } -}); - -/***/ }), - -/***/ "./src/components/hide-on-enter-ar.js": -/*!********************************************!*\ - !*** ./src/components/hide-on-enter-ar.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var register = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = register('hide-on-enter-ar', { - init: function () { - var self = this; - this.el.sceneEl.addEventListener('enter-vr', function () { - if (self.el.sceneEl.is('ar-mode')) { - self.el.object3D.visible = false; - } - }); - this.el.sceneEl.addEventListener('exit-vr', function () { - self.el.object3D.visible = true; - }); - } -}); - -/***/ }), - -/***/ "./src/components/hide-on-enter-vr.js": -/*!********************************************!*\ - !*** ./src/components/hide-on-enter-vr.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var register = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = register('hide-on-enter-vr', { - init: function () { - var self = this; - this.el.sceneEl.addEventListener('enter-vr', function () { - if (self.el.sceneEl.is('vr-mode')) { - self.el.object3D.visible = false; - } - }); - this.el.sceneEl.addEventListener('exit-vr', function () { - self.el.object3D.visible = true; - }); - } -}); - -/***/ }), - -/***/ "./src/components/hp-mixed-reality-controls.js": -/*!*****************************************************!*\ - !*** ./src/components/hp-mixed-reality-controls.js ***! - \*****************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; - -// See Profiles Registry: -// https://github.com/immersive-web/webxr-input-profiles/tree/master/packages/registry -// TODO: Add a more robust system for deriving gamepad name. -var GAMEPAD_ID = 'hp-mixed-reality'; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var HP_MIXED_REALITY_MODEL_GLB_BASE_URL = AFRAME_CDN_ROOT + 'controllers/hp/mixed-reality/'; -var HP_MIXED_REALITY_POSITION_OFFSET = { - x: 0, - y: 0, - z: 0.06 -}; -var HP_MIXED_REALITY_ROTATION_OFFSET = { - _x: Math.PI / 4, - _y: 0, - _z: 0, - _order: 'XYZ' -}; - -/** - * Button IDs: - * 0 - trigger - * 1 - grip - * 3 - X / A - * 4 - Y / B - * - * Axis: - * 2 - joystick x axis - * 3 - joystick y axis - */ -var INPUT_MAPPING_WEBXR = { - left: { - axes: { - touchpad: [2, 3] - }, - buttons: ['trigger', 'grip', 'none', 'thumbstick', 'xbutton', 'ybutton'] - }, - right: { - axes: { - touchpad: [2, 3] - }, - buttons: ['trigger', 'grip', 'none', 'thumbstick', 'abutton', 'bbutton'] - } -}; - -/** - * HP Mixed Reality Controls - */ -module.exports.Component = registerComponent('hp-mixed-reality-controls', { - schema: { - hand: { - default: 'none' - }, - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - } - }, - mapping: INPUT_MAPPING_WEBXR, - init: function () { - var self = this; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self, self.data.hand); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self, self.data.hand); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self, self.data.hand); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self, self.data.hand); - }; - this.previousButtonValues = {}; - this.bindMethods(); - }, - update: function () { - var data = this.data; - this.controllerIndex = data.hand === 'right' ? 0 : data.hand === 'left' ? 1 : 2; - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('axismove', this.onAxisMoved); - el.addEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('axismove', this.onAxisMoved); - el.removeEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - var data = this.data; - checkControllerPresentAndSetup(this, GAMEPAD_ID, { - index: this.controllerIndex, - hand: data.hand - }); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - el.setAttribute('tracked-controls', { - // TODO: verify expected behavior between reserved prefixes. - idPrefix: GAMEPAD_ID, - hand: data.hand, - controller: this.controllerIndex, - orientationOffset: data.orientationOffset - }); - - // Load model. - if (!this.data.model) { - return; - } - this.el.setAttribute('gltf-model', HP_MIXED_REALITY_MODEL_GLB_BASE_URL + this.data.hand + '.glb'); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - // Note that due to gamepadconnected event propagation issues, we don't rely on events. - this.checkIfControllerPresent(); - }, - onButtonChanged: function (evt) { - var button = this.mapping[this.data.hand].buttons[evt.detail.id]; - var analogValue; - if (!button) { - return; - } - if (button === 'trigger') { - analogValue = evt.detail.state.value; - console.log('analog value of trigger press: ' + analogValue); - } - - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onModelLoaded: function (evt) { - var controllerObject3D = evt.detail.model; - if (!this.data.model) { - return; - } - controllerObject3D.position.copy(HP_MIXED_REALITY_POSITION_OFFSET); - controllerObject3D.rotation.copy(HP_MIXED_REALITY_ROTATION_OFFSET); - this.el.emit('controllermodelready', { - name: 'hp-mixed-reality-controls', - model: this.data.model, - rayOrigin: new THREE.Vector3(0, 0, 0) - }); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - } -}); - -/***/ }), - -/***/ "./src/components/index.js": -/*!*********************************!*\ - !*** ./src/components/index.js ***! - \*********************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -__webpack_require__(/*! ./animation */ "./src/components/animation.js"); -__webpack_require__(/*! ./anchored */ "./src/components/anchored.js"); -__webpack_require__(/*! ./camera */ "./src/components/camera.js"); -__webpack_require__(/*! ./cursor */ "./src/components/cursor.js"); -__webpack_require__(/*! ./geometry */ "./src/components/geometry.js"); -__webpack_require__(/*! ./generic-tracked-controller-controls */ "./src/components/generic-tracked-controller-controls.js"); -__webpack_require__(/*! ./gltf-model */ "./src/components/gltf-model.js"); -__webpack_require__(/*! ./grabbable */ "./src/components/grabbable.js"); -__webpack_require__(/*! ./hand-tracking-controls */ "./src/components/hand-tracking-controls.js"); -__webpack_require__(/*! ./hand-tracking-grab-controls */ "./src/components/hand-tracking-grab-controls.js"); -__webpack_require__(/*! ./hand-controls */ "./src/components/hand-controls.js"); -__webpack_require__(/*! ./hide-on-enter-ar */ "./src/components/hide-on-enter-ar.js"); -__webpack_require__(/*! ./hide-on-enter-vr */ "./src/components/hide-on-enter-vr.js"); -__webpack_require__(/*! ./hp-mixed-reality-controls */ "./src/components/hp-mixed-reality-controls.js"); -__webpack_require__(/*! ./layer */ "./src/components/layer.js"); -__webpack_require__(/*! ./laser-controls */ "./src/components/laser-controls.js"); -__webpack_require__(/*! ./light */ "./src/components/light.js"); -__webpack_require__(/*! ./line */ "./src/components/line.js"); -__webpack_require__(/*! ./link */ "./src/components/link.js"); -__webpack_require__(/*! ./look-controls */ "./src/components/look-controls.js"); -__webpack_require__(/*! ./magicleap-controls */ "./src/components/magicleap-controls.js"); -__webpack_require__(/*! ./material */ "./src/components/material.js"); -__webpack_require__(/*! ./obb-collider */ "./src/components/obb-collider.js"); -__webpack_require__(/*! ./obj-model */ "./src/components/obj-model.js"); -__webpack_require__(/*! ./oculus-go-controls */ "./src/components/oculus-go-controls.js"); -__webpack_require__(/*! ./oculus-touch-controls */ "./src/components/oculus-touch-controls.js"); -__webpack_require__(/*! ./pico-controls */ "./src/components/pico-controls.js"); -__webpack_require__(/*! ./position */ "./src/components/position.js"); -__webpack_require__(/*! ./raycaster */ "./src/components/raycaster.js"); -__webpack_require__(/*! ./rotation */ "./src/components/rotation.js"); -__webpack_require__(/*! ./scale */ "./src/components/scale.js"); -__webpack_require__(/*! ./shadow */ "./src/components/shadow.js"); -__webpack_require__(/*! ./sound */ "./src/components/sound.js"); -__webpack_require__(/*! ./text */ "./src/components/text.js"); -__webpack_require__(/*! ./tracked-controls */ "./src/components/tracked-controls.js"); -__webpack_require__(/*! ./tracked-controls-webvr */ "./src/components/tracked-controls-webvr.js"); -__webpack_require__(/*! ./tracked-controls-webxr */ "./src/components/tracked-controls-webxr.js"); -__webpack_require__(/*! ./visible */ "./src/components/visible.js"); -__webpack_require__(/*! ./valve-index-controls */ "./src/components/valve-index-controls.js"); -__webpack_require__(/*! ./vive-controls */ "./src/components/vive-controls.js"); -__webpack_require__(/*! ./vive-focus-controls */ "./src/components/vive-focus-controls.js"); -__webpack_require__(/*! ./wasd-controls */ "./src/components/wasd-controls.js"); -__webpack_require__(/*! ./windows-motion-controls */ "./src/components/windows-motion-controls.js"); -__webpack_require__(/*! ./scene/ar-hit-test */ "./src/components/scene/ar-hit-test.js"); -__webpack_require__(/*! ./scene/background */ "./src/components/scene/background.js"); -__webpack_require__(/*! ./scene/debug */ "./src/components/scene/debug.js"); -__webpack_require__(/*! ./scene/device-orientation-permission-ui */ "./src/components/scene/device-orientation-permission-ui.js"); -__webpack_require__(/*! ./scene/embedded */ "./src/components/scene/embedded.js"); -__webpack_require__(/*! ./scene/inspector */ "./src/components/scene/inspector.js"); -__webpack_require__(/*! ./scene/fog */ "./src/components/scene/fog.js"); -__webpack_require__(/*! ./scene/keyboard-shortcuts */ "./src/components/scene/keyboard-shortcuts.js"); -__webpack_require__(/*! ./scene/pool */ "./src/components/scene/pool.js"); -__webpack_require__(/*! ./scene/real-world-meshing */ "./src/components/scene/real-world-meshing.js"); -__webpack_require__(/*! ./scene/reflection */ "./src/components/scene/reflection.js"); -__webpack_require__(/*! ./scene/screenshot */ "./src/components/scene/screenshot.js"); -__webpack_require__(/*! ./scene/stats */ "./src/components/scene/stats.js"); -__webpack_require__(/*! ./scene/xr-mode-ui */ "./src/components/scene/xr-mode-ui.js"); - -/***/ }), - -/***/ "./src/components/laser-controls.js": -/*!******************************************!*\ - !*** ./src/components/laser-controls.js ***! - \******************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -registerComponent('laser-controls', { - schema: { - hand: { - default: 'right' - }, - model: { - default: true - }, - defaultModelColor: { - type: 'color', - default: 'grey' - } - }, - init: function () { - var config = this.config; - var data = this.data; - var el = this.el; - var self = this; - var controlsConfiguration = { - hand: data.hand, - model: data.model - }; - - // Set all controller models. - el.setAttribute('hp-mixed-reality-controls', controlsConfiguration); - el.setAttribute('magicleap-controls', controlsConfiguration); - el.setAttribute('oculus-go-controls', controlsConfiguration); - el.setAttribute('oculus-touch-controls', controlsConfiguration); - el.setAttribute('pico-controls', controlsConfiguration); - el.setAttribute('valve-index-controls', controlsConfiguration); - el.setAttribute('vive-controls', controlsConfiguration); - el.setAttribute('vive-focus-controls', controlsConfiguration); - el.setAttribute('windows-motion-controls', controlsConfiguration); - el.setAttribute('generic-tracked-controller-controls', { - hand: controlsConfiguration.hand - }); - - // Wait for controller to connect, or have a valid pointing pose, before creating ray - el.addEventListener('controllerconnected', createRay); - el.addEventListener('controllerdisconnected', hideRay); - el.addEventListener('controllermodelready', function (evt) { - createRay(evt); - self.modelReady = true; - }); - function createRay(evt) { - var controllerConfig = config[evt.detail.name]; - if (!controllerConfig) { - return; - } - - // Show the line unless a particular config opts to hide it, until a controllermodelready - // event comes through. - var raycasterConfig = utils.extend({ - showLine: true - }, controllerConfig.raycaster || {}); - - // The controllermodelready event contains a rayOrigin that takes into account - // offsets specific to the loaded model. - if (evt.detail.rayOrigin) { - raycasterConfig.origin = evt.detail.rayOrigin.origin; - raycasterConfig.direction = evt.detail.rayOrigin.direction; - raycasterConfig.showLine = true; - } - - // Only apply a default raycaster if it does not yet exist. This prevents it overwriting - // config applied from a controllermodelready event. - if (evt.detail.rayOrigin || !self.modelReady) { - el.setAttribute('raycaster', raycasterConfig); - } else { - el.setAttribute('raycaster', 'showLine', true); - } - el.setAttribute('cursor', utils.extend({ - fuse: false - }, controllerConfig.cursor)); - } - function hideRay(evt) { - var controllerConfig = config[evt.detail.name]; - if (!controllerConfig) { - return; - } - el.setAttribute('raycaster', 'showLine', false); - } - }, - config: { - 'generic-tracked-controller-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - } - }, - 'hp-mixed-reality-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - }, - raycaster: { - origin: { - x: 0, - y: 0, - z: 0 - } - } - }, - 'magicleap-controls': { - cursor: { - downEvents: ['trackpaddown', 'triggerdown'], - upEvents: ['trackpadup', 'triggerup'] - } - }, - 'oculus-go-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - }, - raycaster: { - origin: { - x: 0, - y: 0.0005, - z: 0 - } - } - }, - 'oculus-touch-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - }, - raycaster: { - origin: { - x: 0, - y: 0, - z: 0 - } - } - }, - 'pico-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - } - }, - 'valve-index-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - } - }, - 'vive-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - } - }, - 'vive-focus-controls': { - cursor: { - downEvents: ['trackpaddown', 'triggerdown'], - upEvents: ['trackpadup', 'triggerup'] - } - }, - 'windows-motion-controls': { - cursor: { - downEvents: ['triggerdown'], - upEvents: ['triggerup'] - }, - raycaster: { - showLine: false - } - } - } -}); - -/***/ }), - -/***/ "./src/components/layer.js": -/*!*********************************!*\ - !*** ./src/components/layer.js ***! - \*********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE, XRRigidTransform, XRWebGLBinding */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var warn = utils.debug('components:layer:warn'); -module.exports.Component = registerComponent('layer', { - schema: { - type: { - default: 'quad', - oneOf: ['quad', 'monocubemap', 'stereocubemap'] - }, - src: { - type: 'map' - }, - rotateCubemap: { - default: false - }, - width: { - default: 0 - }, - height: { - default: 0 - } - }, - init: function () { - var gl = this.el.sceneEl.renderer.getContext(); - this.quaternion = new THREE.Quaternion(); - this.position = new THREE.Vector3(); - this.bindMethods(); - this.needsRedraw = false; - this.frameBuffer = gl.createFramebuffer(); - var webxrData = this.el.sceneEl.getAttribute('webxr'); - var requiredFeaturesArray = webxrData.requiredFeatures; - if (requiredFeaturesArray.indexOf('layers') === -1) { - requiredFeaturesArray.push('layers'); - this.el.sceneEl.setAttribute('webxr', webxrData); - } - this.el.sceneEl.addEventListener('enter-vr', this.onEnterVR); - this.el.sceneEl.addEventListener('exit-vr', this.onExitVR); - }, - bindMethods: function () { - this.onRequestedReferenceSpace = this.onRequestedReferenceSpace.bind(this); - this.onEnterVR = this.onEnterVR.bind(this); - this.onExitVR = this.onExitVR.bind(this); - }, - update: function (oldData) { - if (this.data.src !== oldData.src) { - this.updateSrc(); - } - }, - updateSrc: function () { - var type = this.data.type; - this.texture = undefined; - if (type === 'quad') { - this.loadQuadImage(); - return; - } - if (type === 'monocubemap' || type === 'stereocubemap') { - this.loadCubeMapImages(); - return; - } - }, - loadCubeMapImages: function () { - var glayer; - var xrGLFactory = this.xrGLFactory; - var frame = this.el.sceneEl.frame; - var src = this.data.src; - var type = this.data.type; - this.visibilityChanged = false; - if (!this.layer) { - return; - } - if (type !== 'monocubemap' && type !== 'stereocubemap') { - return; - } - if (!src.complete) { - this.pendingCubeMapUpdate = true; - } else { - this.pendingCubeMapUpdate = false; - } - if (!this.loadingScreen) { - this.loadingScreen = true; - } else { - this.loadingScreen = false; - } - if (type === 'monocubemap') { - glayer = xrGLFactory.getSubImage(this.layer, frame); - this.loadCubeMapImage(glayer.colorTexture, src, 0); - } else { - glayer = xrGLFactory.getSubImage(this.layer, frame, 'left'); - this.loadCubeMapImage(glayer.colorTexture, src, 0); - glayer = xrGLFactory.getSubImage(this.layer, frame, 'right'); - this.loadCubeMapImage(glayer.colorTexture, src, 6); - } - }, - loadQuadImage: function () { - var src = this.data.src; - var self = this; - this.el.sceneEl.systems.material.loadTexture(src, { - src: src - }, function textureLoaded(texture) { - self.el.sceneEl.renderer.initTexture(texture); - self.texture = texture; - if (src.tagName === 'VIDEO') { - setTimeout(function () { - self.textureIsVideo = true; - }, 1000); - } - if (self.layer) { - self.layer.height = self.data.height / 2 || self.texture.image.height / 1000; - self.layer.width = self.data.width / 2 || self.texture.image.width / 1000; - self.needsRedraw = true; - } - self.updateQuadPanel(); - }); - }, - preGenerateCubeMapTextures: function (src, callback) { - if (this.data.type === 'monocubemap') { - this.generateCubeMapTextures(src, 0, callback); - } else { - this.generateCubeMapTextures(src, 0, callback); - this.generateCubeMapTextures(src, 6, callback); - } - }, - generateCubeMapTextures: function (src, faceOffset, callback) { - var data = this.data; - var cubeFaceSize = this.cubeFaceSize; - var textureSourceCubeFaceSize = Math.min(src.width, src.height); - var cubefaceTextures = []; - var imgTmp0; - var imgTmp2; - for (var i = 0; i < 6; i++) { - var tempCanvas = document.createElement('CANVAS'); - tempCanvas.width = tempCanvas.height = cubeFaceSize; - var tempCanvasContext = tempCanvas.getContext('2d'); - if (data.rotateCubemap) { - if (i === 2 || i === 3) { - tempCanvasContext.save(); - tempCanvasContext.translate(cubeFaceSize, cubeFaceSize); - tempCanvasContext.rotate(Math.PI); - } - } - - // Note that this call to drawImage will not only copy the bytes to the - // canvas but also could resized the image if our cube face size is - // smaller than the source image due to GL max texture size. - tempCanvasContext.drawImage(src, (i + faceOffset) * textureSourceCubeFaceSize, - // top left x coord in source - 0, - // top left y coord in source - textureSourceCubeFaceSize, - // x pixel count from source - textureSourceCubeFaceSize, - // y pixel count from source - 0, - // dest x offset in the canvas - 0, - // dest y offset in the canvas - cubeFaceSize, - // x pixel count in dest - cubeFaceSize // y pixel count in dest - ); - - tempCanvasContext.restore(); - if (callback) { - callback(); - } - cubefaceTextures.push(tempCanvas); - } - if (data.rotateCubemap) { - imgTmp0 = cubefaceTextures[0]; - imgTmp2 = cubefaceTextures[1]; - cubefaceTextures[0] = imgTmp2; - cubefaceTextures[1] = imgTmp0; - imgTmp0 = cubefaceTextures[4]; - imgTmp2 = cubefaceTextures[5]; - cubefaceTextures[4] = imgTmp2; - cubefaceTextures[5] = imgTmp0; - } - if (callback) { - callback(); - } - return cubefaceTextures; - }, - loadCubeMapImage: function (layerColorTexture, src, faceOffset) { - var gl = this.el.sceneEl.renderer.getContext(); - var cubefaceTextures; - - // don't flip the pixels as we load them into the texture buffer. - // TEXTURE_CUBE_MAP expects the Y to be flipped for the faces and it already - // is flipped in our texture image. - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); - gl.bindTexture(gl.TEXTURE_CUBE_MAP, layerColorTexture); - if (!src.complete || this.loadingScreen) { - cubefaceTextures = this.loadingScreenImages; - } else { - cubefaceTextures = this.generateCubeMapTextures(src, faceOffset); - } - var errorCode = 0; - cubefaceTextures.forEach(function (canvas, i) { - gl.texSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas); - errorCode = gl.getError(); - }); - if (errorCode !== 0) { - console.log('renderingError, WebGL Error Code: ' + errorCode); - } - gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); - }, - tick: function () { - if (!this.el.sceneEl.xrSession) { - return; - } - if (!this.layer && (this.el.sceneEl.is('vr-mode') || this.el.sceneEl.is('ar-mode'))) { - this.initLayer(); - } - this.updateTransform(); - if (this.data.src.complete && (this.pendingCubeMapUpdate || this.loadingScreen || this.visibilityChanged)) { - this.loadCubeMapImages(); - } - if (!this.needsRedraw && !this.layer.needsRedraw && !this.textureIsVideo) { - return; - } - if (this.data.type === 'quad') { - this.draw(); - } - this.needsRedraw = false; - }, - initLayer: function () { - var self = this; - var type = this.data.type; - this.el.sceneEl.xrSession.onvisibilitychange = function (evt) { - self.visibilityChanged = evt.session.visibilityState !== 'hidden'; - }; - if (type === 'quad') { - this.initQuadLayer(); - return; - } - if (type === 'monocubemap' || type === 'stereocubemap') { - this.initCubeMapLayer(); - return; - } - }, - initQuadLayer: function () { - var sceneEl = this.el.sceneEl; - var gl = sceneEl.renderer.getContext(); - var xrGLFactory = this.xrGLFactory = new XRWebGLBinding(sceneEl.xrSession, gl); - if (!this.texture) { - return; - } - this.layer = xrGLFactory.createQuadLayer({ - space: this.referenceSpace, - viewPixelHeight: 2048, - viewPixelWidth: 2048, - height: this.data.height / 2 || this.texture.image.height / 1000, - width: this.data.width / 2 || this.texture.image.width / 1000 - }); - this.initLoadingScreenImages(); - sceneEl.renderer.xr.addLayer(this.layer); - }, - initCubeMapLayer: function () { - var src = this.data.src; - var sceneEl = this.el.sceneEl; - var gl = sceneEl.renderer.getContext(); - var glSizeLimit = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); - var cubeFaceSize = this.cubeFaceSize = Math.min(glSizeLimit, Math.min(src.width, src.height)); - var xrGLFactory = this.xrGLFactory = new XRWebGLBinding(sceneEl.xrSession, gl); - this.layer = xrGLFactory.createCubeLayer({ - space: this.referenceSpace, - viewPixelWidth: cubeFaceSize, - viewPixelHeight: cubeFaceSize, - layout: this.data.type === 'monocubemap' ? 'mono' : 'stereo', - isStatic: false - }); - this.initLoadingScreenImages(); - this.loadCubeMapImages(); - sceneEl.renderer.xr.addLayer(this.layer); - }, - initLoadingScreenImages: function () { - var cubeFaceSize = this.cubeFaceSize; - var loadingScreenImages = this.loadingScreenImages = []; - for (var i = 0; i < 6; i++) { - var tempCanvas = document.createElement('CANVAS'); - tempCanvas.width = tempCanvas.height = cubeFaceSize; - var tempCanvasContext = tempCanvas.getContext('2d'); - tempCanvas.width = tempCanvas.height = cubeFaceSize; - tempCanvasContext.fillStyle = 'black'; - tempCanvasContext.fillRect(0, 0, cubeFaceSize, cubeFaceSize); - if (i !== 2 && i !== 3) { - tempCanvasContext.translate(cubeFaceSize, 0); - tempCanvasContext.scale(-1, 1); - tempCanvasContext.fillStyle = 'white'; - tempCanvasContext.font = '30px Arial'; - tempCanvasContext.fillText('Loading', cubeFaceSize / 2, cubeFaceSize / 2); - } - loadingScreenImages.push(tempCanvas); - } - }, - destroyLayer: function () { - if (!this.layer) { - return; - } - this.el.sceneEl.renderer.xr.removeLayer(this.layer); - this.layer.destroy(); - this.layer = undefined; - }, - toggleCompositorLayer: function () { - this.enableCompositorLayer(!this.layerEnabled); - }, - enableCompositorLayer: function (enable) { - this.layerEnabled = enable; - this.quadPanelEl.object3D.visible = !this.layerEnabled; - }, - updateQuadPanel: function () { - var quadPanelEl = this.quadPanelEl; - if (!this.quadPanelEl) { - quadPanelEl = this.quadPanelEl = document.createElement('a-entity'); - this.el.appendChild(quadPanelEl); - } - quadPanelEl.setAttribute('material', { - shader: 'flat', - src: this.data.src, - transparent: true - }); - quadPanelEl.setAttribute('geometry', { - primitive: 'plane', - height: this.data.height || this.texture.image.height / 1000, - width: this.data.width || this.texture.image.height / 1000 - }); - }, - draw: function () { - var sceneEl = this.el.sceneEl; - var gl = this.el.sceneEl.renderer.getContext(); - var glayer = this.xrGLFactory.getSubImage(this.layer, sceneEl.frame); - var texture = sceneEl.renderer.properties.get(this.texture).__webglTexture; - var previousFrameBuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); - gl.viewport(glayer.viewport.x, glayer.viewport.y, glayer.viewport.width, glayer.viewport.height); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glayer.colorTexture, 0); - blitTexture(gl, texture, glayer, this.data.src); - gl.bindFramebuffer(gl.FRAMEBUFFER, previousFrameBuffer); - }, - updateTransform: function () { - var el = this.el; - var position = this.position; - var quaternion = this.quaternion; - el.object3D.updateMatrixWorld(); - position.setFromMatrixPosition(el.object3D.matrixWorld); - quaternion.setFromRotationMatrix(el.object3D.matrixWorld); - if (!this.layerEnabled) { - position.set(0, 0, 100000000); - } - this.layer.transform = new XRRigidTransform(position, quaternion); - }, - onEnterVR: function () { - var sceneEl = this.el.sceneEl; - var xrSession = sceneEl.xrSession; - if (!sceneEl.hasWebXR || !XRWebGLBinding || !xrSession) { - warn('The layer component requires WebXR and the layers API enabled'); - return; - } - xrSession.requestReferenceSpace('local-floor').then(this.onRequestedReferenceSpace); - this.layerEnabled = true; - if (this.quadPanelEl) { - this.quadPanelEl.object3D.visible = false; - } - if (this.data.src.play) { - this.data.src.play(); - } - }, - onExitVR: function () { - if (this.quadPanelEl) { - this.quadPanelEl.object3D.visible = true; - } - this.destroyLayer(); - }, - onRequestedReferenceSpace: function (referenceSpace) { - this.referenceSpace = referenceSpace; - } -}); -function blitTexture(gl, texture, subImage, textureEl) { - var xrReadFramebuffer = gl.createFramebuffer(); - var x1offset = subImage.viewport.x; - var y1offset = subImage.viewport.y; - var x2offset = subImage.viewport.x + subImage.viewport.width; - var y2offset = subImage.viewport.y + subImage.viewport.height; - - // Update video texture. - if (textureEl.tagName === 'VIDEO') { - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureEl.width, textureEl.height, gl.RGB, gl.UNSIGNED_BYTE, textureEl); - } - - // Bind texture to read framebuffer. - gl.bindFramebuffer(gl.READ_FRAMEBUFFER, xrReadFramebuffer); - gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); - - // Blit into layer buffer. - gl.readBuffer(gl.COLOR_ATTACHMENT0); - gl.blitFramebuffer(0, 0, textureEl.width, textureEl.height, x1offset, y1offset, x2offset, y2offset, gl.COLOR_BUFFER_BIT, gl.NEAREST); - gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); - gl.deleteFramebuffer(xrReadFramebuffer); -} - -/***/ }), - -/***/ "./src/components/light.js": -/*!*********************************!*\ - !*** ./src/components/light.js ***! - \*********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var utils = __webpack_require__(/*! ../utils */ "./src/utils/index.js"); -var diff = utils.diff; -var debug = __webpack_require__(/*! ../utils/debug */ "./src/utils/debug.js"); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var mathUtils = __webpack_require__(/*! ../utils/math */ "./src/utils/math.js"); -var degToRad = THREE.MathUtils.degToRad; -var warn = debug('components:light:warn'); -var CubeLoader = new THREE.CubeTextureLoader(); -var probeCache = {}; - -/** - * Light component. - */ -module.exports.Component = registerComponent('light', { - schema: { - angle: { - default: 60, - if: { - type: ['spot'] - } - }, - color: { - type: 'color', - if: { - type: ['ambient', 'directional', 'hemisphere', 'point', 'spot'] - } - }, - envMap: { - default: '', - if: { - type: ['probe'] - } - }, - groundColor: { - type: 'color', - if: { - type: ['hemisphere'] - } - }, - decay: { - default: 1, - if: { - type: ['point', 'spot'] - } - }, - distance: { - default: 0.0, - min: 0, - if: { - type: ['point', 'spot'] - } - }, - intensity: { - default: 1.0, - min: 0, - if: { - type: ['ambient', 'directional', 'hemisphere', 'point', 'spot', 'probe'] - } - }, - penumbra: { - default: 0, - min: 0, - max: 1, - if: { - type: ['spot'] - } - }, - type: { - default: 'directional', - oneOf: ['ambient', 'directional', 'hemisphere', 'point', 'spot', 'probe'], - schemaChange: true - }, - target: { - type: 'selector', - if: { - type: ['spot', 'directional'] - } - }, - // Shadows. - castShadow: { - default: false, - if: { - type: ['point', 'spot', 'directional'] - } - }, - shadowBias: { - default: 0, - if: { - castShadow: true - } - }, - shadowCameraFar: { - default: 500, - if: { - castShadow: true - } - }, - shadowCameraFov: { - default: 90, - if: { - castShadow: true - } - }, - shadowCameraNear: { - default: 0.5, - if: { - castShadow: true - } - }, - shadowCameraTop: { - default: 5, - if: { - castShadow: true - } - }, - shadowCameraRight: { - default: 5, - if: { - castShadow: true - } - }, - shadowCameraBottom: { - default: -5, - if: { - castShadow: true - } - }, - shadowCameraLeft: { - default: -5, - if: { - castShadow: true - } - }, - shadowCameraVisible: { - default: false, - if: { - castShadow: true - } - }, - shadowCameraAutomatic: { - default: '', - if: { - type: ['directional'] - } - }, - shadowMapHeight: { - default: 512, - if: { - castShadow: true - } - }, - shadowMapWidth: { - default: 512, - if: { - castShadow: true - } - }, - shadowRadius: { - default: 1, - if: { - castShadow: true - } - } - }, - /** - * Notifies scene a light has been added to remove default lighting. - */ - init: function () { - var el = this.el; - this.light = null; - this.defaultTarget = null; - this.system.registerLight(el); - }, - /** - * (Re)create or update light. - */ - update: function (oldData) { - var data = this.data; - var diffData = diff(data, oldData); - var light = this.light; - var self = this; - - // Existing light. - if (light && !('type' in diffData)) { - var shadowsLoaded = false; - // Light type has not changed. Update light. - Object.keys(diffData).forEach(function (key) { - var value = data[key]; - switch (key) { - case 'color': - { - light.color.set(value); - break; - } - case 'groundColor': - { - light.groundColor.set(value); - break; - } - case 'angle': - { - light.angle = degToRad(value); - break; - } - case 'target': - { - // Reset target if selector is null. - if (value === null) { - if (data.type === 'spot' || data.type === 'directional') { - light.target = self.defaultTarget; - } - } else { - // Target specified, set target to entity's `object3D` when it is loaded. - if (value.hasLoaded) { - self.onSetTarget(value, light); - } else { - value.addEventListener('loaded', self.onSetTarget.bind(self, value, light)); - } - } - break; - } - case 'envMap': - self.updateProbeMap(data, light); - break; - case 'castShadow': - case 'shadowBias': - case 'shadowCameraFar': - case 'shadowCameraFov': - case 'shadowCameraNear': - case 'shadowCameraTop': - case 'shadowCameraRight': - case 'shadowCameraBottom': - case 'shadowCameraLeft': - case 'shadowCameraVisible': - case 'shadowMapHeight': - case 'shadowMapWidth': - case 'shadowRadius': - if (!shadowsLoaded) { - self.updateShadow(); - shadowsLoaded = true; - } - break; - case 'shadowCameraAutomatic': - if (data.shadowCameraAutomatic) { - self.shadowCameraAutomaticEls = Array.from(document.querySelectorAll(data.shadowCameraAutomatic)); - } else { - self.shadowCameraAutomaticEls = []; - } - break; - default: - { - light[key] = value; - } - } - }); - return; - } - - // No light yet or light type has changed. Create and add light. - this.setLight(this.data); - this.updateShadow(); - }, - tick: function () { - var bbox = new THREE.Box3(); - var normal = new THREE.Vector3(); - var cameraWorldPosition = new THREE.Vector3(); - var tempMat = new THREE.Matrix4(); - var sphere = new THREE.Sphere(); - var tempVector = new THREE.Vector3(); - return function () { - if (!(this.data.type === 'directional' && this.light.shadow && this.light.shadow.camera instanceof THREE.OrthographicCamera && this.shadowCameraAutomaticEls.length)) return; - var camera = this.light.shadow.camera; - camera.getWorldDirection(normal); - camera.getWorldPosition(cameraWorldPosition); - tempMat.copy(camera.matrixWorld); - tempMat.invert(); - camera.near = 1; - camera.left = 100000; - camera.right = -100000; - camera.top = -100000; - camera.bottom = 100000; - this.shadowCameraAutomaticEls.forEach(function (el) { - bbox.setFromObject(el.object3D); - bbox.getBoundingSphere(sphere); - var distanceToPlane = mathUtils.distanceOfPointFromPlane(cameraWorldPosition, normal, sphere.center); - var pointOnCameraPlane = mathUtils.nearestPointInPlane(cameraWorldPosition, normal, sphere.center, tempVector); - var pointInXYPlane = pointOnCameraPlane.applyMatrix4(tempMat); - camera.near = Math.min(-distanceToPlane - sphere.radius - 1, camera.near); - camera.left = Math.min(-sphere.radius + pointInXYPlane.x, camera.left); - camera.right = Math.max(sphere.radius + pointInXYPlane.x, camera.right); - camera.top = Math.max(sphere.radius + pointInXYPlane.y, camera.top); - camera.bottom = Math.min(-sphere.radius + pointInXYPlane.y, camera.bottom); - }); - camera.updateProjectionMatrix(); - }; - }(), - setLight: function (data) { - var el = this.el; - var newLight = this.getLight(data); - if (newLight) { - if (this.light) { - el.removeObject3D('light'); - } - this.light = newLight; - this.light.el = el; - el.setObject3D('light', this.light); - - // HACK solution for issue #1624 - if (data.type === 'spot' || data.type === 'directional' || data.type === 'hemisphere') { - el.getObject3D('light').translateY(-1); - } - - // set and position default lighttarget as a child to enable spotlight orientation - if (data.type === 'spot') { - el.setObject3D('light-target', this.defaultTarget); - el.getObject3D('light-target').position.set(0, 0, -1); - } - if (data.shadowCameraAutomatic) { - this.shadowCameraAutomaticEls = Array.from(document.querySelectorAll(data.shadowCameraAutomatic)); - } else { - this.shadowCameraAutomaticEls = []; - } - } - }, - /** - * Updates shadow-related properties on the current light. - */ - updateShadow: function () { - var el = this.el; - var data = this.data; - var light = this.light; - light.castShadow = data.castShadow; - - // Shadow camera helper. - var cameraHelper = el.getObject3D('cameraHelper'); - if (data.shadowCameraVisible && !cameraHelper) { - el.setObject3D('cameraHelper', new THREE.CameraHelper(light.shadow.camera)); - } else if (!data.shadowCameraVisible && cameraHelper) { - el.removeObject3D('cameraHelper'); - } - if (!data.castShadow) { - return light; - } - - // Shadow appearance. - light.shadow.bias = data.shadowBias; - light.shadow.radius = data.shadowRadius; - light.shadow.mapSize.height = data.shadowMapHeight; - light.shadow.mapSize.width = data.shadowMapWidth; - - // Shadow camera. - light.shadow.camera.near = data.shadowCameraNear; - light.shadow.camera.far = data.shadowCameraFar; - if (light.shadow.camera instanceof THREE.OrthographicCamera) { - light.shadow.camera.top = data.shadowCameraTop; - light.shadow.camera.right = data.shadowCameraRight; - light.shadow.camera.bottom = data.shadowCameraBottom; - light.shadow.camera.left = data.shadowCameraLeft; - } else { - light.shadow.camera.fov = data.shadowCameraFov; - } - light.shadow.camera.updateProjectionMatrix(); - if (cameraHelper) { - cameraHelper.update(); - } - }, - /** - * Creates a new three.js light object given data object defining the light. - * - * @param {object} data - */ - getLight: function (data) { - var angle = data.angle; - var color = new THREE.Color(data.color); - color = color.getHex(); - var decay = data.decay; - var distance = data.distance; - var groundColor = new THREE.Color(data.groundColor); - groundColor = groundColor.getHex(); - var intensity = data.intensity; - var type = data.type; - var target = data.target; - var light = null; - switch (type.toLowerCase()) { - case 'ambient': - { - return new THREE.AmbientLight(color, intensity); - } - case 'directional': - { - light = new THREE.DirectionalLight(color, intensity); - this.defaultTarget = light.target; - if (target) { - if (target.hasLoaded) { - this.onSetTarget(target, light); - } else { - target.addEventListener('loaded', this.onSetTarget.bind(this, target, light)); - } - } - return light; - } - case 'hemisphere': - { - return new THREE.HemisphereLight(color, groundColor, intensity); - } - case 'point': - { - return new THREE.PointLight(color, intensity, distance, decay); - } - case 'spot': - { - light = new THREE.SpotLight(color, intensity, distance, degToRad(angle), data.penumbra, decay); - this.defaultTarget = light.target; - if (target) { - if (target.hasLoaded) { - this.onSetTarget(target, light); - } else { - target.addEventListener('loaded', this.onSetTarget.bind(this, target, light)); - } - } - return light; - } - case 'probe': - { - light = new THREE.LightProbe(); - this.updateProbeMap(data, light); - return light; - } - default: - { - warn('%s is not a valid light type. ' + 'Choose from ambient, directional, hemisphere, point, spot.', type); - } - } - }, - /** - * Generate the spherical harmonics for the LightProbe from a cube map - */ - updateProbeMap: function (data, light) { - if (!data.envMap) { - // reset parameters if no map - light.copy(new THREE.LightProbe()); - } - if (probeCache[data.envMap] instanceof window.Promise) { - probeCache[data.envMap].then(function (tempLightProbe) { - light.copy(tempLightProbe); - }); - } - if (probeCache[data.envMap] instanceof THREE.LightProbe) { - light.copy(probeCache[data.envMap]); - } - probeCache[data.envMap] = new window.Promise(function (resolve) { - utils.srcLoader.validateCubemapSrc(data.envMap, function loadEnvMap(urls) { - CubeLoader.load(urls, function (cube) { - var tempLightProbe = THREE.LightProbeGenerator.fromCubeTexture(cube); - probeCache[data.envMap] = tempLightProbe; - light.copy(tempLightProbe); - }); - }); - }); - }, - onSetTarget: function (targetEl, light) { - light.target = targetEl.object3D; - }, - /** - * Remove light on remove (callback). - */ - remove: function () { - var el = this.el; - el.removeObject3D('light'); - if (el.getObject3D('cameraHelper')) { - el.removeObject3D('cameraHelper'); - } - } -}); - -/***/ }), - -/***/ "./src/components/line.js": -/*!********************************!*\ - !*** ./src/components/line.js ***! - \********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = registerComponent('line', { - schema: { - start: { - type: 'vec3', - default: { - x: 0, - y: 0, - z: 0 - } - }, - end: { - type: 'vec3', - default: { - x: 0, - y: 0, - z: 0 - } - }, - color: { - type: 'color', - default: '#74BEC1' - }, - opacity: { - type: 'number', - default: 1 - }, - visible: { - default: true - } - }, - multiple: true, - init: function () { - var data = this.data; - var geometry; - var material; - material = this.material = new THREE.LineBasicMaterial({ - color: data.color, - opacity: data.opacity, - transparent: data.opacity < 1, - visible: data.visible - }); - geometry = this.geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(2 * 3), 3)); - this.line = new THREE.Line(geometry, material); - this.el.setObject3D(this.attrName, this.line); - }, - update: function (oldData) { - var data = this.data; - var geometry = this.geometry; - var geoNeedsUpdate = false; - var material = this.material; - var positionArray = geometry.attributes.position.array; - - // Update geometry. - if (!isEqualVec3(data.start, oldData.start)) { - positionArray[0] = data.start.x; - positionArray[1] = data.start.y; - positionArray[2] = data.start.z; - geoNeedsUpdate = true; - } - if (!isEqualVec3(data.end, oldData.end)) { - positionArray[3] = data.end.x; - positionArray[4] = data.end.y; - positionArray[5] = data.end.z; - geoNeedsUpdate = true; - } - if (geoNeedsUpdate) { - geometry.attributes.position.needsUpdate = true; - geometry.computeBoundingSphere(); - } - material.color.setStyle(data.color); - material.opacity = data.opacity; - material.transparent = data.opacity < 1; - material.visible = data.visible; - }, - remove: function () { - this.el.removeObject3D(this.attrName, this.line); - } -}); -function isEqualVec3(a, b) { - if (!a || !b) { - return false; - } - return a.x === b.x && a.y === b.y && a.z === b.z; -} - -/***/ }), - -/***/ "./src/components/link.js": -/*!********************************!*\ - !*** ./src/components/link.js ***! - \********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var registerShader = (__webpack_require__(/*! ../core/shader */ "./src/core/shader.js").registerShader); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); - -/** - * Link component. Connect experiences and traverse between them in VR - * - * @member {object} hiddenEls - Store the hidden elements during peek mode. - */ -module.exports.Component = registerComponent('link', { - schema: { - backgroundColor: { - default: 'red', - type: 'color' - }, - borderColor: { - default: 'white', - type: 'color' - }, - highlighted: { - default: false - }, - highlightedColor: { - default: '#24CAFF', - type: 'color' - }, - href: { - default: '' - }, - image: { - type: 'asset' - }, - on: { - default: 'click' - }, - peekMode: { - default: false - }, - title: { - default: '' - }, - titleColor: { - default: 'white', - type: 'color' - }, - visualAspectEnabled: { - default: false - } - }, - init: function () { - this.navigate = this.navigate.bind(this); - this.previousQuaternion = undefined; - this.quaternionClone = new THREE.Quaternion(); - // Store hidden elements during peek mode so we can show them again later. - this.hiddenEls = []; - }, - update: function (oldData) { - var data = this.data; - var el = this.el; - var backgroundColor; - var strokeColor; - if (!data.visualAspectEnabled) { - return; - } - this.initVisualAspect(); - backgroundColor = data.highlighted ? data.highlightedColor : data.backgroundColor; - strokeColor = data.highlighted ? data.highlightedColor : data.borderColor; - el.setAttribute('material', 'backgroundColor', backgroundColor); - el.setAttribute('material', 'strokeColor', strokeColor); - if (data.on !== oldData.on) { - this.updateEventListener(); - } - if (oldData.peekMode !== undefined && data.peekMode !== oldData.peekMode) { - this.updatePeekMode(); - } - if (!data.image || oldData.image === data.image) { - return; - } - el.setAttribute('material', 'pano', typeof data.image === 'string' ? data.image : data.image.src); - }, - /* - * Toggle all elements and full 360 preview of the linked page. - */ - updatePeekMode: function () { - var el = this.el; - var sphereEl = this.sphereEl; - if (this.data.peekMode) { - this.hideAll(); - el.getObject3D('mesh').visible = false; - sphereEl.setAttribute('visible', true); - } else { - this.showAll(); - el.getObject3D('mesh').visible = true; - sphereEl.setAttribute('visible', false); - } - }, - play: function () { - this.updateEventListener(); - }, - pause: function () { - this.removeEventListener(); - }, - updateEventListener: function () { - var el = this.el; - if (!el.isPlaying) { - return; - } - this.removeEventListener(); - el.addEventListener(this.data.on, this.navigate); - }, - removeEventListener: function () { - var on = this.data.on; - if (!on) { - return; - } - this.el.removeEventListener(on, this.navigate); - }, - initVisualAspect: function () { - var el = this.el; - var semiSphereEl; - var sphereEl; - var textEl; - if (!this.data.visualAspectEnabled || this.visualAspectInitialized) { - return; - } - textEl = this.textEl = this.textEl || document.createElement('a-entity'); - sphereEl = this.sphereEl = this.sphereEl || document.createElement('a-entity'); - semiSphereEl = this.semiSphereEl = this.semiSphereEl || document.createElement('a-entity'); - - // Set portal. - el.setAttribute('geometry', { - primitive: 'circle', - radius: 1.0, - segments: 64 - }); - el.setAttribute('material', { - shader: 'portal', - pano: this.data.image, - side: 'double' - }); - - // Set text that displays the link title and URL. - textEl.setAttribute('text', { - color: this.data.titleColor, - align: 'center', - font: 'kelsonsans', - value: this.data.title || this.data.href, - width: 4 - }); - textEl.setAttribute('position', '0 1.5 0'); - el.appendChild(textEl); - - // Set sphere rendered when camera is close to portal to allow user to peek inside. - semiSphereEl.setAttribute('geometry', { - primitive: 'sphere', - radius: 1.0, - phiStart: 0, - segmentsWidth: 64, - segmentsHeight: 64, - phiLength: 180, - thetaStart: 0, - thetaLength: 360 - }); - semiSphereEl.setAttribute('material', { - shader: 'portal', - borderEnabled: 0.0, - pano: this.data.image, - side: 'back' - }); - semiSphereEl.setAttribute('rotation', '0 180 0'); - semiSphereEl.setAttribute('position', '0 0 0'); - semiSphereEl.setAttribute('visible', false); - el.appendChild(semiSphereEl); - - // Set sphere rendered when camera is close to portal to allow user to peek inside. - sphereEl.setAttribute('geometry', { - primitive: 'sphere', - radius: 10, - segmentsWidth: 64, - segmentsHeight: 64 - }); - sphereEl.setAttribute('material', { - shader: 'portal', - borderEnabled: 0.0, - pano: this.data.image, - side: 'back' - }); - sphereEl.setAttribute('visible', false); - el.appendChild(sphereEl); - this.visualAspectInitialized = true; - }, - navigate: function () { - window.location = this.data.href; - }, - /** - * 1. Swap plane that represents portal with sphere with a hole when the camera is close - * so user can peek inside portal. Sphere is rendered on opposite side of portal - * from where user enters. - * 2. Place the url/title above or inside portal depending on distance to camera. - * 3. Face portal to camera when far away from user. - */ - tick: function () { - var cameraWorldPosition = new THREE.Vector3(); - var elWorldPosition = new THREE.Vector3(); - var quaternion = new THREE.Quaternion(); - var scale = new THREE.Vector3(); - return function () { - var el = this.el; - var object3D = el.object3D; - var camera = el.sceneEl.camera; - var cameraPortalOrientation; - var distance; - var textEl = this.textEl; - if (!this.data.visualAspectEnabled) { - return; - } - - // Update matrices - object3D.updateMatrixWorld(); - camera.parent.updateMatrixWorld(); - camera.updateMatrixWorld(); - object3D.matrix.decompose(elWorldPosition, quaternion, scale); - elWorldPosition.setFromMatrixPosition(object3D.matrixWorld); - cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld); - distance = elWorldPosition.distanceTo(cameraWorldPosition); - if (distance > 20) { - // Store original orientation to be restored when the portal stops facing the camera. - if (!this.previousQuaternion) { - this.quaternionClone.copy(quaternion); - this.previousQuaternion = this.quaternionClone; - } - // If the portal is far away from the user, face portal to camera. - object3D.lookAt(cameraWorldPosition); - } else { - // When portal is close to the user/camera. - cameraPortalOrientation = this.calculateCameraPortalOrientation(); - // If user gets very close to portal, replace with holed sphere they can peek in. - if (distance < 0.5) { - // Configure text size and sphere orientation depending side user approaches portal. - if (this.semiSphereEl.getAttribute('visible') === true) { - return; - } - textEl.setAttribute('text', 'width', 1.5); - if (cameraPortalOrientation <= 0.0) { - textEl.setAttribute('position', '0 0 0.75'); - textEl.setAttribute('rotation', '0 180 0'); - this.semiSphereEl.setAttribute('rotation', '0 0 0'); - } else { - textEl.setAttribute('position', '0 0 -0.75'); - textEl.setAttribute('rotation', '0 0 0'); - this.semiSphereEl.setAttribute('rotation', '0 180 0'); - } - el.getObject3D('mesh').visible = false; - this.semiSphereEl.setAttribute('visible', true); - this.peekCameraPortalOrientation = cameraPortalOrientation; - } else { - // Calculate which side the camera is approaching the camera (back / front). - // Adjust text orientation based on camera position. - if (cameraPortalOrientation <= 0.0) { - textEl.setAttribute('rotation', '0 180 0'); - } else { - textEl.setAttribute('rotation', '0 0 0'); - } - textEl.setAttribute('text', 'width', 5); - textEl.setAttribute('position', '0 1.5 0'); - el.getObject3D('mesh').visible = true; - this.semiSphereEl.setAttribute('visible', false); - this.peekCameraPortalOrientation = undefined; - } - if (this.previousQuaternion) { - object3D.quaternion.copy(this.previousQuaternion); - this.previousQuaternion = undefined; - } - } - }; - }(), - hideAll: function () { - var el = this.el; - var hiddenEls = this.hiddenEls; - var self = this; - if (hiddenEls.length > 0) { - return; - } - el.sceneEl.object3D.traverse(function (object) { - if (object && object.el && object.el.hasAttribute('link-controls')) { - return; - } - if (!object.el || object === el.sceneEl.object3D || object.el === el || object.el === self.sphereEl || object.el === el.sceneEl.cameraEl || object.el.getAttribute('visible') === false || object.el === self.textEl || object.el === self.semiSphereEl) { - return; - } - object.el.setAttribute('visible', false); - hiddenEls.push(object.el); - }); - }, - showAll: function () { - this.hiddenEls.forEach(function (el) { - el.setAttribute('visible', true); - }); - this.hiddenEls = []; - }, - /** - * Calculate whether the camera faces the front or back face of the portal. - * @returns {number} > 0 if camera faces front of portal, < 0 if it faces back of portal. - */ - calculateCameraPortalOrientation: function () { - var mat4 = new THREE.Matrix4(); - var cameraPosition = new THREE.Vector3(); - var portalNormal = new THREE.Vector3(0, 0, 1); - var portalPosition = new THREE.Vector3(0, 0, 0); - return function () { - var el = this.el; - var camera = el.sceneEl.camera; - - // Reset tmp variables. - cameraPosition.set(0, 0, 0); - portalNormal.set(0, 0, 1); - portalPosition.set(0, 0, 0); - - // Apply portal orientation to the normal. - el.object3D.matrixWorld.extractRotation(mat4); - portalNormal.applyMatrix4(mat4); - - // Calculate portal world position. - el.object3D.updateMatrixWorld(); - el.object3D.localToWorld(portalPosition); - - // Calculate camera world position. - camera.parent.parent.updateMatrixWorld(); - camera.parent.updateMatrixWorld(); - camera.updateMatrixWorld(); - camera.localToWorld(cameraPosition); - - // Calculate vector from portal to camera. - // (portal) -------> (camera) - cameraPosition.sub(portalPosition).normalize(); - portalNormal.normalize(); - - // Side where camera approaches portal is given by sign of dot product of portal normal - // and portal to camera vectors. - return Math.sign(portalNormal.dot(cameraPosition)); - }; - }(), - remove: function () { - this.removeEventListener(); - } -}); - -/* eslint-disable */ -registerShader('portal', { - schema: { - borderEnabled: { - default: 1.0, - type: 'int', - is: 'uniform' - }, - backgroundColor: { - default: 'red', - type: 'color', - is: 'uniform' - }, - pano: { - type: 'map', - is: 'uniform' - }, - strokeColor: { - default: 'white', - type: 'color', - is: 'uniform' - } - }, - vertexShader: ['vec3 portalPosition;', 'varying vec3 vWorldPosition;', 'varying float vDistanceToCenter;', 'varying float vDistance;', 'void main() {', 'vDistanceToCenter = clamp(length(position - vec3(0.0, 0.0, 0.0)), 0.0, 1.0);', 'portalPosition = (modelMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;', 'vDistance = length(portalPosition - cameraPosition);', 'vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;', 'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);', '}'].join('\n'), - fragmentShader: ['#define RECIPROCAL_PI2 0.15915494', 'uniform sampler2D pano;', 'uniform vec3 strokeColor;', 'uniform vec3 backgroundColor;', 'uniform float borderEnabled;', 'varying float vDistanceToCenter;', 'varying float vDistance;', 'varying vec3 vWorldPosition;', 'void main() {', 'vec3 direction = normalize(vWorldPosition - cameraPosition);', 'vec2 sampleUV;', 'float borderThickness = clamp(exp(-vDistance / 50.0), 0.6, 0.95);', 'sampleUV.y = clamp(direction.y * 0.5 + 0.5, 0.0, 1.0);', 'sampleUV.x = atan(direction.z, -direction.x) * -RECIPROCAL_PI2 + 0.5;', 'if (vDistanceToCenter > borderThickness && borderEnabled == 1.0) {', 'gl_FragColor = vec4(strokeColor, 1.0);', '} else {', 'gl_FragColor = mix(texture2D(pano, sampleUV), vec4(backgroundColor, 1.0), clamp(pow((vDistance / 15.0), 2.0), 0.0, 1.0));', '}', '}'].join('\n') -}); -/* eslint-enable */ - -/***/ }), - -/***/ "./src/components/look-controls.js": -/*!*****************************************!*\ - !*** ./src/components/look-controls.js ***! - \*****************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global DeviceOrientationEvent */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); - -// To avoid recalculation at every mouse movement tick -var PI_2 = Math.PI / 2; - -/** - * look-controls. Update entity pose, factoring mouse, touch, and WebVR API data. - */ -module.exports.Component = registerComponent('look-controls', { - dependencies: ['position', 'rotation'], - schema: { - enabled: { - default: true - }, - magicWindowTrackingEnabled: { - default: true - }, - pointerLockEnabled: { - default: false - }, - reverseMouseDrag: { - default: false - }, - reverseTouchDrag: { - default: false - }, - touchEnabled: { - default: true - }, - mouseEnabled: { - default: true - } - }, - init: function () { - this.deltaYaw = 0; - this.previousHMDPosition = new THREE.Vector3(); - this.hmdQuaternion = new THREE.Quaternion(); - this.magicWindowAbsoluteEuler = new THREE.Euler(); - this.magicWindowDeltaEuler = new THREE.Euler(); - this.position = new THREE.Vector3(); - this.magicWindowObject = new THREE.Object3D(); - this.rotation = {}; - this.deltaRotation = {}; - this.savedPose = null; - this.pointerLocked = false; - this.setupMouseControls(); - this.bindMethods(); - this.previousMouseEvent = {}; - this.setupMagicWindowControls(); - - // To save / restore camera pose - this.savedPose = { - position: new THREE.Vector3(), - rotation: new THREE.Euler() - }; - - // Call enter VR handler if the scene has entered VR before the event listeners attached. - if (this.el.sceneEl.is('vr-mode') || this.el.sceneEl.is('ar-mode')) { - this.onEnterVR(); - } - }, - setupMagicWindowControls: function () { - var magicWindowControls; - var data = this.data; - - // Only on mobile devices and only enabled if DeviceOrientation permission has been granted. - if (utils.device.isMobile() || utils.device.isMobileDeviceRequestingDesktopSite()) { - magicWindowControls = this.magicWindowControls = new THREE.DeviceOrientationControls(this.magicWindowObject); - if (typeof DeviceOrientationEvent !== 'undefined' && DeviceOrientationEvent.requestPermission) { - magicWindowControls.enabled = false; - if (this.el.sceneEl.components['device-orientation-permission-ui'].permissionGranted) { - magicWindowControls.enabled = data.magicWindowTrackingEnabled; - } else { - this.el.sceneEl.addEventListener('deviceorientationpermissiongranted', function () { - magicWindowControls.enabled = data.magicWindowTrackingEnabled; - }); - } - } - } - }, - update: function (oldData) { - var data = this.data; - - // Disable grab cursor classes if no longer enabled. - if (data.enabled !== oldData.enabled) { - this.updateGrabCursor(data.enabled); - } - - // Reset magic window eulers if tracking is disabled. - if (oldData && !data.magicWindowTrackingEnabled && oldData.magicWindowTrackingEnabled) { - this.magicWindowAbsoluteEuler.set(0, 0, 0); - this.magicWindowDeltaEuler.set(0, 0, 0); - } - - // Pass on magic window tracking setting to magicWindowControls. - if (this.magicWindowControls) { - this.magicWindowControls.enabled = data.magicWindowTrackingEnabled; - } - if (oldData && !data.pointerLockEnabled !== oldData.pointerLockEnabled) { - this.removeEventListeners(); - this.addEventListeners(); - if (this.pointerLocked) { - this.exitPointerLock(); - } - } - }, - tick: function (t) { - var data = this.data; - if (!data.enabled) { - return; - } - this.updateOrientation(); - }, - play: function () { - this.addEventListeners(); - }, - pause: function () { - this.removeEventListeners(); - if (this.pointerLocked) { - this.exitPointerLock(); - } - }, - remove: function () { - this.removeEventListeners(); - if (this.pointerLocked) { - this.exitPointerLock(); - } - }, - bindMethods: function () { - this.onMouseDown = this.onMouseDown.bind(this); - this.onMouseMove = this.onMouseMove.bind(this); - this.onMouseUp = this.onMouseUp.bind(this); - this.onTouchStart = this.onTouchStart.bind(this); - this.onTouchMove = this.onTouchMove.bind(this); - this.onTouchEnd = this.onTouchEnd.bind(this); - this.onEnterVR = this.onEnterVR.bind(this); - this.onExitVR = this.onExitVR.bind(this); - this.onPointerLockChange = this.onPointerLockChange.bind(this); - this.onPointerLockError = this.onPointerLockError.bind(this); - }, - /** - * Set up states and Object3Ds needed to store rotation data. - */ - setupMouseControls: function () { - this.mouseDown = false; - this.pitchObject = new THREE.Object3D(); - this.yawObject = new THREE.Object3D(); - this.yawObject.position.y = 10; - this.yawObject.add(this.pitchObject); - }, - /** - * Add mouse and touch event listeners to canvas. - */ - addEventListeners: function () { - var sceneEl = this.el.sceneEl; - var canvasEl = sceneEl.canvas; - - // Wait for canvas to load. - if (!canvasEl) { - sceneEl.addEventListener('render-target-loaded', this.addEventListeners.bind(this)); - return; - } - - // Mouse events. - canvasEl.addEventListener('mousedown', this.onMouseDown, false); - window.addEventListener('mousemove', this.onMouseMove, false); - window.addEventListener('mouseup', this.onMouseUp, false); - - // Touch events. - canvasEl.addEventListener('touchstart', this.onTouchStart); - window.addEventListener('touchmove', this.onTouchMove); - window.addEventListener('touchend', this.onTouchEnd); - - // sceneEl events. - sceneEl.addEventListener('enter-vr', this.onEnterVR); - sceneEl.addEventListener('exit-vr', this.onExitVR); - - // Pointer Lock events. - if (this.data.pointerLockEnabled) { - document.addEventListener('pointerlockchange', this.onPointerLockChange, false); - document.addEventListener('mozpointerlockchange', this.onPointerLockChange, false); - document.addEventListener('pointerlockerror', this.onPointerLockError, false); - } - }, - /** - * Remove mouse and touch event listeners from canvas. - */ - removeEventListeners: function () { - var sceneEl = this.el.sceneEl; - var canvasEl = sceneEl && sceneEl.canvas; - if (!canvasEl) { - return; - } - - // Mouse events. - canvasEl.removeEventListener('mousedown', this.onMouseDown); - window.removeEventListener('mousemove', this.onMouseMove); - window.removeEventListener('mouseup', this.onMouseUp); - - // Touch events. - canvasEl.removeEventListener('touchstart', this.onTouchStart); - window.removeEventListener('touchmove', this.onTouchMove); - window.removeEventListener('touchend', this.onTouchEnd); - - // sceneEl events. - sceneEl.removeEventListener('enter-vr', this.onEnterVR); - sceneEl.removeEventListener('exit-vr', this.onExitVR); - - // Pointer Lock events. - document.removeEventListener('pointerlockchange', this.onPointerLockChange, false); - document.removeEventListener('mozpointerlockchange', this.onPointerLockChange, false); - document.removeEventListener('pointerlockerror', this.onPointerLockError, false); - }, - /** - * Update orientation for mobile, mouse drag, and headset. - * Mouse-drag only enabled if HMD is not active. - */ - updateOrientation: function () { - var object3D = this.el.object3D; - var pitchObject = this.pitchObject; - var yawObject = this.yawObject; - var sceneEl = this.el.sceneEl; - - // In VR or AR mode, THREE is in charge of updating the camera pose. - if ((sceneEl.is('vr-mode') || sceneEl.is('ar-mode')) && sceneEl.checkHeadsetConnected()) { - // With WebXR THREE applies headset pose to the object3D internally. - return; - } - this.updateMagicWindowOrientation(); - - // On mobile, do camera rotation with touch events and sensors. - object3D.rotation.x = this.magicWindowDeltaEuler.x + pitchObject.rotation.x; - object3D.rotation.y = this.magicWindowDeltaEuler.y + yawObject.rotation.y; - object3D.rotation.z = this.magicWindowDeltaEuler.z; - }, - updateMagicWindowOrientation: function () { - var magicWindowAbsoluteEuler = this.magicWindowAbsoluteEuler; - var magicWindowDeltaEuler = this.magicWindowDeltaEuler; - // Calculate magic window HMD quaternion. - if (this.magicWindowControls && this.magicWindowControls.enabled) { - this.magicWindowControls.update(); - magicWindowAbsoluteEuler.setFromQuaternion(this.magicWindowObject.quaternion, 'YXZ'); - if (!this.previousMagicWindowYaw && magicWindowAbsoluteEuler.y !== 0) { - this.previousMagicWindowYaw = magicWindowAbsoluteEuler.y; - } - if (this.previousMagicWindowYaw) { - magicWindowDeltaEuler.x = magicWindowAbsoluteEuler.x; - magicWindowDeltaEuler.y += magicWindowAbsoluteEuler.y - this.previousMagicWindowYaw; - magicWindowDeltaEuler.z = magicWindowAbsoluteEuler.z; - this.previousMagicWindowYaw = magicWindowAbsoluteEuler.y; - } - } - }, - /** - * Translate mouse drag into rotation. - * - * Dragging up and down rotates the camera around the X-axis (yaw). - * Dragging left and right rotates the camera around the Y-axis (pitch). - */ - onMouseMove: function (evt) { - var direction; - var movementX; - var movementY; - var pitchObject = this.pitchObject; - var previousMouseEvent = this.previousMouseEvent; - var yawObject = this.yawObject; - - // Not dragging or not enabled. - if (!this.data.enabled || !this.mouseDown && !this.pointerLocked) { - return; - } - - // Calculate delta. - if (this.pointerLocked) { - movementX = evt.movementX || evt.mozMovementX || 0; - movementY = evt.movementY || evt.mozMovementY || 0; - } else { - movementX = evt.screenX - previousMouseEvent.screenX; - movementY = evt.screenY - previousMouseEvent.screenY; - } - this.previousMouseEvent.screenX = evt.screenX; - this.previousMouseEvent.screenY = evt.screenY; - - // Calculate rotation. - direction = this.data.reverseMouseDrag ? 1 : -1; - yawObject.rotation.y += movementX * 0.002 * direction; - pitchObject.rotation.x += movementY * 0.002 * direction; - pitchObject.rotation.x = Math.max(-PI_2, Math.min(PI_2, pitchObject.rotation.x)); - }, - /** - * Register mouse down to detect mouse drag. - */ - onMouseDown: function (evt) { - var sceneEl = this.el.sceneEl; - if (!this.data.enabled || !this.data.mouseEnabled || (sceneEl.is('vr-mode') || sceneEl.is('ar-mode')) && sceneEl.checkHeadsetConnected()) { - return; - } - // Handle only primary button. - if (evt.button !== 0) { - return; - } - var canvasEl = sceneEl && sceneEl.canvas; - this.mouseDown = true; - this.previousMouseEvent.screenX = evt.screenX; - this.previousMouseEvent.screenY = evt.screenY; - this.showGrabbingCursor(); - if (this.data.pointerLockEnabled && !this.pointerLocked) { - if (canvasEl.requestPointerLock) { - canvasEl.requestPointerLock(); - } else if (canvasEl.mozRequestPointerLock) { - canvasEl.mozRequestPointerLock(); - } - } - }, - /** - * Shows grabbing cursor on scene - */ - showGrabbingCursor: function () { - this.el.sceneEl.canvas.style.cursor = 'grabbing'; - }, - /** - * Hides grabbing cursor on scene - */ - hideGrabbingCursor: function () { - this.el.sceneEl.canvas.style.cursor = ''; - }, - /** - * Register mouse up to detect release of mouse drag. - */ - onMouseUp: function () { - this.mouseDown = false; - this.hideGrabbingCursor(); - }, - /** - * Register touch down to detect touch drag. - */ - onTouchStart: function (evt) { - if (evt.touches.length !== 1 || !this.data.touchEnabled || this.el.sceneEl.is('vr-mode') || this.el.sceneEl.is('ar-mode')) { - return; - } - this.touchStart = { - x: evt.touches[0].pageX, - y: evt.touches[0].pageY - }; - this.touchStarted = true; - }, - /** - * Translate touch move to Y-axis rotation. - */ - onTouchMove: function (evt) { - var direction; - var canvas = this.el.sceneEl.canvas; - var deltaY; - var yawObject = this.yawObject; - if (!this.touchStarted || !this.data.touchEnabled) { - return; - } - deltaY = 2 * Math.PI * (evt.touches[0].pageX - this.touchStart.x) / canvas.clientWidth; - direction = this.data.reverseTouchDrag ? 1 : -1; - // Limit touch orientation to to yaw (y axis). - yawObject.rotation.y -= deltaY * 0.5 * direction; - this.touchStart = { - x: evt.touches[0].pageX, - y: evt.touches[0].pageY - }; - }, - /** - * Register touch end to detect release of touch drag. - */ - onTouchEnd: function () { - this.touchStarted = false; - }, - /** - * Save pose. - */ - onEnterVR: function () { - var sceneEl = this.el.sceneEl; - if (!sceneEl.checkHeadsetConnected()) { - return; - } - this.saveCameraPose(); - this.el.object3D.position.set(0, 0, 0); - this.el.object3D.rotation.set(0, 0, 0); - if (sceneEl.hasWebXR) { - this.el.object3D.matrixAutoUpdate = false; - this.el.object3D.updateMatrix(); - } - }, - /** - * Restore the pose. - */ - onExitVR: function () { - if (!this.el.sceneEl.checkHeadsetConnected()) { - return; - } - this.restoreCameraPose(); - this.previousHMDPosition.set(0, 0, 0); - this.el.object3D.matrixAutoUpdate = true; - }, - /** - * Update Pointer Lock state. - */ - onPointerLockChange: function () { - this.pointerLocked = !!(document.pointerLockElement || document.mozPointerLockElement); - }, - /** - * Recover from Pointer Lock error. - */ - onPointerLockError: function () { - this.pointerLocked = false; - }, - // Exits pointer-locked mode. - exitPointerLock: function () { - document.exitPointerLock(); - this.pointerLocked = false; - }, - /** - * Toggle the feature of showing/hiding the grab cursor. - */ - updateGrabCursor: function (enabled) { - var sceneEl = this.el.sceneEl; - function enableGrabCursor() { - sceneEl.canvas.classList.add('a-grab-cursor'); - } - function disableGrabCursor() { - sceneEl.canvas.classList.remove('a-grab-cursor'); - } - if (!sceneEl.canvas) { - if (enabled) { - sceneEl.addEventListener('render-target-loaded', enableGrabCursor); - } else { - sceneEl.addEventListener('render-target-loaded', disableGrabCursor); - } - return; - } - if (enabled) { - enableGrabCursor(); - return; - } - disableGrabCursor(); - }, - /** - * Save camera pose before entering VR to restore later if exiting. - */ - saveCameraPose: function () { - var el = this.el; - this.savedPose.position.copy(el.object3D.position); - this.savedPose.rotation.copy(el.object3D.rotation); - this.hasSavedPose = true; - }, - /** - * Reset camera pose to before entering VR. - */ - restoreCameraPose: function () { - var el = this.el; - var savedPose = this.savedPose; - if (!this.hasSavedPose) { - return; - } - - // Reset camera orientation. - el.object3D.position.copy(savedPose.position); - el.object3D.rotation.copy(savedPose.rotation); - this.hasSavedPose = false; - } -}); - -/***/ }), - -/***/ "./src/components/magicleap-controls.js": -/*!**********************************************!*\ - !*** ./src/components/magicleap-controls.js ***! - \**********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; - -// See Profiles Registry: -// https://github.com/immersive-web/webxr-input-profiles/tree/master/packages/registry -// TODO: Add a more robust system for deriving gamepad name. -var GAMEPAD_ID_PREFIX = 'magicleap'; -var GAMEPAD_ID_SUFFIX = '-one'; -var GAMEPAD_ID_COMPOSITE = GAMEPAD_ID_PREFIX + GAMEPAD_ID_SUFFIX; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var MAGICLEAP_CONTROLLER_MODEL_GLB_URL = AFRAME_CDN_ROOT + 'controllers/magicleap/magicleap-one-controller.glb'; - -/** - * Button IDs: - * 0 - trigger - * 1 - grip - * 2 - touchpad - * 3 - menu (never dispatched on this layer) - * - * Axis: - * 0 - touchpad x axis - * 1 - touchpad y axis - */ -var INPUT_MAPPING_WEBXR = { - axes: { - touchpad: [0, 1] - }, - buttons: ['trigger', 'grip', 'touchpad', 'menu'] -}; - -/** - * Magic Leap Controls - * Interface with Magic Leap control and map Gamepad events to controller - * buttons: trigger, grip, touchpad, and menu. - * Load a controller model. - */ -module.exports.Component = registerComponent('magicleap-controls', { - schema: { - hand: { - default: 'none' - }, - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - } - }, - mapping: INPUT_MAPPING_WEBXR, - init: function () { - var self = this; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.previousButtonValues = {}; - this.bindMethods(); - }, - update: function () { - var data = this.data; - this.controllerIndex = data.hand === 'right' ? 0 : data.hand === 'left' ? 1 : 2; - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('axismove', this.onAxisMoved); - el.addEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('axismove', this.onAxisMoved); - el.removeEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - var data = this.data; - checkControllerPresentAndSetup(this, GAMEPAD_ID_COMPOSITE, { - index: this.controllerIndex, - hand: data.hand - }); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - el.setAttribute('tracked-controls', { - // TODO: verify expected behavior between reserved prefixes. - idPrefix: GAMEPAD_ID_COMPOSITE, - hand: data.hand, - controller: this.controllerIndex, - orientationOffset: data.orientationOffset - }); - - // Load model. - if (!this.data.model) { - return; - } - this.el.setAttribute('gltf-model', MAGICLEAP_CONTROLLER_MODEL_GLB_URL); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - // Note that due to gamepadconnected event propagation issues, we don't rely on events. - this.checkIfControllerPresent(); - }, - /** - * Rotate the trigger button based on how hard the trigger is pressed. - */ - onButtonChanged: function (evt) { - var button = this.mapping.buttons[evt.detail.id]; - var analogValue; - if (!button) { - return; - } - if (button === 'trigger') { - analogValue = evt.detail.state.value; - console.log('analog value of trigger press: ' + analogValue); - } - - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onModelLoaded: function (evt) { - var controllerObject3D = evt.detail.model; - // our glb scale is too large. - controllerObject3D.scale.set(0.01, 0.01, 0.01); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - updateModel: function (buttonName, evtName) {}, - setButtonColor: function (buttonName, color) {} -}); - -/***/ }), - -/***/ "./src/components/material.js": -/*!************************************!*\ - !*** ./src/components/material.js ***! - \************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global Promise */ -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var component = __webpack_require__(/*! ../core/component */ "./src/core/component.js"); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var shader = __webpack_require__(/*! ../core/shader */ "./src/core/shader.js"); -var error = utils.debug('components:material:error'); -var registerComponent = component.registerComponent; -var shaders = shader.shaders; -var shaderNames = shader.shaderNames; - -/** - * Material component. - * - * @member {object} shader - Determines how material is shaded. Defaults to `standard`, - * three.js's implementation of PBR. Another standard shading model is `flat` which - * uses MeshBasicMaterial. - */ -module.exports.Component = registerComponent('material', { - schema: { - alphaTest: { - default: 0.0, - min: 0.0, - max: 1.0 - }, - depthTest: { - default: true - }, - depthWrite: { - default: true - }, - flatShading: { - default: false - }, - npot: { - default: false - }, - offset: { - type: 'vec2', - default: { - x: 0, - y: 0 - } - }, - opacity: { - default: 1.0, - min: 0.0, - max: 1.0 - }, - repeat: { - type: 'vec2', - default: { - x: 1, - y: 1 - } - }, - shader: { - default: 'standard', - oneOf: shaderNames, - schemaChange: true - }, - side: { - default: 'front', - oneOf: ['front', 'back', 'double'] - }, - transparent: { - default: false - }, - vertexColorsEnabled: { - default: false - }, - visible: { - default: true - }, - blending: { - default: 'normal', - oneOf: ['none', 'normal', 'additive', 'subtractive', 'multiply'] - }, - dithering: { - default: true - }, - anisotropy: { - default: 0, - min: 0 - } - }, - init: function () { - this.material = null; - }, - /** - * Update or create material. - * - * @param {object|null} oldData - */ - update: function (oldData) { - var data = this.data; - if (!this.shader || data.shader !== oldData.shader) { - this.updateShader(data.shader); - } - this.shader.update(this.data); - this.updateMaterial(oldData); - }, - updateSchema: function (data) { - var currentShader; - var newShader; - var schema; - var shader; - newShader = data && data.shader; - currentShader = this.oldData && this.oldData.shader; - shader = newShader || currentShader; - schema = shaders[shader] && shaders[shader].schema; - if (!schema) { - error('Unknown shader schema ' + shader); - } - if (currentShader && newShader === currentShader) { - return; - } - this.extendSchema(schema); - this.updateBehavior(); - }, - updateBehavior: function () { - var key; - var sceneEl = this.el.sceneEl; - var schema = this.schema; - var self = this; - var tickProperties; - function tickTime(time, delta) { - var key; - for (key in tickProperties) { - tickProperties[key] = time; - } - self.shader.update(tickProperties); - } - this.tick = undefined; - tickProperties = {}; - for (key in schema) { - if (schema[key].type === 'time') { - this.tick = tickTime; - tickProperties[key] = true; - } - } - if (!sceneEl) { - return; - } - if (this.tick) { - sceneEl.addBehavior(this); - } else { - sceneEl.removeBehavior(this); - } - }, - updateShader: function (shaderName) { - var data = this.data; - var Shader = shaders[shaderName] && shaders[shaderName].Shader; - var shaderInstance; - if (!Shader) { - throw new Error('Unknown shader ' + shaderName); - } - - // Get material from A-Frame shader. - shaderInstance = this.shader = new Shader(); - shaderInstance.el = this.el; - shaderInstance.init(data); - this.setMaterial(shaderInstance.material); - this.updateSchema(data); - }, - /** - * Set and update base material properties. - * Set `needsUpdate` when needed. - */ - updateMaterial: function (oldData) { - var data = this.data; - var material = this.material; - var oldDataHasKeys; - - // Base material properties. - material.alphaTest = data.alphaTest; - material.depthTest = data.depthTest !== false; - material.depthWrite = data.depthWrite !== false; - material.opacity = data.opacity; - material.flatShading = data.flatShading; - material.side = parseSide(data.side); - material.transparent = data.transparent !== false || data.opacity < 1.0; - material.vertexColors = data.vertexColorsEnabled; - material.visible = data.visible; - material.blending = parseBlending(data.blending); - material.dithering = data.dithering; - - // Check if material needs update. - for (oldDataHasKeys in oldData) { - break; - } - if (oldDataHasKeys && (oldData.alphaTest !== data.alphaTest || oldData.side !== data.side || oldData.vertexColorsEnabled !== data.vertexColorsEnabled)) { - material.needsUpdate = true; - } - }, - /** - * Remove material on remove (callback). - * Dispose of it from memory and unsubscribe from scene updates. - */ - remove: function () { - var defaultMaterial = new THREE.MeshBasicMaterial(); - var material = this.material; - var object3D = this.el.getObject3D('mesh'); - if (object3D) { - object3D.material = defaultMaterial; - } - disposeMaterial(material, this.system); - }, - /** - * (Re)create new material. Has side-effects of setting `this.material` and updating - * material registration in scene. - * - * @param {object} data - Material component data. - * @param {object} type - Material type to create. - * @returns {object} Material. - */ - setMaterial: function (material) { - var el = this.el; - var mesh; - var system = this.system; - if (this.material) { - disposeMaterial(this.material, system); - } - this.material = material; - system.registerMaterial(material); - - // Set on mesh. If mesh does not exist, wait for it. - mesh = el.getObject3D('mesh'); - if (mesh) { - mesh.material = material; - } else { - el.addEventListener('object3dset', function waitForMesh(evt) { - if (evt.detail.type !== 'mesh' || evt.target !== el) { - return; - } - el.getObject3D('mesh').material = material; - el.removeEventListener('object3dset', waitForMesh); - }); - } - } -}); - -/** - * Return a three.js constant determining which material face sides to render - * based on the side parameter (passed as a component property). - * - * @param {string} [side=front] - `front`, `back`, or `double`. - * @returns {number} THREE.FrontSide, THREE.BackSide, or THREE.DoubleSide. - */ -function parseSide(side) { - switch (side) { - case 'back': - { - return THREE.BackSide; - } - case 'double': - { - return THREE.DoubleSide; - } - default: - { - // Including case `front`. - return THREE.FrontSide; - } - } -} - -/** - * Return a three.js constant determining blending - * - * @param {string} [blending=normal] - * - `none`, additive`, `subtractive`,`multiply` or `normal`. - * @returns {number} - */ -function parseBlending(blending) { - switch (blending) { - case 'none': - { - return THREE.NoBlending; - } - case 'additive': - { - return THREE.AdditiveBlending; - } - case 'subtractive': - { - return THREE.SubtractiveBlending; - } - case 'multiply': - { - return THREE.MultiplyBlending; - } - default: - { - return THREE.NormalBlending; - } - } -} - -/** - * Dispose of material from memory and unsubscribe material from scene updates like fog. - */ -function disposeMaterial(material, system) { - material.dispose(); - system.unregisterMaterial(material); - - // Dispose textures on this material - Object.keys(material).filter(function (propName) { - return material[propName] && material[propName].isTexture; - }).forEach(function (mapName) { - material[mapName].dispose(); - }); -} - -/***/ }), - -/***/ "./src/components/obb-collider.js": -/*!****************************************!*\ - !*** ./src/components/obb-collider.js ***! - \****************************************/ -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -registerComponent('obb-collider', { - schema: { - size: { - default: 0 - }, - trackedObject3D: { - default: '' - }, - minimumColliderDimension: { - default: 0.02 - }, - centerModel: { - default: false - } - }, - init: function () { - this.previousScale = new THREE.Vector3().copy(this.el.object3D.scale); - this.auxEuler = new THREE.Euler(); - this.boundingBox = new THREE.Box3(); - this.boundingBoxSize = new THREE.Vector3(); - this.updateCollider = this.updateCollider.bind(this); - this.onModelLoaded = this.onModelLoaded.bind(this); - this.updateBoundingBox = this.updateBoundingBox.bind(this); - this.el.addEventListener('model-loaded', this.onModelLoaded); - this.updateCollider(); - this.system.addCollider(this.el); - }, - remove: function () { - this.system.removeCollider(this.el); - }, - update: function () { - if (this.data.trackedObject3D) { - this.trackedObject3DPath = this.data.trackedObject3D.split('.'); - } - }, - onModelLoaded: function () { - if (this.data.centerModel) { - this.centerModel(); - } - this.updateCollider(); - }, - centerModel: function () { - var el = this.el; - var model = el.components['gltf-model'] && el.components['gltf-model'].model; - var box; - var center; - if (!model) { - return; - } - this.el.removeObject3D('mesh'); - box = new THREE.Box3().setFromObject(model); - center = box.getCenter(new THREE.Vector3()); - model.position.x += model.position.x - center.x; - model.position.y += model.position.y - center.y; - model.position.z += model.position.z - center.z; - this.el.setObject3D('mesh', model); - }, - updateCollider: function () { - var el = this.el; - var boundingBoxSize = this.boundingBoxSize; - var aabb = this.aabb = this.aabb || new THREE.OBB(); - this.obb = this.obb || new THREE.OBB(); - - // Defer if entity has not yet loaded. - if (!el.hasLoaded) { - el.addEventListener('loaded', this.updateCollider); - return; - } - this.updateBoundingBox(); - aabb.halfSize.copy(boundingBoxSize).multiplyScalar(0.5); - if (this.el.sceneEl.systems['obb-collider'].data.showColliders) { - this.showCollider(); - } - }, - showCollider: function () { - this.updateColliderMesh(); - this.renderColliderMesh.visible = true; - }, - updateColliderMesh: function () { - var renderColliderMesh = this.renderColliderMesh; - var boundingBoxSize = this.boundingBoxSize; - if (!renderColliderMesh) { - this.initColliderMesh(); - return; - } - - // Destroy current geometry. - renderColliderMesh.geometry.dispose(); - renderColliderMesh.geometry = new THREE.BoxGeometry(boundingBoxSize.x, boundingBoxSize.y, boundingBoxSize.z); - }, - hideCollider: function () { - if (!this.renderColliderMesh) { - return; - } - this.renderColliderMesh.visible = false; - }, - initColliderMesh: function () { - var boundingBoxSize; - var renderColliderGeometry; - var renderColliderMesh; - boundingBoxSize = this.boundingBoxSize; - renderColliderGeometry = this.renderColliderGeometry = new THREE.BoxGeometry(boundingBoxSize.x, boundingBoxSize.y, boundingBoxSize.z); - renderColliderMesh = this.renderColliderMesh = new THREE.Mesh(renderColliderGeometry, new THREE.MeshLambertMaterial({ - color: 0x00ff00, - side: THREE.DoubleSide - })); - renderColliderMesh.matrixAutoUpdate = false; - renderColliderMesh.matrixWorldAutoUpdate = false; - // THREE scene forces matrix world update even if matrixWorldAutoUpdate set to false. - renderColliderMesh.updateMatrixWorld = function () {/* no op */}; - this.el.sceneEl.object3D.add(renderColliderMesh); - }, - updateBoundingBox: function () { - var auxPosition = new THREE.Vector3(); - var auxScale = new THREE.Vector3(); - var auxQuaternion = new THREE.Quaternion(); - var identityQuaternion = new THREE.Quaternion(); - var auxMatrix = new THREE.Matrix4(); - return function () { - var auxEuler = this.auxEuler; - var boundingBox = this.boundingBox; - var size = this.data.size; - var trackedObject3D = this.trackedObject3D || this.el.object3D; - var boundingBoxSize = this.boundingBoxSize; - var minimumColliderDimension = this.data.minimumColliderDimension; - - // user defined size takes precedence. - if (size) { - this.boundingBoxSize.x = size; - this.boundingBoxSize.y = size; - this.boundingBoxSize.z = size; - return; - } - - // Bounding box is created axis-aligned AABB. - // If there's any rotation the box will have the wrong size. - // It undoes the local entity rotation and then restores so box has the expected size. - // We also undo the parent world rotation. - auxEuler.copy(trackedObject3D.rotation); - trackedObject3D.rotation.set(0, 0, 0); - trackedObject3D.parent.matrixWorld.decompose(auxPosition, auxQuaternion, auxScale); - auxMatrix.compose(auxPosition, identityQuaternion, auxScale); - trackedObject3D.parent.matrixWorld.copy(auxMatrix); - - // Calculate bounding box size. - boundingBox.setFromObject(trackedObject3D, true); - boundingBox.getSize(boundingBoxSize); - - // Enforce minimum dimensions. - boundingBoxSize.x = boundingBoxSize.x < minimumColliderDimension ? minimumColliderDimension : boundingBoxSize.x; - boundingBoxSize.y = boundingBoxSize.y < minimumColliderDimension ? minimumColliderDimension : boundingBoxSize.y; - boundingBoxSize.z = boundingBoxSize.z < minimumColliderDimension ? minimumColliderDimension : boundingBoxSize.z; - - // Restore rotations. - trackedObject3D.parent.matrixWorld.compose(auxPosition, auxQuaternion, auxScale); - this.el.object3D.rotation.copy(auxEuler); - }; - }(), - checkTrackedObject: function () { - var trackedObject3DPath = this.trackedObject3DPath; - var trackedObject3D; - if (trackedObject3DPath && trackedObject3DPath.length && !this.trackedObject3D) { - trackedObject3D = this.el; - for (var i = 0; i < trackedObject3DPath.length; i++) { - trackedObject3D = trackedObject3D[trackedObject3DPath[i]]; - if (!trackedObject3D) { - break; - } - } - if (trackedObject3D) { - this.trackedObject3D = trackedObject3D; - this.updateCollider(); - } - } - return this.trackedObject3D; - }, - tick: function () { - var auxPosition = new THREE.Vector3(); - var auxScale = new THREE.Vector3(); - var auxQuaternion = new THREE.Quaternion(); - var auxMatrix = new THREE.Matrix4(); - return function () { - var obb = this.obb; - var renderColliderMesh = this.renderColliderMesh; - var trackedObject3D = this.checkTrackedObject() || this.el.object3D; - if (!trackedObject3D) { - return; - } - trackedObject3D.updateMatrix(); - trackedObject3D.updateMatrixWorld(true); - trackedObject3D.matrixWorld.decompose(auxPosition, auxQuaternion, auxScale); - - // Recalculate collider if scale has changed. - if (Math.abs(auxScale.x - this.previousScale.x) > 0.0001 || Math.abs(auxScale.y - this.previousScale.y) > 0.0001 || Math.abs(auxScale.z - this.previousScale.z) > 0.0001) { - this.updateCollider(); - } - this.previousScale.copy(auxScale); - - // reset scale, keep position and rotation - auxScale.set(1, 1, 1); - auxMatrix.compose(auxPosition, auxQuaternion, auxScale); - // Update OBB visual representation. - if (renderColliderMesh) { - renderColliderMesh.matrixWorld.copy(auxMatrix); - } - - // Reset OBB with AABB and apply entity matrix. applyMatrix4 changes OBB internal state. - obb.copy(this.aabb); - obb.applyMatrix4(auxMatrix); - }; - }() -}); - -/***/ }), - -/***/ "./src/components/obj-model.js": -/*!*************************************!*\ - !*** ./src/components/obj-model.js ***! - \*************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var debug = __webpack_require__(/*! ../utils/debug */ "./src/utils/debug.js"); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var warn = debug('components:obj-model:warn'); -module.exports.Component = registerComponent('obj-model', { - schema: { - mtl: { - type: 'model' - }, - obj: { - type: 'model' - } - }, - init: function () { - var self = this; - this.model = null; - this.objLoader = new THREE.OBJLoader(); - this.mtlLoader = new THREE.MTLLoader(this.objLoader.manager); - // Allow cross-origin images to be loaded. - this.mtlLoader.crossOrigin = ''; - this.el.addEventListener('componentinitialized', function (evt) { - if (!self.model) { - return; - } - if (evt.detail.name !== 'material') { - return; - } - self.applyMaterial(); - }); - }, - update: function () { - var data = this.data; - if (!data.obj) { - return; - } - this.resetMesh(); - this.loadObj(data.obj, data.mtl); - }, - remove: function () { - this.resetMesh(); - }, - resetMesh: function () { - if (!this.model) { - return; - } - this.el.removeObject3D('mesh'); - }, - loadObj: function (objUrl, mtlUrl) { - var self = this; - var el = this.el; - var mtlLoader = this.mtlLoader; - var objLoader = this.objLoader; - var rendererSystem = this.el.sceneEl.systems.renderer; - var BASE_PATH = mtlUrl.substr(0, mtlUrl.lastIndexOf('/') + 1); - if (mtlUrl) { - // .OBJ with an .MTL. - if (el.hasAttribute('material')) { - warn('Material component properties are ignored when a .MTL is provided'); - } - mtlLoader.setResourcePath(BASE_PATH); - mtlLoader.load(mtlUrl, function (materials) { - materials.preload(); - objLoader.setMaterials(materials); - objLoader.load(objUrl, function (objModel) { - self.model = objModel; - self.model.traverse(function (object) { - if (object.isMesh) { - var material = object.material; - if (material.map) rendererSystem.applyColorCorrection(material.map); - if (material.emissiveMap) rendererSystem.applyColorCorrection(material.emissiveMap); - } - }); - el.setObject3D('mesh', objModel); - el.emit('model-loaded', { - format: 'obj', - model: objModel - }); - }); - }); - return; - } - - // .OBJ only. - objLoader.load(objUrl, function loadObjOnly(objModel) { - self.model = objModel; - self.applyMaterial(); - el.setObject3D('mesh', objModel); - el.emit('model-loaded', { - format: 'obj', - model: objModel - }); - }); - }, - /** - * Apply material from material component recursively. - */ - applyMaterial: function () { - var material = this.el.components.material; - if (!material) { - return; - } - this.model.traverse(function (child) { - if (child instanceof THREE.Mesh) { - child.material = material.material; - } - }); - } -}); - -/***/ }), - -/***/ "./src/components/oculus-go-controls.js": -/*!**********************************************!*\ - !*** ./src/components/oculus-go-controls.js ***! - \**********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var isWebXRAvailable = (__webpack_require__(/*! ../utils/ */ "./src/utils/index.js").device.isWebXRAvailable); -var GAMEPAD_ID_WEBXR = 'oculus-go'; -var GAMEPAD_ID_WEBVR = 'Oculus Go'; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var OCULUS_GO_CONTROLLER_MODEL_URL = AFRAME_CDN_ROOT + 'controllers/oculus/go/oculus-go-controller.gltf'; - -// Prefix for Gen1 and Gen2 Oculus Touch Controllers. -var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR; - -/** - * Button indices: - * 0 - trackpad - * 1 - trigger - * - * Axis: - * 0 - trackpad x - * 1 - trackpad y - */ -var INPUT_MAPPING_WEBVR = { - axes: { - trackpad: [0, 1] - }, - buttons: ['trackpad', 'trigger'] -}; - -/** - * Button indices: - * 0 - trigger - * 1 - none - * 2 - touchpad - * - * Axis: - * 0 - touchpad x - * 1 - touchpad y - * Reference: https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/oculus/oculus-go.json - */ -var INPUT_MAPPING_WEBXR = { - axes: { - touchpad: [0, 1] - }, - buttons: ['trigger', 'none', 'touchpad'] -}; -var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR; - -/** - * Oculus Go controls. - * Interface with Oculus Go controller and map Gamepad events to - * controller buttons: trackpad, trigger - * Load a controller model and highlight the pressed buttons. - */ -module.exports.Component = registerComponent('oculus-go-controls', { - schema: { - hand: { - default: '' - }, - // This informs the degenerate arm model. - buttonColor: { - type: 'color', - default: '#FFFFFF' - }, - buttonTouchedColor: { - type: 'color', - default: '#BBBBBB' - }, - buttonHighlightColor: { - type: 'color', - default: '#7A7A7A' - }, - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - }, - armModel: { - default: true - } - }, - mapping: INPUT_MAPPING, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - init: function () { - var self = this; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.bindMethods(); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('model-loaded', this.onModelLoaded); - el.addEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('model-loaded', this.onModelLoaded); - el.removeEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, this.data.hand ? { - hand: this.data.hand - } : {}); - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - el.setAttribute('tracked-controls', { - armModel: data.armModel, - hand: data.hand, - idPrefix: GAMEPAD_ID_PREFIX, - orientationOffset: data.orientationOffset - }); - if (!this.data.model) { - return; - } - this.el.setAttribute('gltf-model', OCULUS_GO_CONTROLLER_MODEL_URL); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - this.checkIfControllerPresent(); - }, - // No need for onButtonChanged, since Oculus Go controller has no analog buttons. - - onModelLoaded: function (evt) { - var controllerObject3D = evt.detail.model; - var buttonMeshes; - if (evt.target !== this.el || !this.data.model) { - return; - } - buttonMeshes = this.buttonMeshes = {}; - buttonMeshes.trigger = controllerObject3D.getObjectByName('oculus_go_button_trigger'); - buttonMeshes.trackpad = controllerObject3D.getObjectByName('oculus_go_touchpad'); - buttonMeshes.touchpad = controllerObject3D.getObjectByName('oculus_go_touchpad'); - }, - onButtonChanged: function (evt) { - var button = this.mapping.buttons[evt.detail.id]; - if (!button) return; - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - updateModel: function (buttonName, evtName) { - if (!this.data.model) { - return; - } - this.updateButtonModel(buttonName, evtName); - }, - updateButtonModel: function (buttonName, state) { - var buttonMeshes = this.buttonMeshes; - if (!buttonMeshes || !buttonMeshes[buttonName]) { - return; - } - var color; - var button; - switch (state) { - case 'down': - color = this.data.buttonHighlightColor; - break; - case 'touchstart': - color = this.data.buttonTouchedColor; - break; - default: - color = this.data.buttonColor; - } - button = buttonMeshes[buttonName]; - button.material.color.set(color); - } -}); - -/***/ }), - -/***/ "./src/components/oculus-touch-controls.js": -/*!*************************************************!*\ - !*** ./src/components/oculus-touch-controls.js ***! - \*************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var isWebXRAvailable = (__webpack_require__(/*! ../utils/ */ "./src/utils/index.js").device.isWebXRAvailable); -var GAMEPAD_ID_WEBXR = 'oculus-touch'; -var GAMEPAD_ID_WEBVR = 'Oculus Touch'; - -// Prefix for Gen1 and Gen2 Oculus Touch Controllers. -var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR; - -// First generation model URL. -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var TOUCH_CONTROLLER_MODEL_BASE_URL = AFRAME_CDN_ROOT + 'controllers/oculus/oculus-touch-controller-'; -var META_CONTROLLER_MODEL_BASE_URL = AFRAME_CDN_ROOT + 'controllers/meta/'; -var OCULUS_TOUCH_WEBVR = { - left: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'left.gltf', - rayOrigin: { - origin: { - x: 0.008, - y: -0.01, - z: 0 - }, - direction: { - x: 0, - y: -0.8, - z: -1 - } - }, - modelPivotOffset: new THREE.Vector3(-0.005, 0.003, -0.055), - modelPivotRotation: new THREE.Euler(0, 0, 0) - }, - right: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'right.gltf', - rayOrigin: { - origin: { - x: -0.008, - y: -0.01, - z: 0 - }, - direction: { - x: 0, - y: -0.8, - z: -1 - } - }, - modelPivotOffset: new THREE.Vector3(0.005, 0.003, -0.055), - modelPivotRotation: new THREE.Euler(0, 0, 0) - } -}; -var OCULUS_TOUCH_WEBXR = { - left: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'left.gltf', - rayOrigin: { - origin: { - x: 0.002, - y: -0.005, - z: -0.03 - }, - direction: { - x: 0, - y: -0.8, - z: -1 - } - }, - modelPivotOffset: new THREE.Vector3(-0.005, 0.036, -0.037), - modelPivotRotation: new THREE.Euler(Math.PI / 4.5, 0, 0) - }, - right: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'right.gltf', - rayOrigin: { - origin: { - x: -0.002, - y: -0.005, - z: -0.03 - }, - direction: { - x: 0, - y: -0.8, - z: -1 - } - }, - modelPivotOffset: new THREE.Vector3(0.005, 0.036, -0.037), - modelPivotRotation: new THREE.Euler(Math.PI / 4.5, 0, 0) - } -}; -var OCULUS_TOUCH_CONFIG = isWebXRAvailable ? OCULUS_TOUCH_WEBXR : OCULUS_TOUCH_WEBVR; -var CONTROLLER_DEFAULT = 'oculus-touch'; -var CONTROLLER_PROPERTIES = { - 'oculus-touch': OCULUS_TOUCH_CONFIG, - 'oculus-touch-v2': { - left: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'gen2-left.gltf', - rayOrigin: { - origin: { - x: -0.006, - y: -0.03, - z: -0.04 - }, - direction: { - x: 0, - y: -0.9, - z: -1 - } - }, - modelPivotOffset: new THREE.Vector3(0, -0.007, -0.021), - modelPivotRotation: new THREE.Euler(-Math.PI / 4, 0, 0) - }, - right: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'gen2-right.gltf', - rayOrigin: { - origin: { - x: 0.006, - y: -0.03, - z: -0.04 - }, - direction: { - x: 0, - y: -0.9, - z: -1 - } - }, - modelPivotOffset: new THREE.Vector3(0, -0.007, -0.021), - modelPivotRotation: new THREE.Euler(-Math.PI / 4, 0, 0) - } - }, - 'oculus-touch-v3': { - left: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'v3-left.glb', - rayOrigin: { - origin: { - x: 0.0065, - y: -0.0186, - z: -0.05 - }, - direction: { - x: 0.12394785839500175, - y: -0.5944043672340157, - z: -0.7945567170519814 - } - }, - modelPivotOffset: new THREE.Vector3(0, 0, 0), - modelPivotRotation: new THREE.Euler(0, 0, 0) - }, - right: { - modelUrl: TOUCH_CONTROLLER_MODEL_BASE_URL + 'v3-right.glb', - rayOrigin: { - origin: { - x: -0.0065, - y: -0.0186, - z: -0.05 - }, - direction: { - x: -0.12394785839500175, - y: -0.5944043672340157, - z: -0.7945567170519814 - } - }, - modelPivotOffset: new THREE.Vector3(0, 0, 0), - modelPivotRotation: new THREE.Euler(0, 0, 0) - } - }, - 'meta-quest-touch-pro': { - left: { - modelUrl: META_CONTROLLER_MODEL_BASE_URL + 'quest-touch-pro-left.glb', - rayOrigin: { - origin: { - x: 0.0065, - y: -0.0186, - z: -0.05 - }, - direction: { - x: 0.12394785839500175, - y: -0.5944043672340157, - z: -0.7945567170519814 - } - }, - modelPivotOffset: new THREE.Vector3(0, 0, 0), - modelPivotRotation: new THREE.Euler(0, 0, 0) - }, - right: { - modelUrl: META_CONTROLLER_MODEL_BASE_URL + 'quest-touch-pro-right.glb', - rayOrigin: { - origin: { - x: -0.0065, - y: -0.0186, - z: -0.05 - }, - direction: { - x: -0.12394785839500175, - y: -0.5944043672340157, - z: -0.7945567170519814 - } - }, - modelPivotOffset: new THREE.Vector3(0, 0, 0), - modelPivotRotation: new THREE.Euler(0, 0, 0) - } - }, - 'meta-quest-touch-plus': { - left: { - modelUrl: META_CONTROLLER_MODEL_BASE_URL + 'quest-touch-plus-left.glb', - rayOrigin: { - origin: { - x: 0.0065, - y: -0.0186, - z: -0.05 - }, - direction: { - x: 0.12394785839500175, - y: -0.5944043672340157, - z: -0.7945567170519814 - } - }, - modelPivotOffset: new THREE.Vector3(0, 0, 0), - modelPivotRotation: new THREE.Euler(0, 0, 0) - }, - right: { - modelUrl: META_CONTROLLER_MODEL_BASE_URL + 'quest-touch-plus-right.glb', - rayOrigin: { - origin: { - x: -0.0065, - y: -0.0186, - z: -0.05 - }, - direction: { - x: -0.12394785839500175, - y: -0.5944043672340157, - z: -0.7945567170519814 - } - }, - modelPivotOffset: new THREE.Vector3(0, 0, 0), - modelPivotRotation: new THREE.Euler(0, 0, 0) - } - } -}; - -/** - * Button indices: - * 0 - thumbstick (which has separate axismove / thumbstickmoved events) - * 1 - trigger (with analog value, which goes up to 1) - * 2 - grip (with analog value, which goes up to 1) - * 3 - X (left) or A (right) - * 4 - Y (left) or B (right) - * 5 - surface (touch only) - */ -var INPUT_MAPPING_WEBVR = { - left: { - axes: { - thumbstick: [0, 1] - }, - buttons: ['thumbstick', 'trigger', 'grip', 'xbutton', 'ybutton', 'surface'] - }, - right: { - axes: { - thumbstick: [0, 1] - }, - buttons: ['thumbstick', 'trigger', 'grip', 'abutton', 'bbutton', 'surface'] - } -}; - -/** - * Button indices: - * 0 - trigger - * 1 - grip - * 2 - none - * 3 - thumbstick - * 4 - X or A button - * 5 - Y or B button - * 6 - surface - * - * Axis: - * 0 - none - * 1 - none - * 2 - thumbstick - * 3 - thumbstick - * Reference: https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/oculus/oculus-touch.json - */ -var INPUT_MAPPING_WEBXR = { - left: { - axes: { - thumbstick: [2, 3] - }, - buttons: ['trigger', 'grip', 'none', 'thumbstick', 'xbutton', 'ybutton', 'surface'] - }, - right: { - axes: { - thumbstick: [2, 3] - }, - buttons: ['trigger', 'grip', 'none', 'thumbstick', 'abutton', 'bbutton', 'surface'] - } -}; -var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR; - -/** - * Oculus Touch controls. - * Interface with Oculus Touch controllers and map Gamepad events to - * controller buttons: thumbstick, trigger, grip, xbutton, ybutton, surface - * Load a controller model and highlight the pressed buttons. - */ -module.exports.Component = registerComponent('oculus-touch-controls', { - schema: { - hand: { - default: 'left' - }, - buttonColor: { - type: 'color', - default: '#999' - }, - // Off-white. - buttonTouchColor: { - type: 'color', - default: '#8AB' - }, - buttonHighlightColor: { - type: 'color', - default: '#2DF' - }, - // Light blue. - model: { - default: true - }, - controllerType: { - default: 'auto', - oneOf: ['auto', 'oculus-touch', 'oculus-touch-v2', 'oculus-touch-v3'] - }, - orientationOffset: { - type: 'vec3', - default: { - x: 43, - y: 0, - z: 0 - } - } - }, - after: ['tracked-controls'], - mapping: INPUT_MAPPING, - bindMethods: function () { - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onThumbstickMoved = this.onThumbstickMoved.bind(this); - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - init: function () { - var self = this; - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self, self.data.hand); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self, self.data.hand); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self, self.data.hand); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self, self.data.hand); - }; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.previousButtonValues = {}; - this.bindMethods(); - this.triggerEuler = new THREE.Euler(); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('axismove', this.onAxisMoved); - el.addEventListener('model-loaded', this.onModelLoaded); - el.addEventListener('thumbstickmoved', this.onThumbstickMoved); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('axismove', this.onAxisMoved); - el.removeEventListener('model-loaded', this.onModelLoaded); - el.removeEventListener('thumbstickmoved', this.onThumbstickMoved); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, { - hand: this.data.hand, - iterateControllerProfiles: true - }); - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - loadModel: function (controller) { - var data = this.data; - var controllerId; - if (!data.model) { - return; - } - // If model has been already loaded - if (this.controllerObject3D) { - this.el.setObject3D('mesh', this.controllerObject3D); - return; - } - - // Set the controller display model based on the data passed in. - this.displayModel = CONTROLLER_PROPERTIES[data.controllerType] || CONTROLLER_PROPERTIES[CONTROLLER_DEFAULT]; - // If the developer is asking for auto-detection, use the retrieved displayName to identify the specific unit. - // This only works for WebVR currently. - if (data.controllerType === 'auto') { - var trackedControlsSystem = this.el.sceneEl.systems['tracked-controls-webvr']; - // WebVR - if (trackedControlsSystem && trackedControlsSystem.vrDisplay) { - var displayName = trackedControlsSystem.vrDisplay.displayName; - if (/^Oculus Quest$/.test(displayName)) { - this.displayModel = CONTROLLER_PROPERTIES['oculus-touch-v2']; - } - } else { - // WebXR - controllerId = CONTROLLER_DEFAULT; - var controllersPropertiesIds = Object.keys(CONTROLLER_PROPERTIES); - for (var i = 0; i < controller.profiles.length; i++) { - if (controllersPropertiesIds.indexOf(controller.profiles[i]) !== -1) { - controllerId = controller.profiles[i]; - break; - } - } - this.displayModel = CONTROLLER_PROPERTIES[controllerId]; - } - } - var modelUrl = this.displayModel[data.hand].modelUrl; - this.isTouchV3orPROorPlus = this.displayModel === CONTROLLER_PROPERTIES['oculus-touch-v3'] || this.displayModel === CONTROLLER_PROPERTIES['meta-quest-touch-pro'] || this.displayModel === CONTROLLER_PROPERTIES['meta-quest-touch-plus']; - this.el.setAttribute('gltf-model', modelUrl); - }, - injectTrackedControls: function (controller) { - var data = this.data; - var webXRId = GAMEPAD_ID_WEBXR; - var webVRId = data.hand === 'right' ? 'Oculus Touch (Right)' : 'Oculus Touch (Left)'; - var id = isWebXRAvailable ? webXRId : webVRId; - this.el.setAttribute('tracked-controls', { - id: id, - hand: data.hand, - orientationOffset: data.orientationOffset, - handTrackingEnabled: false, - iterateControllerProfiles: true, - space: 'gripSpace' - }); - this.loadModel(controller); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - // Note that due to gamepadconnected event propagation issues, we don't rely on events. - this.checkIfControllerPresent(); - }, - onButtonChanged: function (evt) { - var button = this.mapping[this.data.hand].buttons[evt.detail.id]; - if (!button) { - return; - } - // move the button meshes - if (this.isTouchV3orPROorPlus) { - this.onButtonChangedV3orPROorPlus(evt); - } else { - var buttonMeshes = this.buttonMeshes; - var analogValue; - if (button === 'trigger' || button === 'grip') { - analogValue = evt.detail.state.value; - } - if (buttonMeshes) { - if (button === 'trigger' && buttonMeshes.trigger) { - buttonMeshes.trigger.rotation.x = this.originalXRotationTrigger - analogValue * (Math.PI / 26); - } - if (button === 'grip' && buttonMeshes.grip) { - analogValue *= this.data.hand === 'left' ? -1 : 1; - buttonMeshes.grip.position.x = this.originalXPositionGrip + analogValue * 0.004; - } - } - } - // Pass along changed event with button state, using the button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onButtonChangedV3orPROorPlus: function (evt) { - var button = this.mapping[this.data.hand].buttons[evt.detail.id]; - var buttonObjects = this.buttonObjects; - var analogValue; - if (!buttonObjects || !buttonObjects[button]) { - return; - } - analogValue = evt.detail.state.value; - buttonObjects[button].quaternion.slerpQuaternions(this.buttonRanges[button].min.quaternion, this.buttonRanges[button].max.quaternion, analogValue); - buttonObjects[button].position.lerpVectors(this.buttonRanges[button].min.position, this.buttonRanges[button].max.position, analogValue); - }, - onModelLoaded: function (evt) { - if (evt.target !== this.el || !this.data.model) { - return; - } - if (this.isTouchV3orPROorPlus) { - this.onTouchV3orPROorPlusModelLoaded(evt); - } else { - // All oculus headset controller models prior to the Quest 2 (i.e., Oculus Touch V3) - // used a consistent format that is handled here - var controllerObject3D = this.controllerObject3D = evt.detail.model; - var buttonMeshes; - buttonMeshes = this.buttonMeshes = {}; - buttonMeshes.grip = controllerObject3D.getObjectByName('buttonHand'); - this.originalXPositionGrip = buttonMeshes.grip && buttonMeshes.grip.position.x; - buttonMeshes.trigger = controllerObject3D.getObjectByName('buttonTrigger'); - this.originalXRotationTrigger = buttonMeshes.trigger && buttonMeshes.trigger.rotation.x; - buttonMeshes.thumbstick = controllerObject3D.getObjectByName('stick'); - buttonMeshes.xbutton = controllerObject3D.getObjectByName('buttonX'); - buttonMeshes.abutton = controllerObject3D.getObjectByName('buttonA'); - buttonMeshes.ybutton = controllerObject3D.getObjectByName('buttonY'); - buttonMeshes.bbutton = controllerObject3D.getObjectByName('buttonB'); - } - for (var button in this.buttonMeshes) { - if (this.buttonMeshes[button]) { - cloneMeshMaterial(this.buttonMeshes[button]); - } - } - this.applyOffset(evt.detail.model); - this.el.emit('controllermodelready', { - name: 'oculus-touch-controls', - model: this.data.model, - rayOrigin: this.displayModel[this.data.hand].rayOrigin - }); - }, - applyOffset: function (model) { - model.position.copy(this.displayModel[this.data.hand].modelPivotOffset); - model.rotation.copy(this.displayModel[this.data.hand].modelPivotRotation); - }, - onTouchV3orPROorPlusModelLoaded: function (evt) { - var controllerObject3D = this.controllerObject3D = evt.detail.model; - var buttonObjects = this.buttonObjects = {}; - var buttonMeshes = this.buttonMeshes = {}; - var buttonRanges = this.buttonRanges = {}; - buttonMeshes.grip = controllerObject3D.getObjectByName('squeeze'); - buttonObjects.grip = controllerObject3D.getObjectByName('xr_standard_squeeze_pressed_value'); - buttonRanges.grip = { - min: controllerObject3D.getObjectByName('xr_standard_squeeze_pressed_min'), - max: controllerObject3D.getObjectByName('xr_standard_squeeze_pressed_max') - }; - buttonObjects.grip.minX = buttonObjects.grip.position.x; - buttonMeshes.thumbstick = controllerObject3D.getObjectByName('thumbstick'); - buttonObjects.thumbstick = controllerObject3D.getObjectByName('xr_standard_thumbstick_pressed_value'); - buttonRanges.thumbstick = { - min: controllerObject3D.getObjectByName('xr_standard_thumbstick_pressed_min'), - max: controllerObject3D.getObjectByName('xr_standard_thumbstick_pressed_max') - }; - buttonObjects.thumbstickXAxis = controllerObject3D.getObjectByName('xr_standard_thumbstick_xaxis_pressed_value'); - buttonRanges.thumbstickXAxis = { - min: controllerObject3D.getObjectByName('xr_standard_thumbstick_xaxis_pressed_min'), - max: controllerObject3D.getObjectByName('xr_standard_thumbstick_xaxis_pressed_max') - }; - buttonObjects.thumbstickYAxis = controllerObject3D.getObjectByName('xr_standard_thumbstick_yaxis_pressed_value'); - buttonRanges.thumbstickYAxis = { - min: controllerObject3D.getObjectByName('xr_standard_thumbstick_yaxis_pressed_min'), - max: controllerObject3D.getObjectByName('xr_standard_thumbstick_yaxis_pressed_max') - }; - buttonMeshes.trigger = controllerObject3D.getObjectByName('trigger'); - buttonObjects.trigger = controllerObject3D.getObjectByName('xr_standard_trigger_pressed_value'); - buttonRanges.trigger = { - min: controllerObject3D.getObjectByName('xr_standard_trigger_pressed_min'), - max: controllerObject3D.getObjectByName('xr_standard_trigger_pressed_max') - }; - buttonRanges.trigger.diff = { - x: Math.abs(buttonRanges.trigger.max.rotation.x) - Math.abs(buttonRanges.trigger.min.rotation.x), - y: Math.abs(buttonRanges.trigger.max.rotation.y) - Math.abs(buttonRanges.trigger.min.rotation.y), - z: Math.abs(buttonRanges.trigger.max.rotation.z) - Math.abs(buttonRanges.trigger.min.rotation.z) - }; - var button1 = this.data.hand === 'left' ? 'x' : 'a'; - var button2 = this.data.hand === 'left' ? 'y' : 'b'; - var button1id = button1 + 'button'; - var button2id = button2 + 'button'; - buttonMeshes[button1id] = controllerObject3D.getObjectByName(button1 + '_button'); - buttonObjects[button1id] = controllerObject3D.getObjectByName(button1 + '_button_pressed_value'); - buttonRanges[button1id] = { - min: controllerObject3D.getObjectByName(button1 + '_button_pressed_min'), - max: controllerObject3D.getObjectByName(button1 + '_button_pressed_max') - }; - buttonMeshes[button2id] = controllerObject3D.getObjectByName(button2 + '_button'); - buttonObjects[button2id] = controllerObject3D.getObjectByName(button2 + '_button_pressed_value'); - buttonRanges[button2id] = { - min: controllerObject3D.getObjectByName(button2 + '_button_pressed_min'), - max: controllerObject3D.getObjectByName(button2 + '_button_pressed_max') - }; - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping[this.data.hand].axes, evt); - }, - onThumbstickMoved: function (evt) { - if (!this.buttonMeshes || !this.buttonMeshes.thumbstick) { - return; - } - if (this.isTouchV3orPROorPlus) { - this.updateThumbstickTouchV3orPROorPlus(evt); - return; - } - for (var axis in evt.detail) { - this.buttonObjects.thumbstick.rotation[this.axisMap[axis]] = this.buttonRanges.thumbstick.originalRotation[this.axisMap[axis]] - Math.PI / 8 * evt.detail[axis] * (axis === 'y' || this.data.hand === 'right' ? -1 : 1); - } - }, - axisMap: { - y: 'x', - x: 'z' - }, - updateThumbstickTouchV3orPROorPlus: function (evt) { - var normalizedXAxis = (evt.detail.x + 1.0) / 2.0; - this.buttonObjects.thumbstickXAxis.quaternion.slerpQuaternions(this.buttonRanges.thumbstickXAxis.min.quaternion, this.buttonRanges.thumbstickXAxis.max.quaternion, normalizedXAxis); - var normalizedYAxis = (evt.detail.y + 1.0) / 2.0; - this.buttonObjects.thumbstickYAxis.quaternion.slerpQuaternions(this.buttonRanges.thumbstickYAxis.min.quaternion, this.buttonRanges.thumbstickYAxis.max.quaternion, normalizedYAxis); - }, - updateModel: function (buttonName, evtName) { - if (!this.data.model) { - return; - } - this.updateButtonModel(buttonName, evtName); - }, - updateButtonModel: function (buttonName, state) { - // update the button mesh colors - var buttonMeshes = this.buttonMeshes; - var button; - var color; - if (!buttonMeshes) { - return; - } - if (buttonMeshes[buttonName]) { - color = state === 'up' || state === 'touchend' ? buttonMeshes[buttonName].originalColor || this.data.buttonColor : state === 'touchstart' ? this.data.buttonTouchColor : this.data.buttonHighlightColor; - button = buttonMeshes[buttonName]; - button.material.color.set(color); - } - } -}); - -/** - * Some of the controller models share the same material for different parts (buttons, triggers...). - * In order to change their color independently we have to create separate materials. - */ -function cloneMeshMaterial(object3d) { - object3d.traverse(function (node) { - var newMaterial; - if (node.type !== 'Mesh') return; - newMaterial = node.material.clone(); - object3d.originalColor = node.material.color; - node.material.dispose(); - node.material = newMaterial; - }); -} - -/***/ }), - -/***/ "./src/components/pico-controls.js": -/*!*****************************************!*\ - !*** ./src/components/pico-controls.js ***! - \*****************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; - -// See Profiles Registry: -// https://github.com/immersive-web/webxr-input-profiles/tree/master/packages/registry -// TODO: Add a more robust system for deriving gamepad name. -var GAMEPAD_ID = 'pico-4'; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var PICO_MODEL_GLB_BASE_URL = AFRAME_CDN_ROOT + 'controllers/pico/pico4/'; - -/** - * Button IDs: - * 0 - trigger - * 1 - grip - * 3 - X / A - * 4 - Y / B - * - * Axis: - * 2 - joystick x axis - * 3 - joystick y axis - */ -var INPUT_MAPPING_WEBXR = { - left: { - axes: { - touchpad: [2, 3] - }, - buttons: ['trigger', 'squeeze', 'none', 'thumbstick', 'xbutton', 'ybutton'] - }, - right: { - axes: { - touchpad: [2, 3] - }, - buttons: ['trigger', 'squeeze', 'none', 'thumbstick', 'abutton', 'bbutton'] - } -}; - -/** - * Pico Controls - */ -module.exports.Component = registerComponent('pico-controls', { - schema: { - hand: { - default: 'none' - }, - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - } - }, - mapping: INPUT_MAPPING_WEBXR, - init: function () { - var self = this; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self, self.data.hand); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self, self.data.hand); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self, self.data.hand); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self, self.data.hand); - }; - this.bindMethods(); - }, - update: function () { - var data = this.data; - this.controllerIndex = data.hand === 'right' ? 0 : data.hand === 'left' ? 1 : 2; - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('axismove', this.onAxisMoved); - el.addEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('axismove', this.onAxisMoved); - el.removeEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - var data = this.data; - checkControllerPresentAndSetup(this, GAMEPAD_ID, { - index: this.controllerIndex, - hand: data.hand - }); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - el.setAttribute('tracked-controls', { - // TODO: verify expected behavior between reserved prefixes. - idPrefix: GAMEPAD_ID, - hand: data.hand, - controller: this.controllerIndex, - orientationOffset: data.orientationOffset - }); - // Load model. - if (!this.data.model) { - return; - } - this.el.setAttribute('gltf-model', PICO_MODEL_GLB_BASE_URL + this.data.hand + '.glb'); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - // Note that due to gamepadconnected event propagation issues, we don't rely on events. - this.checkIfControllerPresent(); - }, - onButtonChanged: function (evt) { - var button = this.mapping[this.data.hand].buttons[evt.detail.id]; - var analogValue; - if (!button) { - return; - } - if (button === 'trigger') { - analogValue = evt.detail.state.value; - console.log('analog value of trigger press: ' + analogValue); - } - - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onModelLoaded: function (evt) { - if (evt.target !== this.el || !this.data.model) { - return; - } - this.el.emit('controllermodelready', { - name: 'pico-controls', - model: this.data.model, - rayOrigin: new THREE.Vector3(0, 0, 0) - }); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - } -}); - -/***/ }), - -/***/ "./src/components/position.js": -/*!************************************!*\ - !*** ./src/components/position.js ***! - \************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = registerComponent('position', { - schema: { - type: 'vec3' - }, - update: function () { - var object3D = this.el.object3D; - var data = this.data; - object3D.position.set(data.x, data.y, data.z); - }, - remove: function () { - // Pretty much for mixins. - this.el.object3D.position.set(0, 0, 0); - } -}); - -/***/ }), - -/***/ "./src/components/raycaster.js": -/*!*************************************!*\ - !*** ./src/components/raycaster.js ***! - \*************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global MutationObserver */ - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var warn = utils.debug('components:raycaster:warn'); - -// Defines selectors that should be 'safe' for the MutationObserver used to -// refresh the whitelist. Matches classnames, IDs, and presence of attributes. -// Selectors for the value of an attribute, like [position=0 2 0], cannot be -// reliably detected and are therefore disallowed. -var OBSERVER_SELECTOR_RE = /^[\w\s-.,[\]#]*$/; - -// Configuration for the MutationObserver used to refresh the whitelist. -// Listens for addition/removal of elements and attributes within the scene. -var OBSERVER_CONFIG = { - childList: true, - attributes: true, - subtree: true -}; -var EVENTS = { - INTERSECT: 'raycaster-intersected', - INTERSECTION: 'raycaster-intersection', - INTERSECT_CLEAR: 'raycaster-intersected-cleared', - INTERSECTION_CLEAR: 'raycaster-intersection-cleared', - INTERSECTION_CLOSEST_ENTITY_CHANGED: 'raycaster-closest-entity-changed' -}; - -/** - * Raycaster component. - * - * Pass options to three.js Raycaster including which objects to test. - * Poll for intersections. - * Emit event on origin entity and on target entity on intersect. - * - * @member {array} intersectedEls - List of currently intersected entities. - * @member {array} objects - Cached list of meshes to intersect. - * @member {number} prevCheckTime - Previous time intersection was checked. To help interval. - * @member {object} raycaster - three.js Raycaster. - */ -module.exports.Component = registerComponent('raycaster', { - schema: { - autoRefresh: { - default: true - }, - direction: { - type: 'vec3', - default: { - x: 0, - y: 0, - z: -1 - } - }, - enabled: { - default: true - }, - far: { - default: 1000 - }, - interval: { - default: 0 - }, - near: { - default: 0 - }, - objects: { - default: '' - }, - origin: { - type: 'vec3' - }, - showLine: { - default: false - }, - lineColor: { - default: 'white' - }, - lineOpacity: { - default: 1 - }, - useWorldCoordinates: { - default: false - } - }, - multiple: true, - init: function () { - this.clearedIntersectedEls = []; - this.unitLineEndVec3 = new THREE.Vector3(); - this.intersectedEls = []; - this.intersections = []; - this.newIntersectedEls = []; - this.newIntersections = []; - this.objects = []; - this.prevCheckTime = undefined; - this.prevIntersectedEls = []; - this.rawIntersections = []; - this.raycaster = new THREE.Raycaster(); - this.updateOriginDirection(); - this.setDirty = this.setDirty.bind(this); - this.updateLine = this.updateLine.bind(this); - this.observer = new MutationObserver(this.setDirty); - this.dirty = true; - this.lineEndVec3 = new THREE.Vector3(); - this.otherLineEndVec3 = new THREE.Vector3(); - this.lineData = { - end: this.lineEndVec3 - }; - this.getIntersection = this.getIntersection.bind(this); - this.intersectedDetail = { - el: this.el, - getIntersection: this.getIntersection - }; - this.intersectedClearedDetail = { - el: this.el - }; - this.intersectionClearedDetail = { - clearedEls: this.clearedIntersectedEls - }; - this.intersectionDetail = {}; - }, - /** - * Create or update raycaster object. - */ - update: function (oldData) { - var data = this.data; - var el = this.el; - var raycaster = this.raycaster; - - // Set raycaster properties. - raycaster.far = data.far; - raycaster.near = data.near; - - // Draw line. - if (data.showLine && (data.far !== oldData.far || data.origin !== oldData.origin || data.direction !== oldData.direction || !oldData.showLine)) { - // Calculate unit vector for line direction. Can be multiplied via scalar and added - // to origin to adjust line length. - this.unitLineEndVec3.copy(data.direction).normalize(); - this.drawLine(); - } - if (!data.showLine && oldData.showLine) { - el.removeAttribute('line'); - } - if (data.objects !== oldData.objects && !OBSERVER_SELECTOR_RE.test(data.objects)) { - warn('[raycaster] Selector "' + data.objects + '" may not update automatically with DOM changes.'); - } - if (!data.objects) { - warn('[raycaster] For performance, please define raycaster.objects when using ' + 'raycaster or cursor components to whitelist which entities to intersect with. ' + 'e.g., raycaster="objects: [data-raycastable]".'); - } - if (data.autoRefresh !== oldData.autoRefresh && el.isPlaying) { - data.autoRefresh ? this.addEventListeners() : this.removeEventListeners(); - } - if (oldData.enabled && !data.enabled) { - this.clearAllIntersections(); - } - this.setDirty(); - }, - play: function () { - this.addEventListeners(); - }, - pause: function () { - this.removeEventListeners(); - }, - remove: function () { - if (this.data.showLine) { - this.el.removeAttribute('line'); - } - this.clearAllIntersections(); - }, - addEventListeners: function () { - if (!this.data.autoRefresh) { - return; - } - this.observer.observe(this.el.sceneEl, OBSERVER_CONFIG); - this.el.sceneEl.addEventListener('object3dset', this.setDirty); - this.el.sceneEl.addEventListener('object3dremove', this.setDirty); - }, - removeEventListeners: function () { - this.observer.disconnect(); - this.el.sceneEl.removeEventListener('object3dset', this.setDirty); - this.el.sceneEl.removeEventListener('object3dremove', this.setDirty); - }, - /** - * Mark the object list as dirty, to be refreshed before next raycast. - */ - setDirty: function () { - this.dirty = true; - }, - /** - * Update list of objects to test for intersection. - */ - refreshObjects: function () { - var data = this.data; - var els; - - // If objects not defined, intersect with everything. - els = data.objects ? this.el.sceneEl.querySelectorAll(data.objects) : this.el.sceneEl.querySelectorAll('*'); - this.objects = this.flattenObject3DMaps(els); - this.dirty = false; - }, - /** - * Check for intersections and cleared intersections on an interval. - */ - tock: function (time) { - var data = this.data; - var prevCheckTime = this.prevCheckTime; - if (!data.enabled) { - return; - } - - // Only check for intersection if interval time has passed. - if (prevCheckTime && time - prevCheckTime < data.interval) { - return; - } - - // Update check time. - this.prevCheckTime = time; - this.checkIntersections(); - }, - /** - * Raycast for intersections and emit events for current and cleared intersections. - */ - checkIntersections: function () { - var clearedIntersectedEls = this.clearedIntersectedEls; - var el = this.el; - var data = this.data; - var i; - var intersectedEls = this.intersectedEls; - var intersection; - var intersections = this.intersections; - var newIntersectedEls = this.newIntersectedEls; - var newIntersections = this.newIntersections; - var prevIntersectedEls = this.prevIntersectedEls; - var rawIntersections = this.rawIntersections; - - // Refresh the object whitelist if needed. - if (this.dirty) { - this.refreshObjects(); - } - - // Store old previously intersected entities. - copyArray(this.prevIntersectedEls, this.intersectedEls); - - // Raycast. - this.updateOriginDirection(); - rawIntersections.length = 0; - this.raycaster.intersectObjects(this.objects, true, rawIntersections); - - // Only keep intersections against objects that have a reference to an entity. - intersections.length = 0; - intersectedEls.length = 0; - for (i = 0; i < rawIntersections.length; i++) { - intersection = rawIntersections[i]; - // Don't intersect with own line. - if (data.showLine && intersection.object === el.getObject3D('line')) { - continue; - } - if (intersection.object.el) { - intersections.push(intersection); - intersectedEls.push(intersection.object.el); - } - } - - // Get newly intersected entities. - newIntersections.length = 0; - newIntersectedEls.length = 0; - for (i = 0; i < intersections.length; i++) { - if (prevIntersectedEls.indexOf(intersections[i].object.el) === -1) { - newIntersections.push(intersections[i]); - newIntersectedEls.push(intersections[i].object.el); - } - } - - // Emit intersection cleared on both entities per formerly intersected entity. - clearedIntersectedEls.length = 0; - for (i = 0; i < prevIntersectedEls.length; i++) { - if (intersectedEls.indexOf(prevIntersectedEls[i]) !== -1) { - continue; - } - prevIntersectedEls[i].emit(EVENTS.INTERSECT_CLEAR, this.intersectedClearedDetail); - clearedIntersectedEls.push(prevIntersectedEls[i]); - } - if (clearedIntersectedEls.length) { - el.emit(EVENTS.INTERSECTION_CLEAR, this.intersectionClearedDetail); - } - - // Emit intersected on intersected entity per intersected entity. - for (i = 0; i < newIntersectedEls.length; i++) { - newIntersectedEls[i].emit(EVENTS.INTERSECT, this.intersectedDetail); - } - - // Emit all intersections at once on raycasting entity. - if (newIntersections.length) { - this.intersectionDetail.els = newIntersectedEls; - this.intersectionDetail.intersections = newIntersections; - el.emit(EVENTS.INTERSECTION, this.intersectionDetail); - } - - // Emit event when the closest intersected entity has changed. - if (prevIntersectedEls.length === 0 && intersections.length > 0 || prevIntersectedEls.length > 0 && intersections.length === 0 || prevIntersectedEls.length && intersections.length && prevIntersectedEls[0] !== intersections[0].object.el) { - this.intersectionDetail.els = this.intersectedEls; - this.intersectionDetail.intersections = intersections; - el.emit(EVENTS.INTERSECTION_CLOSEST_ENTITY_CHANGED, this.intersectionDetail); - } - - // Update line length. - if (data.showLine) { - setTimeout(this.updateLine); - } - }, - updateLine: function () { - var el = this.el; - var intersections = this.intersections; - var lineLength; - if (intersections.length) { - if (intersections[0].object.el === el && intersections[1]) { - lineLength = intersections[1].distance; - } else { - lineLength = intersections[0].distance; - } - } - this.drawLine(lineLength); - }, - /** - * Return the most recent intersection details for a given entity, if any. - * @param {AEntity} el - * @return {Object} - */ - getIntersection: function (el) { - var i; - var intersection; - for (i = 0; i < this.intersections.length; i++) { - intersection = this.intersections[i]; - if (intersection.object.el === el) { - return intersection; - } - } - return null; - }, - /** - * Update origin and direction of raycaster using entity transforms and supplied origin or - * direction offsets. - */ - updateOriginDirection: function () { - var direction = new THREE.Vector3(); - var originVec3 = new THREE.Vector3(); - - // Closure to make quaternion/vector3 objects private. - return function updateOriginDirection() { - var el = this.el; - var data = this.data; - if (data.useWorldCoordinates) { - this.raycaster.set(data.origin, data.direction); - return; - } - el.object3D.updateMatrixWorld(); - originVec3.setFromMatrixPosition(el.object3D.matrixWorld); - - // If non-zero origin, translate the origin into world space. - if (data.origin.x !== 0 || data.origin.y !== 0 || data.origin.z !== 0) { - originVec3 = el.object3D.localToWorld(originVec3.copy(data.origin)); - } - - // three.js raycaster direction is relative to 0, 0, 0 NOT the origin / offset we - // provide. Apply the offset to the direction, then rotation from the object, - // and normalize. - direction.copy(data.direction).transformDirection(el.object3D.matrixWorld).normalize(); - - // Apply offset and direction, in world coordinates. - this.raycaster.set(originVec3, direction); - }; - }(), - /** - * Create or update line to give raycaster visual representation. - * Customize the line through through line component. - * We draw the line in the raycaster component to customize the line to the - * raycaster's origin, direction, and far. - * - * Unlike the raycaster, we create the line as a child of the object. The line will - * be affected by the transforms of the objects, so we don't have to calculate transforms - * like we do with the raycaster. - * - * @param {number} length - Length of line. Pass in to shorten the line to the intersection - * point. If not provided, length will default to the max length, `raycaster.far`. - */ - drawLine: function (length) { - var data = this.data; - var el = this.el; - var endVec3; - - // Switch each time vector so line update triggered and to avoid unnecessary vector clone. - endVec3 = this.lineData.end === this.lineEndVec3 ? this.otherLineEndVec3 : this.lineEndVec3; - - // Treat Infinity as 1000m for the line. - if (length === undefined) { - length = data.far === Infinity ? 1000 : data.far; - } - - // Update the length of the line if given. `unitLineEndVec3` is the direction - // given by data.direction, then we apply a scalar to give it a length and the - // origin point to offset it. - this.lineData.start = data.origin; - this.lineData.end = endVec3.copy(this.unitLineEndVec3).multiplyScalar(length).add(data.origin); - this.lineData.color = data.lineColor; - this.lineData.opacity = data.lineOpacity; - el.setAttribute('line', this.lineData); - }, - /** - * Return A-Frame attachments of each element's object3D group (e.g., mesh). - * Children are flattened by one level, removing the THREE.Group wrapper, - * so that non-recursive raycasting remains useful. - * - * Only push children defined as component attachments (e.g., setObject3D), - * NOT actual children in the scene graph hierarchy. - * - * @param {Array} els - * @return {Array} - */ - flattenObject3DMaps: function (els) { - var key; - var i; - var objects = this.objects; - var scene = this.el.sceneEl.object3D; - function isAttachedToScene(object) { - if (object.parent) { - return isAttachedToScene(object.parent); - } else { - return object === scene; - } - } - - // Push meshes and other attachments onto list of objects to intersect. - objects.length = 0; - for (i = 0; i < els.length; i++) { - var el = els[i]; - if (el.isEntity && el.object3D && isAttachedToScene(el.object3D)) { - for (key in el.object3DMap) { - objects.push(el.getObject3D(key)); - } - } - } - return objects; - }, - clearAllIntersections: function () { - var i; - for (i = 0; i < this.intersectedEls.length; i++) { - this.intersectedEls[i].emit(EVENTS.INTERSECT_CLEAR, this.intersectedClearedDetail); - } - copyArray(this.clearedIntersectedEls, this.intersectedEls); - this.intersectedEls.length = 0; - this.intersections.length = 0; - this.el.emit(EVENTS.INTERSECTION_CLEAR, this.intersectionClearedDetail); - } -}); - -/** - * Copy contents of one array to another without allocating new array. - */ -function copyArray(a, b) { - var i; - a.length = b.length; - for (i = 0; i < b.length; i++) { - a[i] = b[i]; - } -} - -/***/ }), - -/***/ "./src/components/rotation.js": -/*!************************************!*\ - !*** ./src/components/rotation.js ***! - \************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var degToRad = (__webpack_require__(/*! ../lib/three */ "./src/lib/three.js").MathUtils.degToRad); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = registerComponent('rotation', { - schema: { - type: 'vec3' - }, - /** - * Updates object3D rotation. - */ - update: function () { - var data = this.data; - var object3D = this.el.object3D; - object3D.rotation.set(degToRad(data.x), degToRad(data.y), degToRad(data.z), 'YXZ'); - }, - remove: function () { - // Pretty much for mixins. - this.el.object3D.rotation.set(0, 0, 0); - } -}); - -/***/ }), - -/***/ "./src/components/scale.js": -/*!*********************************!*\ - !*** ./src/components/scale.js ***! - \*********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = registerComponent('scale', { - schema: { - type: 'vec3', - default: { - x: 1, - y: 1, - z: 1 - } - }, - update: function () { - var data = this.data; - var object3D = this.el.object3D; - object3D.scale.set(data.x, data.y, data.z); - }, - remove: function () { - // Pretty much for mixins. - this.el.object3D.scale.set(1, 1, 1); - } -}); - -/***/ }), - -/***/ "./src/components/scene/ar-hit-test.js": -/*!*********************************************!*\ - !*** ./src/components/scene/ar-hit-test.js ***! - \*********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global ImageData, Map, Set */ -var arrowURL = 'data:image/webp;base64,UklGRkQHAABXRUJQVlA4WAoAAAAQAAAA/wEA/wEAQUxQSL0DAAARDzD/ERGCjrY9sYYFfgo6aa1kJ7K0w9Lo3AadLSVeFxevQwj5kuM8RfR/Atw/C0+ozB/oUBrloFZs6ElSW88j1KA4yExNWQaqRZquIDF0JYmlq0hAuUDTFu66tng3teW7pa3cQf1V1edvur54M/Slm6Wv3Gx9zw0MXlQLntcsBN6wkHjTQuYtC4W3LTw8mGRVG57TbAROtxHfZNhInGkjc5aNwtk2Hg6Mvki14k+NkZzCwQgCxalcAv3kddRTPI1DcUrXId1FLf1uHpzaQz4tquhZVLlKesbVpqKeTj0n0F5PpXDlFN9UqmhalL/ImuZFo6KmToWLoKlddMprqlS8cKovBvHo2kTiFV2LN4msaxKZl3QNiair8xYRdDWivIvXVXmbcMqJ51UebZuFXxZt6xd4laxtciqRtA3Cv0nU1t+kEUFbI8JvCa+tvkm3FDlO/W+OR99+kWEp/YYo+tYfTVnf/K8cE/F///3vv//993eeL+a+uvjawLcX3xjYvJotBFY3kVjTRGFtE+BU2AiMbiQyhpHMWEYeBozAH5qNBYRDB5KBCaTDBKKBAZTDBoKBDjwHAN5ABeCJBsAZcAAC0YHHxAYSMYBiYgGZWEA2MYFCbCCZGAAIANFEB+AnYgMQTDQAYSJ2AN5EBZAm4gDgTDgAeSIu4DGygTIRN1CMLOCZiACykQlg4jsAycgA8AO+BxCNdJyDkcbwRirDGXGnx8w+FDPrkM3MQ9JQZMYhiiwV/RDMtIM3U1/DmXHUo+IR2kSR2ToWkQ1NIn2qf2J8LCqJKiDUiSADHY3whirhdHgZ94HKaR97PhE+twEUJUFoAcgyTct8hfSxSkShASDKdMJ/ritKHwgyQ0sD4D/miCxU5SbhOOUDTnZpccCjYP/i0bZ/8bAgtVGEoGapWIQXyzKVKLwgNJFk2rtMIgoNRJlOZF7SNSSyUEeQmbxBFKEmtYjEe8S8zOZ1AkJVCmS88FJOtF40Ksg4oUaFiygk3C8qlTVNyl8UTevCUdAE2t14PfVqU1FPp57TopKeQZWromddTQp6QOfTOEQt/ZDuipZ11w/wOiqO8dRORcc6BQEkDQMClaHcn5wV9yLbxsNZNgpn2sicYSNxuo34Js1G4FQbnuNsOPa28PCWhcKbFjJvWEi8ZiHwqgXPcxbc5db33Cx95WboSzddX7yp+vyN0+eul7ZyN7Xlu64t3jVt4c5pc4JLV5EYupJE0xUknC4nOjVlmaYpyLit53HCQ0+ScnqceNcS5dzUkd0/CwMAVlA4IGADAAAQXwCdASoAAgACP8ne6Wy/tjCpqJ/IA/A5CWlu4XYBG/Pz8AfwD8APz//f3v8E1fuHZnxKYACtfuHZnxKYACrYTb5mOslhxu843ecbvON3nG7zjd3a0VCn7G1MABVxwH/Xd25gAK1+4dmfEpe2+PHhQaj75++riG6FuYACtfuHZnxKYACRrK3q9xO8Ss3uWKnMhs/rDF1hi6wxdYYusMXWGI5QRcCFDZog5OgqNlse1NDuz/UoFa/cOzPiUwAEsAOK4/nu5eZHK2tlXxJfNYlMABWv3Dsz4bvNJ5YA/LtxJ38SmAArX7h2Z8Sk5vdZUYv7mZPiUwAFa/cOzPh21s5OgZxf1mfEpemRyFr/rM+JS9noA/LtxJ38SmAAlUJIotzAASn6TjdhK+D3Dsz4dyvB7h2Z8O2tnJ0DOL+sz4lL2nKLT4lL/+iSLOocxq639w7M34MNZdm55uJ8v8ra2cpVZnxKTq2F3PN/cNksAfl24k7+JTAASqrD37h2Z7b1W+VtbOUqsz4lJ1bC7nm/uGyWAPy7cSd/EpgAJVVh79w7M9t6rfK2tnKVWZ8Sk6thdzzf3DZLAH5duJO/iUwAEqqw9+4dme29VvlbWzlKrM+JSdWwu55v7hslgD8u3EnfxKYACVVYe/cOzPbeq3ytrZylVme0kYJ8557FLerqFrzIbPrrf3DZLAH5duJO/iUvaVMS9BoaF4p7pSDFTP1XMyfElelrM0DOL+sz4eBJ13nV1OppBGPuKb4YzXQgq9uH19uS/0+JS9t9fr6ZUlQBelDG6GMgq97otb5QMPJwtKyBTbFp8Sl7b6/X0ykkawEOsgdiE6Fi0vb/Eve6xkwsmug0Z4nGNHQO8839bpTsjpz7SWIJxKagvd1QWMa6FYT1KEw3j4XDT6vJ9Xk+nyfT5Pq8n1eEmk5dinMM/9Fcfz4Z3Dsz3KD2dw7LxBRxKrqUUGQPH/7zxr1KIfNpLEJ0MZB2ITM/0Z2EFoh12NlXnEcpYcbvON3nG7zjd5xu84vfcNIAAP7+y8ceyzbVxkakPYY4lcr72fqOnDwipv+yxC71wAADBrjKnAAAAAAAAAAAAAAw7oNGHttqWONcoFN/2WIDc2pa6WVFtFYROlsaMaTXdcOjXHz93+YxAglKa4AAAAA='; -var register = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../../lib/three */ "./src/lib/three.js"); -var CAM_LAYER = 21; -var applyPose = function () { - var tempQuaternion = new THREE.Quaternion(); - var tempVec3 = new THREE.Vector3(); - function applyPose(pose, object3D, offset) { - object3D.position.copy(pose.transform.position); - object3D.quaternion.copy(pose.transform.orientation); - tempVec3.copy(offset); - tempQuaternion.copy(pose.transform.orientation); - tempVec3.applyQuaternion(tempQuaternion); - object3D.position.sub(tempVec3); - } - return applyPose; -}(); -applyPose.tempFakePose = { - transform: { - orientation: new THREE.Quaternion(), - position: new THREE.Vector3() - } -}; - -/** - * Class to handle hit-test from a single source - * - * For a normal space provide it as a space option - * new HitTest(renderer, { - * space: viewerSpace - * }); - * - * this is also useful for the targetRaySpace of an XRInputSource - * - * It can also describe a transient input source like so: - * - * var profileToSupport = 'generic-touchscreen'; - * var transientHitTest = new HitTest(renderer, { - * profile: profileToSupport - * }); - * - * Where the profile matches an item in a type of controller, profiles matching 'generic-touchscreen' - * will always be a transient input and as of 08/2021 all transient inputs are 'generic-touchscreen' - * - * @param {WebGLRenderer} renderer THREE.JS Renderer - * @param {} hitTestSourceDetails The source information either as the information for a transient hit-test or a regular hit-test - */ -function HitTest(renderer, hitTestSourceDetails) { - this.renderer = renderer; - this.xrHitTestSource = null; - renderer.xr.addEventListener('sessionend', function () { - this.xrHitTestSource = null; - }.bind(this)); - renderer.xr.addEventListener('sessionstart', function () { - this.sessionStart(hitTestSourceDetails); - }.bind(this)); - if (this.renderer.xr.isPresenting) { - this.sessionStart(hitTestSourceDetails); - } -} -HitTest.prototype.previousFrameAnchors = new Set(); -HitTest.prototype.anchorToObject3D = new Map(); -function warnAboutHitTest(e) { - console.warn(e.message); - console.warn('Cannot requestHitTestSource Are you missing: webxr="optionalFeatures: hit-test;" from ?'); -} -HitTest.prototype.sessionStart = function sessionStart(hitTestSourceDetails) { - this.session = this.renderer.xr.getSession(); - if (!('requestHitTestSource' in this.session)) { - warnAboutHitTest({ - message: 'No requestHitTestSource on the session.' - }); - return; - } - if (hitTestSourceDetails.space) { - this.session.requestHitTestSource(hitTestSourceDetails).then(function (xrHitTestSource) { - this.xrHitTestSource = xrHitTestSource; - }.bind(this)).catch(warnAboutHitTest); - } else if (hitTestSourceDetails.profile) { - this.session.requestHitTestSourceForTransientInput(hitTestSourceDetails).then(function (xrHitTestSource) { - this.xrHitTestSource = xrHitTestSource; - this.transient = true; - }.bind(this)).catch(warnAboutHitTest); - } -}; - -/** - * Turns the last hit test into an anchor, the provided Object3D will have it's - * position update to track the anchor. - * - * @param {Object3D} object3D object to track - * @param {Vector3} offset offset of the object from the origin that gets subtracted - * @returns - */ -HitTest.prototype.anchorFromLastHitTestResult = function (object3D, offset) { - var hitTest = this.lastHitTest; - if (!hitTest) { - return; - } - var object3DOptions = { - object3D: object3D, - offset: offset - }; - Array.from(this.anchorToObject3D.entries()).forEach(function (entry) { - var entryObject = entry[1].object3D; - var anchor = entry[0]; - if (entryObject === object3D) { - this.anchorToObject3D.delete(anchor); - anchor.delete(); - } - }.bind(this)); - if (hitTest.createAnchor) { - hitTest.createAnchor().then(function (anchor) { - this.anchorToObject3D.set(anchor, object3DOptions); - }.bind(this)).catch(function (e) { - console.warn(e.message); - console.warn('Cannot create anchor, are you missing: webxr="optionalFeatures: anchors;" from ?'); - }); - } -}; -HitTest.prototype.doHit = function doHit(frame) { - if (!this.renderer.xr.isPresenting) { - return; - } - var refSpace = this.renderer.xr.getReferenceSpace(); - var xrViewerPose = frame.getViewerPose(refSpace); - var hitTestResults; - var results; - if (this.xrHitTestSource && xrViewerPose) { - if (this.transient) { - hitTestResults = frame.getHitTestResultsForTransientInput(this.xrHitTestSource); - if (hitTestResults.length > 0) { - results = hitTestResults[0].results; - if (results.length > 0) { - this.lastHitTest = results[0]; - return results[0].getPose(refSpace); - } else { - return false; - } - } else { - return false; - } - } else { - hitTestResults = frame.getHitTestResults(this.xrHitTestSource); - if (hitTestResults.length > 0) { - this.lastHitTest = hitTestResults[0]; - return hitTestResults[0].getPose(refSpace); - } else { - return false; - } - } - } -}; - -// static function -HitTest.updateAnchorPoses = function (frame, refSpace) { - // If tracked anchors isn't defined because it's not supported then just use the empty set - var trackedAnchors = frame.trackedAnchors || HitTest.prototype.previousFrameAnchors; - HitTest.prototype.previousFrameAnchors.forEach(function (anchor) { - // Handle anchor tracking loss - `anchor` was present - // in the present frame but is no longer tracked. - if (!trackedAnchors.has(anchor)) { - HitTest.prototype.anchorToObject3D.delete(anchor); - } - }); - trackedAnchors.forEach(function (anchor) { - var anchorPose; - var object3DOptions; - var offset; - var object3D; - try { - // Query most recent pose of the anchor relative to some reference space: - anchorPose = frame.getPose(anchor.anchorSpace, refSpace); - } catch (e) { - // This will fail if the anchor has been deleted that frame - } - if (anchorPose) { - object3DOptions = HitTest.prototype.anchorToObject3D.get(anchor); - if (!object3DOptions) { - return; - } - offset = object3DOptions.offset; - object3D = object3DOptions.object3D; - applyPose(anchorPose, object3D, offset); - } - }); -}; -var hitTestCache; -module.exports.Component = register('ar-hit-test', { - schema: { - target: { - type: 'selector' - }, - enabled: { - default: true - }, - src: { - default: arrowURL, - type: 'map' - }, - type: { - default: 'footprint', - oneOf: ['footprint', 'map'] - }, - footprintDepth: { - default: 0.1 - }, - mapSize: { - type: 'vec2', - default: { - x: 0.5, - y: 0.5 - } - } - }, - sceneOnly: true, - init: function () { - this.hitTest = null; - this.imageDataArray = new Uint8ClampedArray(512 * 512 * 4); - this.imageData = new ImageData(this.imageDataArray, 512, 512); - this.textureCache = new Map(); - this.orthoCam = new THREE.OrthographicCamera(); - this.orthoCam.layers.set(CAM_LAYER); - this.textureTarget = new THREE.WebGLRenderTarget(512, 512, {}); - this.basicMaterial = new THREE.MeshBasicMaterial({ - color: 0x000000, - side: THREE.DoubleSide - }); - this.canvas = document.createElement('canvas'); - this.context = this.canvas.getContext('2d'); - this.context.imageSmoothingEnabled = false; - this.canvas.width = 512; - this.canvas.height = 512; - this.canvasTexture = new THREE.CanvasTexture(this.canvas, { - alpha: true - }); - this.canvasTexture.flipY = false; - - // Update WebXR to support hit-test and anchors - var webxrData = this.el.getAttribute('webxr'); - var optionalFeaturesArray = webxrData.optionalFeatures; - if (!optionalFeaturesArray.includes('hit-test') || !optionalFeaturesArray.includes('anchors')) { - optionalFeaturesArray.push('hit-test'); - optionalFeaturesArray.push('anchors'); - this.el.setAttribute('webxr', webxrData); - } - this.el.sceneEl.renderer.xr.addEventListener('sessionend', function () { - this.hitTest = null; - }.bind(this)); - this.el.sceneEl.renderer.xr.addEventListener('sessionstart', function () { - // Don't request Hit Test unless AR (breaks WebXR Emulator) - if (!this.el.is('ar-mode')) { - return; - } - var renderer = this.el.sceneEl.renderer; - var session = this.session = renderer.xr.getSession(); - this.hasPosedOnce = false; - this.bboxMesh.visible = false; - if (!hitTestCache) { - hitTestCache = new Map(); - } - - // Default to selecting through the face - session.requestReferenceSpace('viewer').then(function (viewerSpace) { - this.hitTest = new HitTest(renderer, { - space: viewerSpace - }); - hitTestCache.set(viewerSpace, this.hitTest); - this.el.emit('ar-hit-test-start'); - }.bind(this)); - - // These are transient inputs so need to be handled separately - var profileToSupport = 'generic-touchscreen'; - var transientHitTest = new HitTest(renderer, { - profile: profileToSupport - }); - session.addEventListener('selectstart', function (e) { - if (this.data.enabled !== true) { - return; - } - var inputSource = e.inputSource; - this.bboxMesh.visible = true; - if (this.hasPosedOnce === true) { - this.el.emit('ar-hit-test-select-start', { - inputSource: inputSource, - position: this.bboxMesh.position, - orientation: this.bboxMesh.quaternion - }); - if (inputSource.profiles[0] === profileToSupport) { - this.hitTest = transientHitTest; - } else { - this.hitTest = hitTestCache.get(inputSource) || new HitTest(renderer, { - space: inputSource.targetRaySpace - }); - hitTestCache.set(inputSource, this.hitTest); - } - } - }.bind(this)); - session.addEventListener('selectend', function (e) { - if (!this.hitTest || this.data.enabled !== true) { - this.hitTest = null; - return; - } - var inputSource = e.inputSource; - var object; - if (this.hasPosedOnce === true) { - this.bboxMesh.visible = false; - - // if we have a target with a 3D object then automatically generate an anchor for it. - if (this.data.target) { - object = this.data.target.object3D; - if (object) { - applyPose.tempFakePose.transform.position.copy(this.bboxMesh.position); - applyPose.tempFakePose.transform.orientation.copy(this.bboxMesh.quaternion); - applyPose(applyPose.tempFakePose, object, this.bboxOffset); - object.visible = true; - - // create an anchor attached to the object - this.hitTest.anchorFromLastHitTestResult(object, this.bboxOffset); - } - } - this.el.emit('ar-hit-test-select', { - inputSource: inputSource, - position: this.bboxMesh.position, - orientation: this.bboxMesh.quaternion - }); - } - this.hitTest = null; - }.bind(this)); - }.bind(this)); - this.bboxOffset = new THREE.Vector3(); - this.update = this.update.bind(this); - this.makeBBox(); - }, - update: function () { - // If it is disabled it's cleaned up - if (this.data.enabled === false) { - this.hitTest = null; - this.bboxMesh.visible = false; - } - if (this.data.target) { - if (this.data.target.object3D) { - this.data.target.addEventListener('model-loaded', this.update); - this.data.target.object3D.layers.enable(CAM_LAYER); - this.data.target.object3D.traverse(function (child) { - child.layers.enable(CAM_LAYER); - }); - } else { - this.data.target.addEventListener('loaded', this.update, { - once: true - }); - } - } - this.bboxNeedsUpdate = true; - }, - makeBBox: function () { - var geometry = new THREE.PlaneGeometry(1, 1); - var material = new THREE.MeshBasicMaterial({ - transparent: true, - color: 0xffffff - }); - geometry.rotateX(-Math.PI / 2); - geometry.rotateY(-Math.PI / 2); - this.bbox = new THREE.Box3(); - this.bboxMesh = new THREE.Mesh(geometry, material); - this.el.setObject3D('ar-hit-test', this.bboxMesh); - this.bboxMesh.visible = false; - }, - updateFootprint: function () { - var tempImageData; - var renderer = this.el.sceneEl.renderer; - var oldRenderTarget, oldBackground; - var isXREnabled = renderer.xr.enabled; - this.bboxMesh.material.map = this.canvasTexture; - this.bboxMesh.material.needsUpdate = true; - this.orthoCam.rotation.set(-Math.PI / 2, 0, -Math.PI / 2); - this.orthoCam.position.copy(this.bboxMesh.position); - this.orthoCam.position.y -= this.bboxMesh.scale.y / 2; - this.orthoCam.near = 0.1; - this.orthoCam.far = this.orthoCam.near + this.data.footprintDepth * this.bboxMesh.scale.y; - this.orthoCam.position.y += this.orthoCam.far; - this.orthoCam.right = this.bboxMesh.scale.z / 2; - this.orthoCam.left = -this.bboxMesh.scale.z / 2; - this.orthoCam.top = this.bboxMesh.scale.x / 2; - this.orthoCam.bottom = -this.bboxMesh.scale.x / 2; - this.orthoCam.updateProjectionMatrix(); - oldRenderTarget = renderer.getRenderTarget(); - renderer.setRenderTarget(this.textureTarget); - renderer.xr.enabled = false; - oldBackground = this.el.object3D.background; - this.el.object3D.overrideMaterial = this.basicMaterial; - this.el.object3D.background = null; - renderer.render(this.el.object3D, this.orthoCam); - this.el.object3D.background = oldBackground; - this.el.object3D.overrideMaterial = null; - renderer.xr.enabled = isXREnabled; - renderer.setRenderTarget(oldRenderTarget); - renderer.readRenderTargetPixels(this.textureTarget, 0, 0, 512, 512, this.imageDataArray); - this.context.putImageData(this.imageData, 0, 0); - this.context.shadowColor = 'white'; - this.context.shadowBlur = 10; - this.context.drawImage(this.canvas, 0, 0); - tempImageData = this.context.getImageData(0, 0, 512, 512); - for (var i = 0; i < 512 * 512; i++) { - // if it's a little bit transparent but not opaque make it middle transparent - if (tempImageData.data[i * 4 + 3] !== 0 && tempImageData.data[i * 4 + 3] !== 255) { - tempImageData.data[i * 4 + 3] = 128; - } - } - this.context.putImageData(tempImageData, 0, 0); - this.canvasTexture.needsUpdate = true; - }, - tick: function () { - var pose; - var frame = this.el.sceneEl.frame; - var renderer = this.el.sceneEl.renderer; - if (frame) { - // if we are in XR then update the positions of the objects attached to anchors - HitTest.updateAnchorPoses(frame, renderer.xr.getReferenceSpace()); - } - if (this.bboxNeedsUpdate) { - this.bboxNeedsUpdate = false; - if (!this.data.target || this.data.type === 'map') { - var texture; - if (this.textureCache.has(this.data.src)) { - texture = this.textureCache.get(this.data.src); - } else { - texture = new THREE.TextureLoader().load(this.data.src); - this.textureCache.set(this.data.src, texture); - } - this.bboxMesh.material.map = texture; - this.bboxMesh.material.needsUpdate = true; - } - if (this.data.target && this.data.target.object3D) { - this.bbox.setFromObject(this.data.target.object3D); - this.bbox.getCenter(this.bboxMesh.position); - this.bbox.getSize(this.bboxMesh.scale); - if (this.data.type === 'footprint') { - // Add a little buffer for the footprint border - this.bboxMesh.scale.x *= 1.04; - this.bboxMesh.scale.z *= 1.04; - this.updateFootprint(); - } - this.bboxMesh.position.y -= this.bboxMesh.scale.y / 2; - this.bboxOffset.copy(this.bboxMesh.position); - this.bboxOffset.sub(this.data.target.object3D.position); - } else { - this.bboxMesh.scale.set(this.data.mapSize.x, 1, this.data.mapSize.y); - } - } - if (this.hitTest) { - pose = this.hitTest.doHit(frame); - if (pose) { - if (this.hasPosedOnce !== true) { - this.hasPosedOnce = true; - this.el.emit('ar-hit-test-achieved'); - } - this.bboxMesh.visible = true; - this.bboxMesh.position.copy(pose.transform.position); - this.bboxMesh.quaternion.copy(pose.transform.orientation); - } - } - } -}); - -/***/ }), - -/***/ "./src/components/scene/background.js": -/*!********************************************!*\ - !*** ./src/components/scene/background.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE */ -var register = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = register('background', { - schema: { - color: { - type: 'color', - default: 'black' - }, - transparent: { - default: false - } - }, - sceneOnly: true, - update: function () { - var data = this.data; - var object3D = this.el.object3D; - if (data.transparent) { - object3D.background = null; - } else { - object3D.background = new THREE.Color(data.color); - } - }, - remove: function () { - var object3D = this.el.object3D; - object3D.background = null; - } -}); - -/***/ }), - -/***/ "./src/components/scene/debug.js": -/*!***************************************!*\ - !*** ./src/components/scene/debug.js ***! - \***************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var register = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -module.exports.Component = register('debug', { - schema: { - default: true - }, - sceneOnly: true -}); - -/***/ }), - -/***/ "./src/components/scene/device-orientation-permission-ui.js": -/*!******************************************************************!*\ - !*** ./src/components/scene/device-orientation-permission-ui.js ***! - \******************************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global DeviceOrientationEvent, location */ -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var constants = __webpack_require__(/*! ../../constants/ */ "./src/constants/index.js"); -var MODAL_CLASS = 'a-modal'; -var DIALOG_CLASS = 'a-dialog'; -var DIALOG_TEXT_CLASS = 'a-dialog-text'; -var DIALOG_TEXT_CONTAINER_CLASS = 'a-dialog-text-container'; -var DIALOG_BUTTONS_CONTAINER_CLASS = 'a-dialog-buttons-container'; -var DIALOG_BUTTON_CLASS = 'a-dialog-button'; -var DIALOG_ALLOW_BUTTON_CLASS = 'a-dialog-allow-button'; -var DIALOG_DENY_BUTTON_CLASS = 'a-dialog-deny-button'; -var DIALOG_OK_BUTTON_CLASS = 'a-dialog-ok-button'; - -/** - * UI for enabling device motion permission - */ -module.exports.Component = registerComponent('device-orientation-permission-ui', { - schema: { - enabled: { - default: true - }, - deviceMotionMessage: { - default: 'This immersive website requires access to your device motion sensors.' - }, - httpsMessage: { - default: 'Access this site over HTTPS to enter VR mode and grant access to the device sensors.' - }, - denyButtonText: { - default: 'Deny' - }, - allowButtonText: { - default: 'Allow' - }, - cancelButtonText: { - default: 'Cancel' - } - }, - sceneOnly: true, - init: function () { - var self = this; - if (!this.data.enabled) { - return; - } - if (!window.isSecureContext) { - this.showHTTPAlert(); - } - - // Browser doesn't support or doesn't require permission to DeviceOrientationEvent API. - if (typeof DeviceOrientationEvent === 'undefined' || !DeviceOrientationEvent.requestPermission) { - this.permissionGranted = true; - return; - } - this.onDeviceMotionDialogAllowClicked = this.onDeviceMotionDialogAllowClicked.bind(this); - this.onDeviceMotionDialogDenyClicked = this.onDeviceMotionDialogDenyClicked.bind(this); - // Show dialog only if permission has not yet been granted. - DeviceOrientationEvent.requestPermission().then(function () { - self.el.emit('deviceorientationpermissiongranted'); - self.permissionGranted = true; - }).catch(function () { - self.devicePermissionDialogEl = createPermissionDialog(self.data.denyButtonText, self.data.allowButtonText, self.data.deviceMotionMessage, self.onDeviceMotionDialogAllowClicked, self.onDeviceMotionDialogDenyClicked); - self.el.appendChild(self.devicePermissionDialogEl); - }); - }, - remove: function () { - // This removes the modal screen - if (this.devicePermissionDialogEl) { - this.el.removeChild(this.devicePermissionDialogEl); - } - }, - onDeviceMotionDialogDenyClicked: function () { - this.remove(); - }, - showHTTPAlert: function () { - var self = this; - var httpAlertEl = createAlertDialog(self.data.cancelButtonText, self.data.httpsMessage, function () { - self.el.removeChild(httpAlertEl); - }); - this.el.appendChild(httpAlertEl); - }, - /** - * Enable device motion permission when clicked. - */ - onDeviceMotionDialogAllowClicked: function () { - var self = this; - this.el.emit('deviceorientationpermissionrequested'); - DeviceOrientationEvent.requestPermission().then(function (response) { - if (response === 'granted') { - self.el.emit('deviceorientationpermissiongranted'); - self.permissionGranted = true; - } else { - self.el.emit('deviceorientationpermissionrejected'); - } - self.remove(); - }).catch(console.error); - } -}); - -/** - * Create a modal dialog that request users permission to access the Device Motion API. - * - * @param {function} onAllowClicked - click event handler - * @param {function} onDenyClicked - click event handler - * - * @returns {Element} Wrapper
. - */ -function createPermissionDialog(denyText, allowText, dialogText, onAllowClicked, onDenyClicked) { - var buttonsContainer; - var denyButton; - var acceptButton; - buttonsContainer = document.createElement('div'); - buttonsContainer.classList.add(DIALOG_BUTTONS_CONTAINER_CLASS); - - // Buttons - denyButton = document.createElement('button'); - denyButton.classList.add(DIALOG_BUTTON_CLASS, DIALOG_DENY_BUTTON_CLASS); - denyButton.setAttribute(constants.AFRAME_INJECTED, ''); - denyButton.innerHTML = denyText; - buttonsContainer.appendChild(denyButton); - acceptButton = document.createElement('button'); - acceptButton.classList.add(DIALOG_BUTTON_CLASS, DIALOG_ALLOW_BUTTON_CLASS); - acceptButton.setAttribute(constants.AFRAME_INJECTED, ''); - acceptButton.innerHTML = allowText; - buttonsContainer.appendChild(acceptButton); - - // Ask for sensor events to be used - acceptButton.addEventListener('click', function (evt) { - evt.stopPropagation(); - onAllowClicked(); - }); - denyButton.addEventListener('click', function (evt) { - evt.stopPropagation(); - onDenyClicked(); - }); - return createDialog(dialogText, buttonsContainer); -} -function createAlertDialog(closeText, dialogText, onOkClicked) { - var buttonsContainer; - var okButton; - buttonsContainer = document.createElement('div'); - buttonsContainer.classList.add(DIALOG_BUTTONS_CONTAINER_CLASS); - - // Buttons - okButton = document.createElement('button'); - okButton.classList.add(DIALOG_BUTTON_CLASS, DIALOG_OK_BUTTON_CLASS); - okButton.setAttribute(constants.AFRAME_INJECTED, ''); - okButton.innerHTML = closeText; - buttonsContainer.appendChild(okButton); - - // Ask for sensor events to be used - okButton.addEventListener('click', function (evt) { - evt.stopPropagation(); - onOkClicked(); - }); - return createDialog(dialogText, buttonsContainer); -} -function createDialog(text, buttonsContainerEl) { - var modalContainer; - var dialog; - var dialogTextContainer; - var dialogText; - modalContainer = document.createElement('div'); - modalContainer.classList.add(MODAL_CLASS); - modalContainer.setAttribute(constants.AFRAME_INJECTED, ''); - dialog = document.createElement('div'); - dialog.className = DIALOG_CLASS; - dialog.setAttribute(constants.AFRAME_INJECTED, ''); - modalContainer.appendChild(dialog); - dialogTextContainer = document.createElement('div'); - dialogTextContainer.classList.add(DIALOG_TEXT_CONTAINER_CLASS); - dialog.appendChild(dialogTextContainer); - dialogText = document.createElement('div'); - dialogText.classList.add(DIALOG_TEXT_CLASS); - dialogText.innerHTML = text; - dialogTextContainer.appendChild(dialogText); - dialog.appendChild(buttonsContainerEl); - return modalContainer; -} - -/***/ }), - -/***/ "./src/components/scene/embedded.js": -/*!******************************************!*\ - !*** ./src/components/scene/embedded.js ***! - \******************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); - -/** - * Component to embed an a-frame scene within the layout of a 2D page. - */ -module.exports.Component = registerComponent('embedded', { - dependencies: ['xr-mode-ui'], - schema: { - default: true - }, - sceneOnly: true, - update: function () { - var sceneEl = this.el; - var enterVREl = sceneEl.querySelector('.a-enter-vr'); - if (this.data === true) { - if (enterVREl) { - enterVREl.classList.add('embedded'); - } - sceneEl.removeFullScreenStyles(); - } else { - if (enterVREl) { - enterVREl.classList.remove('embedded'); - } - sceneEl.addFullScreenStyles(); - } - } -}); - -/***/ }), - -/***/ "./src/components/scene/fog.js": -/*!*************************************!*\ - !*** ./src/components/scene/fog.js ***! - \*************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var register = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../../lib/three */ "./src/lib/three.js"); -var debug = __webpack_require__(/*! ../../utils/debug */ "./src/utils/debug.js"); -var warn = debug('components:fog:warn'); - -/** - * Fog component. - * Applies only to the scene entity. - */ -module.exports.Component = register('fog', { - schema: { - color: { - type: 'color', - default: '#000' - }, - density: { - default: 0.00025 - }, - far: { - default: 1000, - min: 0 - }, - near: { - default: 1, - min: 0 - }, - type: { - default: 'linear', - oneOf: ['linear', 'exponential'] - } - }, - sceneOnly: true, - update: function () { - var data = this.data; - var el = this.el; - var fog = this.el.object3D.fog; - - // (Re)create fog if fog doesn't exist or fog type changed. - if (!fog || data.type !== fog.name) { - el.object3D.fog = getFog(data); - return; - } - - // Fog data changed. Update fog. - Object.keys(this.schema).forEach(function (key) { - var value = data[key]; - if (key === 'color') { - value = new THREE.Color(value); - } - fog[key] = value; - }); - }, - /** - * Remove fog on remove (callback). - */ - remove: function () { - var el = this.el; - var fog = this.el.object3D.fog; - if (!fog) { - return; - } - el.object3D.fog = null; - } -}); - -/** - * Creates a fog object. Sets fog.name to be able to detect fog type changes. - * - * @param {object} data - Fog data. - * @returns {object} fog - */ -function getFog(data) { - var fog; - if (data.type === 'exponential') { - fog = new THREE.FogExp2(data.color, data.density); - } else { - fog = new THREE.Fog(data.color, data.near, data.far); - } - fog.name = data.type; - return fog; -} - -/***/ }), - -/***/ "./src/components/scene/inspector.js": -/*!*******************************************!*\ - !*** ./src/components/scene/inspector.js ***! - \*******************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global AFRAME, INSPECTOR_VERSION */ -var AFRAME_INJECTED = (__webpack_require__(/*! ../../constants */ "./src/constants/index.js").AFRAME_INJECTED); -var pkg = __webpack_require__(/*! ../../../package */ "./package.json"); -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var utils = __webpack_require__(/*! ../../utils/ */ "./src/utils/index.js"); - -/** - * 0.4.2 to 0.4.x - * Will need to update this when A-Frame goes to 1.x.x. - */ -function getFuzzyPatchVersion(version) { - var split = version.split('.'); - split[2] = 'x'; - return split.join('.'); -} -var INSPECTOR_DEV_URL = 'https://aframe.io/aframe-inspector/dist/aframe-inspector.js'; -var INSPECTOR_RELEASE_URL = 'https://unpkg.com/aframe-inspector@' + getFuzzyPatchVersion(pkg.version) + '/dist/aframe-inspector.min.js'; -var INSPECTOR_URL = false ? 0 : INSPECTOR_RELEASE_URL; -var LOADING_MESSAGE = 'Loading Inspector'; -var LOADING_ERROR_MESSAGE = 'Error loading Inspector'; -module.exports.Component = registerComponent('inspector', { - schema: { - url: { - default: INSPECTOR_URL - } - }, - sceneOnly: true, - init: function () { - this.firstPlay = true; - this.onKeydown = this.onKeydown.bind(this); - this.onMessage = this.onMessage.bind(this); - this.initOverlay(); - window.addEventListener('keydown', this.onKeydown); - window.addEventListener('message', this.onMessage); - }, - play: function () { - var urlParam; - if (!this.firstPlay) { - return; - } - urlParam = utils.getUrlParameter('inspector'); - if (urlParam !== 'false' && !!urlParam) { - this.openInspector(); - this.firstPlay = false; - } - }, - initOverlay: function () { - var dotsHTML = '...'; - this.loadingMessageEl = document.createElement('div'); - this.loadingMessageEl.classList.add('a-inspector-loader'); - this.loadingMessageEl.innerHTML = LOADING_MESSAGE + dotsHTML; - }, - remove: function () { - this.removeEventListeners(); - }, - /** - * + + i keyboard shortcut. - */ - onKeydown: function (evt) { - var shortcutPressed = evt.keyCode === 73 && (evt.ctrlKey && evt.altKey || evt.getModifierState('AltGraph')); - if (!shortcutPressed) { - return; - } - this.openInspector(); - }, - showLoader: function () { - document.body.appendChild(this.loadingMessageEl); - }, - hideLoader: function () { - document.body.removeChild(this.loadingMessageEl); - }, - /** - * postMessage. aframe.io uses this to create a button on examples to open Inspector. - */ - onMessage: function (evt) { - if (evt.data === 'INJECT_AFRAME_INSPECTOR') { - this.openInspector(); - } - }, - openInspector: function (focusEl) { - var self = this; - var script; - - // Already injected. Open. - if (AFRAME.INSPECTOR || AFRAME.inspectorInjected) { - AFRAME.INSPECTOR.open(focusEl); - return; - } - this.showLoader(); - - // Inject. - script = document.createElement('script'); - script.src = this.data.url; - script.setAttribute('data-name', 'aframe-inspector'); - script.setAttribute(AFRAME_INJECTED, ''); - script.onload = function () { - AFRAME.INSPECTOR.open(focusEl); - self.hideLoader(); - self.removeEventListeners(); - }; - script.onerror = function () { - self.loadingMessageEl.innerHTML = LOADING_ERROR_MESSAGE; - }; - document.head.appendChild(script); - AFRAME.inspectorInjected = true; - }, - removeEventListeners: function () { - window.removeEventListener('keydown', this.onKeydown); - window.removeEventListener('message', this.onMessage); - } -}); - -/***/ }), - -/***/ "./src/components/scene/keyboard-shortcuts.js": -/*!****************************************************!*\ - !*** ./src/components/scene/keyboard-shortcuts.js ***! - \****************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var shouldCaptureKeyEvent = (__webpack_require__(/*! ../../utils/ */ "./src/utils/index.js").shouldCaptureKeyEvent); -module.exports.Component = registerComponent('keyboard-shortcuts', { - schema: { - enterVR: { - default: true - }, - exitVR: { - default: true - } - }, - sceneOnly: true, - init: function () { - this.onKeyup = this.onKeyup.bind(this); - }, - update: function (oldData) { - var data = this.data; - this.enterVREnabled = data.enterVR; - }, - play: function () { - window.addEventListener('keyup', this.onKeyup, false); - }, - pause: function () { - window.removeEventListener('keyup', this.onKeyup); - }, - onKeyup: function (evt) { - var scene = this.el; - if (!shouldCaptureKeyEvent(evt)) { - return; - } - if (this.enterVREnabled && evt.keyCode === 70) { - // f. - scene.enterVR(); - } - if (this.enterVREnabled && evt.keyCode === 27) { - // escape. - scene.exitVR(); - } - } -}); - -/***/ }), - -/***/ "./src/components/scene/pool.js": -/*!**************************************!*\ - !*** ./src/components/scene/pool.js ***! - \**************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var debug = __webpack_require__(/*! ../../utils/debug */ "./src/utils/debug.js"); -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var warn = debug('components:pool:warn'); - -/** - * Pool component to reuse entities. - * Avoids creating and destroying the same kind of entities. - * Helps reduce GC pauses. For example in a game to reuse enemies entities. - * - * @member {array} availableEls - Available entities in the pool. - * @member {array} usedEls - Entities of the pool in use. - */ -module.exports.Component = registerComponent('pool', { - schema: { - container: { - default: '' - }, - mixin: { - default: '' - }, - size: { - default: 0 - }, - dynamic: { - default: false - } - }, - sceneOnly: true, - multiple: true, - initPool: function () { - var i; - this.availableEls = []; - this.usedEls = []; - if (!this.data.mixin) { - warn('No mixin provided for pool component.'); - } - if (this.data.container) { - this.container = document.querySelector(this.data.container); - if (!this.container) { - warn('Container ' + this.data.container + ' not found.'); - } - } - this.container = this.container || this.el; - for (i = 0; i < this.data.size; ++i) { - this.createEntity(); - } - }, - update: function (oldData) { - var data = this.data; - if (oldData.mixin !== data.mixin || oldData.size !== data.size) { - this.initPool(); - } - }, - /** - * Add a new entity to the list of available entities. - */ - createEntity: function () { - var el; - el = document.createElement('a-entity'); - el.play = this.wrapPlay(el.play); - el.setAttribute('mixin', this.data.mixin); - el.object3D.visible = false; - el.pause(); - this.container.appendChild(el); - this.availableEls.push(el); - var usedEls = this.usedEls; - el.addEventListener('loaded', function () { - if (usedEls.indexOf(el) !== -1) { - return; - } - el.object3DParent = el.object3D.parent; - el.object3D.parent.remove(el.object3D); - }); - }, - /** - * Play wrapper for pooled entities. When pausing and playing a scene, don't want to play - * entities that are not in use. - */ - wrapPlay: function (playMethod) { - var usedEls = this.usedEls; - return function () { - if (usedEls.indexOf(this) === -1) { - return; - } - playMethod.call(this); - }; - }, - /** - * Used to request one of the available entities of the pool. - */ - requestEntity: function () { - var el; - if (this.availableEls.length === 0) { - if (this.data.dynamic === false) { - warn('Requested entity from empty pool: ' + this.attrName); - return; - } else { - warn('Requested entity from empty pool. This pool is dynamic and will resize ' + 'automatically. You might want to increase its initial size: ' + this.attrName); - } - this.createEntity(); - } - el = this.availableEls.shift(); - this.usedEls.push(el); - if (el.object3DParent) { - el.object3DParent.add(el.object3D); - this.updateRaycasters(); - } - el.object3D.visible = true; - return el; - }, - /** - * Used to return a used entity to the pool. - */ - returnEntity: function (el) { - var index = this.usedEls.indexOf(el); - if (index === -1) { - warn('The returned entity was not previously pooled from ' + this.attrName); - return; - } - this.usedEls.splice(index, 1); - this.availableEls.push(el); - el.object3DParent = el.object3D.parent; - el.object3D.parent.remove(el.object3D); - this.updateRaycasters(); - el.object3D.visible = false; - el.pause(); - return el; - }, - updateRaycasters: function () { - var raycasterEls = document.querySelectorAll('[raycaster]'); - raycasterEls.forEach(function (el) { - el.components['raycaster'].setDirty(); - }); - } -}); - -/***/ }), - -/***/ "./src/components/scene/real-world-meshing.js": -/*!****************************************************!*\ - !*** ./src/components/scene/real-world-meshing.js ***! - \****************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global XRPlane, XRMesh */ -var register = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../../lib/three */ "./src/lib/three.js"); - -/** - * Real World Meshing. - * - * Create entities with meshes corresponding to 3D surfaces detected in user's environment. - * It requires a browser with support for the WebXR Mesh and Plane detection modules. - * - */ -module.exports.Component = register('real-world-meshing', { - schema: { - filterLabels: { - type: 'array' - }, - meshesEnabled: { - default: true - }, - meshMixin: { - default: true - }, - planesEnabled: { - default: true - }, - planeMixin: { - default: '' - } - }, - sceneOnly: true, - init: function () { - var webxrData = this.el.getAttribute('webxr'); - var requiredFeaturesArray = webxrData.requiredFeatures; - if (requiredFeaturesArray.indexOf('mesh-detection') === -1) { - requiredFeaturesArray.push('mesh-detection'); - this.el.setAttribute('webxr', webxrData); - } - if (requiredFeaturesArray.indexOf('plane-detection') === -1) { - requiredFeaturesArray.push('plane-detection'); - this.el.setAttribute('webxr', webxrData); - } - this.meshEntities = []; - this.initWorldMeshEntity = this.initWorldMeshEntity.bind(this); - }, - tick: function () { - if (!this.el.is('ar-mode')) { - return; - } - this.detectMeshes(); - this.updateMeshes(); - }, - detectMeshes: function () { - var data = this.data; - var detectedMeshes; - var detectedPlanes; - var sceneEl = this.el; - var xrManager = sceneEl.renderer.xr; - var frame; - var meshEntities = this.meshEntities; - var present = false; - var newMeshes = []; - var filterLabels = this.data.filterLabels; - frame = sceneEl.frame; - detectedMeshes = frame.detectedMeshes; - detectedPlanes = frame.detectedPlanes; - for (var i = 0; i < meshEntities.length; i++) { - meshEntities[i].present = false; - } - if (data.meshesEnabled) { - for (var mesh of detectedMeshes.values()) { - // Ignore meshes that don't match the filterLabels. - if (filterLabels.length && filterLabels.indexOf(mesh.semanticLabel) === -1) { - continue; - } - for (i = 0; i < meshEntities.length; i++) { - if (mesh === meshEntities[i].mesh) { - present = true; - meshEntities[i].present = true; - if (meshEntities[i].lastChangedTime < mesh.lastChangedTime) { - this.updateMeshGeometry(meshEntities[i].el, mesh); - } - meshEntities[i].lastChangedTime = mesh.lastChangedTime; - break; - } - } - if (!present) { - newMeshes.push(mesh); - } - present = false; - } - } - if (data.planesEnabled) { - for (mesh of detectedPlanes.values()) { - // Ignore meshes that don't match the filterLabels. - if (filterLabels.length && filterLabels.indexOf(mesh.semanticLabel) === -1) { - continue; - } - for (i = 0; i < meshEntities.length; i++) { - if (mesh === meshEntities[i].mesh) { - present = true; - meshEntities[i].present = true; - if (meshEntities[i].lastChangedTime < mesh.lastChangedTime) { - this.updateMeshGeometry(meshEntities[i].el, mesh); - } - meshEntities[i].lastChangedTime = mesh.lastChangedTime; - break; - } - } - if (!present) { - newMeshes.push(mesh); - } - present = false; - } - } - this.deleteMeshes(); - this.createNewMeshes(newMeshes); - }, - updateMeshes: function () { - var auxMatrix = new THREE.Matrix4(); - return function () { - var meshPose; - var sceneEl = this.el; - var meshEl; - var frame = sceneEl.frame; - var meshEntities = this.meshEntities; - var referenceSpace = sceneEl.renderer.xr.getReferenceSpace(); - var meshSpace; - for (var i = 0; i < meshEntities.length; i++) { - meshSpace = meshEntities[i].mesh.meshSpace || meshEntities[i].mesh.planeSpace; - meshPose = frame.getPose(meshSpace, referenceSpace); - meshEl = meshEntities[i].el; - if (!meshEl.hasLoaded) { - continue; - } - auxMatrix.fromArray(meshPose.transform.matrix); - auxMatrix.decompose(meshEl.object3D.position, meshEl.object3D.quaternion, meshEl.object3D.scale); - } - }; - }(), - deleteMeshes: function () { - var meshEntities = this.meshEntities; - var newMeshEntities = []; - for (var i = 0; i < meshEntities.length; i++) { - if (!meshEntities[i].present) { - this.el.removeChild(meshEntities[i]); - } else { - newMeshEntities.push(meshEntities[i]); - } - } - this.meshEntities = newMeshEntities; - }, - createNewMeshes: function (newMeshes) { - var meshEl; - for (var i = 0; i < newMeshes.length; i++) { - meshEl = document.createElement('a-entity'); - this.meshEntities.push({ - mesh: newMeshes[i], - el: meshEl - }); - meshEl.addEventListener('loaded', this.initWorldMeshEntity); - this.el.appendChild(meshEl); - } - }, - initMeshGeometry: function (mesh) { - var geometry; - var shape; - var polygon; - if (mesh instanceof XRPlane) { - shape = new THREE.Shape(); - polygon = mesh.polygon; - for (var i = 0; i < polygon.length; ++i) { - if (i === 0) { - shape.moveTo(polygon[i].x, polygon[i].z); - } else { - shape.lineTo(polygon[i].x, polygon[i].z); - } - } - geometry = new THREE.ShapeGeometry(shape); - geometry.rotateX(Math.PI / 2); - return geometry; - } - geometry = new THREE.BufferGeometry(); - geometry.setAttribute('position', new THREE.BufferAttribute(mesh.vertices, 3)); - geometry.setIndex(new THREE.BufferAttribute(mesh.indices, 1)); - return geometry; - }, - initWorldMeshEntity: function (evt) { - var el = evt.target; - var geometry; - var mesh; - var meshEntity; - var meshEntities = this.meshEntities; - for (var i = 0; i < meshEntities.length; i++) { - if (meshEntities[i].el === el) { - meshEntity = meshEntities[i]; - break; - } - } - geometry = this.initMeshGeometry(meshEntity.mesh); - mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ - color: Math.random() * 0xFFFFFF, - side: THREE.DoubleSide - })); - el.setObject3D('mesh', mesh); - if (meshEntity.mesh instanceof XRPlane && this.data.planeMixin) { - el.setAttribute('mixin', this.data.planeMixin); - } else { - if (this.data.meshMixin) { - el.setAttribute('mixin', this.data.meshMixin); - } - } - el.setAttribute('data-world-mesh', meshEntity.mesh.semanticLabel); - }, - updateMeshGeometry: function (entityEl, mesh) { - var entityMesh = entityEl.getObject3D('mesh'); - entityMesh.geometry.dispose(); - entityMesh.geometry = this.initMeshGeometry(mesh); - } -}); - -/***/ }), - -/***/ "./src/components/scene/reflection.js": -/*!********************************************!*\ - !*** ./src/components/scene/reflection.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE, XRWebGLBinding */ -var register = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); - -// source: view-source:https://storage.googleapis.com/chromium-webxr-test/r886480/proposals/lighting-estimation.html -function updateLights(estimate, probeLight, directionalLight, directionalLightPosition) { - var intensityScalar = Math.max(estimate.primaryLightIntensity.x, Math.max(estimate.primaryLightIntensity.y, estimate.primaryLightIntensity.z)); - probeLight.sh.fromArray(estimate.sphericalHarmonicsCoefficients); - probeLight.intensity = 1; - if (directionalLight) { - directionalLight.color.setRGB(estimate.primaryLightIntensity.x / intensityScalar, estimate.primaryLightIntensity.y / intensityScalar, estimate.primaryLightIntensity.z / intensityScalar); - directionalLight.intensity = intensityScalar; - directionalLightPosition.copy(estimate.primaryLightDirection); - } -} -module.exports.Component = register('reflection', { - schema: { - directionalLight: { - type: 'selector' - } - }, - sceneOnly: true, - init: function () { - var self = this; - this.cubeRenderTarget = new THREE.WebGLCubeRenderTarget(16); - this.cubeCamera = new THREE.CubeCamera(0.1, 1000, this.cubeRenderTarget); - this.lightingEstimationTexture = new THREE.WebGLCubeRenderTarget(16).texture; - this.needsVREnvironmentUpdate = true; - - // Update WebXR to support light-estimation - var webxrData = this.el.getAttribute('webxr'); - var optionalFeaturesArray = webxrData.optionalFeatures; - if (!optionalFeaturesArray.includes('light-estimation')) { - optionalFeaturesArray.push('light-estimation'); - this.el.setAttribute('webxr', webxrData); - } - this.el.addEventListener('enter-vr', function () { - if (!self.el.is('ar-mode')) { - return; - } - var renderer = self.el.renderer; - var session = renderer.xr.getSession(); - if (session.requestLightProbe) { - self.startLightProbe(); - } - }); - this.el.addEventListener('exit-vr', function () { - if (self.xrLightProbe) { - self.stopLightProbe(); - } - }); - this.el.object3D.environment = this.cubeRenderTarget.texture; - }, - stopLightProbe: function () { - this.xrLightProbe = null; - if (this.probeLight) { - this.probeLight.components.light.light.intensity = 0; - } - this.needsVREnvironmentUpdate = true; - this.el.object3D.environment = this.cubeRenderTarget.texture; - }, - startLightProbe: function () { - this.needsLightProbeUpdate = true; - }, - setupLightProbe: function () { - var renderer = this.el.renderer; - var xrSession = renderer.xr.getSession(); - var self = this; - var gl = renderer.getContext(); - if (!this.probeLight) { - var probeLight = document.createElement('a-light'); - probeLight.setAttribute('type', 'probe'); - probeLight.setAttribute('intensity', 0); - this.el.appendChild(probeLight); - this.probeLight = probeLight; - } - - // Ensure that we have any extensions needed to use the preferred cube map format. - switch (xrSession.preferredReflectionFormat) { - case 'srgba8': - gl.getExtension('EXT_sRGB'); - break; - case 'rgba16f': - gl.getExtension('OES_texture_half_float'); - break; - } - this.glBinding = new XRWebGLBinding(xrSession, gl); - gl.getExtension('EXT_sRGB'); - gl.getExtension('OES_texture_half_float'); - xrSession.requestLightProbe().then(function (lightProbe) { - self.xrLightProbe = lightProbe; - lightProbe.addEventListener('reflectionchange', self.updateXRCubeMap.bind(self)); - }).catch(function (err) { - console.warn('Lighting estimation not supported: ' + err.message); - console.warn('Are you missing: webxr="optionalFeatures: light-estimation;" from ?'); - }); - }, - updateXRCubeMap: function () { - // Update Cube Map, cubeMap maybe some unavailable on some hardware - var renderer = this.el.renderer; - var cubeMap = this.glBinding.getReflectionCubeMap(this.xrLightProbe); - if (cubeMap) { - var rendererProps = renderer.properties.get(this.lightingEstimationTexture); - rendererProps.__webglTexture = cubeMap; - this.lightingEstimationTexture.needsPMREMUpdate = true; - this.el.object3D.environment = this.lightingEstimationTexture; - } - }, - tick: function () { - var scene = this.el.object3D; - var renderer = this.el.renderer; - var frame = this.el.frame; - if (frame && this.xrLightProbe) { - // light estimate may not yet be available, it takes a few frames to start working - var estimate = frame.getLightEstimate(this.xrLightProbe); - if (estimate) { - updateLights(estimate, this.probeLight.components.light.light, this.data.directionalLight && this.data.directionalLight.components.light.light, this.data.directionalLight && this.data.directionalLight.object3D.position); - } - } - if (this.needsVREnvironmentUpdate) { - scene.environment = null; - this.needsVREnvironmentUpdate = false; - this.cubeCamera.position.set(0, 1.6, 0); - this.cubeCamera.update(renderer, scene); - scene.environment = this.cubeRenderTarget.texture; - } - if (this.needsLightProbeUpdate && frame) { - // wait until the XR Session has started before trying to make - // the light probe - this.setupLightProbe(); - this.needsLightProbeUpdate = false; - } - }, - remove: function () { - this.el.object3D.environment = null; - if (this.probeLight) { - this.el.removeChild(this.probeLight); - } - } -}); - -/***/ }), - -/***/ "./src/components/scene/screenshot.js": -/*!********************************************!*\ - !*** ./src/components/scene/screenshot.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global ImageData, URL */ -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../../lib/three */ "./src/lib/three.js"); -var VERTEX_SHADER = ['attribute vec3 position;', 'attribute vec2 uv;', 'uniform mat4 projectionMatrix;', 'uniform mat4 modelViewMatrix;', 'varying vec2 vUv;', 'void main() {', ' vUv = vec2( 1.- uv.x, uv.y );', ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', '}'].join('\n'); -var FRAGMENT_SHADER = ['precision mediump float;', 'uniform samplerCube map;', 'varying vec2 vUv;', '#define M_PI 3.141592653589793238462643383279', 'void main() {', ' vec2 uv = vUv;', ' float longitude = uv.x * 2. * M_PI - M_PI + M_PI / 2.;', ' float latitude = uv.y * M_PI;', ' vec3 dir = vec3(', ' - sin( longitude ) * sin( latitude ),', ' cos( latitude ),', ' - cos( longitude ) * sin( latitude )', ' );', ' normalize( dir );', ' gl_FragColor = vec4( textureCube( map, dir ).rgb, 1.0 );', '}'].join('\n'); - -/** - * Component to take screenshots of the scene using a keyboard shortcut (alt+s). - * It can be configured to either take 360° captures (`equirectangular`) - * or regular screenshots (`projection`) - * - * This is based on https://github.com/spite/THREE.CubemapToEquirectangular - * To capture an equirectangular projection of the scene a THREE.CubeCamera is used - * The cube map produced by the CubeCamera is projected on a quad and then rendered to - * WebGLRenderTarget with an orthographic camera. - */ -module.exports.Component = registerComponent('screenshot', { - schema: { - width: { - default: 4096 - }, - height: { - default: 2048 - }, - camera: { - type: 'selector' - } - }, - sceneOnly: true, - setup: function () { - var el = this.el; - if (this.canvas) { - return; - } - var gl = el.renderer.getContext(); - if (!gl) { - return; - } - this.cubeMapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); - this.material = new THREE.RawShaderMaterial({ - uniforms: { - map: { - type: 't', - value: null - } - }, - vertexShader: VERTEX_SHADER, - fragmentShader: FRAGMENT_SHADER, - side: THREE.DoubleSide - }); - this.quad = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), this.material); - this.quad.visible = false; - this.camera = new THREE.OrthographicCamera(-1 / 2, 1 / 2, 1 / 2, -1 / 2, -10000, 10000); - this.canvas = document.createElement('canvas'); - this.ctx = this.canvas.getContext('2d'); - el.object3D.add(this.quad); - this.onKeyDown = this.onKeyDown.bind(this); - }, - getRenderTarget: function (width, height) { - return new THREE.WebGLRenderTarget(width, height, { - colorSpace: this.el.sceneEl.renderer.outputColorSpace, - minFilter: THREE.LinearFilter, - magFilter: THREE.LinearFilter, - wrapS: THREE.ClampToEdgeWrapping, - wrapT: THREE.ClampToEdgeWrapping, - format: THREE.RGBAFormat, - type: THREE.UnsignedByteType - }); - }, - resize: function (width, height) { - // Resize quad. - this.quad.scale.set(width, height, 1); - - // Resize camera. - this.camera.left = -1 * width / 2; - this.camera.right = width / 2; - this.camera.top = height / 2; - this.camera.bottom = -1 * height / 2; - this.camera.updateProjectionMatrix(); - - // Resize canvas. - this.canvas.width = width; - this.canvas.height = height; - }, - play: function () { - window.addEventListener('keydown', this.onKeyDown); - }, - /** - * + + s = Regular screenshot. - * + + + s = Equirectangular screenshot. - */ - onKeyDown: function (evt) { - var shortcutPressed = evt.keyCode === 83 && evt.ctrlKey && evt.altKey; - if (!this.data || !shortcutPressed) { - return; - } - var projection = evt.shiftKey ? 'equirectangular' : 'perspective'; - this.capture(projection); - }, - /** - * Capture a screenshot of the scene. - * - * @param {string} projection - Screenshot projection (equirectangular or perspective). - */ - setCapture: function (projection) { - var el = this.el; - var size; - var camera; - var cubeCamera; - var cubeRenderTarget; - // Configure camera. - if (projection === 'perspective') { - // Quad is only used in equirectangular mode. Hide it in this case. - this.quad.visible = false; - // Use scene camera. - camera = this.data.camera && this.data.camera.components.camera.camera || el.camera; - size = { - width: this.data.width, - height: this.data.height - }; - } else { - // Use ortho camera. - camera = this.camera; - cubeRenderTarget = new THREE.WebGLCubeRenderTarget(Math.min(this.cubeMapSize, 2048), { - format: THREE.RGBFormat, - generateMipmaps: true, - minFilter: THREE.LinearMipmapLinearFilter, - colorSpace: THREE.SRGBColorSpace - }); - // Create cube camera and copy position from scene camera. - cubeCamera = new THREE.CubeCamera(el.camera.near, el.camera.far, cubeRenderTarget); - // Copy camera position into cube camera; - el.camera.getWorldPosition(cubeCamera.position); - el.camera.getWorldQuaternion(cubeCamera.quaternion); - // Render scene with cube camera. - cubeCamera.update(el.renderer, el.object3D); - this.quad.material.uniforms.map.value = cubeCamera.renderTarget.texture; - size = { - width: this.data.width, - height: this.data.height - }; - // Use quad to project image taken by the cube camera. - this.quad.visible = true; - } - return { - camera: camera, - size: size, - projection: projection - }; - }, - /** - * Maintained for backwards compatibility. - */ - capture: function (projection) { - var isVREnabled = this.el.renderer.xr.enabled; - var renderer = this.el.renderer; - var params; - this.setup(); - // Disable VR. - renderer.xr.enabled = false; - params = this.setCapture(projection); - this.renderCapture(params.camera, params.size, params.projection); - // Trigger file download. - this.saveCapture(); - // Restore VR. - renderer.xr.enabled = isVREnabled; - }, - /** - * Return canvas instead of triggering download (e.g., for uploading blob to server). - */ - getCanvas: function (projection) { - var isVREnabled = this.el.renderer.xr.enabled; - var renderer = this.el.renderer; - this.setup(); - // Disable VR. - var params = this.setCapture(projection); - renderer.xr.enabled = false; - this.renderCapture(params.camera, params.size, params.projection); - // Restore VR. - renderer.xr.enabled = isVREnabled; - return this.canvas; - }, - renderCapture: function (camera, size, projection) { - var autoClear = this.el.renderer.autoClear; - var el = this.el; - var imageData; - var output; - var pixels; - var renderer = el.renderer; - // Create rendering target and buffer to store the read pixels. - output = this.getRenderTarget(size.width, size.height); - pixels = new Uint8Array(4 * size.width * size.height); - // Resize quad, camera, and canvas. - this.resize(size.width, size.height); - // Render scene to render target. - renderer.autoClear = true; - renderer.clear(); - renderer.setRenderTarget(output); - renderer.render(el.object3D, camera); - renderer.autoClear = autoClear; - // Read image pixels back. - renderer.readRenderTargetPixels(output, 0, 0, size.width, size.height, pixels); - renderer.setRenderTarget(null); - if (projection === 'perspective') { - pixels = this.flipPixelsVertically(pixels, size.width, size.height); - } - imageData = new ImageData(new Uint8ClampedArray(pixels), size.width, size.height); - // Hide quad after projecting the image. - this.quad.visible = false; - // Copy pixels into canvas. - this.ctx.putImageData(imageData, 0, 0); - }, - flipPixelsVertically: function (pixels, width, height) { - var flippedPixels = pixels.slice(0); - for (var x = 0; x < width; ++x) { - for (var y = 0; y < height; ++y) { - flippedPixels[x * 4 + y * width * 4] = pixels[x * 4 + (height - y) * width * 4]; - flippedPixels[x * 4 + 1 + y * width * 4] = pixels[x * 4 + 1 + (height - y) * width * 4]; - flippedPixels[x * 4 + 2 + y * width * 4] = pixels[x * 4 + 2 + (height - y) * width * 4]; - flippedPixels[x * 4 + 3 + y * width * 4] = pixels[x * 4 + 3 + (height - y) * width * 4]; - } - } - return flippedPixels; - }, - /** - * Download capture to file. - */ - saveCapture: function () { - this.canvas.toBlob(function (blob) { - var fileName = 'screenshot-' + document.title.toLowerCase() + '-' + Date.now() + '.png'; - var linkEl = document.createElement('a'); - var url = URL.createObjectURL(blob); - linkEl.href = url; - linkEl.setAttribute('download', fileName); - linkEl.innerHTML = 'downloading...'; - linkEl.style.display = 'none'; - document.body.appendChild(linkEl); - setTimeout(function () { - linkEl.click(); - document.body.removeChild(linkEl); - }, 1); - }, 'image/png'); - } -}); - -/***/ }), - -/***/ "./src/components/scene/stats.js": -/*!***************************************!*\ - !*** ./src/components/scene/stats.js ***! - \***************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var RStats = __webpack_require__(/*! ../../../vendor/rStats */ "./vendor/rStats.js"); -var utils = __webpack_require__(/*! ../../utils */ "./src/utils/index.js"); -__webpack_require__(/*! ../../../vendor/rStats.extras */ "./vendor/rStats.extras.js"); -__webpack_require__(/*! ../../lib/rStatsAframe */ "./src/lib/rStatsAframe.js"); -var AFrameStats = window.aframeStats; -var HIDDEN_CLASS = 'a-hidden'; -var ThreeStats = window.threeStats; - -/** - * Stats appended to document.body by RStats. - */ -module.exports.Component = registerComponent('stats', { - schema: { - default: true - }, - sceneOnly: true, - init: function () { - var scene = this.el; - if (utils.getUrlParameter('stats') === 'false') { - return; - } - this.stats = createStats(scene); - this.statsEl = document.querySelector('.rs-base'); - this.hideBound = this.hide.bind(this); - this.showBound = this.show.bind(this); - scene.addEventListener('enter-vr', this.hideBound); - scene.addEventListener('exit-vr', this.showBound); - }, - update: function () { - if (!this.stats) { - return; - } - return !this.data ? this.hide() : this.show(); - }, - remove: function () { - this.el.removeEventListener('enter-vr', this.hideBound); - this.el.removeEventListener('exit-vr', this.showBound); - if (!this.statsEl) { - return; - } // Scene detached. - this.statsEl.parentNode.removeChild(this.statsEl); - }, - tick: function () { - var stats = this.stats; - if (!stats) { - return; - } - stats('rAF').tick(); - stats('FPS').frame(); - stats().update(); - }, - hide: function () { - this.statsEl.classList.add(HIDDEN_CLASS); - }, - show: function () { - this.statsEl.classList.remove(HIDDEN_CLASS); - } -}); -function createStats(scene) { - var threeStats = new ThreeStats(scene.renderer); - var aframeStats = new AFrameStats(scene); - var plugins = scene.isMobile ? [] : [threeStats, aframeStats]; - return new RStats({ - css: [], - // Our stylesheet is injected from `src/index.js`. - values: { - fps: { - caption: 'fps', - below: 30 - } - }, - groups: [{ - caption: 'Framerate', - values: ['fps', 'raf'] - }], - plugins: plugins - }); -} - -/***/ }), - -/***/ "./src/components/scene/xr-mode-ui.js": -/*!********************************************!*\ - !*** ./src/components/scene/xr-mode-ui.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../../core/component */ "./src/core/component.js").registerComponent); -var constants = __webpack_require__(/*! ../../constants/ */ "./src/constants/index.js"); -var utils = __webpack_require__(/*! ../../utils/ */ "./src/utils/index.js"); -var ENTER_VR_CLASS = 'a-enter-vr'; -var ENTER_AR_CLASS = 'a-enter-ar'; -var ENTER_VR_BTN_CLASS = 'a-enter-vr-button'; -var ENTER_AR_BTN_CLASS = 'a-enter-ar-button'; -var HIDDEN_CLASS = 'a-hidden'; -var ORIENTATION_MODAL_CLASS = 'a-orientation-modal'; - -/** - * UI for entering VR mode. - */ -module.exports.Component = registerComponent('xr-mode-ui', { - dependencies: ['canvas'], - schema: { - enabled: { - default: true - }, - cardboardModeEnabled: { - default: false - }, - enterVRButton: { - default: '' - }, - enterVREnabled: { - default: true - }, - enterARButton: { - default: '' - }, - enterAREnabled: { - default: true - }, - XRMode: { - default: 'vr', - oneOf: ['vr', 'ar', 'xr'] - } - }, - sceneOnly: true, - init: function () { - var self = this; - var sceneEl = this.el; - if (utils.getUrlParameter('ui') === 'false') { - return; - } - this.insideLoader = false; - this.enterVREl = null; - this.enterAREl = null; - this.orientationModalEl = null; - this.bindMethods(); - - // Hide/show VR UI when entering/exiting VR mode. - sceneEl.addEventListener('enter-vr', this.updateEnterInterfaces); - sceneEl.addEventListener('exit-vr', this.updateEnterInterfaces); - sceneEl.addEventListener('update-vr-devices', this.updateEnterInterfaces); - window.addEventListener('message', function (event) { - if (event.data.type === 'loaderReady') { - self.insideLoader = true; - self.remove(); - } - }); - - // Modal that tells the user to change orientation if in portrait. - window.addEventListener('orientationchange', this.toggleOrientationModalIfNeeded); - }, - bindMethods: function () { - this.onEnterVRButtonClick = this.onEnterVRButtonClick.bind(this); - this.onEnterARButtonClick = this.onEnterARButtonClick.bind(this); - this.onModalClick = this.onModalClick.bind(this); - this.toggleOrientationModalIfNeeded = this.toggleOrientationModalIfNeeded.bind(this); - this.updateEnterInterfaces = this.updateEnterInterfaces.bind(this); - }, - /** - * Exit VR when modal clicked. - */ - onModalClick: function () { - this.el.exitVR(); - }, - /** - * Enter VR when clicked. - */ - onEnterVRButtonClick: function () { - this.el.enterVR(); - }, - /** - * Enter AR when clicked. - */ - onEnterARButtonClick: function () { - this.el.enterAR(); - }, - update: function () { - var data = this.data; - var sceneEl = this.el; - if (!data.enabled || this.insideLoader || utils.getUrlParameter('ui') === 'false') { - return this.remove(); - } - if (this.enterVREl || this.enterAREl || this.orientationModalEl) { - return; - } - - // Add UI if enabled and not already present. - if (!this.enterVREl && data.enterVREnabled && (data.XRMode === 'xr' || data.XRMode === 'vr')) { - if (data.enterVRButton) { - // Custom button. - this.enterVREl = document.querySelector(data.enterVRButton); - this.enterVREl.addEventListener('click', this.onEnterVRButtonClick); - } else { - this.enterVREl = createEnterVRButton(this.onEnterVRButtonClick); - sceneEl.appendChild(this.enterVREl); - } - } - if (!this.enterAREl && data.enterAREnabled && (data.XRMode === 'xr' || data.XRMode === 'ar')) { - if (data.enterARButton) { - // Custom button. - this.enterAREl = document.querySelector(data.enterARButton); - this.enterAREl.addEventListener('click', this.onEnterARButtonClick); - } else { - this.enterAREl = createEnterARButton(this.onEnterARButtonClick, data.XRMode === 'xr'); - sceneEl.appendChild(this.enterAREl); - } - } - this.orientationModalEl = createOrientationModal(this.onModalClick); - sceneEl.appendChild(this.orientationModalEl); - this.updateEnterInterfaces(); - }, - remove: function () { - [this.enterVREl, this.enterAREl, this.orientationModalEl].forEach(function (uiElement) { - if (uiElement && uiElement.parentNode) { - uiElement.parentNode.removeChild(uiElement); - } - }); - this.enterVREl = undefined; - this.enterAREl = undefined; - this.orientationModalEl = undefined; - }, - updateEnterInterfaces: function () { - this.toggleEnterVRButtonIfNeeded(); - this.toggleEnterARButtonIfNeeded(); - this.toggleOrientationModalIfNeeded(); - }, - toggleEnterVRButtonIfNeeded: function () { - var sceneEl = this.el; - if (!this.enterVREl) { - return; - } - if (sceneEl.is('vr-mode') || (sceneEl.isMobile || utils.device.isMobileDeviceRequestingDesktopSite()) && !this.data.cardboardModeEnabled && !utils.device.checkVRSupport()) { - this.enterVREl.classList.add(HIDDEN_CLASS); - } else { - if (!utils.device.checkVRSupport()) { - this.enterVREl.classList.add('fullscreen'); - } - this.enterVREl.classList.remove(HIDDEN_CLASS); - sceneEl.enterVR(false, true); - } - }, - toggleEnterARButtonIfNeeded: function () { - var sceneEl = this.el; - if (!this.enterAREl) { - return; - } - // Hide the button while in a session, or if AR is not supported. - if (sceneEl.is('vr-mode') || !utils.device.checkARSupport()) { - this.enterAREl.classList.add(HIDDEN_CLASS); - } else { - this.enterAREl.classList.remove(HIDDEN_CLASS); - sceneEl.enterVR(true, true); - } - }, - toggleOrientationModalIfNeeded: function () { - var sceneEl = this.el; - var orientationModalEl = this.orientationModalEl; - if (!orientationModalEl || !sceneEl.isMobile) { - return; - } - if (!utils.device.isLandscape() && sceneEl.is('vr-mode')) { - // Show if in VR mode on portrait. - orientationModalEl.classList.remove(HIDDEN_CLASS); - } else { - orientationModalEl.classList.add(HIDDEN_CLASS); - } - } -}); - -/** - * Create a button that when clicked will enter into stereo-rendering mode for VR. - * - * Structure:
- * - * @param {function} onClick - click event handler - * @returns {Element} Wrapper
. - */ -function createEnterVRButton(onClick) { - var vrButton; - var wrapper; - - // Create elements. - wrapper = document.createElement('div'); - wrapper.classList.add(ENTER_VR_CLASS); - wrapper.setAttribute(constants.AFRAME_INJECTED, ''); - vrButton = document.createElement('button'); - vrButton.className = ENTER_VR_BTN_CLASS; - vrButton.setAttribute('title', 'Enter VR mode with a headset or fullscreen without'); - vrButton.setAttribute(constants.AFRAME_INJECTED, ''); - if (utils.device.isMobile()) { - applyStickyHoverFix(vrButton); - } - // Insert elements. - wrapper.appendChild(vrButton); - vrButton.addEventListener('click', function (evt) { - onClick(); - evt.stopPropagation(); - }); - return wrapper; -} - -/** - * Create a button that when clicked will enter into AR mode - * - * Structure:
- * - * @param {function} onClick - click event handler - * @returns {Element} Wrapper
. - */ -function createEnterARButton(onClick, xrMode) { - var arButton; - var wrapper; - - // Create elements. - wrapper = document.createElement('div'); - wrapper.classList.add(ENTER_AR_CLASS); - if (xrMode) { - wrapper.classList.add('xr'); - } - wrapper.setAttribute(constants.AFRAME_INJECTED, ''); - arButton = document.createElement('button'); - arButton.className = ENTER_AR_BTN_CLASS; - arButton.setAttribute('title', 'Enter AR mode with a headset or handheld device.'); - arButton.setAttribute(constants.AFRAME_INJECTED, ''); - if (utils.device.isMobile()) { - applyStickyHoverFix(arButton); - } - // Insert elements. - wrapper.appendChild(arButton); - arButton.addEventListener('click', function (evt) { - onClick(); - evt.stopPropagation(); - }); - return wrapper; -} - -/** - * Creates a modal dialog to request the user to switch to landscape orientation. - * - * @param {function} onClick - click event handler - * @returns {Element} Wrapper
. - */ -function createOrientationModal(onClick) { - var modal = document.createElement('div'); - modal.className = ORIENTATION_MODAL_CLASS; - modal.classList.add(HIDDEN_CLASS); - modal.setAttribute(constants.AFRAME_INJECTED, ''); - var exit = document.createElement('button'); - exit.setAttribute(constants.AFRAME_INJECTED, ''); - exit.innerHTML = 'Exit VR'; - - // Exit VR on close. - exit.addEventListener('click', onClick); - modal.appendChild(exit); - return modal; -} - -/** - * CSS hover state is sticky in iOS (as in 12/18/2019) - * They are not removed on mouseleave and this function applies a class - * to resets the style. - * - * @param {function} buttonEl - Button element - */ -function applyStickyHoverFix(buttonEl) { - buttonEl.addEventListener('touchstart', function () { - buttonEl.classList.remove('resethover'); - }); - buttonEl.addEventListener('touchend', function () { - buttonEl.classList.add('resethover'); - }); -} - -/***/ }), - -/***/ "./src/components/shadow.js": -/*!**********************************!*\ - !*** ./src/components/shadow.js ***! - \**********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var component = __webpack_require__(/*! ../core/component */ "./src/core/component.js"); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var registerComponent = component.registerComponent; - -/** - * Shadow component. - * - * When applied to an entity, that entity's geometry and any descendants will cast or receive - * shadows as specified by the `cast` and `receive` properties. - */ -module.exports.Component = registerComponent('shadow', { - schema: { - cast: { - default: true - }, - receive: { - default: true - } - }, - init: function () { - this.onMeshChanged = this.update.bind(this); - this.el.addEventListener('object3dset', this.onMeshChanged); - this.system.setShadowMapEnabled(true); - }, - update: function () { - var data = this.data; - this.updateDescendants(data.cast, data.receive); - }, - remove: function () { - var el = this.el; - el.removeEventListener('object3dset', this.onMeshChanged); - this.updateDescendants(false, false); - }, - updateDescendants: function (cast, receive) { - var sceneEl = this.el.sceneEl; - this.el.object3D.traverse(function (node) { - if (!(node instanceof THREE.Mesh)) { - return; - } - node.castShadow = cast; - node.receiveShadow = receive; - - // If scene has already rendered, materials must be updated. - if (sceneEl.hasLoaded && node.material) { - var materials = Array.isArray(node.material) ? node.material : [node.material]; - for (var i = 0; i < materials.length; i++) { - materials[i].needsUpdate = true; - } - } - }); - } -}); - -/***/ }), - -/***/ "./src/components/sound.js": -/*!*********************************!*\ - !*** ./src/components/sound.js ***! - \*********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var debug = __webpack_require__(/*! ../utils/debug */ "./src/utils/debug.js"); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var warn = debug('components:sound:warn'); - -/** - * Sound component. - */ -module.exports.Component = registerComponent('sound', { - schema: { - autoplay: { - default: false - }, - distanceModel: { - default: 'inverse', - oneOf: ['linear', 'inverse', 'exponential'] - }, - loop: { - default: false - }, - loopStart: { - default: 0 - }, - loopEnd: { - default: 0 - }, - maxDistance: { - default: 10000 - }, - on: { - default: '' - }, - poolSize: { - default: 1 - }, - positional: { - default: true - }, - refDistance: { - default: 1 - }, - rolloffFactor: { - default: 1 - }, - src: { - type: 'audio' - }, - volume: { - default: 1 - } - }, - multiple: true, - init: function () { - var self = this; - this.listener = null; - this.audioLoader = new THREE.AudioLoader(); - this.pool = new THREE.Group(); - this.loaded = false; - this.mustPlay = false; - - // Don't pass evt because playSound takes a function as parameter. - this.playSoundBound = function () { - self.playSound(); - }; - }, - update: function (oldData) { - var data = this.data; - var i; - var sound; - var srcChanged = data.src !== oldData.src; - - // Create new sound if not yet created or changing `src`. - if (srcChanged) { - if (!data.src) { - return; - } - this.setupSound(); - } - for (i = 0; i < this.pool.children.length; i++) { - sound = this.pool.children[i]; - if (data.positional) { - sound.setDistanceModel(data.distanceModel); - sound.setMaxDistance(data.maxDistance); - sound.setRefDistance(data.refDistance); - sound.setRolloffFactor(data.rolloffFactor); - } - sound.setLoop(data.loop); - sound.setLoopStart(data.loopStart); - - // With a loop start specified without a specified loop end, the end of the loop should be the end of the file - if (data.loopStart !== 0 && data.loopEnd === 0) { - sound.setLoopEnd(sound.buffer.duration); - } else { - sound.setLoopEnd(data.loopEnd); - } - sound.setVolume(data.volume); - sound.isPaused = false; - } - if (data.on !== oldData.on) { - this.updateEventListener(oldData.on); - } - - // All sound values set. Load in `src`. - if (srcChanged) { - var self = this; - this.loaded = false; - this.audioLoader.load(data.src, function (buffer) { - for (i = 0; i < self.pool.children.length; i++) { - sound = self.pool.children[i]; - sound.setBuffer(buffer); - } - self.loaded = true; - - // Remove this key from cache, otherwise we can't play it again - THREE.Cache.remove(data.src); - if (self.data.autoplay || self.mustPlay) { - self.playSound(self.processSound); - } - self.el.emit('sound-loaded', self.evtDetail, false); - }); - } - }, - pause: function () { - this.stopSound(); - this.removeEventListener(); - }, - play: function () { - if (this.data.autoplay) { - this.playSound(); - } - this.updateEventListener(); - }, - remove: function () { - var i; - var sound; - this.removeEventListener(); - if (this.el.getObject3D(this.attrName)) { - this.el.removeObject3D(this.attrName); - } - try { - for (i = 0; i < this.pool.children.length; i++) { - sound = this.pool.children[i]; - sound.disconnect(); - } - } catch (e) { - // disconnect() will throw if it was never connected initially. - warn('Audio source not properly disconnected'); - } - }, - /** - * Update listener attached to the user defined on event. - */ - updateEventListener: function (oldEvt) { - var el = this.el; - if (oldEvt) { - el.removeEventListener(oldEvt, this.playSoundBound); - } - el.addEventListener(this.data.on, this.playSoundBound); - }, - removeEventListener: function () { - this.el.removeEventListener(this.data.on, this.playSoundBound); - }, - /** - * Removes current sound object, creates new sound object, adds to entity. - * - * @returns {object} sound - */ - setupSound: function () { - var el = this.el; - var i; - var sceneEl = el.sceneEl; - var self = this; - var sound; - if (this.pool.children.length > 0) { - this.stopSound(); - el.removeObject3D('sound'); - } - - // Only want one AudioListener. Cache it on the scene. - var listener = this.listener = sceneEl.audioListener || new THREE.AudioListener(); - sceneEl.audioListener = listener; - if (sceneEl.camera) { - sceneEl.camera.add(listener); - } - - // Wait for camera if necessary. - sceneEl.addEventListener('camera-set-active', function (evt) { - evt.detail.cameraEl.getObject3D('camera').add(listener); - }); - - // Create [poolSize] audio instances and attach them to pool - this.pool = new THREE.Group(); - for (i = 0; i < this.data.poolSize; i++) { - sound = this.data.positional ? new THREE.PositionalAudio(listener) : new THREE.Audio(listener); - this.pool.add(sound); - } - el.setObject3D(this.attrName, this.pool); - for (i = 0; i < this.pool.children.length; i++) { - sound = this.pool.children[i]; - sound.onEnded = function () { - this.isPlaying = false; - self.el.emit('sound-ended', self.evtDetail, false); - }; - } - }, - /** - * Pause all the sounds in the pool. - */ - pauseSound: function () { - var i; - var sound; - this.isPlaying = false; - for (i = 0; i < this.pool.children.length; i++) { - sound = this.pool.children[i]; - if (!sound.source || !sound.source.buffer || !sound.isPlaying || sound.isPaused) { - continue; - } - sound.isPaused = true; - sound.pause(); - } - }, - /** - * Look for an unused sound in the pool and play it if found. - */ - playSound: function (processSound) { - var found; - var i; - var sound; - if (!this.loaded) { - warn('Sound not loaded yet. It will be played once it finished loading'); - this.mustPlay = true; - this.processSound = processSound; - return; - } - found = false; - this.isPlaying = true; - for (i = 0; i < this.pool.children.length; i++) { - sound = this.pool.children[i]; - if (!sound.isPlaying && sound.buffer && !found) { - if (processSound) { - processSound(sound); - } - sound.play(); - sound.isPaused = false; - found = true; - continue; - } - } - if (!found) { - warn('All the sounds are playing. If you need to play more sounds simultaneously ' + 'consider increasing the size of pool with the `poolSize` attribute.', this.el); - return; - } - this.mustPlay = false; - this.processSound = undefined; - }, - /** - * Stop all the sounds in the pool. - */ - stopSound: function () { - var i; - var sound; - this.isPlaying = false; - for (i = 0; i < this.pool.children.length; i++) { - sound = this.pool.children[i]; - if (!sound.source || !sound.source.buffer) { - return; - } - sound.stop(); - } - } -}); - -/***/ }), - -/***/ "./src/components/text.js": -/*!********************************!*\ - !*** ./src/components/text.js ***! - \********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var createTextGeometry = __webpack_require__(/*! three-bmfont-text */ "./node_modules/three-bmfont-text/index.js"); -var loadBMFont = __webpack_require__(/*! load-bmfont */ "./node_modules/load-bmfont/browser.js"); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var coreShader = __webpack_require__(/*! ../core/shader */ "./src/core/shader.js"); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var error = utils.debug('components:text:error'); -var shaders = coreShader.shaders; -var warn = utils.debug('components:text:warn'); - -// 1 to match other A-Frame default widths. -var DEFAULT_WIDTH = 1; - -// @bryik set anisotropy to 16. Improves look of large amounts of text when viewed from angle. -var MAX_ANISOTROPY = 16; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var FONT_BASE_URL = AFRAME_CDN_ROOT + 'fonts/'; -var FONTS = { - aileronsemibold: FONT_BASE_URL + 'Aileron-Semibold.fnt', - dejavu: FONT_BASE_URL + 'DejaVu-sdf.fnt', - exo2bold: FONT_BASE_URL + 'Exo2Bold.fnt', - exo2semibold: FONT_BASE_URL + 'Exo2SemiBold.fnt', - kelsonsans: FONT_BASE_URL + 'KelsonSans.fnt', - monoid: FONT_BASE_URL + 'Monoid.fnt', - mozillavr: FONT_BASE_URL + 'mozillavr.fnt', - roboto: FONT_BASE_URL + 'Roboto-msdf.json', - sourcecodepro: FONT_BASE_URL + 'SourceCodePro.fnt' -}; -var MSDF_FONTS = ['roboto']; -var DEFAULT_FONT = 'roboto'; -module.exports.FONTS = FONTS; -var cache = new PromiseCache(); -var fontWidthFactors = {}; -var textures = {}; - -// Regular expression for detecting a URLs with a protocol prefix. -var protocolRe = /^\w+:/; - -/** - * SDF-based text component. - * Based on https://github.com/Jam3/three-bmfont-text. - * - * All the stock fonts are for the `sdf` registered shader, an improved version of jam3's - * original `sdf` shader. - */ -module.exports.Component = registerComponent('text', { - multiple: true, - schema: { - align: { - type: 'string', - default: 'left', - oneOf: ['left', 'right', 'center'] - }, - alphaTest: { - default: 0.5 - }, - // `anchor` defaults to center to match geometries. - anchor: { - default: 'center', - oneOf: ['left', 'right', 'center', 'align'] - }, - baseline: { - default: 'center', - oneOf: ['top', 'center', 'bottom'] - }, - color: { - type: 'color', - default: '#FFF' - }, - font: { - type: 'string', - default: DEFAULT_FONT - }, - // `fontImage` defaults to the font name as a .png (e.g., mozillavr.fnt -> mozillavr.png). - fontImage: { - type: 'string' - }, - // `height` has no default, will be populated at layout. - height: { - type: 'number' - }, - letterSpacing: { - type: 'number', - default: 0 - }, - // `lineHeight` defaults to font's `lineHeight` value. - lineHeight: { - type: 'number' - }, - // `negate` must be true for fonts generated with older versions of msdfgen (white background). - negate: { - type: 'boolean', - default: true - }, - opacity: { - type: 'number', - default: 1.0 - }, - shader: { - default: 'sdf', - oneOf: shaders - }, - side: { - default: 'front', - oneOf: ['front', 'back', 'double'] - }, - tabSize: { - default: 4 - }, - transparent: { - default: true - }, - value: { - type: 'string' - }, - whiteSpace: { - default: 'normal', - oneOf: ['normal', 'pre', 'nowrap'] - }, - // `width` defaults to geometry width if present, else `DEFAULT_WIDTH`. - width: { - type: 'number' - }, - // `wrapCount` units are about one default font character. Wrap roughly at this number. - wrapCount: { - type: 'number', - default: 40 - }, - // `wrapPixels` will wrap using bmfont pixel units (e.g., dejavu's is 32 pixels). - wrapPixels: { - type: 'number' - }, - // `xOffset` to add padding. - xOffset: { - type: 'number', - default: 0 - }, - // `yOffset` to adjust generated fonts from tools that may have incorrect metrics. - yOffset: { - type: 'number', - default: 0 - }, - // `zOffset` will provide a small z offset to avoid z-fighting. - zOffset: { - type: 'number', - default: 0.001 - } - }, - init: function () { - this.shaderData = {}; - this.geometry = createTextGeometry(); - this.createOrUpdateMaterial(); - this.explicitGeoDimensionsChecked = false; - }, - update: function (oldData) { - var data = this.data; - var font = this.currentFont; - if (textures[data.font]) { - this.texture = textures[data.font]; - } else { - // Create texture per font. - this.texture = textures[data.font] = new THREE.Texture(); - this.texture.anisotropy = MAX_ANISOTROPY; - } - - // Update material. - this.createOrUpdateMaterial(); - - // New font. `updateFont` will later change data and layout. - if (oldData.font !== data.font) { - this.updateFont(); - return; - } - - // Update geometry and layout. - if (font) { - this.updateGeometry(this.geometry, font); - this.updateLayout(); - } - }, - /** - * Clean up geometry, material, texture, mesh, objects. - */ - remove: function () { - this.geometry.dispose(); - this.geometry = null; - this.el.removeObject3D(this.attrName); - this.material.dispose(); - this.material = null; - this.texture.dispose(); - this.texture = null; - if (this.shaderObject) { - delete this.shaderObject; - } - }, - /** - * Update the shader of the material. - */ - createOrUpdateMaterial: function () { - var data = this.data; - var hasChangedShader; - var material = this.material; - var NewShader; - var shaderData = this.shaderData; - var shaderName; - - // Infer shader if using a stock font (or from `-msdf` filename convention). - shaderName = data.shader; - if (MSDF_FONTS.indexOf(data.font) !== -1 || data.font.indexOf('-msdf.') >= 0) { - shaderName = 'msdf'; - } else if (data.font in FONTS && MSDF_FONTS.indexOf(data.font) === -1) { - shaderName = 'sdf'; - } - hasChangedShader = (this.shaderObject && this.shaderObject.name) !== shaderName; - shaderData.alphaTest = data.alphaTest; - shaderData.color = data.color; - shaderData.map = this.texture; - shaderData.opacity = data.opacity; - shaderData.side = parseSide(data.side); - shaderData.transparent = data.transparent; - shaderData.negate = data.negate; - - // Shader has not changed, do an update. - if (!hasChangedShader) { - // Update shader material. - this.shaderObject.update(shaderData); - // Apparently, was not set on `init` nor `update`. - material.transparent = shaderData.transparent; - material.side = shaderData.side; - return; - } - - // Shader has changed. Create a shader material. - NewShader = createShader(this.el, shaderName, shaderData); - this.material = NewShader.material; - this.shaderObject = NewShader.shader; - - // Set new shader material. - this.material.side = shaderData.side; - if (this.mesh) { - this.mesh.material = this.material; - } - }, - /** - * Load font for geometry, load font image for material, and apply. - */ - updateFont: function () { - var data = this.data; - var el = this.el; - var fontSrc; - var geometry = this.geometry; - var self = this; - if (!data.font) { - warn('No font specified. Using the default font.'); - } - - // Make invisible during font swap. - if (this.mesh) { - this.mesh.visible = false; - } - - // Look up font URL to use, and perform cached load. - fontSrc = this.lookupFont(data.font || DEFAULT_FONT) || data.font; - cache.get(fontSrc, function doLoadFont() { - return loadFont(fontSrc, data.yOffset); - }).then(function setFont(font) { - var fontImgSrc; - if (font.pages.length !== 1) { - throw new Error('Currently only single-page bitmap fonts are supported.'); - } - if (!fontWidthFactors[fontSrc]) { - font.widthFactor = fontWidthFactors[font] = computeFontWidthFactor(font); - } - self.currentFont = font; - // Look up font image URL to use, and perform cached load. - fontImgSrc = self.getFontImageSrc(); - cache.get(fontImgSrc, function () { - return loadTexture(fontImgSrc); - }).then(function (image) { - // Make mesh visible and apply font image as texture. - var texture = self.texture; - // The component may have been removed at this point and texture will - // be null. This happens mainly while executing the tests, - // in this case we just return. - if (!texture) return; - texture.image = image; - texture.needsUpdate = true; - textures[data.font] = texture; - self.texture = texture; - self.initMesh(); - self.currentFont = font; - // Update geometry given font metrics. - self.updateGeometry(geometry, font); - self.updateLayout(); - self.mesh.visible = true; - el.emit('textfontset', { - font: data.font, - fontObj: font - }); - }).catch(function (err) { - error(err.message); - error(err.stack); - }); - }).catch(function (err) { - error(err.message); - error(err.stack); - }); - }, - initMesh: function () { - if (this.mesh) { - return; - } - this.mesh = new THREE.Mesh(this.geometry, this.material); - this.el.setObject3D(this.attrName, this.mesh); - }, - getFontImageSrc: function () { - if (this.data.fontImage) { - return this.data.fontImage; - } - var fontSrc = this.lookupFont(this.data.font || DEFAULT_FONT) || this.data.font; - var imageSrc = this.currentFont.pages[0]; - // If the image URL contains a non-HTTP(S) protocol, assume it's an absolute - // path on disk and try to infer the path from the font source instead. - if (imageSrc.match(protocolRe) && imageSrc.indexOf('http') !== 0) { - return fontSrc.replace(/(\.fnt)|(\.json)/, '.png'); - } - return THREE.LoaderUtils.extractUrlBase(fontSrc) + imageSrc; - }, - /** - * Update layout with anchor, alignment, baseline, and considering any meshes. - */ - updateLayout: function () { - var anchor; - var baseline; - var el = this.el; - var data = this.data; - var geometry = this.geometry; - var geometryComponent; - var height; - var layout; - var mesh = this.mesh; - var textRenderWidth; - var textScale; - var width; - var x; - var y; - if (!mesh || !geometry.layout) { - return; - } - - // Determine width to use (defined width, geometry's width, or default width). - geometryComponent = el.getAttribute('geometry'); - width = data.width || geometryComponent && geometryComponent.width || DEFAULT_WIDTH; - - // Determine wrap pixel count. Either specified or by experimental fudge factor. - // Note that experimental factor will never be correct for variable width fonts. - textRenderWidth = computeWidth(data.wrapPixels, data.wrapCount, this.currentFont.widthFactor); - textScale = width / textRenderWidth; - - // Determine height to use. - layout = geometry.layout; - height = textScale * (layout.height + layout.descender); - - // Update geometry dimensions to match text layout if width and height are set to 0. - // For example, scales a plane to fit text. - if (geometryComponent && geometryComponent.primitive === 'plane') { - if (!this.explicitGeoDimensionsChecked) { - this.explicitGeoDimensionsChecked = true; - this.hasExplicitGeoWidth = !!geometryComponent.width; - this.hasExplicitGeoHeight = !!geometryComponent.height; - } - if (!this.hasExplicitGeoWidth) { - el.setAttribute('geometry', 'width', width); - } - if (!this.hasExplicitGeoHeight) { - el.setAttribute('geometry', 'height', height); - } - } - - // Calculate X position to anchor text left, center, or right. - anchor = data.anchor === 'align' ? data.align : data.anchor; - if (anchor === 'left') { - x = 0; - } else if (anchor === 'right') { - x = -1 * layout.width; - } else if (anchor === 'center') { - x = -1 * layout.width / 2; - } else { - throw new TypeError('Invalid text.anchor property value', anchor); - } - - // Calculate Y position to anchor text top, center, or bottom. - baseline = data.baseline; - if (baseline === 'bottom') { - y = 0; - } else if (baseline === 'top') { - y = -1 * layout.height + layout.ascender; - } else if (baseline === 'center') { - y = -1 * layout.height / 2; - } else { - throw new TypeError('Invalid text.baseline property value', baseline); - } - - // Position and scale mesh to apply layout. - mesh.position.x = x * textScale + data.xOffset; - mesh.position.y = y * textScale; - // Place text slightly in front to avoid Z-fighting. - mesh.position.z = data.zOffset; - mesh.scale.set(textScale, -1 * textScale, textScale); - }, - /** - * Grab font from the constant. - * Set as a method for test stubbing purposes. - */ - lookupFont: function (key) { - return FONTS[key]; - }, - /** - * Update the text geometry using `three-bmfont-text.update`. - */ - updateGeometry: function () { - var geometryUpdateBase = {}; - var geometryUpdateData = {}; - var newLineRegex = /\\n/g; - var tabRegex = /\\t/g; - return function (geometry, font) { - var data = this.data; - geometryUpdateData.font = font; - geometryUpdateData.lineHeight = data.lineHeight && isFinite(data.lineHeight) ? data.lineHeight : font.common.lineHeight; - geometryUpdateData.text = data.value.toString().replace(newLineRegex, '\n').replace(tabRegex, '\t'); - geometryUpdateData.width = computeWidth(data.wrapPixels, data.wrapCount, font.widthFactor); - geometry.update(utils.extend(geometryUpdateBase, data, geometryUpdateData)); - }; - }() -}); - -/** - * Due to using negative scale, we return the opposite side specified. - * https://github.com/mrdoob/three.js/pull/12787/ - */ -function parseSide(side) { - switch (side) { - case 'back': - { - return THREE.FrontSide; - } - case 'double': - { - return THREE.DoubleSide; - } - default: - { - return THREE.BackSide; - } - } -} - -/** - * @returns {Promise} - */ -function loadFont(src, yOffset) { - return new Promise(function (resolve, reject) { - loadBMFont(src, function (err, font) { - if (err) { - error('Error loading font', src); - reject(err); - return; - } - - // Fix negative Y offsets for Roboto MSDF font from tool. Experimentally determined. - if (src.indexOf('/Roboto-msdf.json') >= 0) { - yOffset = 30; - } - if (yOffset) { - font.chars.map(function doOffset(ch) { - ch.yoffset += yOffset; - }); - } - resolve(font); - }); - }); -} - -/** - * @returns {Promise} - */ -function loadTexture(src) { - return new Promise(function (resolve, reject) { - new THREE.ImageLoader().load(src, function (image) { - resolve(image); - }, undefined, function () { - error('Error loading font image', src); - reject(null); - }); - }); -} -function createShader(el, shaderName, data) { - var shader; - var shaderObject; - - // Set up Shader. - shaderObject = new shaders[shaderName].Shader(); - shaderObject.el = el; - shaderObject.init(data); - shaderObject.update(data); - - // Get material. - shader = shaderObject.material; - // Apparently, was not set on `init` nor `update`. - shader.transparent = data.transparent; - return { - material: shader, - shader: shaderObject - }; -} - -/** - * Determine wrap pixel count. Either specified or by experimental fudge factor. - * Note that experimental factor will never be correct for variable width fonts. - */ -function computeWidth(wrapPixels, wrapCount, widthFactor) { - return wrapPixels || (0.5 + wrapCount) * widthFactor; -} - -/** - * Compute default font width factor to use. - */ -function computeFontWidthFactor(font) { - var sum = 0; - var digitsum = 0; - var digits = 0; - font.chars.map(function (ch) { - sum += ch.xadvance; - if (ch.id >= 48 && ch.id <= 57) { - digits++; - digitsum += ch.xadvance; - } - }); - return digits ? digitsum / digits : sum / font.chars.length; -} - -/** - * Get or create a promise given a key and promise generator. - * @todo Move to a utility and use in other parts of A-Frame. - */ -function PromiseCache() { - var cache = this.cache = {}; - this.get = function (key, promiseGenerator) { - if (key in cache) { - return cache[key]; - } - cache[key] = promiseGenerator(); - return cache[key]; - }; -} - -/***/ }), - -/***/ "./src/components/tracked-controls-webvr.js": -/*!**************************************************!*\ - !*** ./src/components/tracked-controls-webvr.js ***! - \**************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var controllerUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var DEFAULT_CAMERA_HEIGHT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").DEFAULT_CAMERA_HEIGHT); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var DEFAULT_HANDEDNESS = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").DEFAULT_HANDEDNESS); -// Vector from eyes to elbow (divided by user height). -var EYES_TO_ELBOW = { - x: 0.175, - y: -0.3, - z: -0.03 -}; -// Vector from eyes to elbow (divided by user height). -var FOREARM = { - x: 0, - y: 0, - z: -0.175 -}; - -// Due to unfortunate name collision, add empty touches array to avoid Daydream error. -var EMPTY_DAYDREAM_TOUCHES = { - touches: [] -}; -var EVENTS = { - AXISMOVE: 'axismove', - BUTTONCHANGED: 'buttonchanged', - BUTTONDOWN: 'buttondown', - BUTTONUP: 'buttonup', - TOUCHSTART: 'touchstart', - TOUCHEND: 'touchend' -}; - -/** - * Tracked controls component. - * Wrap the gamepad API for pose and button states. - * Select the appropriate controller and apply pose to the entity. - * Observe button states and emit appropriate events. - * - * @property {number} controller - Index of controller in array returned by Gamepad API. - * Only used if hand property is not set. - * @property {string} id - Selected controller among those returned by Gamepad API. - * @property {number} hand - If multiple controllers found with id, choose the one with the - * given value for hand. If set, we ignore 'controller' property - */ -module.exports.Component = registerComponent('tracked-controls-webvr', { - schema: { - autoHide: { - default: true - }, - controller: { - default: 0 - }, - id: { - type: 'string', - default: '' - }, - hand: { - type: 'string', - default: '' - }, - idPrefix: { - type: 'string', - default: '' - }, - orientationOffset: { - type: 'vec3' - }, - // Arm model parameters when not 6DoF. - armModel: { - default: false - }, - headElement: { - type: 'selector' - } - }, - init: function () { - // Copy variables back to tracked-controls for backwards compatibility. - // Some 3rd components rely on them. - this.axis = this.el.components['tracked-controls'].axis = [0, 0, 0]; - this.buttonStates = this.el.components['tracked-controls'].buttonStates = {}; - this.changedAxes = []; - this.targetControllerNumber = this.data.controller; - this.axisMoveEventDetail = { - axis: this.axis, - changed: this.changedAxes - }; - this.deltaControllerPosition = new THREE.Vector3(); - this.controllerQuaternion = new THREE.Quaternion(); - this.controllerEuler = new THREE.Euler(); - this.updateGamepad(); - this.buttonEventDetails = {}; - }, - tick: function (time, delta) { - var mesh = this.el.getObject3D('mesh'); - // Update mesh animations. - if (mesh && mesh.update) { - mesh.update(delta / 1000); - } - this.updateGamepad(); - this.updatePose(); - this.updateButtons(); - }, - /** - * Return default user height to use for non-6DOF arm model. - */ - defaultUserHeight: function () { - return DEFAULT_CAMERA_HEIGHT; - }, - /** - * Return head element to use for non-6DOF arm model. - */ - getHeadElement: function () { - return this.data.headElement || this.el.sceneEl.camera.el; - }, - /** - * Handle update controller match criteria (such as `id`, `idPrefix`, `hand`, `controller`) - */ - updateGamepad: function () { - var data = this.data; - var controller = controllerUtils.findMatchingControllerWebVR(this.system.controllers, data.id, data.idPrefix, data.hand, data.controller); - this.controller = controller; - // Legacy handle to the controller for old components. - this.el.components['tracked-controls'].controller = controller; - if (this.data.autoHide) { - this.el.object3D.visible = !!this.controller; - } - }, - /** - * Applies an artificial arm model to simulate elbow to wrist positioning - * based on the orientation of the controller. - * - * @param {object} controllerPosition - Existing vector to update with controller position. - */ - applyArmModel: function (controllerPosition) { - // Use controllerPosition and deltaControllerPosition to avoid creating variables. - var controller = this.controller; - var controllerEuler = this.controllerEuler; - var controllerQuaternion = this.controllerQuaternion; - var deltaControllerPosition = this.deltaControllerPosition; - var hand; - var headEl; - var headObject3D; - var pose; - var userHeight; - headEl = this.getHeadElement(); - headObject3D = headEl.object3D; - userHeight = this.defaultUserHeight(); - pose = controller.pose; - hand = (controller ? controller.hand : undefined) || DEFAULT_HANDEDNESS; - - // Use camera position as head position. - controllerPosition.copy(headObject3D.position); - // Set offset for degenerate "arm model" to elbow. - deltaControllerPosition.set(EYES_TO_ELBOW.x * (hand === 'left' ? -1 : hand === 'right' ? 1 : 0), EYES_TO_ELBOW.y, - // Lower than our eyes. - EYES_TO_ELBOW.z); // Slightly out in front. - // Scale offset by user height. - deltaControllerPosition.multiplyScalar(userHeight); - // Apply camera Y rotation (not X or Z, so you can look down at your hand). - deltaControllerPosition.applyAxisAngle(headObject3D.up, headObject3D.rotation.y); - // Apply rotated offset to position. - controllerPosition.add(deltaControllerPosition); - - // Set offset for degenerate "arm model" forearm. Forearm sticking out from elbow. - deltaControllerPosition.set(FOREARM.x, FOREARM.y, FOREARM.z); - // Scale offset by user height. - deltaControllerPosition.multiplyScalar(userHeight); - // Apply controller X/Y rotation (tilting up/down/left/right is usually moving the arm). - if (pose.orientation) { - controllerQuaternion.fromArray(pose.orientation); - } else { - controllerQuaternion.copy(headObject3D.quaternion); - } - controllerEuler.setFromQuaternion(controllerQuaternion); - controllerEuler.set(controllerEuler.x, controllerEuler.y, 0); - deltaControllerPosition.applyEuler(controllerEuler); - // Apply rotated offset to position. - controllerPosition.add(deltaControllerPosition); - }, - /** - * Read pose from controller (from Gamepad API), apply transforms, apply to entity. - */ - updatePose: function () { - var controller = this.controller; - var data = this.data; - var object3D = this.el.object3D; - var pose; - var vrDisplay = this.system.vrDisplay; - var standingMatrix; - if (!controller) { - return; - } - - // Compose pose from Gamepad. - pose = controller.pose; - if (pose.position) { - object3D.position.fromArray(pose.position); - } else { - // Controller not 6DOF, apply arm model. - if (data.armModel) { - this.applyArmModel(object3D.position); - } - } - if (pose.orientation) { - object3D.quaternion.fromArray(pose.orientation); - } - - // Apply transforms, if 6DOF and in VR. - if (vrDisplay && pose.position) { - standingMatrix = this.el.sceneEl.renderer.xr.getStandingMatrix(); - object3D.matrix.compose(object3D.position, object3D.quaternion, object3D.scale); - object3D.matrix.multiplyMatrices(standingMatrix, object3D.matrix); - object3D.matrix.decompose(object3D.position, object3D.quaternion, object3D.scale); - } - object3D.rotateX(this.data.orientationOffset.x * THREE.MathUtils.DEG2RAD); - object3D.rotateY(this.data.orientationOffset.y * THREE.MathUtils.DEG2RAD); - object3D.rotateZ(this.data.orientationOffset.z * THREE.MathUtils.DEG2RAD); - }, - /** - * Handle button changes including axes, presses, touches, values. - */ - updateButtons: function () { - var buttonState; - var controller = this.controller; - var id; - if (!controller) { - return; - } - - // Check every button. - for (id = 0; id < controller.buttons.length; ++id) { - // Initialize button state. - if (!this.buttonStates[id]) { - this.buttonStates[id] = { - pressed: false, - touched: false, - value: 0 - }; - } - if (!this.buttonEventDetails[id]) { - this.buttonEventDetails[id] = { - id: id, - state: this.buttonStates[id] - }; - } - buttonState = controller.buttons[id]; - this.handleButton(id, buttonState); - } - // Check axes. - this.handleAxes(); - }, - /** - * Handle presses and touches for a single button. - * - * @param {number} id - Index of button in Gamepad button array. - * @param {number} buttonState - Value of button state from 0 to 1. - * @returns {boolean} Whether button has changed in any way. - */ - handleButton: function (id, buttonState) { - var changed; - changed = this.handlePress(id, buttonState) | this.handleTouch(id, buttonState) | this.handleValue(id, buttonState); - if (!changed) { - return false; - } - this.el.emit(EVENTS.BUTTONCHANGED, this.buttonEventDetails[id], false); - return true; - }, - /** - * An axis is an array of values from -1 (up, left) to 1 (down, right). - * Compare each component of the axis to the previous value to determine change. - * - * @returns {boolean} Whether axes changed. - */ - handleAxes: function () { - var changed = false; - var controllerAxes = this.controller.axes; - var i; - var previousAxis = this.axis; - var changedAxes = this.changedAxes; - - // Check if axis changed. - this.changedAxes.splice(0, this.changedAxes.length); - for (i = 0; i < controllerAxes.length; ++i) { - changedAxes.push(previousAxis[i] !== controllerAxes[i]); - if (changedAxes[i]) { - changed = true; - } - } - if (!changed) { - return false; - } - this.axis.splice(0, this.axis.length); - for (i = 0; i < controllerAxes.length; i++) { - this.axis.push(controllerAxes[i]); - } - this.el.emit(EVENTS.AXISMOVE, this.axisMoveEventDetail, false); - return true; - }, - /** - * Determine whether a button press has occurred and emit events as appropriate. - * - * @param {string} id - ID of the button to check. - * @param {object} buttonState - State of the button to check. - * @returns {boolean} Whether button press state changed. - */ - handlePress: function (id, buttonState) { - var evtName; - var previousButtonState = this.buttonStates[id]; - - // Not changed. - if (buttonState.pressed === previousButtonState.pressed) { - return false; - } - evtName = buttonState.pressed ? EVENTS.BUTTONDOWN : EVENTS.BUTTONUP; - this.el.emit(evtName, this.buttonEventDetails[id], false); - previousButtonState.pressed = buttonState.pressed; - return true; - }, - /** - * Determine whether a button touch has occurred and emit events as appropriate. - * - * @param {string} id - ID of the button to check. - * @param {object} buttonState - State of the button to check. - * @returns {boolean} Whether button touch state changed. - */ - handleTouch: function (id, buttonState) { - var evtName; - var previousButtonState = this.buttonStates[id]; - - // Not changed. - if (buttonState.touched === previousButtonState.touched) { - return false; - } - evtName = buttonState.touched ? EVENTS.TOUCHSTART : EVENTS.TOUCHEND; - this.el.emit(evtName, this.buttonEventDetails[id], false, EMPTY_DAYDREAM_TOUCHES); - previousButtonState.touched = buttonState.touched; - return true; - }, - /** - * Determine whether a button value has changed. - * - * @param {string} id - Id of the button to check. - * @param {object} buttonState - State of the button to check. - * @returns {boolean} Whether button value changed. - */ - handleValue: function (id, buttonState) { - var previousButtonState = this.buttonStates[id]; - - // Not changed. - if (buttonState.value === previousButtonState.value) { - return false; - } - previousButtonState.value = buttonState.value; - return true; - } -}); - -/***/ }), - -/***/ "./src/components/tracked-controls-webxr.js": -/*!**************************************************!*\ - !*** ./src/components/tracked-controls-webxr.js ***! - \**************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var controllerUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var EVENTS = { - AXISMOVE: 'axismove', - BUTTONCHANGED: 'buttonchanged', - BUTTONDOWN: 'buttondown', - BUTTONUP: 'buttonup', - TOUCHSTART: 'touchstart', - TOUCHEND: 'touchend' -}; -module.exports.Component = registerComponent('tracked-controls-webxr', { - schema: { - id: { - type: 'string', - default: '' - }, - hand: { - type: 'string', - default: '' - }, - handTrackingEnabled: { - default: false - }, - index: { - type: 'int', - default: -1 - }, - iterateControllerProfiles: { - default: false - }, - space: { - type: 'string', - oneOf: ['targetRaySpace', 'gripSpace'], - default: 'gripSpace' - } - }, - init: function () { - this.updateController = this.updateController.bind(this); - this.buttonEventDetails = {}; - this.buttonStates = this.el.components['tracked-controls'].buttonStates = {}; - this.axis = this.el.components['tracked-controls'].axis = [0, 0, 0]; - this.changedAxes = []; - this.axisMoveEventDetail = { - axis: this.axis, - changed: this.changedAxes - }; - }, - update: function () { - this.updateController(); - }, - play: function () { - var sceneEl = this.el.sceneEl; - this.updateController(); - sceneEl.addEventListener('controllersupdated', this.updateController); - }, - pause: function () { - var sceneEl = this.el.sceneEl; - sceneEl.removeEventListener('controllersupdated', this.updateController); - }, - isControllerPresent: function (evt) { - if (!this.controller || this.controller.gamepad) { - return false; - } - if (evt.inputSource.handedness !== 'none' && evt.inputSource.handedness !== this.data.hand) { - return false; - } - return true; - }, - /** - * Handle update controller match criteria (such as `id`, `idPrefix`, `hand`, `controller`) - */ - updateController: function () { - this.controller = controllerUtils.findMatchingControllerWebXR(this.system.controllers, this.data.id, this.data.hand, this.data.index, this.data.iterateControllerProfiles, this.data.handTrackingEnabled); - // Legacy handle to the controller for old components. - this.el.components['tracked-controls'].controller = this.controller; - if (this.data.autoHide) { - this.el.object3D.visible = !!this.controller; - } - }, - tick: function () { - var sceneEl = this.el.sceneEl; - var controller = this.controller; - var frame = sceneEl.frame; - if (!controller || !sceneEl.frame || !this.system.referenceSpace) { - return; - } - if (!controller.hand) { - this.pose = frame.getPose(controller[this.data.space], this.system.referenceSpace); - this.updatePose(); - this.updateButtons(); - } - }, - updatePose: function () { - var object3D = this.el.object3D; - var pose = this.pose; - if (!pose) { - return; - } - object3D.matrix.elements = pose.transform.matrix; - object3D.matrix.decompose(object3D.position, object3D.rotation, object3D.scale); - }, - /** - * Handle button changes including axes, presses, touches, values. - */ - updateButtons: function () { - var buttonState; - var id; - var controller = this.controller; - var gamepad; - if (!controller || !controller.gamepad) { - return; - } - gamepad = controller.gamepad; - // Check every button. - for (id = 0; id < gamepad.buttons.length; ++id) { - // Initialize button state. - if (!this.buttonStates[id]) { - this.buttonStates[id] = { - pressed: false, - touched: false, - value: 0 - }; - } - if (!this.buttonEventDetails[id]) { - this.buttonEventDetails[id] = { - id: id, - state: this.buttonStates[id] - }; - } - buttonState = gamepad.buttons[id]; - this.handleButton(id, buttonState); - } - // Check axes. - this.handleAxes(); - }, - /** - * Handle presses and touches for a single button. - * - * @param {number} id - Index of button in Gamepad button array. - * @param {number} buttonState - Value of button state from 0 to 1. - * @returns {boolean} Whether button has changed in any way. - */ - handleButton: function (id, buttonState) { - var changed; - changed = this.handlePress(id, buttonState) | this.handleTouch(id, buttonState) | this.handleValue(id, buttonState); - if (!changed) { - return false; - } - this.el.emit(EVENTS.BUTTONCHANGED, this.buttonEventDetails[id], false); - return true; - }, - /** - * An axis is an array of values from -1 (up, left) to 1 (down, right). - * Compare each component of the axis to the previous value to determine change. - * - * @returns {boolean} Whether axes changed. - */ - handleAxes: function () { - var changed = false; - var controllerAxes = this.controller.gamepad.axes; - var i; - var previousAxis = this.axis; - var changedAxes = this.changedAxes; - - // Check if axis changed. - this.changedAxes.splice(0, this.changedAxes.length); - for (i = 0; i < controllerAxes.length; ++i) { - changedAxes.push(previousAxis[i] !== controllerAxes[i]); - if (changedAxes[i]) { - changed = true; - } - } - if (!changed) { - return false; - } - this.axis.splice(0, this.axis.length); - for (i = 0; i < controllerAxes.length; i++) { - this.axis.push(controllerAxes[i]); - } - this.el.emit(EVENTS.AXISMOVE, this.axisMoveEventDetail, false); - return true; - }, - /** - * Determine whether a button press has occurred and emit events as appropriate. - * - * @param {string} id - ID of the button to check. - * @param {object} buttonState - State of the button to check. - * @returns {boolean} Whether button press state changed. - */ - handlePress: function (id, buttonState) { - var evtName; - var previousButtonState = this.buttonStates[id]; - - // Not changed. - if (buttonState.pressed === previousButtonState.pressed) { - return false; - } - evtName = buttonState.pressed ? EVENTS.BUTTONDOWN : EVENTS.BUTTONUP; - this.el.emit(evtName, this.buttonEventDetails[id], false); - previousButtonState.pressed = buttonState.pressed; - return true; - }, - /** - * Determine whether a button touch has occurred and emit events as appropriate. - * - * @param {string} id - ID of the button to check. - * @param {object} buttonState - State of the button to check. - * @returns {boolean} Whether button touch state changed. - */ - handleTouch: function (id, buttonState) { - var evtName; - var previousButtonState = this.buttonStates[id]; - - // Not changed. - if (buttonState.touched === previousButtonState.touched) { - return false; - } - evtName = buttonState.touched ? EVENTS.TOUCHSTART : EVENTS.TOUCHEND; - this.el.emit(evtName, this.buttonEventDetails[id], false); - previousButtonState.touched = buttonState.touched; - return true; - }, - /** - * Determine whether a button value has changed. - * - * @param {string} id - Id of the button to check. - * @param {object} buttonState - State of the button to check. - * @returns {boolean} Whether button value changed. - */ - handleValue: function (id, buttonState) { - var previousButtonState = this.buttonStates[id]; - - // Not changed. - if (buttonState.value === previousButtonState.value) { - return false; - } - previousButtonState.value = buttonState.value; - return true; - } -}); - -/***/ }), - -/***/ "./src/components/tracked-controls.js": -/*!********************************************!*\ - !*** ./src/components/tracked-controls.js ***! - \********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); - -/** - * Tracked controls. - * Abstract controls that decide if the WebVR or WebXR version is going to be applied. - * - * @property {number} controller - Index of controller in array returned by Gamepad API. - * Only used if hand property is not set. - * @property {string} id - Selected controller among those returned by Gamepad API. - * @property {number} hand - If multiple controllers found with id, choose the one with the - * given value for hand. If set, we ignore 'controller' property - */ -module.exports.Component = registerComponent('tracked-controls', { - schema: { - autoHide: { - default: true - }, - controller: { - default: -1 - }, - id: { - type: 'string', - default: '' - }, - hand: { - type: 'string', - default: '' - }, - idPrefix: { - type: 'string', - default: '' - }, - handTrackingEnabled: { - default: false - }, - orientationOffset: { - type: 'vec3' - }, - // Arm model parameters when not 6DoF. - armModel: { - default: false - }, - headElement: { - type: 'selector' - }, - iterateControllerProfiles: { - default: false - }, - space: { - type: 'string', - oneOf: ['targetRaySpace', 'gripSpace'], - default: 'targetRaySpace' - } - }, - // Run after both tracked-controls-webvr and tracked-controls-webxr to allow other components - // to be after either without having to list them both. - after: ['tracked-controls-webvr', 'tracked-controls-webxr'], - update: function () { - var data = this.data; - var el = this.el; - if (el.sceneEl.hasWebXR) { - el.setAttribute('tracked-controls-webxr', { - id: data.id, - hand: data.hand, - index: data.controller, - iterateControllerProfiles: data.iterateControllerProfiles, - handTrackingEnabled: data.handTrackingEnabled, - space: data.space - }); - } else { - el.setAttribute('tracked-controls-webvr', data); - } - } -}); - -/***/ }), - -/***/ "./src/components/valve-index-controls.js": -/*!************************************************!*\ - !*** ./src/components/valve-index-controls.js ***! - \************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var INDEX_CONTROLLER_MODEL_BASE_URL = AFRAME_CDN_ROOT + 'controllers/valve/index/valve-index-'; -var INDEX_CONTROLLER_MODEL_URL = { - left: INDEX_CONTROLLER_MODEL_BASE_URL + 'left.glb', - right: INDEX_CONTROLLER_MODEL_BASE_URL + 'right.glb' -}; -var GAMEPAD_ID_PREFIX = 'valve'; -var isWebXRAvailable = (__webpack_require__(/*! ../utils/ */ "./src/utils/index.js").device.isWebXRAvailable); -var INDEX_CONTROLLER_POSITION_OFFSET_WEBVR = { - left: { - x: -0.00023692678902063457, - y: 0.04724540367838371, - z: -0.061959880395271096 - }, - right: { - x: 0.002471558599671131, - y: 0.055765208987076195, - z: -0.061068168708348844 - } -}; -var INDEX_CONTROLLER_POSITION_OFFSET_WEBXR = { - left: { - x: 0, - y: -0.05, - z: 0.06 - }, - right: { - x: 0, - y: -0.05, - z: 0.06 - } -}; -var INDEX_CONTROLLER_ROTATION_OFFSET_WEBVR = { - left: { - _x: 0.692295102620542, - _y: -0.0627618864318427, - _z: -0.06265893149611756, - _order: 'XYZ' - }, - right: { - _x: 0.6484021229942998, - _y: -0.032563619881892894, - _z: -0.1327973171917482, - _order: 'XYZ' - } -}; -var INDEX_CONTROLLER_ROTATION_OFFSET_WEBXR = { - left: { - _x: Math.PI / 3, - _y: 0, - _z: 0, - _order: 'XYZ' - }, - right: { - _x: Math.PI / 3, - _y: 0, - _z: 0, - _order: 'XYZ' - } -}; -var INDEX_CONTROLLER_ROTATION_OFFSET = isWebXRAvailable ? INDEX_CONTROLLER_ROTATION_OFFSET_WEBXR : INDEX_CONTROLLER_ROTATION_OFFSET_WEBVR; -var INDEX_CONTROLLER_POSITION_OFFSET = isWebXRAvailable ? INDEX_CONTROLLER_POSITION_OFFSET_WEBXR : INDEX_CONTROLLER_POSITION_OFFSET_WEBVR; -/** - * Vive controls. - * Interface with Vive controllers and map Gamepad events to controller buttons: - * trackpad, trigger, grip, menu, system - * Load a controller model and highlight the pressed buttons. - */ -module.exports.Component = registerComponent('valve-index-controls', { - schema: { - hand: { - default: 'left' - }, - buttonColor: { - type: 'color', - default: '#FAFAFA' - }, - // Off-white. - buttonHighlightColor: { - type: 'color', - default: '#22D1EE' - }, - // Light blue. - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - } - }, - after: ['tracked-controls'], - mapping: { - axes: { - trackpad: [0, 1], - thumbstick: [2, 3] - }, - buttons: ['trigger', 'grip', 'trackpad', 'thumbstick', 'abutton'] - }, - init: function () { - var self = this; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.previousButtonValues = {}; - this.bindMethods(); - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('model-loaded', this.onModelLoaded); - el.addEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('model-loaded', this.onModelLoaded); - el.removeEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = false; - }, - /** - * Once OpenVR returns correct hand data in supporting browsers, we can use hand property. - * var isPresent = checkControllerPresentAndSetup(this.el.sceneEl, GAMEPAD_ID_PREFIX, - { hand: data.hand }); - * Until then, use hardcoded index. - */ - checkIfControllerPresent: function () { - var data = this.data; - var controllerIndex = data.hand === 'right' ? 0 : data.hand === 'left' ? 1 : 2; - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, { - index: controllerIndex, - iterateControllerProfiles: true, - hand: data.hand - }); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - - // If we have an OpenVR Gamepad, use the fixed mapping. - el.setAttribute('tracked-controls', { - idPrefix: GAMEPAD_ID_PREFIX, - // Hand IDs: 1 = right, 0 = left, 2 = anything else. - controller: data.hand === 'right' ? 1 : data.hand === 'left' ? 0 : 2, - hand: data.hand, - orientationOffset: data.orientationOffset - }); - this.loadModel(); - }, - loadModel: function () { - var data = this.data; - if (!data.model) { - return; - } - this.el.setAttribute('gltf-model', '' + INDEX_CONTROLLER_MODEL_URL[data.hand] + ''); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - this.checkIfControllerPresent(); - }, - /** - * Rotate the trigger button based on how hard the trigger is pressed. - */ - onButtonChanged: function (evt) { - var button = this.mapping.buttons[evt.detail.id]; - var buttonMeshes = this.buttonMeshes; - var analogValue; - if (!button) { - return; - } - if (button === 'trigger') { - analogValue = evt.detail.state.value; - // Update trigger rotation depending on button value. - if (buttonMeshes && buttonMeshes.trigger) { - buttonMeshes.trigger.rotation.x = this.triggerOriginalRotationX - analogValue * (Math.PI / 40); - } - } - - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onModelLoaded: function (evt) { - var buttonMeshes; - var controllerObject3D = evt.detail.model; - var self = this; - if (evt.target !== this.el || !this.data.model) { - return; - } - - // Store button meshes object to be able to change their colors. - buttonMeshes = this.buttonMeshes = {}; - buttonMeshes.grip = { - left: controllerObject3D.getObjectByName('leftgrip'), - right: controllerObject3D.getObjectByName('rightgrip') - }; - buttonMeshes.menu = controllerObject3D.getObjectByName('menubutton'); - buttonMeshes.system = controllerObject3D.getObjectByName('systembutton'); - buttonMeshes.trackpad = controllerObject3D.getObjectByName('touchpad'); - buttonMeshes.trigger = controllerObject3D.getObjectByName('trigger'); - this.triggerOriginalRotationX = buttonMeshes.trigger.rotation.x; - - // Set default colors. - Object.keys(buttonMeshes).forEach(function (buttonName) { - self.setButtonColor(buttonName, self.data.buttonColor); - }); - - // Offset pivot point. - controllerObject3D.position.copy(INDEX_CONTROLLER_POSITION_OFFSET[this.data.hand]); - controllerObject3D.rotation.copy(INDEX_CONTROLLER_ROTATION_OFFSET[this.data.hand]); - this.el.emit('controllermodelready', { - name: 'valve-index-controls', - model: this.data.model, - rayOrigin: new THREE.Vector3(0, 0, 0) - }); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - updateModel: function (buttonName, evtName) { - var color; - var isTouch; - if (!this.data.model) { - return; - } - isTouch = evtName.indexOf('touch') !== -1; - // Don't change color for trackpad touch. - if (isTouch) { - return; - } - - // Update colors. - color = evtName === 'up' ? this.data.buttonColor : this.data.buttonHighlightColor; - this.setButtonColor(buttonName, color); - }, - setButtonColor: function (buttonName, color) { - // TODO: The meshes aren't set up correctly now, skipping for the moment - return; - } -}); - -/***/ }), - -/***/ "./src/components/visible.js": -/*!***********************************!*\ - !*** ./src/components/visible.js ***! - \***********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); - -/** - * Visibility component. - */ -module.exports.Component = registerComponent('visible', { - schema: { - default: true - }, - update: function () { - this.el.object3D.visible = this.data; - } -}); - -/***/ }), - -/***/ "./src/components/vive-controls.js": -/*!*****************************************!*\ - !*** ./src/components/vive-controls.js ***! - \*****************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var VIVE_CONTROLLER_MODEL_OBJ_URL = AFRAME_CDN_ROOT + 'controllers/vive/vr_controller_vive.obj'; -var VIVE_CONTROLLER_MODEL_OBJ_MTL = AFRAME_CDN_ROOT + 'controllers/vive/vr_controller_vive.mtl'; -var isWebXRAvailable = (__webpack_require__(/*! ../utils/ */ "./src/utils/index.js").device.isWebXRAvailable); -var GAMEPAD_ID_WEBXR = 'htc-vive'; -var GAMEPAD_ID_WEBVR = 'OpenVR '; - -// Prefix for HTC Vive Controllers. -var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR; - -/** - * Button IDs: - * 0 - trackpad - * 1 - trigger (intensity value from 0.5 to 1) - * 2 - grip - * 3 - menu (dispatch but better for menu options) - * 4 - system (never dispatched on this layer) - */ -var INPUT_MAPPING_WEBVR = { - axes: { - trackpad: [0, 1] - }, - buttons: ['trackpad', 'trigger', 'grip', 'menu', 'system'] -}; - -/** - * Button IDs: - * 0 - trigger - * 1 - squeeze - * 2 - touchpad - * 3 - none (dispatch but better for menu options) - * 4 - menu (never dispatched on this layer) - * - * Axis: - * 0 - touchpad x axis - * 1 - touchpad y axis - * Reference: https://github.com/immersive-web/webxr-input-profiles/blob/master/packages/registry/profiles/htc/htc-vive.json - */ -var INPUT_MAPPING_WEBXR = { - axes: { - touchpad: [0, 1] - }, - buttons: ['trigger', 'grip', 'touchpad', 'none'] -}; -var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR; - -/** - * Vive controls. - * Interface with Vive controllers and map Gamepad events to controller buttons: - * touchpad, trigger, grip, menu, system - * Load a controller model and highlight the pressed buttons. - */ -module.exports.Component = registerComponent('vive-controls', { - schema: { - hand: { - default: 'left' - }, - buttonColor: { - type: 'color', - default: '#FAFAFA' - }, - // Off-white. - buttonHighlightColor: { - type: 'color', - default: '#22D1EE' - }, - // Light blue. - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - } - }, - after: ['tracked-controls'], - mapping: INPUT_MAPPING, - init: function () { - var self = this; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.previousButtonValues = {}; - this.bindMethods(); - }, - update: function () { - var data = this.data; - this.controllerIndex = data.hand === 'right' ? 0 : data.hand === 'left' ? 1 : 2; - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('model-loaded', this.onModelLoaded); - el.addEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('model-loaded', this.onModelLoaded); - el.removeEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = false; - }, - /** - * Once OpenVR returns correct hand data in supporting browsers, we can use hand property. - * var isPresent = checkControllerPresentAndSetup(this.el.sceneEl, GAMEPAD_ID_PREFIX, - { hand: data.hand }); - * Until then, use hardcoded index. - */ - checkIfControllerPresent: function () { - var data = this.data; - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, { - index: this.controllerIndex, - hand: data.hand - }); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - - // If we have an OpenVR Gamepad, use the fixed mapping. - el.setAttribute('tracked-controls', { - idPrefix: GAMEPAD_ID_PREFIX, - hand: data.hand, - controller: this.controllerIndex, - orientationOffset: data.orientationOffset - }); - - // Load model. - if (!this.data.model) { - return; - } - this.el.setAttribute('obj-model', { - obj: VIVE_CONTROLLER_MODEL_OBJ_URL, - mtl: VIVE_CONTROLLER_MODEL_OBJ_MTL - }); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - this.checkIfControllerPresent(); - }, - /** - * Rotate the trigger button based on how hard the trigger is pressed. - */ - onButtonChanged: function (evt) { - var button = this.mapping.buttons[evt.detail.id]; - var buttonMeshes = this.buttonMeshes; - var analogValue; - if (!button) { - return; - } - if (button === 'trigger') { - analogValue = evt.detail.state.value; - // Update trigger rotation depending on button value. - if (buttonMeshes && buttonMeshes.trigger) { - buttonMeshes.trigger.rotation.x = -analogValue * (Math.PI / 12); - } - } - - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onModelLoaded: function (evt) { - var buttonMeshes; - var controllerObject3D = evt.detail.model; - var self = this; - if (evt.target !== this.el || !this.data.model) { - return; - } - - // Store button meshes object to be able to change their colors. - buttonMeshes = this.buttonMeshes = {}; - buttonMeshes.grip = { - left: controllerObject3D.getObjectByName('leftgrip'), - right: controllerObject3D.getObjectByName('rightgrip') - }; - buttonMeshes.menu = controllerObject3D.getObjectByName('menubutton'); - buttonMeshes.system = controllerObject3D.getObjectByName('systembutton'); - buttonMeshes.trackpad = controllerObject3D.getObjectByName('touchpad'); - buttonMeshes.touchpad = controllerObject3D.getObjectByName('touchpad'); - buttonMeshes.trigger = controllerObject3D.getObjectByName('trigger'); - - // Set default colors. - Object.keys(buttonMeshes).forEach(function (buttonName) { - self.setButtonColor(buttonName, self.data.buttonColor); - }); - - // Offset pivot point. - controllerObject3D.position.set(0, -0.015, 0.04); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - updateModel: function (buttonName, evtName) { - var color; - var isTouch; - if (!this.data.model) { - return; - } - isTouch = evtName.indexOf('touch') !== -1; - // Don't change color for trackpad touch. - if (isTouch) { - return; - } - - // Update colors. - color = evtName === 'up' ? this.data.buttonColor : this.data.buttonHighlightColor; - this.setButtonColor(buttonName, color); - }, - setButtonColor: function (buttonName, color) { - var buttonMeshes = this.buttonMeshes; - if (!buttonMeshes) { - return; - } - - // Need to do both left and right sides for grip. - if (buttonName === 'grip') { - buttonMeshes.grip.left.material.color.set(color); - buttonMeshes.grip.right.material.color.set(color); - return; - } - buttonMeshes[buttonName].material.color.set(color); - } -}); - -/***/ }), - -/***/ "./src/components/vive-focus-controls.js": -/*!***********************************************!*\ - !*** ./src/components/vive-focus-controls.js ***! - \***********************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var VIVE_FOCUS_CONTROLLER_MODEL_URL = AFRAME_CDN_ROOT + 'controllers/vive/focus-controller/focus-controller.gltf'; -var isWebXRAvailable = (__webpack_require__(/*! ../utils/ */ "./src/utils/index.js").device.isWebXRAvailable); -var GAMEPAD_ID_WEBXR = 'htc-vive-focus'; -var GAMEPAD_ID_WEBVR = 'HTC Vive Focus '; - -// Prefix for HTC Vive Focus Controllers. -var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR; - -/** - * Button IDs: - * 0 - trackpad - * 1 - trigger - */ -var INPUT_MAPPING_WEBVR = { - axes: { - trackpad: [0, 1] - }, - buttons: ['trackpad', 'trigger'] -}; - -/** - * Button IDs: - * 0 - trigger - * 2 - touchpad - * 4 - menu - */ -var INPUT_MAPPING_WEBXR = { - axes: { - touchpad: [0, 1] - }, - buttons: ['trigger', 'none', 'touchpad', 'none', 'menu'] -}; -var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR; - -/** - * Vive Focus controls. - * Interface with Vive Focus controller and map Gamepad events to - * controller buttons: trackpad, trigger - * Load a controller model and highlight the pressed buttons. - */ -module.exports.Component = registerComponent('vive-focus-controls', { - schema: { - hand: { - default: '' - }, - // This informs the degenerate arm model. - buttonTouchedColor: { - type: 'color', - default: '#BBBBBB' - }, - buttonHighlightColor: { - type: 'color', - default: '#7A7A7A' - }, - model: { - default: true - }, - orientationOffset: { - type: 'vec3' - }, - armModel: { - default: true - } - }, - after: ['tracked-controls'], - mapping: INPUT_MAPPING, - bindMethods: function () { - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.removeControllersUpdateListener = this.removeControllersUpdateListener.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - init: function () { - var self = this; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.bindMethods(); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('model-loaded', this.onModelLoaded); - el.addEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = true; - this.addControllersUpdateListener(); - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('model-loaded', this.onModelLoaded); - el.removeEventListener('axismove', this.onAxisMoved); - this.controllerEventsActive = false; - this.removeControllersUpdateListener(); - }, - checkIfControllerPresent: function () { - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, this.data.hand ? { - hand: this.data.hand - } : {}); - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - injectTrackedControls: function () { - var el = this.el; - var data = this.data; - el.setAttribute('tracked-controls', { - armModel: data.armModel, - idPrefix: GAMEPAD_ID_PREFIX, - orientationOffset: data.orientationOffset - }); - if (!this.data.model) { - return; - } - this.el.setAttribute('gltf-model', VIVE_FOCUS_CONTROLLER_MODEL_URL); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - this.checkIfControllerPresent(); - }, - onModelLoaded: function (evt) { - var controllerObject3D = evt.detail.model; - var buttonMeshes; - if (evt.target !== this.el || !this.data.model) { - return; - } - buttonMeshes = this.buttonMeshes = {}; - buttonMeshes.trigger = controllerObject3D.getObjectByName('BumperKey'); - buttonMeshes.triggerPressed = controllerObject3D.getObjectByName('BumperKey_Press'); - if (buttonMeshes.triggerPressed) { - buttonMeshes.triggerPressed.visible = false; - } - buttonMeshes.trackpad = controllerObject3D.getObjectByName('TouchPad'); - buttonMeshes.trackpadPressed = controllerObject3D.getObjectByName('TouchPad_Press'); - if (buttonMeshes.trackpadPressed) { - buttonMeshes.trackpadPressed.visible = false; - } - }, - // No analog buttons, only emits 0/1 values - onButtonChanged: function (evt) { - var button = this.mapping.buttons[evt.detail.id]; - if (!button) return; - // Pass along changed event with button state, using button mapping for convenience. - this.el.emit(button + 'changed', evt.detail.state); - }, - onAxisMoved: function (evt) { - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - updateModel: function (buttonName, evtName) { - if (!this.data.model) { - return; - } - this.updateButtonModel(buttonName, evtName); - }, - updateButtonModel: function (buttonName, state) { - var buttonMeshes = this.buttonMeshes; - var pressedName = buttonName + 'Pressed'; - if (!buttonMeshes || !buttonMeshes[buttonName] || !buttonMeshes[pressedName]) { - return; - } - var color; - switch (state) { - case 'down': - color = this.data.buttonHighlightColor; - break; - case 'touchstart': - color = this.data.buttonTouchedColor; - break; - } - if (color) { - buttonMeshes[pressedName].material.color.set(color); - } - buttonMeshes[pressedName].visible = !!color; - buttonMeshes[buttonName].visible = !color; - } -}); - -/***/ }), - -/***/ "./src/components/wasd-controls.js": -/*!*****************************************!*\ - !*** ./src/components/wasd-controls.js ***! - \*****************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var KEYCODE_TO_CODE = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").keyboardevent.KEYCODE_TO_CODE); -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var shouldCaptureKeyEvent = utils.shouldCaptureKeyEvent; -var CLAMP_VELOCITY = 0.00001; -var MAX_DELTA = 0.2; -var KEYS = ['KeyW', 'KeyA', 'KeyS', 'KeyD', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'ArrowDown']; - -/** - * WASD component to control entities using WASD keys. - */ -module.exports.Component = registerComponent('wasd-controls', { - schema: { - acceleration: { - default: 65 - }, - adAxis: { - default: 'x', - oneOf: ['x', 'y', 'z'] - }, - adEnabled: { - default: true - }, - adInverted: { - default: false - }, - enabled: { - default: true - }, - fly: { - default: false - }, - wsAxis: { - default: 'z', - oneOf: ['x', 'y', 'z'] - }, - wsEnabled: { - default: true - }, - wsInverted: { - default: false - } - }, - after: ['look-controls'], - init: function () { - // To keep track of the pressed keys. - this.keys = {}; - this.easing = 1.1; - this.velocity = new THREE.Vector3(); - - // Bind methods and add event listeners. - this.onBlur = this.onBlur.bind(this); - this.onContextMenu = this.onContextMenu.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); - this.onKeyUp = this.onKeyUp.bind(this); - this.onVisibilityChange = this.onVisibilityChange.bind(this); - this.attachVisibilityEventListeners(); - }, - tick: function (time, delta) { - var data = this.data; - var el = this.el; - var velocity = this.velocity; - if (!velocity[data.adAxis] && !velocity[data.wsAxis] && isEmptyObject(this.keys)) { - return; - } - - // Update velocity. - delta = delta / 1000; - this.updateVelocity(delta); - if (!velocity[data.adAxis] && !velocity[data.wsAxis]) { - return; - } - - // Get movement vector and translate position. - el.object3D.position.add(this.getMovementVector(delta)); - }, - update: function (oldData) { - // Reset velocity if axis have changed. - if (oldData.adAxis !== this.data.adAxis) { - this.velocity[oldData.adAxis] = 0; - } - if (oldData.wsAxis !== this.data.wsAxis) { - this.velocity[oldData.wsAxis] = 0; - } - }, - remove: function () { - this.removeKeyEventListeners(); - this.removeVisibilityEventListeners(); - }, - play: function () { - this.attachKeyEventListeners(); - }, - pause: function () { - this.keys = {}; - this.removeKeyEventListeners(); - }, - updateVelocity: function (delta) { - var acceleration; - var adAxis; - var adSign; - var data = this.data; - var keys = this.keys; - var velocity = this.velocity; - var wsAxis; - var wsSign; - adAxis = data.adAxis; - wsAxis = data.wsAxis; - - // If FPS too low, reset velocity. - if (delta > MAX_DELTA) { - velocity[adAxis] = 0; - velocity[wsAxis] = 0; - return; - } - - // https://gamedev.stackexchange.com/questions/151383/frame-rate-independant-movement-with-acceleration - var scaledEasing = Math.pow(1 / this.easing, delta * 60); - // Velocity Easing. - if (velocity[adAxis] !== 0) { - velocity[adAxis] = velocity[adAxis] * scaledEasing; - } - if (velocity[wsAxis] !== 0) { - velocity[wsAxis] = velocity[wsAxis] * scaledEasing; - } - - // Clamp velocity easing. - if (Math.abs(velocity[adAxis]) < CLAMP_VELOCITY) { - velocity[adAxis] = 0; - } - if (Math.abs(velocity[wsAxis]) < CLAMP_VELOCITY) { - velocity[wsAxis] = 0; - } - if (!data.enabled) { - return; - } - - // Update velocity using keys pressed. - acceleration = data.acceleration; - if (data.adEnabled) { - adSign = data.adInverted ? -1 : 1; - if (keys.KeyA || keys.ArrowLeft) { - velocity[adAxis] -= adSign * acceleration * delta; - } - if (keys.KeyD || keys.ArrowRight) { - velocity[adAxis] += adSign * acceleration * delta; - } - } - if (data.wsEnabled) { - wsSign = data.wsInverted ? -1 : 1; - if (keys.KeyW || keys.ArrowUp) { - velocity[wsAxis] -= wsSign * acceleration * delta; - } - if (keys.KeyS || keys.ArrowDown) { - velocity[wsAxis] += wsSign * acceleration * delta; - } - } - }, - getMovementVector: function () { - var directionVector = new THREE.Vector3(0, 0, 0); - var rotationEuler = new THREE.Euler(0, 0, 0, 'YXZ'); - return function (delta) { - var rotation = this.el.getAttribute('rotation'); - var velocity = this.velocity; - var xRotation; - directionVector.copy(velocity); - directionVector.multiplyScalar(delta); - - // Absolute. - if (!rotation) { - return directionVector; - } - xRotation = this.data.fly ? rotation.x : 0; - - // Transform direction relative to heading. - rotationEuler.set(THREE.MathUtils.degToRad(xRotation), THREE.MathUtils.degToRad(rotation.y), 0); - directionVector.applyEuler(rotationEuler); - return directionVector; - }; - }(), - attachVisibilityEventListeners: function () { - window.oncontextmenu = this.onContextMenu; - window.addEventListener('blur', this.onBlur); - window.addEventListener('focus', this.onFocus); - document.addEventListener('visibilitychange', this.onVisibilityChange); - }, - removeVisibilityEventListeners: function () { - window.removeEventListener('blur', this.onBlur); - window.removeEventListener('focus', this.onFocus); - document.removeEventListener('visibilitychange', this.onVisibilityChange); - }, - attachKeyEventListeners: function () { - window.addEventListener('keydown', this.onKeyDown); - window.addEventListener('keyup', this.onKeyUp); - }, - removeKeyEventListeners: function () { - window.removeEventListener('keydown', this.onKeyDown); - window.removeEventListener('keyup', this.onKeyUp); - }, - onContextMenu: function () { - var keys = Object.keys(this.keys); - for (var i = 0; i < keys.length; i++) { - delete this.keys[keys[i]]; - } - }, - onBlur: function () { - this.pause(); - }, - onFocus: function () { - this.play(); - }, - onVisibilityChange: function () { - if (document.hidden) { - this.onBlur(); - } else { - this.onFocus(); - } - }, - onKeyDown: function (event) { - var code; - if (!shouldCaptureKeyEvent(event)) { - return; - } - code = event.code || KEYCODE_TO_CODE[event.keyCode]; - if (KEYS.indexOf(code) !== -1) { - this.keys[code] = true; - } - }, - onKeyUp: function (event) { - var code; - code = event.code || KEYCODE_TO_CODE[event.keyCode]; - delete this.keys[code]; - } -}); -function isEmptyObject(keys) { - var key; - for (key in keys) { - return false; - } - return true; -} - -/***/ }), - -/***/ "./src/components/windows-motion-controls.js": -/*!***************************************************!*\ - !*** ./src/components/windows-motion-controls.js ***! - \***************************************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global THREE */ -var registerComponent = (__webpack_require__(/*! ../core/component */ "./src/core/component.js").registerComponent); -var trackedControlsUtils = __webpack_require__(/*! ../utils/tracked-controls */ "./src/utils/tracked-controls.js"); -var checkControllerPresentAndSetup = trackedControlsUtils.checkControllerPresentAndSetup; -var emitIfAxesChanged = trackedControlsUtils.emitIfAxesChanged; -var onButtonEvent = trackedControlsUtils.onButtonEvent; -var utils = __webpack_require__(/*! ../utils/ */ "./src/utils/index.js"); -var debug = utils.debug('components:windows-motion-controls:debug'); -var warn = utils.debug('components:windows-motion-controls:warn'); -var DEFAULT_HANDEDNESS = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").DEFAULT_HANDEDNESS); -var AFRAME_CDN_ROOT = (__webpack_require__(/*! ../constants */ "./src/constants/index.js").AFRAME_CDN_ROOT); -var MODEL_BASE_URL = AFRAME_CDN_ROOT + 'controllers/microsoft/'; -var MODEL_FILENAMES = { - left: 'left.glb', - right: 'right.glb', - default: 'universal.glb' -}; -var isWebXRAvailable = (__webpack_require__(/*! ../utils/ */ "./src/utils/index.js").device.isWebXRAvailable); -var GAMEPAD_ID_WEBXR = 'windows-mixed-reality'; -var GAMEPAD_ID_WEBVR = 'Spatial Controller (Spatial Interaction Source) '; -var GAMEPAD_ID_PATTERN = /([0-9a-zA-Z]+-[0-9a-zA-Z]+)$/; -var GAMEPAD_ID_PREFIX = isWebXRAvailable ? GAMEPAD_ID_WEBXR : GAMEPAD_ID_WEBVR; -var INPUT_MAPPING_WEBVR = { - // A-Frame specific semantic axis names - axes: { - 'thumbstick': [0, 1], - 'trackpad': [2, 3] - }, - // A-Frame specific semantic button names - buttons: ['thumbstick', 'trigger', 'grip', 'menu', 'trackpad'], - // A mapping of the semantic name to node name in the glTF model file, - // that should be transformed by axis value. - // This array mirrors the browser Gamepad.axes array, such that - // the mesh corresponding to axis 0 is in this array index 0. - axisMeshNames: ['THUMBSTICK_X', 'THUMBSTICK_Y', 'TOUCHPAD_TOUCH_X', 'TOUCHPAD_TOUCH_Y'], - // A mapping of the semantic name to button node name in the glTF model file, - // that should be transformed by button value. - buttonMeshNames: { - 'trigger': 'SELECT', - 'menu': 'MENU', - 'grip': 'GRASP', - 'thumbstick': 'THUMBSTICK_PRESS', - 'trackpad': 'TOUCHPAD_PRESS' - }, - pointingPoseMeshName: 'POINTING_POSE' -}; -var INPUT_MAPPING_WEBXR = { - // A-Frame specific semantic axis names - axes: { - 'touchpad': [0, 1], - 'thumbstick': [2, 3] - }, - // A-Frame specific semantic button names - buttons: ['trigger', 'squeeze', 'touchpad', 'thumbstick', 'menu'], - // A mapping of the semantic name to node name in the glTF model file, - // that should be transformed by axis value. - // This array mirrors the browser Gamepad.axes array, such that - // the mesh corresponding to axis 0 is in this array index 0. - axisMeshNames: ['TOUCHPAD_TOUCH_X', 'TOUCHPAD_TOUCH_X', 'THUMBSTICK_X', 'THUMBSTICK_Y'], - // A mapping of the semantic name to button node name in the glTF model file, - // that should be transformed by button value. - buttonMeshNames: { - 'trigger': 'SELECT', - 'menu': 'MENU', - 'squeeze': 'GRASP', - 'thumbstick': 'THUMBSTICK_PRESS', - 'touchpad': 'TOUCHPAD_PRESS' - }, - pointingPoseMeshName: 'POINTING_POSE' -}; -var INPUT_MAPPING = isWebXRAvailable ? INPUT_MAPPING_WEBXR : INPUT_MAPPING_WEBVR; - -/** - * Windows Motion Controller controls. - * Interface with Windows Motion Controller controllers and map Gamepad events to - * controller buttons: trackpad, trigger, grip, menu, thumbstick - * Load a controller model and transform the pressed buttons. - */ -module.exports.Component = registerComponent('windows-motion-controls', { - schema: { - hand: { - default: DEFAULT_HANDEDNESS - }, - // It is possible to have multiple pairs of controllers attached (a pair has both left and right). - // Set this to 1 to use a controller from the second pair, 2 from the third pair, etc. - pair: { - default: 0 - }, - // If true, loads the controller glTF asset. - model: { - default: true - }, - // If true, will hide the model from the scene if no matching gamepad (based on ID & hand) is connected. - hideDisconnected: { - default: true - } - }, - after: ['tracked-controls'], - mapping: INPUT_MAPPING, - bindMethods: function () { - this.onModelError = this.onModelError.bind(this); - this.onModelLoaded = this.onModelLoaded.bind(this); - this.onControllersUpdate = this.onControllersUpdate.bind(this); - this.checkIfControllerPresent = this.checkIfControllerPresent.bind(this); - this.onAxisMoved = this.onAxisMoved.bind(this); - }, - init: function () { - var self = this; - var el = this.el; - this.onButtonChanged = this.onButtonChanged.bind(this); - this.onButtonDown = function (evt) { - onButtonEvent(evt.detail.id, 'down', self); - }; - this.onButtonUp = function (evt) { - onButtonEvent(evt.detail.id, 'up', self); - }; - this.onButtonTouchStart = function (evt) { - onButtonEvent(evt.detail.id, 'touchstart', self); - }; - this.onButtonTouchEnd = function (evt) { - onButtonEvent(evt.detail.id, 'touchend', self); - }; - this.onControllerConnected = function () { - self.setModelVisibility(true); - }; - this.onControllerDisconnected = function () { - self.setModelVisibility(false); - }; - this.controllerPresent = false; - this.lastControllerCheck = 0; - this.previousButtonValues = {}; - this.bindMethods(); - - // Cache for submeshes that we have looked up by name. - this.loadedMeshInfo = { - buttonMeshes: null, - axisMeshes: null - }; - - // Pointing poses - this.rayOrigin = { - origin: new THREE.Vector3(), - direction: new THREE.Vector3(0, 0, -1), - createdFromMesh: false - }; - el.addEventListener('controllerconnected', this.onControllerConnected); - el.addEventListener('controllerdisconnected', this.onControllerDisconnected); - }, - addEventListeners: function () { - var el = this.el; - el.addEventListener('buttonchanged', this.onButtonChanged); - el.addEventListener('buttondown', this.onButtonDown); - el.addEventListener('buttonup', this.onButtonUp); - el.addEventListener('touchstart', this.onButtonTouchStart); - el.addEventListener('touchend', this.onButtonTouchEnd); - el.addEventListener('axismove', this.onAxisMoved); - el.addEventListener('model-error', this.onModelError); - el.addEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = true; - }, - removeEventListeners: function () { - var el = this.el; - el.removeEventListener('buttonchanged', this.onButtonChanged); - el.removeEventListener('buttondown', this.onButtonDown); - el.removeEventListener('buttonup', this.onButtonUp); - el.removeEventListener('touchstart', this.onButtonTouchStart); - el.removeEventListener('touchend', this.onButtonTouchEnd); - el.removeEventListener('axismove', this.onAxisMoved); - el.removeEventListener('model-error', this.onModelError); - el.removeEventListener('model-loaded', this.onModelLoaded); - this.controllerEventsActive = false; - }, - checkIfControllerPresent: function () { - checkControllerPresentAndSetup(this, GAMEPAD_ID_PREFIX, { - hand: this.data.hand, - index: this.data.pair, - iterateControllerProfiles: true - }); - }, - play: function () { - this.checkIfControllerPresent(); - this.addControllersUpdateListener(); - }, - pause: function () { - this.removeEventListeners(); - this.removeControllersUpdateListener(); - }, - updateControllerModel: function () { - // If we do not want to load a model, or, have already loaded the model, emit the controllermodelready event. - if (!this.data.model || this.rayOrigin.createdFromMesh) { - this.modelReady(); - return; - } - var sourceUrl = this.createControllerModelUrl(); - this.loadModel(sourceUrl); - }, - /** - * Helper function that constructs a URL from the controller ID suffix, for future proofed - * art assets. - */ - createControllerModelUrl: function (forceDefault) { - // Determine the device specific folder based on the ID suffix - var trackedControlsComponent = this.el.components['tracked-controls']; - var controller = trackedControlsComponent ? trackedControlsComponent.controller : null; - var device = 'default'; - var hand = this.data.hand; - var filename; - if (controller && !window.hasNativeWebXRImplementation) { - // Read hand directly from the controller, rather than this.data, as in the case that the controller - // is unhanded this.data will still have 'left' or 'right' (depending on what the user inserted in to the scene). - // In this case, we want to load the universal model, so need to get the '' from the controller. - hand = controller.hand; - if (!forceDefault) { - var match = controller.id.match(GAMEPAD_ID_PATTERN); - device = match && match[0] || device; - } - } - - // Hand - filename = MODEL_FILENAMES[hand] || MODEL_FILENAMES.default; - - // Final url - return MODEL_BASE_URL + device + '/' + filename; - }, - injectTrackedControls: function () { - var data = this.data; - this.el.setAttribute('tracked-controls', { - idPrefix: GAMEPAD_ID_PREFIX, - controller: data.pair, - hand: data.hand, - armModel: false - }); - this.updateControllerModel(); - }, - addControllersUpdateListener: function () { - this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); - }, - removeControllersUpdateListener: function () { - this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); - }, - onControllersUpdate: function () { - this.checkIfControllerPresent(); - }, - onModelError: function (evt) { - var defaultUrl = this.createControllerModelUrl(true); - if (evt.detail.src !== defaultUrl) { - warn('Failed to load controller model for device, attempting to load default.'); - this.loadModel(defaultUrl); - } else { - warn('Failed to load default controller model.'); - } - }, - loadModel: function (url) { - // The model is loaded by the gltf-model component when this attribute is initially set, - // removed and re-loaded if the given url changes. - this.el.setAttribute('gltf-model', 'url(' + url + ')'); - }, - onModelLoaded: function (evt) { - var rootNode = this.controllerModel = evt.detail.model; - var loadedMeshInfo = this.loadedMeshInfo; - var i; - var meshName; - var mesh; - var meshInfo; - if (evt.target !== this.el) { - return; - } - debug('Processing model'); - - // Reset the caches - loadedMeshInfo.buttonMeshes = {}; - loadedMeshInfo.axisMeshes = {}; - - // Cache our meshes so we aren't traversing the hierarchy per frame - if (rootNode) { - // Button Meshes - for (i = 0; i < this.mapping.buttons.length; i++) { - meshName = this.mapping.buttonMeshNames[this.mapping.buttons[i]]; - if (!meshName) { - debug('Skipping unknown button at index: ' + i + ' with mapped name: ' + this.mapping.buttons[i]); - continue; - } - mesh = rootNode.getObjectByName(meshName); - if (!mesh) { - warn('Missing button mesh with name: ' + meshName); - continue; - } - meshInfo = { - index: i, - value: getImmediateChildByName(mesh, 'VALUE'), - pressed: getImmediateChildByName(mesh, 'PRESSED'), - unpressed: getImmediateChildByName(mesh, 'UNPRESSED') - }; - if (meshInfo.value && meshInfo.pressed && meshInfo.unpressed) { - loadedMeshInfo.buttonMeshes[this.mapping.buttons[i]] = meshInfo; - } else { - // If we didn't find the mesh, it simply means this button won't have transforms applied as mapped button value changes. - warn('Missing button submesh under mesh with name: ' + meshName + '(VALUE: ' + !!meshInfo.value + ', PRESSED: ' + !!meshInfo.pressed + ', UNPRESSED:' + !!meshInfo.unpressed + ')'); - } - } - - // Axis Meshes - for (i = 0; i < this.mapping.axisMeshNames.length; i++) { - meshName = this.mapping.axisMeshNames[i]; - if (!meshName) { - debug('Skipping unknown axis at index: ' + i); - continue; - } - mesh = rootNode.getObjectByName(meshName); - if (!mesh) { - warn('Missing axis mesh with name: ' + meshName); - continue; - } - meshInfo = { - index: i, - value: getImmediateChildByName(mesh, 'VALUE'), - min: getImmediateChildByName(mesh, 'MIN'), - max: getImmediateChildByName(mesh, 'MAX') - }; - if (meshInfo.value && meshInfo.min && meshInfo.max) { - loadedMeshInfo.axisMeshes[i] = meshInfo; - } else { - // If we didn't find the mesh, it simply means this axis won't have transforms applied as mapped axis values change. - warn('Missing axis submesh under mesh with name: ' + meshName + '(VALUE: ' + !!meshInfo.value + ', MIN: ' + !!meshInfo.min + ', MAX:' + !!meshInfo.max + ')'); - } - } - this.calculateRayOriginFromMesh(rootNode); - // Determine if the model has to be visible or not. - this.setModelVisibility(); - } - debug('Model load complete.'); - - // Look through only immediate children. This will return null if no mesh exists with the given name. - function getImmediateChildByName(object3d, value) { - for (var i = 0, l = object3d.children.length; i < l; i++) { - var obj = object3d.children[i]; - if (obj && obj['name'] === value) { - return obj; - } - } - return undefined; - } - }, - calculateRayOriginFromMesh: function () { - var quaternion = new THREE.Quaternion(); - return function (rootNode) { - var mesh; - - // Calculate the pointer pose (used for rays), by applying the world transform of th POINTER_POSE node - // in the glTF (assumes that root node is at world origin) - this.rayOrigin.origin.set(0, 0, 0); - this.rayOrigin.direction.set(0, 0, -1); - this.rayOrigin.createdFromMesh = true; - - // Try to read Pointing pose from the source model - mesh = rootNode.getObjectByName(this.mapping.pointingPoseMeshName); - if (mesh) { - var parent = rootNode.parent; - - // We need to read pose transforms accumulated from the root of the glTF, not the scene. - if (parent) { - rootNode.parent = null; - rootNode.updateMatrixWorld(true); - rootNode.parent = parent; - } - mesh.getWorldPosition(this.rayOrigin.origin); - mesh.getWorldQuaternion(quaternion); - this.rayOrigin.direction.applyQuaternion(quaternion); - - // Recalculate the world matrices now that the rootNode is re-attached to the parent. - if (parent) { - rootNode.updateMatrixWorld(true); - } - } else { - debug('Mesh does not contain pointing origin data, defaulting to none.'); - } - - // Emit event stating that our pointing ray is now accurate. - this.modelReady(); - }; - }(), - lerpAxisTransform: function () { - var quaternion = new THREE.Quaternion(); - return function (axis, axisValue) { - var axisMeshInfo = this.loadedMeshInfo.axisMeshes[axis]; - if (!axisMeshInfo) return; - var min = axisMeshInfo.min; - var max = axisMeshInfo.max; - var target = axisMeshInfo.value; - - // Convert from gamepad value range (-1 to +1) to lerp range (0 to 1) - var lerpValue = axisValue * 0.5 + 0.5; - target.setRotationFromQuaternion(quaternion.copy(min.quaternion).slerp(max.quaternion, lerpValue)); - target.position.lerpVectors(min.position, max.position, lerpValue); - }; - }(), - lerpButtonTransform: function () { - var quaternion = new THREE.Quaternion(); - return function (buttonName, buttonValue) { - var buttonMeshInfo = this.loadedMeshInfo.buttonMeshes[buttonName]; - if (!buttonMeshInfo) return; - var min = buttonMeshInfo.unpressed; - var max = buttonMeshInfo.pressed; - var target = buttonMeshInfo.value; - target.setRotationFromQuaternion(quaternion.copy(min.quaternion).slerp(max.quaternion, buttonValue)); - target.position.lerpVectors(min.position, max.position, buttonValue); - }; - }(), - modelReady: function () { - this.el.emit('controllermodelready', { - name: 'windows-motion-controls', - model: this.data.model, - rayOrigin: this.rayOrigin - }); - }, - onButtonChanged: function (evt) { - var buttonName = this.mapping.buttons[evt.detail.id]; - if (buttonName) { - // Update the button mesh transform - if (this.loadedMeshInfo && this.loadedMeshInfo.buttonMeshes) { - this.lerpButtonTransform(buttonName, evt.detail.state.value); - } - - // Only emit events for buttons that we know how to map from index to name - this.el.emit(buttonName + 'changed', evt.detail.state); - } - }, - onAxisMoved: function (evt) { - var numAxes = this.mapping.axisMeshNames.length; - - // Only attempt to update meshes if we have valid data. - if (this.loadedMeshInfo && this.loadedMeshInfo.axisMeshes) { - for (var axis = 0; axis < numAxes; axis++) { - // Update the button mesh transform - this.lerpAxisTransform(axis, evt.detail.axis[axis] || 0.0); - } - } - emitIfAxesChanged(this, this.mapping.axes, evt); - }, - setModelVisibility: function (visible) { - var model = this.el.getObject3D('mesh'); - if (!this.controllerPresent) { - return; - } - visible = visible !== undefined ? visible : this.modelVisible; - this.modelVisible = visible; - if (!model) { - return; - } - model.visible = visible; - } -}); - -/***/ }), - -/***/ "./src/constants/index.js": -/*!********************************!*\ - !*** ./src/constants/index.js ***! - \********************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = { - AFRAME_CDN_ROOT: window.AFRAME_CDN_ROOT || 'https://cdn.aframe.io/', - AFRAME_INJECTED: 'aframe-injected', - DEFAULT_CAMERA_HEIGHT: 1.6, - DEFAULT_HANDEDNESS: 'right', - keyboardevent: __webpack_require__(/*! ./keyboardevent */ "./src/constants/keyboardevent.js") -}; - -/***/ }), - -/***/ "./src/constants/keyboardevent.js": -/*!****************************************!*\ - !*** ./src/constants/keyboardevent.js ***! - \****************************************/ -/***/ ((module) => { - -module.exports = { - // Tiny KeyboardEvent.code polyfill. - KEYCODE_TO_CODE: { - '38': 'ArrowUp', - '37': 'ArrowLeft', - '40': 'ArrowDown', - '39': 'ArrowRight', - '87': 'KeyW', - '65': 'KeyA', - '83': 'KeyS', - '68': 'KeyD' - } -}; - -/***/ }), - -/***/ "./src/core/a-assets.js": -/*!******************************!*\ - !*** ./src/core/a-assets.js ***! - \******************************/ -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/* global customElements */ -var ANode = (__webpack_require__(/*! ./a-node */ "./src/core/a-node.js").ANode); -var debug = __webpack_require__(/*! ../utils/debug */ "./src/utils/debug.js"); -var THREE = __webpack_require__(/*! ../lib/three */ "./src/lib/three.js"); -var fileLoader = new THREE.FileLoader(); -var warn = debug('core:a-assets:warn'); - -/** - * Asset management system. Handles blocking on asset loading. - */ -class AAssets extends ANode { - constructor() { - super(); - this.isAssets = true; - this.fileLoader = fileLoader; - this.timeout = null; - } - doConnectedCallback() { - var self = this; - var i; - var loaded = []; - var mediaEl; - var mediaEls; - var imgEl; - var imgEls; - var timeout; - var children; - super.doConnectedCallback(); - if (!this.parentNode.isScene) { - throw new Error(' must be a child of a .'); - } - - // Wait for s. - imgEls = this.querySelectorAll('img'); - for (i = 0; i < imgEls.length; i++) { - imgEl = fixUpMediaElement(imgEls[i]); - loaded.push(new Promise(function (resolve, reject) { - // Set in cache because we won't be needing to call three.js loader if we have. - // a loaded media element. - THREE.Cache.add(imgEls[i].getAttribute('src'), imgEl); - if (imgEl.complete) { - resolve(); - return; - } - imgEl.onload = resolve; - imgEl.onerror = reject; - })); - } - - // Wait for