UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

1,365 lines (1,343 loc) 21.8 MB
/** * @license * PlayCanvas Engine v2.14.4 revision a8e9f39 (DEBUG) * Copyright 2011-2025 PlayCanvas Ltd. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.pc = {})); })(this, (function (exports) { 'use strict'; var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; /** * A short hand function to polyfill prototype methods which are not iterated in e.g. for-in loops. * * @param {ObjectConstructor} cls * @param {string} name * @param {Function} func * @ignore */ function defineProtoFunc(cls, name, func) { if (!cls.prototype[name]) { Object.defineProperty(cls.prototype, name, { value: func, configurable: true, enumerable: false, writable: true }); } } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill#polyfill defineProtoFunc(Array, 'fill', function(value) { // Steps 1-2. if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); // Steps 3-5. var len = O.length >>> 0; // Steps 6-7. var start = arguments[1]; var relativeStart = start >> 0; // Step 8. var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); // Steps 9-10. var end = arguments[2]; var relativeEnd = end === undefined ? len : end >> 0; // Step 11. var finalValue = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); // Step 12. while(k < finalValue){ O[k] = value; k++; } // Step 13. return O; }); // https://tc39.github.io/ecma262/#sec-array.prototype.find defineProtoFunc(Array, 'find', function(predicate) { // 1. Let O be ? ToObject(this value). if (this == null) { throw TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception. if (typeof predicate !== 'function') { throw TypeError('predicate must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while(k < len){ // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). // d. If testResult is true, return kValue. var kValue = o[k]; if (predicate.call(thisArg, kValue, k, o)) { return kValue; } // e. Increase k by 1. k++; } // 7. Return undefined. return undefined; }); // https://tc39.github.io/ecma262/#sec-array.prototype.findindex defineProtoFunc(Array, 'findIndex', function(predicate) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception. if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while(k < len){ // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). // d. If testResult is true, return k. var kValue = o[k]; if (predicate.call(thisArg, kValue, k, o)) { return k; } // e. Increase k by 1. k++; } // 7. Return -1. return -1; }); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2#Polyfill Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; }; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign#Polyfill if (!Math.sign) { Math.sign = function(x) { // If x is NaN, the result is NaN. // If x is -0, the result is -0. // If x is +0, the result is +0. // If x is negative and not -0, the result is -1. // If x is positive and not +0, the result is +1. return (x > 0) - (x < 0) || +x; // A more aesthetic pseudo-representation: // // ( (x > 0) ? 1 : 0 ) // if x is positive, then positive one // + // else (because you can't be both - and +) // ( (x < 0) ? -1 : 0 ) // if x is negative, then negative one // || // if x is 0, -0, or NaN, or not a number, // +x // then the result will be x, (or) if x is // // not a number, then x converts to number }; } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite#polyfill if (Number.isFinite === undefined) Number.isFinite = function(value) { return typeof value === 'number' && isFinite(value); }; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill if (typeof Object.assign != 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", { value: function assign(target, varArgs) { 'use strict'; if (target == null) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for(var index = 1; index < arguments.length; index++){ var nextSource = arguments[index]; if (nextSource != null) { for(var nextKey in nextSource){ // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries // https://stackoverflow.com/questions/68654735/ie11-compatible-object-fromentries Object.fromEntries = Object.fromEntries || function fromEntries(entries) { if (!entries || !entries[Symbol.iterator]) { throw new Error('Object.fromEntries() requires a single iterable argument'); } var res = {}; for(var i = 0; i < entries.length; i++){ res[entries[i][0]] = entries[i][1]; } return res; }; Object.entries = Object.entries || function(obj) { var ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array while(i--)resArray[i] = [ ownProps[i], obj[ownProps[i]] ]; return resArray; }; Object.values = Object.values || function(object) { return Object.keys(object).map(function(key) { return object[key]; }); }; // Apply PointerLock shims (function() { // Old API if (typeof navigator === 'undefined' || typeof document === 'undefined') { // Not running in a browser return; } navigator.pointer = navigator.pointer || navigator.webkitPointer || navigator.mozPointer; // Events var pointerlockchange = function pointerlockchange() { var e = document.createEvent('CustomEvent'); e.initCustomEvent('pointerlockchange', true, false, null); document.dispatchEvent(e); }; var pointerlockerror = function pointerlockerror() { var e = document.createEvent('CustomEvent'); e.initCustomEvent('pointerlockerror', true, false, null); document.dispatchEvent(e); }; document.addEventListener('webkitpointerlockchange', pointerlockchange, false); document.addEventListener('webkitpointerlocklost', pointerlockchange, false); document.addEventListener('mozpointerlockchange', pointerlockchange, false); document.addEventListener('mozpointerlocklost', pointerlockchange, false); document.addEventListener('webkitpointerlockerror', pointerlockerror, false); document.addEventListener('mozpointerlockerror', pointerlockerror, false); // requestPointerLock if (Element.prototype.mozRequestPointerLock) { // FF requires a new function for some reason Element.prototype.requestPointerLock = function() { this.mozRequestPointerLock(); }; } else { Element.prototype.requestPointerLock = Element.prototype.requestPointerLock || Element.prototype.webkitRequestPointerLock || Element.prototype.mozRequestPointerLock; } if (!Element.prototype.requestPointerLock && navigator.pointer) { Element.prototype.requestPointerLock = function() { var el = this; document.pointerLockElement = el; navigator.pointer.lock(el, pointerlockchange, pointerlockerror); }; } // exitPointerLock document.exitPointerLock = document.exitPointerLock || document.webkitExitPointerLock || document.mozExitPointerLock; if (!document.exitPointerLock) { document.exitPointerLock = function() { if (navigator.pointer) { document.pointerLockElement = null; navigator.pointer.unlock(); } }; } })(); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith#Polyfill defineProtoFunc(String, 'endsWith', function(search, this_len) { if (this_len === undefined || this_len > this.length) { this_len = this.length; } return this.substring(this_len - search.length, this_len) === search; }); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill defineProtoFunc(String, 'includes', function(search, start) { 'use strict'; if (typeof start !== 'number') { start = 0; } if (start + search.length > this.length) { return false; } else { return this.indexOf(search, start) !== -1; } }); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith#Polyfill defineProtoFunc(String, 'startsWith', function(search, rawPos) { var pos = rawPos > 0 ? rawPos | 0 : 0; return this.substring(pos, pos + search.length) === search; }); // https://vanillajstoolkit.com/polyfills/stringtrimend/ defineProtoFunc(String, 'trimEnd', function() { return this.replace(new RegExp(/[\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF]+/.source + '$', 'g'), ''); }); function _array_like_to_array$J(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; return arr2; } function _unsupported_iterable_to_array$J(o, minLen) { if (!o) return; if (typeof o === "string") return _array_like_to_array$J(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$J(o, minLen); } function _create_for_of_iterator_helper_loose$J(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupported_iterable_to_array$J(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function() { if (i >= o.length) { return { done: true }; } return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var typedArrays = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array ]; for(var _iterator = _create_for_of_iterator_helper_loose$J(typedArrays), _step; !(_step = _iterator()).done;){ var typedArray = _step.value; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/fill#polyfill defineProtoFunc(typedArray, "fill", Array.prototype.fill); defineProtoFunc(typedArray, "join", Array.prototype.join); } /** * Logs a frame number. * * @category Debug */ var TRACEID_RENDER_FRAME = 'RenderFrame'; /** * Logs a frame time. * * @category Debug */ var TRACEID_RENDER_FRAME_TIME = 'RenderFrameTime'; /** * Logs basic information about generated render passes. * * @category Debug */ var TRACEID_RENDER_PASS = 'RenderPass'; /** * Logs additional detail for render passes. * * @category Debug */ var TRACEID_RENDER_PASS_DETAIL = 'RenderPassDetail'; /** * Logs render actions created by the layer composition. Only executes when the * layer composition changes. * * @category Debug */ var TRACEID_RENDER_ACTION = 'RenderAction'; /** * Logs the allocation of render targets. * * @category Debug */ var TRACEID_RENDER_TARGET_ALLOC = 'RenderTargetAlloc'; /** * Logs the allocation of textures. * * @category Debug */ var TRACEID_TEXTURE_ALLOC = 'TextureAlloc'; /** * Logs the creation of shaders. * * @category Debug */ var TRACEID_SHADER_ALLOC = 'ShaderAlloc'; /** * Logs the compilation time of shaders. * * @category Debug */ var TRACEID_SHADER_COMPILE = 'ShaderCompile'; /** * Logs the vram use by the textures. * * @category Debug */ var TRACEID_VRAM_TEXTURE = 'VRAM.Texture'; /** * Logs the vram use by the vertex buffers. * * @category Debug */ var TRACEID_VRAM_VB = 'VRAM.Vb'; /** * Logs the vram use by the index buffers. * * @category Debug */ var TRACEID_VRAM_IB = 'VRAM.Ib'; /** * Logs the vram use by the storage buffers. * * @category Debug */ var TRACEID_VRAM_SB = 'VRAM.Sb'; /** * Logs the creation of bind groups. * * @category Debug */ var TRACEID_BINDGROUP_ALLOC = 'BindGroupAlloc'; /** * Logs the creation of bind group formats. * * @category Debug */ var TRACEID_BINDGROUPFORMAT_ALLOC = 'BindGroupFormatAlloc'; /** * Logs the creation of render pipelines. WebBPU only. * * @category Debug */ var TRACEID_RENDERPIPELINE_ALLOC = 'RenderPipelineAlloc'; /** * Logs the creation of compute pipelines. WebGPU only. * * @category Debug */ var TRACEID_COMPUTEPIPELINE_ALLOC = 'ComputePipelineAlloc'; /** * Logs the creation of pipeline layouts. WebBPU only. * * @category Debug */ var TRACEID_PIPELINELAYOUT_ALLOC = 'PipelineLayoutAlloc'; /** * Logs the internal debug information for Elements. * * @category Debug */ var TRACEID_ELEMENT = 'Element'; /** * Logs the vram use by all textures in memory. * * @category Debug */ var TRACEID_TEXTURES = 'Textures'; /** * Logs all assets in the asset registry. * * @category Debug */ var TRACEID_ASSETS = 'Assets'; /** * Logs the render queue commands. * * @category Debug */ var TRACEID_RENDER_QUEUE = 'RenderQueue'; /** * Logs the loaded GSplat resources for individual LOD levels of an octree. * * @category Debug */ var TRACEID_OCTREE_RESOURCES = 'OctreeResources'; /** * Logs the GPU timings. * * @category Debug */ var TRACEID_GPU_TIMINGS = 'GpuTimings'; /** * The engine version number. This is in semantic versioning format (MAJOR.MINOR.PATCH). */ var version = '2.14.4'; /** * The engine revision number. This is the Git hash of the last commit made to the branch * from which the engine was built. */ var revision = 'a8e9f39'; /** * Merge the contents of two objects into a single object. * * @param {object} target - The target object of the merge. * @param {object} ex - The object that is merged with target. * @returns {object} The target object. * @example * const A = { * a: function () { * console.log(this.a); * } * }; * const B = { * b: function () { * console.log(this.b); * } * }; * * extend(A, B); * A.a(); * // logs "a" * A.b(); * // logs "b" * @ignore */ function extend(target, ex) { for(var prop in ex){ var copy = ex[prop]; if (Array.isArray(copy)) { target[prop] = extend([], copy); } else if (copy && typeof copy === 'object') { target[prop] = extend({}, copy); } else { target[prop] = copy; } } return target; } /** * Basically a very large random number (128-bit) which means the probability of creating two that * clash is vanishingly small. GUIDs are used as the unique identifiers for Entities. * * @namespace */ var guid = { /** * Create an RFC4122 version 4 compliant GUID. * * @returns {string} A new GUID. */ create: function create() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0; var v = c === 'x' ? r : r & 0x3 | 0x8; return v.toString(16); }); } }; /** * File path API. * * @namespace */ var path = { /** * The character that separates path segments. * * @type {string} */ delimiter: '/', /** * Join two or more sections of file path together, inserting a delimiter if needed. * * @param {...string} sections - Sections of the path to join. * @returns {string} The joined file path. * @example * const path = pc.path.join('foo', 'bar'); * console.log(path); // Prints 'foo/bar' * @example * const path = pc.path.join('alpha', 'beta', 'gamma'); * console.log(path); // Prints 'alpha/beta/gamma' */ join: function join() { for(var _len = arguments.length, sections = new Array(_len), _key = 0; _key < _len; _key++){ sections[_key] = arguments[_key]; } var result = sections[0]; for(var i = 0; i < sections.length - 1; i++){ var one = sections[i]; var two = sections[i + 1]; if (two[0] === path.delimiter) { result = two; continue; } if (one && two && one[one.length - 1] !== path.delimiter && two[0] !== path.delimiter) { result += path.delimiter + two; } else { result += two; } } return result; }, /** * Normalize the path by removing '.' and '..' instances. * * @param {string} pathname - The path to normalize. * @returns {string} The normalized path. */ normalize: function normalize(pathname) { var lead = pathname.startsWith(path.delimiter); var trail = pathname.endsWith(path.delimiter); var parts = pathname.split('/'); var result = ''; var cleaned = []; for(var i = 0; i < parts.length; i++){ if (parts[i] === '') continue; if (parts[i] === '.') continue; if (parts[i] === '..' && cleaned.length > 0) { cleaned = cleaned.slice(0, cleaned.length - 2); continue; } if (i > 0) cleaned.push(path.delimiter); cleaned.push(parts[i]); } result = cleaned.join(''); if (!lead && result[0] === path.delimiter) { result = result.slice(1); } if (trail && result[result.length - 1] !== path.delimiter) { result += path.delimiter; } return result; }, /** * Split the pathname path into a pair [head, tail] where tail is the final part of the path * after the last delimiter and head is everything leading up to that. tail will never contain * a slash. * * @param {string} pathname - The path to split. * @returns {string[]} The split path which is an array of two strings, the path and the * filename. */ split: function split(pathname) { var lastDelimiterIndex = pathname.lastIndexOf(path.delimiter); if (lastDelimiterIndex !== -1) { return [ pathname.substring(0, lastDelimiterIndex), pathname.substring(lastDelimiterIndex + 1) ]; } return [ '', pathname ]; }, /** * Return the basename of the path. That is the second element of the pair returned by passing * path into {@link path.split}. * * @param {string} pathname - The path to process. * @returns {string} The basename. * @example * pc.path.getBasename("/path/to/file.txt"); // returns "file.txt" * pc.path.getBasename("/path/to/dir"); // returns "dir" */ getBasename: function getBasename(pathname) { return path.split(pathname)[1]; }, /** * Get the directory name from the path. This is everything up to the final instance of * {@link path.delimiter}. * * @param {string} pathname - The path to get the directory from. * @returns {string} The directory part of the path. */ getDirectory: function getDirectory(pathname) { return path.split(pathname)[0]; }, /** * Return the extension of the path. Pop the last value of a list after path is split by * question mark and comma. * * @param {string} pathname - The path to process. * @returns {string} The extension. * @example * pc.path.getExtension("/path/to/file.txt"); // returns ".txt" * pc.path.getExtension("/path/to/file.jpg"); // returns ".jpg" * pc.path.getExtension("/path/to/file.txt?function=getExtension"); // returns ".txt" */ getExtension: function getExtension(pathname) { var ext = pathname.split('?')[0].split('.').pop(); if (ext !== pathname) { return "." + ext; } return ''; }, /** * Check if a string s is relative path. * * @param {string} pathname - The path to process. * @returns {boolean} True if s doesn't start with slash and doesn't include colon and double * slash. * * @example * pc.path.isRelativePath("file.txt"); // returns true * pc.path.isRelativePath("path/to/file.txt"); // returns true * pc.path.isRelativePath("./path/to/file.txt"); // returns true * pc.path.isRelativePath("../path/to/file.jpg"); // returns true * pc.path.isRelativePath("/path/to/file.jpg"); // returns false * pc.path.isRelativePath("http://path/to/file.jpg"); // returns false */ isRelativePath: function isRelativePath(pathname) { return pathname.charAt(0) !== '/' && pathname.match(/:\/\//) === null; }, /** * Return the path without file name. If path is relative path, start with period. * * @param {string} pathname - The full path to process. * @returns {string} The path without a last element from list split by slash. * @example * pc.path.extractPath("path/to/file.txt"); // returns "./path/to" * pc.path.extractPath("./path/to/file.txt"); // returns "./path/to" * pc.path.extractPath("../path/to/file.txt"); // returns "../path/to" * pc.path.extractPath("/path/to/file.txt"); // returns "/path/to" */ extractPath: function extractPath(pathname) { var result = ''; var parts = pathname.split('/'); var i = 0; if (parts.length > 1) { if (path.isRelativePath(pathname)) { if (parts[0] === '.') { for(i = 0; i < parts.length - 1; ++i){ result += i === 0 ? parts[i] : "/" + parts[i]; } } else if (parts[0] === '..') { for(i = 0; i < parts.length - 1; ++i){ result += i === 0 ? parts[i] : "/" + parts[i]; } } else { result = '.'; for(i = 0; i < parts.length - 1; ++i){ result += "/" + parts[i]; } } } else { for(i = 0; i < parts.length - 1; ++i){ result += i === 0 ? parts[i] : "/" + parts[i]; } } } return result; } }; // detect whether passive events are supported by the browser var detectPassiveEvents = function() { var result = false; try { var opts = Object.defineProperty({}, 'passive', { get: function get() { result = true; return false; } }); window.addEventListener('testpassive', null, opts); window.removeEventListener('testpassive', null, opts); } catch (e) {} return result; }; var ua = typeof navigator !== 'undefined' ? navigator.userAgent : ''; var environment = typeof window !== 'undefined' ? 'browser' : typeof global !== 'undefined' ? 'node' : 'worker'; // detect platform var platformName = /android/i.test(ua) ? 'android' : /ip(?:[ao]d|hone)/i.test(ua) ? 'ios' : /windows/i.test(ua) ? 'windows' : /mac os/i.test(ua) ? 'osx' : /linux/i.test(ua) ? 'linux' : /cros/i.test(ua) ? 'cros' : null; // detect browser var browserName = environment !== 'browser' ? null : /Chrome\/|Chromium\/|Edg.*\//.test(ua) ? 'chrome' : /Safari\//.test(ua) ? 'safari' : /Firefox\//.test(ua) ? 'firefox' : 'other'; var xbox = /xbox/i.test(ua); var touch = environment === 'browser' && ('ontouchstart' in window || 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0); var gamepads = environment === 'browser' && (!!navigator.getGamepads || !!navigator.webkitGetGamepads); var workers = typeof Worker !== 'undefined'; var passiveEvents = detectPassiveEvents(); var _ref, _ref1, _ref2; /** * Global namespace that stores flags regarding platform environment and features support. * * @namespace * @example * if (pc.platform.touch) { * // touch is supported * } */ var platform = { /** * String identifying the current platform. Can be one of: android, ios, windows, osx, linux, * cros or null. * * @type {'android' | 'ios' | 'windows' | 'osx' | 'linux' | 'cros' | null} * @ignore */ name: platformName, /** * String identifying the current runtime environment. Either 'browser', 'node' or 'worker'. * * @type {'browser' | 'node' | 'worker'} */ environment: environment, /** * The global object. This will be the window object when running in a browser and the global * object when running in nodejs and self when running in a worker. * * @type {object} */ global: (_ref2 = (_ref1 = (_ref = typeof globalThis !== 'undefined' && globalThis) != null ? _ref : environment === 'browser' && window) != null ? _ref1 : environment === 'node' && global) != null ? _ref2 : environment === 'worker' && self, /** * Convenience boolean indicating whether we're running in the browser. * * @type {boolean} */ browser: environment === 'browser', /** * True if running in a Web Worker. * * @type {boolean} * @ignore */ worker: environment === 'worker', /** * True if running on a desktop or laptop device. * * @type {boolean} */ desktop: [ 'windows', 'osx', 'linux', 'cros' ].includes(platformName), /** * True if running on a mobile or tablet device. * * @type {boolean} */ mobile: [ 'android', 'ios' ].includes(platformName), /** * True if running on an iOS device. * * @type {boolean} */ ios: platformName === 'ios', /** * True if running on an Android device. * * @type {boolean} */ android: platformName === 'android', /** * True if running on an Xbox device. * * @type {boolean} */ xbox: xbox, /** * True if the platform supports gamepads. * * @type {boolean} */ gamepads: gamepads, /** * True if the supports touch input. * * @type {boolean} */ touch: touch, /** * True if the platform supports Web Workers. * * @type {boolean} */ workers: workers, /** * True if the platform supports an options object as the third parameter to * `EventTarget.addEventListener()` and the passive property is supported. * * @type {boolean} * @ignore */ passiveEvents: passiveEvents, /** * Get the browser name. * * @type {'chrome' | 'safari' | 'firefox' | 'other' | null} * @ignore */ browserName: browserName }; var ASCII_LOWERCASE = 'abcdefghijklmnopqrstuvwxyz'; var ASCII_UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var ASCII_LETTERS = ASCII_LOWERCASE + ASCII_UPPERCASE; var HIGH_SURROGATE_BEGIN = 0xD800; var HIGH_SURROGATE_END = 0xDBFF; var LOW_SURROGATE_BEGIN = 0xDC00; var LOW_SURROGATE_END = 0xDFFF; var ZERO_WIDTH_JOINER = 0x200D; // Flag emoji var REGIONAL_INDICATOR_BEGIN = 0x1F1E6; var REGIONAL_INDICATOR_END = 0x1F1FF; // Skin color modifications to emoji var FITZPATRICK_MODIFIER_BEGIN = 0x1F3FB; var FITZPATRICK_MODIFIER_END = 0x1F3FF; // Accent characters var DIACRITICAL_MARKS_BEGIN = 0x20D0; var DIACRITICAL_MARKS_END = 0x20FF; // Special emoji joins var VARIATION_MODIFIER_BEGIN = 0xFE00; var VARIATION_MODIFIER_END = 0xFE0F; function getCodePointData(string, i) { if (i === void 0) i = 0; var size = string.length; // Account for out-of-bounds indices: if (i < 0 || i >= size) { return null; } var first = string.charCodeAt(i); if (size > 1 && first >= HIGH_SURROGATE_BEGIN && first <= HIGH_SURROGATE_END) { var second = string.charCodeAt(i + 1); if (second >= LOW_SURROGATE_BEGIN && second <= LOW_SURROGATE_END) { // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae return { code: (first - HIGH_SURROGATE_BEGIN) * 0x400 + second - LOW_SURROGATE_BEGIN + 0x10000, long: true }; } } return { code: first, long: false }; } function isCodeBetween(string, begin, end) { if (!string) { return false; } var codeData = getCodePointData(string); if (codeData) { var code = codeData.code; return code >= begin && code <= end; } return false; } function numCharsToTakeForNextSymbol(string, index) { if (index === string.length - 1) { // Last character in the string, so we can only take 1 return 1; } if (isCodeBetween(string[index], HIGH_SURROGATE_BEGIN, HIGH_SURROGATE_END)) { var first = string.substring(index, index + 2); var second = string.substring(index + 2, index + 4); // check if second character is fitzpatrick (color) modifier // or if this is a pair of regional indicators (a flag) if (isCodeBetween(second, FITZPATRICK_MODIFIER_BEGIN, FITZPATRICK_MODIFIER_END) || isCodeBetween(first, REGIONAL_INDICATOR_BEGIN, REGIONAL_INDICATOR_END) && isCodeBetween(second, REGIONAL_INDICATOR_BEGIN, REGIONAL_INDICATOR_END)) { return 4; } // check if next character is a modifier, in which case we should return it if (isCodeBetween(second, VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) { return 3; } // return surrogate pair return 2; } // check if next character is the emoji modifier, in which case we should include it if (isCodeBetween(string[index + 1], VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) { return 2; } // just a regular character return 1; } /** * Extended String API. * * @namespace */ var string = { /** * All lowercase letters. * * @type {string} */ ASCII_LOWERCASE: ASCII_LOWERCASE, /** * All uppercase letters. * * @type {string} */ ASCII_UPPERCASE: ASCII_UPPERCASE, /** * All ASCII letters. * * @type {string} */ ASCII_LETTERS: ASCII_LETTERS, /** * Return a string with \{n\} replaced with the n-th argument. * * @param {string} s - The string to format. * @param {...*} args - All other arguments are substituted into the string. * @returns {string} The formatted string. * @example * const s = pc.string.format("Hello {0}", "world"); * console.log(s); // Prints "Hello world" */ format: function format(s) { for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){ args[_key - 1] = arguments[_key]; } for(var i = 0; i < args.length; i++){ s = s.replace("{" + i + "}", args[i]); } return s; }, /** * Get the code point number for a character in a string. Polyfill for * [`codePointAt`]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt}. * * @param {string} string - The string to get the code point from. * @param {number} [i] - The index in the string. * @returns {number} The code point value for the character in the string. */ getCodePoint: function getCodePoint(string, i) { var codePointData = getCodePointData(string, i); return codePointData && codePointData.code; }, /** * Gets an array of all code points in a string. * * @param {string} string - The string to get code points from. * @returns {number[]} The code points in the string. */ getCodePoints: function getCodePoints(string) { if (typeof string !== 'string') { throw new TypeError('Not a string'); } var i = 0; var arr = []; var codePoint; while(!!(codePoint = getCodePointData(string, i))){ arr.push(codePoint.code); i += codePoint.long ? 2 : 1; } return arr; }, /** * Gets an array of all grapheme clusters (visible symbols) in a string. This is needed because * some symbols (such as emoji or accented characters) are actually made up of multiple * character codes. See {@link https://mathiasbynens.be/notes/javascript-unicode here} for more * info. * * @param {string} string - The string to break into symbols. * @returns {string[]} The symbols in the string. */ getSymbols: function getSymbols(string) { if (typeof string !== 'string') { throw new TypeError('Not a string'); } var index = 0; var length = string.length; var output = []; var take = 0; var ch; while(index < length){ take += numCharsToTakeForNextSymbol(string, index + take); ch = string[index + take]; // Handle special cases if (isCodeBetween(ch, DIACRITICAL_MARKS_BEGIN, DIACRITICAL_MARKS_END)) { ch = string[index + take++]; } if (isCodeBetween(ch, VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) { ch = string[index + take++]; } if (ch && ch.charCodeAt(0) === ZERO_WIDTH_JOINER) { ch = string[index + take++]; continue; } var char = string.substring(index, index + take); output.push(char); index += take; take = 0; } return output; }, /** * Get the string for a given code point or set of code points. Polyfill for * [`fromCodePoint`]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint}. * * @param {...number} args - The code points to convert to a string. * @returns {string} The converted string. * @ignore */ fromCodePoint: function fromCodePoint() { for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ args[_key] = arguments[_key]; } return args.map(function(codePoint) { if (codePoint > 0xFFFF) { codePoint -= 0x10000; return String.fromCharCode((codePoint >> 10) + 0xD800, codePoint % 0x400 + 0xDC00); } return String.fromCharCode(codePoint); }).join(''); } }; /** * Log tracing functionality, allowing for tracing of the internal functionality of the engine. * Note that the trace logging only takes place in the debug build of the engine and is stripped * out in other builds. * * @category Debug */ var Tracing = /*#__PURE__*/ function() { function Tracing() {} /** * Enable or disable a trace channel. * * @param {string} channel - Name of the trace channel. Can be: * * - {@link TRACEID_RENDER_FRAME} * - {@link TRACEID_RENDER_FRAME_TIME} * - {@link TRACEID_RENDER_PASS} * - {@link TRACEID_RENDER_PASS_DETAIL} * - {@link TRACEID_RENDER_ACTION} * - {@link TRACEID_RENDER_TARGET_ALLOC} * - {@link TRACEID_TEXTURE_ALLOC} * - {@link TRACEID_SHADER_ALLOC} * - {@link TRACEID_SHADER_COMPILE} * - {@link TRACEID_VRAM_TEXTURE} * - {@link TRACEID_VRAM_VB} * - {@link TRACEID_VRAM_IB} * - {@link TRACEID_RENDERPIPELINE_ALLOC} * - {@link TRACEID_COMPUTEPIPELINE_ALLOC} * - {@link TRACEID_PIPELINELAYOUT_ALLOC} * - {@link TRACEID_TEXTURES} * - {@link TRACEID_ASSETS} * - {@link TRACEID_GPU_TIMINGS} * * @param {boolean} enabled - New enabled state for the channel. */ Tracing.set = function set(channel, enabled) { if (enabled === void 0) enabled = true; if (enabled) { Tracing._traceChannels.add(channel); } else { Tracing._traceChannels.delete(channel); } }; /** * Test if the trace channel is enabled. * * @param {string} channel - Name of the trace channel. * @returns {boolean} - True if the trace channel is enabled. */ Tracing.get = function get(channel) { return Tracing._traceChannels.has(channel); }; return Tracing; }(); /** * Set storing the names of enabled trace channels. * * @type {Set<string>} * @private */ Tracing._traceChannels = new Set(); /** * Enable call stack logging for trace calls. Defaults to false. * * @type {boolean} */ Tracing.stack = false; /** * Engine debug log system. Note that the logging only executes in the debug build of the engine, * and is stripped out in other builds. */ var Debug = /*#__PURE__*/ function() { function Debug() {} /** * Deprecated warning message. * * @param {string} message - The message to log. */ Debug.deprecated = function deprecated(message) { if (!Debug._loggedMessages.has(message)) { Debug._loggedMessages.add(message); console.warn("DEPRECATED: " + message); } }; /** * Removed warning message. * * @param {string} message - The message to log. */ Debug.removed = function removed(message) { if (!Debug._loggedMessages.has(message)) { Debug._loggedMessages.add(message); console.error("REMOVED: " + message); } }; /** * Assertion deprecated message. If the assertion is false, the deprecated message is written to the log. * * @param {boolean|object} assertion - The assertion to check. * @param {string} message - The message to log. */ Debug.assertDeprecated = function assertDeprecated(assertion, message) { if (!assertion) { Debug.deprecated(message); } }; /** * Assertion error message. If the assertion is false, the error message is written to the log. * * @param {boolean|object} assertion - The assertion to check. * @param {...*} args - The values to be written to the log. */ Debug.assert = function assert(assertion) { for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){ args[_key - 1] = arguments[_key]; } if (!assertion) { var _console; (_console = console).error.apply(_console, [].concat([ 'ASSERT FAILED: ' ], args)); } }; /** * Assertion error message that writes an error message to the log if the object has already * been destroyed. To be used along setDestroyed. * * @param {object} object - The object to check. */ Debug.assertDestroyed = function assertDestroyed(object) { if (object == null ? void 0 : object.__alreadyDestroyed) { var _object_constructor; var message = "[" + ((_object_constructor = object.constructor) == null ? void 0 : _object_constructor.name) + "] with name [" + object.name + "] has already been destroyed, and cannot be used."; if (!Debug._loggedMessages.has(message)) { Debug._loggedMessages.add(message); console.error('ASSERT FAILED: ', message, object); } } }; /** * Executes a function in debug mode only. * * @param {Function} func - Function to call. */ Debug.call = function call(func) { func(); }; /** * Info message. * * @param {...*} args - The values to be written to the log. */ Debug.log = function log() { for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ args[_key] = arguments[_key]; } var _console; (_console = console).log.apply(_console, [].concat(args)); }; /** * Info message logged no more than once. * * @param {string} message - The message to log. * @param {...*} args - The values to be written to the log. */ Debug.logOnce = function logOnce(message) { for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){ args[_key - 1] = arguments[_key]; } if (!Debug._loggedMessages.has(message)) { var _console; Debug._loggedMessages.add(message); (_console = console).log.apply(_console, [].concat([ message ], args)); } }; /** * Warning message. * * @param {...*} args - The values to be written to the log. */ Debug.warn = function warn() { for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ args[_key] = arguments[_key]; } var _console; (_console = console).warn.apply(_console, [].concat(args)); }; /** * Warning message logged no more than once. * * @param {string} message - The message to log. * @param {...*} args - The values to be written to the log. */ Debug.warnOnce = function warnOnce(message) { for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){ args[_key - 1] = arguments[_key]; } if (!Debug._loggedMessages.has(message)) { var _console; Debug._loggedMessages.add(message); (_console = console).warn.apply(_console, [].concat([ message ], args)); } }; /** * Error message. * * @param {...*} args - The values to be written to the log. */ Debug.error = function error() { for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ args[_key] = arguments[_key]; } var _console; (_console = console).error.apply(_console, [].concat(args)); }; /** * Error message logged no more than once. * * @param {string} message - The message to log. * @param {...*} args - The values to be written to the log. */ Debug.errorOnce = function errorOnce(message) { for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){ args[_key - 1] = arguments[_key]; } if (!Debug._loggedMessages.has(message)) { var _console; Debug._loggedMessages.add(message); (_console = console).error.apply(_console, [].concat([ message ], args)); } }; /** * Trace message, which is logged to the console if the tracing for the channel is enabled * * @param {string} channel - The trace channel * @param {...*} args - The values to be written to the log. */ Debug.trace = function trace(channel) { for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){ args[_key - 1] = arguments[_key]; } if (Tracing.get(channel)) { var _console; (_console = console).groupCollapsed.apply(_console, [].concat([ "" + channel.padEnd(20, ' ') + "|" ], args)); if (Tracing.stack) { console.trace(); } console.groupEnd(); } }; return Debug; }(); /** * Set storing already logged messages, to only print each unique message one time. * * @type {Set<string>} * @private */ Debug._loggedMessages = new Set(); /** * A helper debug functionality. */ var DebugHelper = /*#__PURE__*/ function() { function DebugHelper() {} /** * Set a name to the name property of the object. Executes only in the debug build. * * @param {object} object - The object to assign the name to. * @param {string} name - The name to assign. */ DebugHelper.setName = function setName(object, name) { if (object) { object.name = name; } }; /** * Set a label to the label property of the object. Executes only in the debug build. * * @param {object} object - The object to assign the name to. * @param {string} label - The label to assign. */ DebugHelper.setLabel = function setLabel(object, label) { if (object) { object.label = label; } }; /** * Marks object as destroyed. Executes only in the debug build. To be used along assertDestroyed. * * @param {object} object - The object to mark as destroyed. */ DebugHelper.setDestroyed = function setDestroyed(object) { if (object) { object.__alreadyDestroyed = true; } }; return DebugHelper; }(); function _defineProperties$30(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); } } function _create_class$30(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties$30(Constructor.prototype, protoProps); return Constructor; } /** * @import { EventHandler } from './event-handler.js' * @import { HandleEventCallback } from './event-handler.js' */ /** * Event Handle that is created by {@link EventHandler} and can be used for easier event removal * and management. * * @example * const evt = obj.on('test', (a, b) => { * console.log(a + b); * }); * obj.fire('test'); * * evt.off(); // easy way to remove