diff --git a/.autosave/MouseDefense/2025-06-30_17-45-52.wlp b/.autosave/MouseDefense/2025-06-30_17-45-52.wlp new file mode 100644 index 0000000..73e4c8c --- /dev/null +++ b/.autosave/MouseDefense/2025-06-30_17-45-52.wlp @@ -0,0 +1,7210 @@ +{ + "objects": { + "14": { + "name": "Player", + "components": [ + { + "type": "bg-music" + }, + { + "type": "player-location" + }, + { + "type": "fixed-foveation", + "fixed-foveation": { + "fixedFoveation": 1.0 + } + } + ], + "translation": [ + 0.0, + 0.0, + 4.0 + ], + "rotation": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "scaling": [ + 0.9999998, + 1.0, + 0.9999998 + ] + }, + "15": { + "name": "FloorPlane", + "components": [ + { + "type": "mesh", + "mesh": { + "mesh": "12", + "material": "540" + }, + "active": true + }, + { + "type": "collision", + "collision": { + "collider": "aabb", + "aabb": { + "extents": [ + 16.0, + -0.1, + 16.0 + ] + }, + "groups": 4 + }, + "active": true + } + ], + "scaling": [ + 60.0, + 60.0000267, + 60.0000038 + ], + "rotation": [ + -0.707264, + 0.0, + 0.0, + 0.7069494 + ] + }, + "17": { + "name": "NonVrCamera", + "parent": "205", + "components": [ + { + "type": "view" + }, + { + "type": "wasd-controls-custom", + "wasd-controls-custom": { + "speed": 0.1, + "headObject": "14" + }, + "active": true + }, + { + "type": "mouse-look-custom", + "mouse-look-custom": { + "pointerLockOnClick": true, + "requireMouseDown": false, + "bulletMesh": "9", + "bulletMaterial": "20", + "bulletSpeed": 5.0 + } + } + ], + "translation": [ + 0.0, + 0.0, + 0.0 + ], + "rotation": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "scaling": [ + 1.0, + 1.0, + 1.0 + ] + }, + "18": { + "name": "EyeLeft", + "parent": "205", + "components": [ + { + "type": "view" + }, + { + "type": "input", + "input": { + "type": "eye left" + } + }, + { + "type": "howler-audio-listener" + } + ], + "translation": [ + 0.0, + 0.0, + -2.384185791015625e-7 + ], + "rotation": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "scaling": [ + 1.0, + 1.0, + 1.0 + ] + }, + "19": { + "name": "EyeRight", + "parent": "205", + "components": [ + { + "type": "view" + }, + { + "type": "input", + "input": { + "type": "eye right" + } + } + ], + "translation": [ + 0.0, + 0.0, + -2.384185791015625e-7 + ], + "rotation": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "scaling": [ + 1.0, + 1.0, + 1.0 + ] + }, + "21": { + "name": "Sunlight", + "components": [ + { + "type": "light", + "light": { + "type": "sun", + "color": [ + 1.0, + 0.8839215, + 0.4196078 + ], + "shadows": true, + "cascadeCount": 3, + "shadowRange": 50.0, + "intensity": 1.0 + } + } + ], + "parent": "501", + "translation": [ + -0.0000013, + 3.0000147, + 8.8421812 + ], + "rotation": [ + 0.2330272, + -0.2990215, + 0.1749209, + 0.9086732 + ], + "scaling": [ + 0.9999998, + 0.9999994, + 0.9999992 + ] + }, + "53": { + "link": { + "name": "root", + "file": "models\\oculus-touch-v2-left.glb" + }, + "parent": "130", + "name": "ControllerModelLeft", + "translation": [ + -0.0117514, + 0.0008869, + 0.0161192 + ], + "scaling": [ + 0.9999998, + 0.9999998, + 0.9999997 + ] + }, + "54": { + "link": { + "name": "controller key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; +}; +var __accessCheck = (obj, member, msg) => { + if (!member.has(obj)) + throw TypeError("Cannot " + msg); +}; +var __privateGet = (obj, member, getter) => { + __accessCheck(obj, member, "read from private field"); + return getter ? getter.call(obj) : member.get(obj); +}; +var __privateAdd = (obj, member, value) => { + if (member.has(obj)) + throw TypeError("Cannot add the same private member more than once"); + member instanceof WeakSet ? member.add(obj) : member.set(obj, value); +}; +var __privateMethod = (obj, member, method) => { + __accessCheck(obj, member, "access private method"); + return method; +}; + +// node_modules/howler/dist/howler.js +var require_howler = __commonJS({ + "node_modules/howler/dist/howler.js"(exports) { + (function() { + "use strict"; + var HowlerGlobal2 = function() { + this.init(); + }; + HowlerGlobal2.prototype = { + /** + * Initialize the global Howler object. + * @return {Howler} + */ + init: function() { + var self2 = this || Howler2; + self2._counter = 1e3; + self2._html5AudioPool = []; + self2.html5PoolSize = 10; + self2._codecs = {}; + self2._howls = []; + self2._muted = false; + self2._volume = 1; + self2._canPlayEvent = "canplaythrough"; + self2._navigator = typeof window !== "undefined" && window.navigator ? window.navigator : null; + self2.masterGain = null; + self2.noAudio = false; + self2.usingWebAudio = true; + self2.autoSuspend = true; + self2.ctx = null; + self2.autoUnlock = true; + self2._setup(); + return self2; + }, + /** + * Get/set the global volume for all sounds. + * @param {Float} vol Volume from 0.0 to 1.0. + * @return {Howler/Float} Returns self or current volume. + */ + volume: function(vol) { + var self2 = this || Howler2; + vol = parseFloat(vol); + if (!self2.ctx) { + setupAudioContext(); + } + if (typeof vol !== "undefined" && vol >= 0 && vol <= 1) { + self2._volume = vol; + if (self2._muted) { + return self2; + } + if (self2.usingWebAudio) { + self2.masterGain.gain.setValueAtTime(vol, Howler2.ctx.currentTime); + } + for (var i = 0; i < self2._howls.length; i++) { + if (!self2._howls[i]._webAudio) { + var ids = self2._howls[i]._getSoundIds(); + for (var j = 0; j < ids.length; j++) { + var sound = self2._howls[i]._soundById(ids[j]); + if (sound && sound._node) { + sound._node.volume = sound._volume * vol; + } + } + } + } + return self2; + } + return self2._volume; + }, + /** + * Handle muting and unmuting globally. + * @param {Boolean} muted Is muted or not. + */ + mute: function(muted) { + var self2 = this || Howler2; + if (!self2.ctx) { + setupAudioContext(); + } + self2._muted = muted; + if (self2.usingWebAudio) { + self2.masterGain.gain.setValueAtTime(muted ? 0 : self2._volume, Howler2.ctx.currentTime); + } + for (var i = 0; i < self2._howls.length; i++) { + if (!self2._howls[i]._webAudio) { + var ids = self2._howls[i]._getSoundIds(); + for (var j = 0; j < ids.length; j++) { + var sound = self2._howls[i]._soundById(ids[j]); + if (sound && sound._node) { + sound._node.muted = muted ? true : sound._muted; + } + } + } + } + return self2; + }, + /** + * Handle stopping all sounds globally. + */ + stop: function() { + var self2 = this || Howler2; + for (var i = 0; i < self2._howls.length; i++) { + self2._howls[i].stop(); + } + return self2; + }, + /** + * Unload and destroy all currently loaded Howl objects. + * @return {Howler} + */ + unload: function() { + var self2 = this || Howler2; + for (var i = self2._howls.length - 1; i >= 0; i--) { + self2._howls[i].unload(); + } + if (self2.usingWebAudio && self2.ctx && typeof self2.ctx.close !== "undefined") { + self2.ctx.close(); + self2.ctx = null; + setupAudioContext(); + } + return self2; + }, + /** + * Check for codec support of specific extension. + * @param {String} ext Audio file extention. + * @return {Boolean} + */ + codecs: function(ext) { + return (this || Howler2)._codecs[ext.replace(/^x-/, "")]; + }, + /** + * Setup various state values for global tracking. + * @return {Howler} + */ + _setup: function() { + var self2 = this || Howler2; + self2.state = self2.ctx ? self2.ctx.state || "suspended" : "suspended"; + self2._autoSuspend(); + if (!self2.usingWebAudio) { + if (typeof Audio !== "undefined") { + try { + var test = new Audio(); + if (typeof test.oncanplaythrough === "undefined") { + self2._canPlayEvent = "canplay"; + } + } catch (e) { + self2.noAudio = true; + } + } else { + self2.noAudio = true; + } + } + try { + var test = new Audio(); + if (test.muted) { + self2.noAudio = true; + } + } catch (e) { + } + if (!self2.noAudio) { + self2._setupCodecs(); + } + return self2; + }, + /** + * Check for browser support for various codecs and cache the results. + * @return {Howler} + */ + _setupCodecs: function() { + var self2 = this || Howler2; + var audioTest = null; + try { + audioTest = typeof Audio !== "undefined" ? new Audio() : null; + } catch (err) { + return self2; + } + if (!audioTest || typeof audioTest.canPlayType !== "function") { + return self2; + } + var mpegTest = audioTest.canPlayType("audio/mpeg;").replace(/^no$/, ""); + var ua = self2._navigator ? self2._navigator.userAgent : ""; + var checkOpera = ua.match(/OPR\/([0-6].)/g); + var isOldOpera = checkOpera && parseInt(checkOpera[0].split("/")[1], 10) < 33; + var checkSafari = ua.indexOf("Safari") !== -1 && ua.indexOf("Chrome") === -1; + var safariVersion = ua.match(/Version\/(.*?) /); + var isOldSafari = checkSafari && safariVersion && parseInt(safariVersion[1], 10) < 15; + self2._codecs = { + mp3: !!(!isOldOpera && (mpegTest || audioTest.canPlayType("audio/mp3;").replace(/^no$/, ""))), + mpeg: !!mpegTest, + opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ""), + ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ""), + oga: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ""), + wav: !!(audioTest.canPlayType('audio/wav; codecs="1"') || audioTest.canPlayType("audio/wav")).replace(/^no$/, ""), + aac: !!audioTest.canPlayType("audio/aac;").replace(/^no$/, ""), + caf: !!audioTest.canPlayType("audio/x-caf;").replace(/^no$/, ""), + m4a: !!(audioTest.canPlayType("audio/x-m4a;") || audioTest.canPlayType("audio/m4a;") || audioTest.canPlayType("audio/aac;")).replace(/^no$/, ""), + m4b: !!(audioTest.canPlayType("audio/x-m4b;") || audioTest.canPlayType("audio/m4b;") || audioTest.canPlayType("audio/aac;")).replace(/^no$/, ""), + mp4: !!(audioTest.canPlayType("audio/x-mp4;") || audioTest.canPlayType("audio/mp4;") || audioTest.canPlayType("audio/aac;")).replace(/^no$/, ""), + weba: !!(!isOldSafari && audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, "")), + webm: !!(!isOldSafari && audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, "")), + dolby: !!audioTest.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/, ""), + flac: !!(audioTest.canPlayType("audio/x-flac;") || audioTest.canPlayType("audio/flac;")).replace(/^no$/, "") + }; + return self2; + }, + /** + * Some browsers/devices will only allow audio to be played after a user interaction. + * Attempt to automatically unlock audio on the first user interaction. + * Concept from: http://paulbakaus.com/tutorials/html5/web-audio-on-ios/ + * @return {Howler} + */ + _unlockAudio: function() { + var self2 = this || Howler2; + if (self2._audioUnlocked || !self2.ctx) { + return; + } + self2._audioUnlocked = false; + self2.autoUnlock = false; + if (!self2._mobileUnloaded && self2.ctx.sampleRate !== 44100) { + self2._mobileUnloaded = true; + self2.unload(); + } + self2._scratchBuffer = self2.ctx.createBuffer(1, 1, 22050); + var unlock = function(e) { + while (self2._html5AudioPool.length < self2.html5PoolSize) { + try { + var audioNode = new Audio(); + audioNode._unlocked = true; + self2._releaseHtml5Audio(audioNode); + } catch (e2) { + self2.noAudio = true; + break; + } + } + for (var i = 0; i < self2._howls.length; i++) { + if (!self2._howls[i]._webAudio) { + var ids = self2._howls[i]._getSoundIds(); + for (var j = 0; j < ids.length; j++) { + var sound = self2._howls[i]._soundById(ids[j]); + if (sound && sound._node && !sound._node._unlocked) { + sound._node._unlocked = true; + sound._node.load(); + } + } + } + } + self2._autoResume(); + var source = self2.ctx.createBufferSource(); + source.buffer = self2._scratchBuffer; + source.connect(self2.ctx.destination); + if (typeof source.start === "undefined") { + source.noteOn(0); + } else { + source.start(0); + } + if (typeof self2.ctx.resume === "function") { + self2.ctx.resume(); + } + source.onended = function() { + source.disconnect(0); + self2._audioUnlocked = true; + document.removeEventListener("touchstart", unlock, true); + document.removeEventListener("touchend", unlock, true); + document.removeEventListener("click", unlock, true); + document.removeEventListener("keydown", unlock, true); + for (var i2 = 0; i2 < self2._howls.length; i2++) { + self2._howls[i2]._emit("unlock"); + } + }; + }; + document.addEventListener("touchstart", unlock, true); + document.addEventListener("touchend", unlock, true); + document.addEventListener("click", unlock, true); + document.addEventListener("keydown", unlock, true); + return self2; + }, + /** + * Get an unlocked HTML5 Audio object from the pool. If none are left, + * return a new Audio object and throw a warning. + * @return {Audio} HTML5 Audio object. + */ + _obtainHtml5Audio: function() { + var self2 = this || Howler2; + if (self2._html5AudioPool.length) { + return self2._html5AudioPool.pop(); + } + var testPlay = new Audio().play(); + if (testPlay && typeof Promise !== "undefined" && (testPlay instanceof Promise || typeof testPlay.then === "function")) { + testPlay.catch(function() { + console.warn("HTML5 Audio pool exhausted, returning potentially locked audio object."); + }); + } + return new Audio(); + }, + /** + * Return an activated HTML5 Audio object to the pool. + * @return {Howler} + */ + _releaseHtml5Audio: function(audio) { + var self2 = this || Howler2; + if (audio._unlocked) { + self2._html5AudioPool.push(audio); + } + return self2; + }, + /** + * Automatically suspend the Web Audio AudioContext after no sound has played for 30 seconds. + * This saves processing/energy and fixes various browser-specific bugs with audio getting stuck. + * @return {Howler} + */ + _autoSuspend: function() { + var self2 = this; + if (!self2.autoSuspend || !self2.ctx || typeof self2.ctx.suspend === "undefined" || !Howler2.usingWebAudio) { + return; + } + for (var i = 0; i < self2._howls.length; i++) { + if (self2._howls[i]._webAudio) { + for (var j = 0; j < self2._howls[i]._sounds.length; j++) { + if (!self2._howls[i]._sounds[j]._paused) { + return self2; + } + } + } + } + if (self2._suspendTimer) { + clearTimeout(self2._suspendTimer); + } + self2._suspendTimer = setTimeout(function() { + if (!self2.autoSuspend) { + return; + } + self2._suspendTimer = null; + self2.state = "suspending"; + var handleSuspension = function() { + self2.state = "suspended"; + if (self2._resumeAfterSuspend) { + delete self2._resumeAfterSuspend; + self2._autoResume(); + } + }; + self2.ctx.suspend().then(handleSuspension, handleSuspension); + }, 3e4); + return self2; + }, + /** + * Automatically resume the Web Audio AudioContext when a new sound is played. + * @return {Howler} + */ + _autoResume: function() { + var self2 = this; + if (!self2.ctx || typeof self2.ctx.resume === "undefined" || !Howler2.usingWebAudio) { + return; + } + if (self2.state === "running" && self2.ctx.state !== "interrupted" && self2._suspendTimer) { + clearTimeout(self2._suspendTimer); + self2._suspendTimer = null; + } else if (self2.state === "suspended" || self2.state === "running" && self2.ctx.state === "interrupted") { + self2.ctx.resume().then(function() { + self2.state = "running"; + for (var i = 0; i < self2._howls.length; i++) { + self2._howls[i]._emit("resume"); + } + }); + if (self2._suspendTimer) { + clearTimeout(self2._suspendTimer); + self2._suspendTimer = null; + } + } else if (self2.state === "suspending") { + self2._resumeAfterSuspend = true; + } + return self2; + } + }; + var Howler2 = new HowlerGlobal2(); + var Howl2 = function(o) { + var self2 = this; + if (!o.src || o.src.length === 0) { + console.error("An array of source files must be passed with any new Howl."); + return; + } + self2.init(o); + }; + Howl2.prototype = { + /** + * Initialize a new Howl group object. + * @param {Object} o Passed in properties for this group. + * @return {Howl} + */ + init: function(o) { + var self2 = this; + if (!Howler2.ctx) { + setupAudioContext(); + } + self2._autoplay = o.autoplay || false; + self2._format = typeof o.format !== "string" ? o.format : [o.format]; + self2._html5 = o.html5 || false; + self2._muted = o.mute || false; + self2._loop = o.loop || false; + self2._pool = o.pool || 5; + self2._preload = typeof o.preload === "boolean" || o.preload === "metadata" ? o.preload : true; + self2._rate = o.rate || 1; + self2._sprite = o.sprite || {}; + self2._src = typeof o.src !== "string" ? o.src : [o.src]; + self2._volume = o.volume !== void 0 ? o.volume : 1; + self2._xhr = { + method: o.xhr && o.xhr.method ? o.xhr.method : "GET", + headers: o.xhr && o.xhr.headers ? o.xhr.headers : null, + withCredentials: o.xhr && o.xhr.withCredentials ? o.xhr.withCredentials : false + }; + self2._duration = 0; + self2._state = "unloaded"; + self2._sounds = []; + self2._endTimers = {}; + self2._queue = []; + self2._playLock = false; + self2._onend = o.onend ? [{ fn: o.onend }] : []; + self2._onfade = o.onfade ? [{ fn: o.onfade }] : []; + self2._onload = o.onload ? [{ fn: o.onload }] : []; + self2._onloaderror = o.onloaderror ? [{ fn: o.onloaderror }] : []; + self2._onplayerror = o.onplayerror ? [{ fn: o.onplayerror }] : []; + self2._onpause = o.onpause ? [{ fn: o.onpause }] : []; + self2._onplay = o.onplay ? [{ fn: o.onplay }] : []; + self2._onstop = o.onstop ? [{ fn: o.onstop }] : []; + self2._onmute = o.onmute ? [{ fn: o.onmute }] : []; + self2._onvolume = o.onvolume ? [{ fn: o.onvolume }] : []; + self2._onrate = o.onrate ? [{ fn: o.onrate }] : []; + self2._onseek = o.onseek ? [{ fn: o.onseek }] : []; + self2._onunlock = o.onunlock ? [{ fn: o.onunlock }] : []; + self2._onresume = []; + self2._webAudio = Howler2.usingWebAudio && !self2._html5; + if (typeof Howler2.ctx !== "undefined" && Howler2.ctx && Howler2.autoUnlock) { + Howler2._unlockAudio(); + } + Howler2._howls.push(self2); + if (self2._autoplay) { + self2._queue.push({ + event: "play", + action: function() { + self2.play(); + } + }); + } + if (self2._preload && self2._preload !== "none") { + self2.load(); + } + return self2; + }, + /** + * Load the audio file. + * @return {Howler} + */ + load: function() { + var self2 = this; + var url = null; + if (Howler2.noAudio) { + self2._emit("loaderror", null, "No audio support."); + return; + } + if (typeof self2._src === "string") { + self2._src = [self2._src]; + } + for (var i = 0; i < self2._src.length; i++) { + var ext, str5; + if (self2._format && self2._format[i]) { + ext = self2._format[i]; + } else { + str5 = self2._src[i]; + if (typeof str5 !== "string") { + self2._emit("loaderror", null, "Non-string found in selected audio sources - ignoring."); + continue; + } + ext = /^data:audio\/([^;,]+);/i.exec(str5); + if (!ext) { + ext = /\.([^.]+)$/.exec(str5.split("?", 1)[0]); + } + if (ext) { + ext = ext[1].toLowerCase(); + } + } + if (!ext) { + console.warn('No file extension was found. Consider using the "format" property or specify an extension.'); + } + if (ext && Howler2.codecs(ext)) { + url = self2._src[i]; + break; + } + } + if (!url) { + self2._emit("loaderror", null, "No codec support for selected audio sources."); + return; + } + self2._src = url; + self2._state = "loading"; + if (window.location.protocol === "https:" && url.slice(0, 5) === "http:") { + self2._html5 = true; + self2._webAudio = false; + } + new Sound2(self2); + if (self2._webAudio) { + loadBuffer(self2); + } + return self2; + }, + /** + * Play a sound or resume previous playback. + * @param {String/Number} sprite Sprite name for sprite playback or sound id to continue previous. + * @param {Boolean} internal Internal Use: true prevents event firing. + * @return {Number} Sound ID. + */ + play: function(sprite, internal) { + var self2 = this; + var id = null; + if (typeof sprite === "number") { + id = sprite; + sprite = null; + } else if (typeof sprite === "string" && self2._state === "loaded" && !self2._sprite[sprite]) { + return null; + } else if (typeof sprite === "undefined") { + sprite = "__default"; + if (!self2._playLock) { + var num = 0; + for (var i = 0; i < self2._sounds.length; i++) { + if (self2._sounds[i]._paused && !self2._sounds[i]._ended) { + num++; + id = self2._sounds[i]._id; + } + } + if (num === 1) { + sprite = null; + } else { + id = null; + } + } + } + var sound = id ? self2._soundById(id) : self2._inactiveSound(); + if (!sound) { + return null; + } + if (id && !sprite) { + sprite = sound._sprite || "__default"; + } + if (self2._state !== "loaded") { + sound._sprite = sprite; + sound._ended = false; + var soundId = sound._id; + self2._queue.push({ + event: "play", + action: function() { + self2.play(soundId); + } + }); + return soundId; + } + if (id && !sound._paused) { + if (!internal) { + self2._loadQueue("play"); + } + return sound._id; + } + if (self2._webAudio) { + Howler2._autoResume(); + } + var seek = Math.max(0, sound._seek > 0 ? sound._seek : self2._sprite[sprite][0] / 1e3); + var duration = Math.max(0, (self2._sprite[sprite][0] + self2._sprite[sprite][1]) / 1e3 - seek); + var timeout2 = duration * 1e3 / Math.abs(sound._rate); + var start = self2._sprite[sprite][0] / 1e3; + var stop = (self2._sprite[sprite][0] + self2._sprite[sprite][1]) / 1e3; + sound._sprite = sprite; + sound._ended = false; + var setParams = function() { + sound._paused = false; + sound._seek = seek; + sound._start = start; + sound._stop = stop; + sound._loop = !!(sound._loop || self2._sprite[sprite][2]); + }; + if (seek >= stop) { + self2._ended(sound); + return; + } + var node = sound._node; + if (self2._webAudio) { + var playWebAudio = function() { + self2._playLock = false; + setParams(); + self2._refreshBuffer(sound); + var vol = sound._muted || self2._muted ? 0 : sound._volume; + node.gain.setValueAtTime(vol, Howler2.ctx.currentTime); + sound._playStart = Howler2.ctx.currentTime; + if (typeof node.bufferSource.start === "undefined") { + sound._loop ? node.bufferSource.noteGrainOn(0, seek, 86400) : node.bufferSource.noteGrainOn(0, seek, duration); + } else { + sound._loop ? node.bufferSource.start(0, seek, 86400) : node.bufferSource.start(0, seek, duration); + } + if (timeout2 !== Infinity) { + self2._endTimers[sound._id] = setTimeout(self2._ended.bind(self2, sound), timeout2); + } + if (!internal) { + setTimeout(function() { + self2._emit("play", sound._id); + self2._loadQueue(); + }, 0); + } + }; + if (Howler2.state === "running" && Howler2.ctx.state !== "interrupted") { + playWebAudio(); + } else { + self2._playLock = true; + self2.once("resume", playWebAudio); + self2._clearTimer(sound._id); + } + } else { + var playHtml5 = function() { + node.currentTime = seek; + node.muted = sound._muted || self2._muted || Howler2._muted || node.muted; + node.volume = sound._volume * Howler2.volume(); + node.playbackRate = sound._rate; + try { + var play = node.play(); + if (play && typeof Promise !== "undefined" && (play instanceof Promise || typeof play.then === "function")) { + self2._playLock = true; + setParams(); + play.then(function() { + self2._playLock = false; + node._unlocked = true; + if (!internal) { + self2._emit("play", sound._id); + } else { + self2._loadQueue(); + } + }).catch(function() { + self2._playLock = false; + self2._emit("playerror", sound._id, "Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."); + sound._ended = true; + sound._paused = true; + }); + } else if (!internal) { + self2._playLock = false; + setParams(); + self2._emit("play", sound._id); + } + node.playbackRate = sound._rate; + if (node.paused) { + self2._emit("playerror", sound._id, "Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."); + return; + } + if (sprite !== "__default" || sound._loop) { + self2._endTimers[sound._id] = setTimeout(self2._ended.bind(self2, sound), timeout2); + } else { + self2._endTimers[sound._id] = function() { + self2._ended(sound); + node.removeEventListener("ended", self2._endTimers[sound._id], false); + }; + node.addEventListener("ended", self2._endTimers[sound._id], false); + } + } catch (err) { + self2._emit("playerror", sound._id, err); + } + }; + if (node.src === "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA") { + node.src = self2._src; + node.load(); + } + var loadedNoReadyState = window && window.ejecta || !node.readyState && Howler2._navigator.isCocoonJS; + if (node.readyState >= 3 || loadedNoReadyState) { + playHtml5(); + } else { + self2._playLock = true; + self2._state = "loading"; + var listener = function() { + self2._state = "loaded"; + playHtml5(); + node.removeEventListener(Howler2._canPlayEvent, listener, false); + }; + node.addEventListener(Howler2._canPlayEvent, listener, false); + self2._clearTimer(sound._id); + } + } + return sound._id; + }, + /** + * Pause playback and save current position. + * @param {Number} id The sound ID (empty to pause all in group). + * @return {Howl} + */ + pause: function(id) { + var self2 = this; + if (self2._state !== "loaded" || self2._playLock) { + self2._queue.push({ + event: "pause", + action: function() { + self2.pause(id); + } + }); + return self2; + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + self2._clearTimer(ids[i]); + var sound = self2._soundById(ids[i]); + if (sound && !sound._paused) { + sound._seek = self2.seek(ids[i]); + sound._rateSeek = 0; + sound._paused = true; + self2._stopFade(ids[i]); + if (sound._node) { + if (self2._webAudio) { + if (!sound._node.bufferSource) { + continue; + } + if (typeof sound._node.bufferSource.stop === "undefined") { + sound._node.bufferSource.noteOff(0); + } else { + sound._node.bufferSource.stop(0); + } + self2._cleanBuffer(sound._node); + } else if (!isNaN(sound._node.duration) || sound._node.duration === Infinity) { + sound._node.pause(); + } + } + } + if (!arguments[1]) { + self2._emit("pause", sound ? sound._id : null); + } + } + return self2; + }, + /** + * Stop playback and reset to start. + * @param {Number} id The sound ID (empty to stop all in group). + * @param {Boolean} internal Internal Use: true prevents event firing. + * @return {Howl} + */ + stop: function(id, internal) { + var self2 = this; + if (self2._state !== "loaded" || self2._playLock) { + self2._queue.push({ + event: "stop", + action: function() { + self2.stop(id); + } + }); + return self2; + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + self2._clearTimer(ids[i]); + var sound = self2._soundById(ids[i]); + if (sound) { + sound._seek = sound._start || 0; + sound._rateSeek = 0; + sound._paused = true; + sound._ended = true; + self2._stopFade(ids[i]); + if (sound._node) { + if (self2._webAudio) { + if (sound._node.bufferSource) { + if (typeof sound._node.bufferSource.stop === "undefined") { + sound._node.bufferSource.noteOff(0); + } else { + sound._node.bufferSource.stop(0); + } + self2._cleanBuffer(sound._node); + } + } else if (!isNaN(sound._node.duration) || sound._node.duration === Infinity) { + sound._node.currentTime = sound._start || 0; + sound._node.pause(); + if (sound._node.duration === Infinity) { + self2._clearSound(sound._node); + } + } + } + if (!internal) { + self2._emit("stop", sound._id); + } + } + } + return self2; + }, + /** + * Mute/unmute a single sound or all sounds in this Howl group. + * @param {Boolean} muted Set to true to mute and false to unmute. + * @param {Number} id The sound ID to update (omit to mute/unmute all). + * @return {Howl} + */ + mute: function(muted, id) { + var self2 = this; + if (self2._state !== "loaded" || self2._playLock) { + self2._queue.push({ + event: "mute", + action: function() { + self2.mute(muted, id); + } + }); + return self2; + } + if (typeof id === "undefined") { + if (typeof muted === "boolean") { + self2._muted = muted; + } else { + return self2._muted; + } + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + var sound = self2._soundById(ids[i]); + if (sound) { + sound._muted = muted; + if (sound._interval) { + self2._stopFade(sound._id); + } + if (self2._webAudio && sound._node) { + sound._node.gain.setValueAtTime(muted ? 0 : sound._volume, Howler2.ctx.currentTime); + } else if (sound._node) { + sound._node.muted = Howler2._muted ? true : muted; + } + self2._emit("mute", sound._id); + } + } + return self2; + }, + /** + * Get/set the volume of this sound or of the Howl group. This method can optionally take 0, 1 or 2 arguments. + * volume() -> Returns the group's volume value. + * volume(id) -> Returns the sound id's current volume. + * volume(vol) -> Sets the volume of all sounds in this Howl group. + * volume(vol, id) -> Sets the volume of passed sound id. + * @return {Howl/Number} Returns self or current volume. + */ + volume: function() { + var self2 = this; + var args = arguments; + var vol, id; + if (args.length === 0) { + return self2._volume; + } else if (args.length === 1 || args.length === 2 && typeof args[1] === "undefined") { + var ids = self2._getSoundIds(); + var index = ids.indexOf(args[0]); + if (index >= 0) { + id = parseInt(args[0], 10); + } else { + vol = parseFloat(args[0]); + } + } else if (args.length >= 2) { + vol = parseFloat(args[0]); + id = parseInt(args[1], 10); + } + var sound; + if (typeof vol !== "undefined" && vol >= 0 && vol <= 1) { + if (self2._state !== "loaded" || self2._playLock) { + self2._queue.push({ + event: "volume", + action: function() { + self2.volume.apply(self2, args); + } + }); + return self2; + } + if (typeof id === "undefined") { + self2._volume = vol; + } + id = self2._getSoundIds(id); + for (var i = 0; i < id.length; i++) { + sound = self2._soundById(id[i]); + if (sound) { + sound._volume = vol; + if (!args[2]) { + self2._stopFade(id[i]); + } + if (self2._webAudio && sound._node && !sound._muted) { + sound._node.gain.setValueAtTime(vol, Howler2.ctx.currentTime); + } else if (sound._node && !sound._muted) { + sound._node.volume = vol * Howler2.volume(); + } + self2._emit("volume", sound._id); + } + } + } else { + sound = id ? self2._soundById(id) : self2._sounds[0]; + return sound ? sound._volume : 0; + } + return self2; + }, + /** + * Fade a currently playing sound between two volumes (if no id is passed, all sounds will fade). + * @param {Number} from The value to fade from (0.0 to 1.0). + * @param {Number} to The volume to fade to (0.0 to 1.0). + * @param {Number} len Time in milliseconds to fade. + * @param {Number} id The sound id (omit to fade all sounds). + * @return {Howl} + */ + fade: function(from, to, len4, id) { + var self2 = this; + if (self2._state !== "loaded" || self2._playLock) { + self2._queue.push({ + event: "fade", + action: function() { + self2.fade(from, to, len4, id); + } + }); + return self2; + } + from = Math.min(Math.max(0, parseFloat(from)), 1); + to = Math.min(Math.max(0, parseFloat(to)), 1); + len4 = parseFloat(len4); + self2.volume(from, id); + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + var sound = self2._soundById(ids[i]); + if (sound) { + if (!id) { + self2._stopFade(ids[i]); + } + if (self2._webAudio && !sound._muted) { + var currentTime = Howler2.ctx.currentTime; + var end = currentTime + len4 / 1e3; + sound._volume = from; + sound._node.gain.setValueAtTime(from, currentTime); + sound._node.gain.linearRampToValueAtTime(to, end); + } + self2._startFadeInterval(sound, from, to, len4, ids[i], typeof id === "undefined"); + } + } + return self2; + }, + /** + * Starts the internal interval to fade a sound. + * @param {Object} sound Reference to sound to fade. + * @param {Number} from The value to fade from (0.0 to 1.0). + * @param {Number} to The volume to fade to (0.0 to 1.0). + * @param {Number} len Time in milliseconds to fade. + * @param {Number} id The sound id to fade. + * @param {Boolean} isGroup If true, set the volume on the group. + */ + _startFadeInterval: function(sound, from, to, len4, id, isGroup) { + var self2 = this; + var vol = from; + var diff = to - from; + var steps = Math.abs(diff / 0.01); + var stepLen = Math.max(4, steps > 0 ? len4 / steps : len4); + var lastTick = Date.now(); + sound._fadeTo = to; + sound._interval = setInterval(function() { + var tick = (Date.now() - lastTick) / len4; + lastTick = Date.now(); + vol += diff * tick; + vol = Math.round(vol * 100) / 100; + if (diff < 0) { + vol = Math.max(to, vol); + } else { + vol = Math.min(to, vol); + } + if (self2._webAudio) { + sound._volume = vol; + } else { + self2.volume(vol, sound._id, true); + } + if (isGroup) { + self2._volume = vol; + } + if (to < from && vol <= to || to > from && vol >= to) { + clearInterval(sound._interval); + sound._interval = null; + sound._fadeTo = null; + self2.volume(to, sound._id); + self2._emit("fade", sound._id); + } + }, stepLen); + }, + /** + * Internal method that stops the currently playing fade when + * a new fade starts, volume is changed or the sound is stopped. + * @param {Number} id The sound id. + * @return {Howl} + */ + _stopFade: function(id) { + var self2 = this; + var sound = self2._soundById(id); + if (sound && sound._interval) { + if (self2._webAudio) { + sound._node.gain.cancelScheduledValues(Howler2.ctx.currentTime); + } + clearInterval(sound._interval); + sound._interval = null; + self2.volume(sound._fadeTo, id); + sound._fadeTo = null; + self2._emit("fade", id); + } + return self2; + }, + /** + * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments. + * loop() -> Returns the group's loop value. + * loop(id) -> Returns the sound id's loop value. + * loop(loop) -> Sets the loop value for all sounds in this Howl group. + * loop(loop, id) -> Sets the loop value of passed sound id. + * @return {Howl/Boolean} Returns self or current loop value. + */ + loop: function() { + var self2 = this; + var args = arguments; + var loop, id, sound; + if (args.length === 0) { + return self2._loop; + } else if (args.length === 1) { + if (typeof args[0] === "boolean") { + loop = args[0]; + self2._loop = loop; + } else { + sound = self2._soundById(parseInt(args[0], 10)); + return sound ? sound._loop : false; + } + } else if (args.length === 2) { + loop = args[0]; + id = parseInt(args[1], 10); + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + sound = self2._soundById(ids[i]); + if (sound) { + sound._loop = loop; + if (self2._webAudio && sound._node && sound._node.bufferSource) { + sound._node.bufferSource.loop = loop; + if (loop) { + sound._node.bufferSource.loopStart = sound._start || 0; + sound._node.bufferSource.loopEnd = sound._stop; + if (self2.playing(ids[i])) { + self2.pause(ids[i], true); + self2.play(ids[i], true); + } + } + } + } + } + return self2; + }, + /** + * Get/set the playback rate of a sound. This method can optionally take 0, 1 or 2 arguments. + * rate() -> Returns the first sound node's current playback rate. + * rate(id) -> Returns the sound id's current playback rate. + * rate(rate) -> Sets the playback rate of all sounds in this Howl group. + * rate(rate, id) -> Sets the playback rate of passed sound id. + * @return {Howl/Number} Returns self or the current playback rate. + */ + rate: function() { + var self2 = this; + var args = arguments; + var rate, id; + if (args.length === 0) { + id = self2._sounds[0]._id; + } else if (args.length === 1) { + var ids = self2._getSoundIds(); + var index = ids.indexOf(args[0]); + if (index >= 0) { + id = parseInt(args[0], 10); + } else { + rate = parseFloat(args[0]); + } + } else if (args.length === 2) { + rate = parseFloat(args[0]); + id = parseInt(args[1], 10); + } + var sound; + if (typeof rate === "number") { + if (self2._state !== "loaded" || self2._playLock) { + self2._queue.push({ + event: "rate", + action: function() { + self2.rate.apply(self2, args); + } + }); + return self2; + } + if (typeof id === "undefined") { + self2._rate = rate; + } + id = self2._getSoundIds(id); + for (var i = 0; i < id.length; i++) { + sound = self2._soundById(id[i]); + if (sound) { + if (self2.playing(id[i])) { + sound._rateSeek = self2.seek(id[i]); + sound._playStart = self2._webAudio ? Howler2.ctx.currentTime : sound._playStart; + } + sound._rate = rate; + if (self2._webAudio && sound._node && sound._node.bufferSource) { + sound._node.bufferSource.playbackRate.setValueAtTime(rate, Howler2.ctx.currentTime); + } else if (sound._node) { + sound._node.playbackRate = rate; + } + var seek = self2.seek(id[i]); + var duration = (self2._sprite[sound._sprite][0] + self2._sprite[sound._sprite][1]) / 1e3 - seek; + var timeout2 = duration * 1e3 / Math.abs(sound._rate); + if (self2._endTimers[id[i]] || !sound._paused) { + self2._clearTimer(id[i]); + self2._endTimers[id[i]] = setTimeout(self2._ended.bind(self2, sound), timeout2); + } + self2._emit("rate", sound._id); + } + } + } else { + sound = self2._soundById(id); + return sound ? sound._rate : self2._rate; + } + return self2; + }, + /** + * Get/set the seek position of a sound. This method can optionally take 0, 1 or 2 arguments. + * seek() -> Returns the first sound node's current seek position. + * seek(id) -> Returns the sound id's current seek position. + * seek(seek) -> Sets the seek position of the first sound node. + * seek(seek, id) -> Sets the seek position of passed sound id. + * @return {Howl/Number} Returns self or the current seek position. + */ + seek: function() { + var self2 = this; + var args = arguments; + var seek, id; + if (args.length === 0) { + if (self2._sounds.length) { + id = self2._sounds[0]._id; + } + } else if (args.length === 1) { + var ids = self2._getSoundIds(); + var index = ids.indexOf(args[0]); + if (index >= 0) { + id = parseInt(args[0], 10); + } else if (self2._sounds.length) { + id = self2._sounds[0]._id; + seek = parseFloat(args[0]); + } + } else if (args.length === 2) { + seek = parseFloat(args[0]); + id = parseInt(args[1], 10); + } + if (typeof id === "undefined") { + return 0; + } + if (typeof seek === "number" && (self2._state !== "loaded" || self2._playLock)) { + self2._queue.push({ + event: "seek", + action: function() { + self2.seek.apply(self2, args); + } + }); + return self2; + } + var sound = self2._soundById(id); + if (sound) { + if (typeof seek === "number" && seek >= 0) { + var playing = self2.playing(id); + if (playing) { + self2.pause(id, true); + } + sound._seek = seek; + sound._ended = false; + self2._clearTimer(id); + if (!self2._webAudio && sound._node && !isNaN(sound._node.duration)) { + sound._node.currentTime = seek; + } + var seekAndEmit = function() { + if (playing) { + self2.play(id, true); + } + self2._emit("seek", id); + }; + if (playing && !self2._webAudio) { + var emitSeek = function() { + if (!self2._playLock) { + seekAndEmit(); + } else { + setTimeout(emitSeek, 0); + } + }; + setTimeout(emitSeek, 0); + } else { + seekAndEmit(); + } + } else { + if (self2._webAudio) { + var realTime = self2.playing(id) ? Howler2.ctx.currentTime - sound._playStart : 0; + var rateSeek = sound._rateSeek ? sound._rateSeek - sound._seek : 0; + return sound._seek + (rateSeek + realTime * Math.abs(sound._rate)); + } else { + return sound._node.currentTime; + } + } + } + return self2; + }, + /** + * Check if a specific sound is currently playing or not (if id is provided), or check if at least one of the sounds in the group is playing or not. + * @param {Number} id The sound id to check. If none is passed, the whole sound group is checked. + * @return {Boolean} True if playing and false if not. + */ + playing: function(id) { + var self2 = this; + if (typeof id === "number") { + var sound = self2._soundById(id); + return sound ? !sound._paused : false; + } + for (var i = 0; i < self2._sounds.length; i++) { + if (!self2._sounds[i]._paused) { + return true; + } + } + return false; + }, + /** + * Get the duration of this sound. Passing a sound id will return the sprite duration. + * @param {Number} id The sound id to check. If none is passed, return full source duration. + * @return {Number} Audio duration in seconds. + */ + duration: function(id) { + var self2 = this; + var duration = self2._duration; + var sound = self2._soundById(id); + if (sound) { + duration = self2._sprite[sound._sprite][1] / 1e3; + } + return duration; + }, + /** + * Returns the current loaded state of this Howl. + * @return {String} 'unloaded', 'loading', 'loaded' + */ + state: function() { + return this._state; + }, + /** + * Unload and destroy the current Howl object. + * This will immediately stop all sound instances attached to this group. + */ + unload: function() { + var self2 = this; + var sounds = self2._sounds; + for (var i = 0; i < sounds.length; i++) { + if (!sounds[i]._paused) { + self2.stop(sounds[i]._id); + } + if (!self2._webAudio) { + self2._clearSound(sounds[i]._node); + sounds[i]._node.removeEventListener("error", sounds[i]._errorFn, false); + sounds[i]._node.removeEventListener(Howler2._canPlayEvent, sounds[i]._loadFn, false); + sounds[i]._node.removeEventListener("ended", sounds[i]._endFn, false); + Howler2._releaseHtml5Audio(sounds[i]._node); + } + delete sounds[i]._node; + self2._clearTimer(sounds[i]._id); + } + var index = Howler2._howls.indexOf(self2); + if (index >= 0) { + Howler2._howls.splice(index, 1); + } + var remCache = true; + for (i = 0; i < Howler2._howls.length; i++) { + if (Howler2._howls[i]._src === self2._src || self2._src.indexOf(Howler2._howls[i]._src) >= 0) { + remCache = false; + break; + } + } + if (cache && remCache) { + delete cache[self2._src]; + } + Howler2.noAudio = false; + self2._state = "unloaded"; + self2._sounds = []; + self2 = null; + return null; + }, + /** + * Listen to a custom event. + * @param {String} event Event name. + * @param {Function} fn Listener to call. + * @param {Number} id (optional) Only listen to events for this sound. + * @param {Number} once (INTERNAL) Marks event to fire only once. + * @return {Howl} + */ + on: function(event, fn, id, once) { + var self2 = this; + var events = self2["_on" + event]; + if (typeof fn === "function") { + events.push(once ? { id, fn, once } : { id, fn }); + } + return self2; + }, + /** + * Remove a custom event. Call without parameters to remove all events. + * @param {String} event Event name. + * @param {Function} fn Listener to remove. Leave empty to remove all. + * @param {Number} id (optional) Only remove events for this sound. + * @return {Howl} + */ + off: function(event, fn, id) { + var self2 = this; + var events = self2["_on" + event]; + var i = 0; + if (typeof fn === "number") { + id = fn; + fn = null; + } + if (fn || id) { + for (i = 0; i < events.length; i++) { + var isId = id === events[i].id; + if (fn === events[i].fn && isId || !fn && isId) { + events.splice(i, 1); + break; + } + } + } else if (event) { + self2["_on" + event] = []; + } else { + var keys = Object.keys(self2); + for (i = 0; i < keys.length; i++) { + if (keys[i].indexOf("_on") === 0 && Array.isArray(self2[keys[i]])) { + self2[keys[i]] = []; + } + } + } + return self2; + }, + /** + * Listen to a custom event and remove it once fired. + * @param {String} event Event name. + * @param {Function} fn Listener to call. + * @param {Number} id (optional) Only listen to events for this sound. + * @return {Howl} + */ + once: function(event, fn, id) { + var self2 = this; + self2.on(event, fn, id, 1); + return self2; + }, + /** + * Emit all events of a specific type and pass the sound id. + * @param {String} event Event name. + * @param {Number} id Sound ID. + * @param {Number} msg Message to go with event. + * @return {Howl} + */ + _emit: function(event, id, msg) { + var self2 = this; + var events = self2["_on" + event]; + for (var i = events.length - 1; i >= 0; i--) { + if (!events[i].id || events[i].id === id || event === "load") { + setTimeout(function(fn) { + fn.call(this, id, msg); + }.bind(self2, events[i].fn), 0); + if (events[i].once) { + self2.off(event, events[i].fn, events[i].id); + } + } + } + self2._loadQueue(event); + return self2; + }, + /** + * Queue of actions initiated before the sound has loaded. + * These will be called in sequence, with the next only firing + * after the previous has finished executing (even if async like play). + * @return {Howl} + */ + _loadQueue: function(event) { + var self2 = this; + if (self2._queue.length > 0) { + var task = self2._queue[0]; + if (task.event === event) { + self2._queue.shift(); + self2._loadQueue(); + } + if (!event) { + task.action(); + } + } + return self2; + }, + /** + * Fired when playback ends at the end of the duration. + * @param {Sound} sound The sound object to work with. + * @return {Howl} + */ + _ended: function(sound) { + var self2 = this; + var sprite = sound._sprite; + if (!self2._webAudio && sound._node && !sound._node.paused && !sound._node.ended && sound._node.currentTime < sound._stop) { + setTimeout(self2._ended.bind(self2, sound), 100); + return self2; + } + var loop = !!(sound._loop || self2._sprite[sprite][2]); + self2._emit("end", sound._id); + if (!self2._webAudio && loop) { + self2.stop(sound._id, true).play(sound._id); + } + if (self2._webAudio && loop) { + self2._emit("play", sound._id); + sound._seek = sound._start || 0; + sound._rateSeek = 0; + sound._playStart = Howler2.ctx.currentTime; + var timeout2 = (sound._stop - sound._start) * 1e3 / Math.abs(sound._rate); + self2._endTimers[sound._id] = setTimeout(self2._ended.bind(self2, sound), timeout2); + } + if (self2._webAudio && !loop) { + sound._paused = true; + sound._ended = true; + sound._seek = sound._start || 0; + sound._rateSeek = 0; + self2._clearTimer(sound._id); + self2._cleanBuffer(sound._node); + Howler2._autoSuspend(); + } + if (!self2._webAudio && !loop) { + self2.stop(sound._id, true); + } + return self2; + }, + /** + * Clear the end timer for a sound playback. + * @param {Number} id The sound ID. + * @return {Howl} + */ + _clearTimer: function(id) { + var self2 = this; + if (self2._endTimers[id]) { + if (typeof self2._endTimers[id] !== "function") { + clearTimeout(self2._endTimers[id]); + } else { + var sound = self2._soundById(id); + if (sound && sound._node) { + sound._node.removeEventListener("ended", self2._endTimers[id], false); + } + } + delete self2._endTimers[id]; + } + return self2; + }, + /** + * Return the sound identified by this ID, or return null. + * @param {Number} id Sound ID + * @return {Object} Sound object or null. + */ + _soundById: function(id) { + var self2 = this; + for (var i = 0; i < self2._sounds.length; i++) { + if (id === self2._sounds[i]._id) { + return self2._sounds[i]; + } + } + return null; + }, + /** + * Return an inactive sound from the pool or create a new one. + * @return {Sound} Sound playback object. + */ + _inactiveSound: function() { + var self2 = this; + self2._drain(); + for (var i = 0; i < self2._sounds.length; i++) { + if (self2._sounds[i]._ended) { + return self2._sounds[i].reset(); + } + } + return new Sound2(self2); + }, + /** + * Drain excess inactive sounds from the pool. + */ + _drain: function() { + var self2 = this; + var limit = self2._pool; + var cnt = 0; + var i = 0; + if (self2._sounds.length < limit) { + return; + } + for (i = 0; i < self2._sounds.length; i++) { + if (self2._sounds[i]._ended) { + cnt++; + } + } + for (i = self2._sounds.length - 1; i >= 0; i--) { + if (cnt <= limit) { + return; + } + if (self2._sounds[i]._ended) { + if (self2._webAudio && self2._sounds[i]._node) { + self2._sounds[i]._node.disconnect(0); + } + self2._sounds.splice(i, 1); + cnt--; + } + } + }, + /** + * Get all ID's from the sounds pool. + * @param {Number} id Only return one ID if one is passed. + * @return {Array} Array of IDs. + */ + _getSoundIds: function(id) { + var self2 = this; + if (typeof id === "undefined") { + var ids = []; + for (var i = 0; i < self2._sounds.length; i++) { + ids.push(self2._sounds[i]._id); + } + return ids; + } else { + return [id]; + } + }, + /** + * Load the sound back into the buffer source. + * @param {Sound} sound The sound object to work with. + * @return {Howl} + */ + _refreshBuffer: function(sound) { + var self2 = this; + sound._node.bufferSource = Howler2.ctx.createBufferSource(); + sound._node.bufferSource.buffer = cache[self2._src]; + if (sound._panner) { + sound._node.bufferSource.connect(sound._panner); + } else { + sound._node.bufferSource.connect(sound._node); + } + sound._node.bufferSource.loop = sound._loop; + if (sound._loop) { + sound._node.bufferSource.loopStart = sound._start || 0; + sound._node.bufferSource.loopEnd = sound._stop || 0; + } + sound._node.bufferSource.playbackRate.setValueAtTime(sound._rate, Howler2.ctx.currentTime); + return self2; + }, + /** + * Prevent memory leaks by cleaning up the buffer source after playback. + * @param {Object} node Sound's audio node containing the buffer source. + * @return {Howl} + */ + _cleanBuffer: function(node) { + var self2 = this; + var isIOS = Howler2._navigator && Howler2._navigator.vendor.indexOf("Apple") >= 0; + if (Howler2._scratchBuffer && node.bufferSource) { + node.bufferSource.onended = null; + node.bufferSource.disconnect(0); + if (isIOS) { + try { + node.bufferSource.buffer = Howler2._scratchBuffer; + } catch (e) { + } + } + } + node.bufferSource = null; + return self2; + }, + /** + * Set the source to a 0-second silence to stop any downloading (except in IE). + * @param {Object} node Audio node to clear. + */ + _clearSound: function(node) { + var checkIE = /MSIE |Trident\//.test(Howler2._navigator && Howler2._navigator.userAgent); + if (!checkIE) { + node.src = "data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA"; + } + } + }; + var Sound2 = function(howl) { + this._parent = howl; + this.init(); + }; + Sound2.prototype = { + /** + * Initialize a new Sound object. + * @return {Sound} + */ + init: function() { + var self2 = this; + var parent = self2._parent; + self2._muted = parent._muted; + self2._loop = parent._loop; + self2._volume = parent._volume; + self2._rate = parent._rate; + self2._seek = 0; + self2._paused = true; + self2._ended = true; + self2._sprite = "__default"; + self2._id = ++Howler2._counter; + parent._sounds.push(self2); + self2.create(); + return self2; + }, + /** + * Create and setup a new sound object, whether HTML5 Audio or Web Audio. + * @return {Sound} + */ + create: function() { + var self2 = this; + var parent = self2._parent; + var volume = Howler2._muted || self2._muted || self2._parent._muted ? 0 : self2._volume; + if (parent._webAudio) { + self2._node = typeof Howler2.ctx.createGain === "undefined" ? Howler2.ctx.createGainNode() : Howler2.ctx.createGain(); + self2._node.gain.setValueAtTime(volume, Howler2.ctx.currentTime); + self2._node.paused = true; + self2._node.connect(Howler2.masterGain); + } else if (!Howler2.noAudio) { + self2._node = Howler2._obtainHtml5Audio(); + self2._errorFn = self2._errorListener.bind(self2); + self2._node.addEventListener("error", self2._errorFn, false); + self2._loadFn = self2._loadListener.bind(self2); + self2._node.addEventListener(Howler2._canPlayEvent, self2._loadFn, false); + self2._endFn = self2._endListener.bind(self2); + self2._node.addEventListener("ended", self2._endFn, false); + self2._node.src = parent._src; + self2._node.preload = parent._preload === true ? "auto" : parent._preload; + self2._node.volume = volume * Howler2.volume(); + self2._node.load(); + } + return self2; + }, + /** + * Reset the parameters of this sound to the original state (for recycle). + * @return {Sound} + */ + reset: function() { + var self2 = this; + var parent = self2._parent; + self2._muted = parent._muted; + self2._loop = parent._loop; + self2._volume = parent._volume; + self2._rate = parent._rate; + self2._seek = 0; + self2._rateSeek = 0; + self2._paused = true; + self2._ended = true; + self2._sprite = "__default"; + self2._id = ++Howler2._counter; + return self2; + }, + /** + * HTML5 Audio error listener callback. + */ + _errorListener: function() { + var self2 = this; + self2._parent._emit("loaderror", self2._id, self2._node.error ? self2._node.error.code : 0); + self2._node.removeEventListener("error", self2._errorFn, false); + }, + /** + * HTML5 Audio canplaythrough listener callback. + */ + _loadListener: function() { + var self2 = this; + var parent = self2._parent; + parent._duration = Math.ceil(self2._node.duration * 10) / 10; + if (Object.keys(parent._sprite).length === 0) { + parent._sprite = { __default: [0, parent._duration * 1e3] }; + } + if (parent._state !== "loaded") { + parent._state = "loaded"; + parent._emit("load"); + parent._loadQueue(); + } + self2._node.removeEventListener(Howler2._canPlayEvent, self2._loadFn, false); + }, + /** + * HTML5 Audio ended listener callback. + */ + _endListener: function() { + var self2 = this; + var parent = self2._parent; + if (parent._duration === Infinity) { + parent._duration = Math.ceil(self2._node.duration * 10) / 10; + if (parent._sprite.__default[1] === Infinity) { + parent._sprite.__default[1] = parent._duration * 1e3; + } + parent._ended(self2); + } + self2._node.removeEventListener("ended", self2._endFn, false); + } + }; + var cache = {}; + var loadBuffer = function(self2) { + var url = self2._src; + if (cache[url]) { + self2._duration = cache[url].duration; + loadSound(self2); + return; + } + if (/^data:[^;]+;base64,/.test(url)) { + var data = atob(url.split(",")[1]); + var dataView = new Uint8Array(data.length); + for (var i = 0; i < data.length; ++i) { + dataView[i] = data.charCodeAt(i); + } + decodeAudioData(dataView.buffer, self2); + } else { + var xhr = new XMLHttpRequest(); + xhr.open(self2._xhr.method, url, true); + xhr.withCredentials = self2._xhr.withCredentials; + xhr.responseType = "arraybuffer"; + if (self2._xhr.headers) { + Object.keys(self2._xhr.headers).forEach(function(key) { + xhr.setRequestHeader(key, self2._xhr.headers[key]); + }); + } + xhr.onload = function() { + var code = (xhr.status + "")[0]; + if (code !== "0" && code !== "2" && code !== "3") { + self2._emit("loaderror", null, "Failed loading audio file with status: " + xhr.status + "."); + return; + } + decodeAudioData(xhr.response, self2); + }; + xhr.onerror = function() { + if (self2._webAudio) { + self2._html5 = true; + self2._webAudio = false; + self2._sounds = []; + delete cache[url]; + self2.load(); + } + }; + safeXhrSend(xhr); + } + }; + var safeXhrSend = function(xhr) { + try { + xhr.send(); + } catch (e) { + xhr.onerror(); + } + }; + var decodeAudioData = function(arraybuffer, self2) { + var error = function() { + self2._emit("loaderror", null, "Decoding audio data failed."); + }; + var success = function(buffer) { + if (buffer && self2._sounds.length > 0) { + cache[self2._src] = buffer; + loadSound(self2, buffer); + } else { + error(); + } + }; + if (typeof Promise !== "undefined" && Howler2.ctx.decodeAudioData.length === 1) { + Howler2.ctx.decodeAudioData(arraybuffer).then(success).catch(error); + } else { + Howler2.ctx.decodeAudioData(arraybuffer, success, error); + } + }; + var loadSound = function(self2, buffer) { + if (buffer && !self2._duration) { + self2._duration = buffer.duration; + } + if (Object.keys(self2._sprite).length === 0) { + self2._sprite = { __default: [0, self2._duration * 1e3] }; + } + if (self2._state !== "loaded") { + self2._state = "loaded"; + self2._emit("load"); + self2._loadQueue(); + } + }; + var setupAudioContext = function() { + if (!Howler2.usingWebAudio) { + return; + } + try { + if (typeof AudioContext !== "undefined") { + Howler2.ctx = new AudioContext(); + } else if (typeof webkitAudioContext !== "undefined") { + Howler2.ctx = new webkitAudioContext(); + } else { + Howler2.usingWebAudio = false; + } + } catch (e) { + Howler2.usingWebAudio = false; + } + if (!Howler2.ctx) { + Howler2.usingWebAudio = false; + } + var iOS = /iP(hone|od|ad)/.test(Howler2._navigator && Howler2._navigator.platform); + var appVersion = Howler2._navigator && Howler2._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/); + var version = appVersion ? parseInt(appVersion[1], 10) : null; + if (iOS && version && version < 9) { + var safari = /safari/.test(Howler2._navigator && Howler2._navigator.userAgent.toLowerCase()); + if (Howler2._navigator && !safari) { + Howler2.usingWebAudio = false; + } + } + if (Howler2.usingWebAudio) { + Howler2.masterGain = typeof Howler2.ctx.createGain === "undefined" ? Howler2.ctx.createGainNode() : Howler2.ctx.createGain(); + Howler2.masterGain.gain.setValueAtTime(Howler2._muted ? 0 : Howler2._volume, Howler2.ctx.currentTime); + Howler2.masterGain.connect(Howler2.ctx.destination); + } + Howler2._setup(); + }; + if (typeof define === "function" && define.amd) { + define([], function() { + return { + Howler: Howler2, + Howl: Howl2 + }; + }); + } + if (typeof exports !== "undefined") { + exports.Howler = Howler2; + exports.Howl = Howl2; + } + if (typeof global !== "undefined") { + global.HowlerGlobal = HowlerGlobal2; + global.Howler = Howler2; + global.Howl = Howl2; + global.Sound = Sound2; + } else if (typeof window !== "undefined") { + window.HowlerGlobal = HowlerGlobal2; + window.Howler = Howler2; + window.Howl = Howl2; + window.Sound = Sound2; + } + })(); + (function() { + "use strict"; + HowlerGlobal.prototype._pos = [0, 0, 0]; + HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0]; + HowlerGlobal.prototype.stereo = function(pan) { + var self2 = this; + if (!self2.ctx || !self2.ctx.listener) { + return self2; + } + for (var i = self2._howls.length - 1; i >= 0; i--) { + self2._howls[i].stereo(pan); + } + return self2; + }; + HowlerGlobal.prototype.pos = function(x, y, z) { + var self2 = this; + if (!self2.ctx || !self2.ctx.listener) { + return self2; + } + y = typeof y !== "number" ? self2._pos[1] : y; + z = typeof z !== "number" ? self2._pos[2] : z; + if (typeof x === "number") { + self2._pos = [x, y, z]; + if (typeof self2.ctx.listener.positionX !== "undefined") { + self2.ctx.listener.positionX.setTargetAtTime(self2._pos[0], Howler.ctx.currentTime, 0.1); + self2.ctx.listener.positionY.setTargetAtTime(self2._pos[1], Howler.ctx.currentTime, 0.1); + self2.ctx.listener.positionZ.setTargetAtTime(self2._pos[2], Howler.ctx.currentTime, 0.1); + } else { + self2.ctx.listener.setPosition(self2._pos[0], self2._pos[1], self2._pos[2]); + } + } else { + return self2._pos; + } + return self2; + }; + HowlerGlobal.prototype.orientation = function(x, y, z, xUp, yUp, zUp) { + var self2 = this; + if (!self2.ctx || !self2.ctx.listener) { + return self2; + } + var or = self2._orientation; + y = typeof y !== "number" ? or[1] : y; + z = typeof z !== "number" ? or[2] : z; + xUp = typeof xUp !== "number" ? or[3] : xUp; + yUp = typeof yUp !== "number" ? or[4] : yUp; + zUp = typeof zUp !== "number" ? or[5] : zUp; + if (typeof x === "number") { + self2._orientation = [x, y, z, xUp, yUp, zUp]; + if (typeof self2.ctx.listener.forwardX !== "undefined") { + self2.ctx.listener.forwardX.setTargetAtTime(x, Howler.ctx.currentTime, 0.1); + self2.ctx.listener.forwardY.setTargetAtTime(y, Howler.ctx.currentTime, 0.1); + self2.ctx.listener.forwardZ.setTargetAtTime(z, Howler.ctx.currentTime, 0.1); + self2.ctx.listener.upX.setTargetAtTime(xUp, Howler.ctx.currentTime, 0.1); + self2.ctx.listener.upY.setTargetAtTime(yUp, Howler.ctx.currentTime, 0.1); + self2.ctx.listener.upZ.setTargetAtTime(zUp, Howler.ctx.currentTime, 0.1); + } else { + self2.ctx.listener.setOrientation(x, y, z, xUp, yUp, zUp); + } + } else { + return or; + } + return self2; + }; + Howl.prototype.init = function(_super) { + return function(o) { + var self2 = this; + self2._orientation = o.orientation || [1, 0, 0]; + self2._stereo = o.stereo || null; + self2._pos = o.pos || null; + self2._pannerAttr = { + coneInnerAngle: typeof o.coneInnerAngle !== "undefined" ? o.coneInnerAngle : 360, + coneOuterAngle: typeof o.coneOuterAngle !== "undefined" ? o.coneOuterAngle : 360, + coneOuterGain: typeof o.coneOuterGain !== "undefined" ? o.coneOuterGain : 0, + distanceModel: typeof o.distanceModel !== "undefined" ? o.distanceModel : "inverse", + maxDistance: typeof o.maxDistance !== "undefined" ? o.maxDistance : 1e4, + panningModel: typeof o.panningModel !== "undefined" ? o.panningModel : "HRTF", + refDistance: typeof o.refDistance !== "undefined" ? o.refDistance : 1, + rolloffFactor: typeof o.rolloffFactor !== "undefined" ? o.rolloffFactor : 1 + }; + self2._onstereo = o.onstereo ? [{ fn: o.onstereo }] : []; + self2._onpos = o.onpos ? [{ fn: o.onpos }] : []; + self2._onorientation = o.onorientation ? [{ fn: o.onorientation }] : []; + return _super.call(this, o); + }; + }(Howl.prototype.init); + Howl.prototype.stereo = function(pan, id) { + var self2 = this; + if (!self2._webAudio) { + return self2; + } + if (self2._state !== "loaded") { + self2._queue.push({ + event: "stereo", + action: function() { + self2.stereo(pan, id); + } + }); + return self2; + } + var pannerType = typeof Howler.ctx.createStereoPanner === "undefined" ? "spatial" : "stereo"; + if (typeof id === "undefined") { + if (typeof pan === "number") { + self2._stereo = pan; + self2._pos = [pan, 0, 0]; + } else { + return self2._stereo; + } + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + var sound = self2._soundById(ids[i]); + if (sound) { + if (typeof pan === "number") { + sound._stereo = pan; + sound._pos = [pan, 0, 0]; + if (sound._node) { + sound._pannerAttr.panningModel = "equalpower"; + if (!sound._panner || !sound._panner.pan) { + setupPanner(sound, pannerType); + } + if (pannerType === "spatial") { + if (typeof sound._panner.positionX !== "undefined") { + sound._panner.positionX.setValueAtTime(pan, Howler.ctx.currentTime); + sound._panner.positionY.setValueAtTime(0, Howler.ctx.currentTime); + sound._panner.positionZ.setValueAtTime(0, Howler.ctx.currentTime); + } else { + sound._panner.setPosition(pan, 0, 0); + } + } else { + sound._panner.pan.setValueAtTime(pan, Howler.ctx.currentTime); + } + } + self2._emit("stereo", sound._id); + } else { + return sound._stereo; + } + } + } + return self2; + }; + Howl.prototype.pos = function(x, y, z, id) { + var self2 = this; + if (!self2._webAudio) { + return self2; + } + if (self2._state !== "loaded") { + self2._queue.push({ + event: "pos", + action: function() { + self2.pos(x, y, z, id); + } + }); + return self2; + } + y = typeof y !== "number" ? 0 : y; + z = typeof z !== "number" ? -0.5 : z; + if (typeof id === "undefined") { + if (typeof x === "number") { + self2._pos = [x, y, z]; + } else { + return self2._pos; + } + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + var sound = self2._soundById(ids[i]); + if (sound) { + if (typeof x === "number") { + sound._pos = [x, y, z]; + if (sound._node) { + if (!sound._panner || sound._panner.pan) { + setupPanner(sound, "spatial"); + } + if (typeof sound._panner.positionX !== "undefined") { + sound._panner.positionX.setValueAtTime(x, Howler.ctx.currentTime); + sound._panner.positionY.setValueAtTime(y, Howler.ctx.currentTime); + sound._panner.positionZ.setValueAtTime(z, Howler.ctx.currentTime); + } else { + sound._panner.setPosition(x, y, z); + } + } + self2._emit("pos", sound._id); + } else { + return sound._pos; + } + } + } + return self2; + }; + Howl.prototype.orientation = function(x, y, z, id) { + var self2 = this; + if (!self2._webAudio) { + return self2; + } + if (self2._state !== "loaded") { + self2._queue.push({ + event: "orientation", + action: function() { + self2.orientation(x, y, z, id); + } + }); + return self2; + } + y = typeof y !== "number" ? self2._orientation[1] : y; + z = typeof z !== "number" ? self2._orientation[2] : z; + if (typeof id === "undefined") { + if (typeof x === "number") { + self2._orientation = [x, y, z]; + } else { + return self2._orientation; + } + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + var sound = self2._soundById(ids[i]); + if (sound) { + if (typeof x === "number") { + sound._orientation = [x, y, z]; + if (sound._node) { + if (!sound._panner) { + if (!sound._pos) { + sound._pos = self2._pos || [0, 0, -0.5]; + } + setupPanner(sound, "spatial"); + } + if (typeof sound._panner.orientationX !== "undefined") { + sound._panner.orientationX.setValueAtTime(x, Howler.ctx.currentTime); + sound._panner.orientationY.setValueAtTime(y, Howler.ctx.currentTime); + sound._panner.orientationZ.setValueAtTime(z, Howler.ctx.currentTime); + } else { + sound._panner.setOrientation(x, y, z); + } + } + self2._emit("orientation", sound._id); + } else { + return sound._orientation; + } + } + } + return self2; + }; + Howl.prototype.pannerAttr = function() { + var self2 = this; + var args = arguments; + var o, id, sound; + if (!self2._webAudio) { + return self2; + } + if (args.length === 0) { + return self2._pannerAttr; + } else if (args.length === 1) { + if (typeof args[0] === "object") { + o = args[0]; + if (typeof id === "undefined") { + if (!o.pannerAttr) { + o.pannerAttr = { + coneInnerAngle: o.coneInnerAngle, + coneOuterAngle: o.coneOuterAngle, + coneOuterGain: o.coneOuterGain, + distanceModel: o.distanceModel, + maxDistance: o.maxDistance, + refDistance: o.refDistance, + rolloffFactor: o.rolloffFactor, + panningModel: o.panningModel + }; + } + self2._pannerAttr = { + coneInnerAngle: typeof o.pannerAttr.coneInnerAngle !== "undefined" ? o.pannerAttr.coneInnerAngle : self2._coneInnerAngle, + coneOuterAngle: typeof o.pannerAttr.coneOuterAngle !== "undefined" ? o.pannerAttr.coneOuterAngle : self2._coneOuterAngle, + coneOuterGain: typeof o.pannerAttr.coneOuterGain !== "undefined" ? o.pannerAttr.coneOuterGain : self2._coneOuterGain, + distanceModel: typeof o.pannerAttr.distanceModel !== "undefined" ? o.pannerAttr.distanceModel : self2._distanceModel, + maxDistance: typeof o.pannerAttr.maxDistance !== "undefined" ? o.pannerAttr.maxDistance : self2._maxDistance, + refDistance: typeof o.pannerAttr.refDistance !== "undefined" ? o.pannerAttr.refDistance : self2._refDistance, + rolloffFactor: typeof o.pannerAttr.rolloffFactor !== "undefined" ? o.pannerAttr.rolloffFactor : self2._rolloffFactor, + panningModel: typeof o.pannerAttr.panningModel !== "undefined" ? o.pannerAttr.panningModel : self2._panningModel + }; + } + } else { + sound = self2._soundById(parseInt(args[0], 10)); + return sound ? sound._pannerAttr : self2._pannerAttr; + } + } else if (args.length === 2) { + o = args[0]; + id = parseInt(args[1], 10); + } + var ids = self2._getSoundIds(id); + for (var i = 0; i < ids.length; i++) { + sound = self2._soundById(ids[i]); + if (sound) { + var pa = sound._pannerAttr; + pa = { + coneInnerAngle: typeof o.coneInnerAngle !== "undefined" ? o.coneInnerAngle : pa.coneInnerAngle, + coneOuterAngle: typeof o.coneOuterAngle !== "undefined" ? o.coneOuterAngle : pa.coneOuterAngle, + coneOuterGain: typeof o.coneOuterGain !== "undefined" ? o.coneOuterGain : pa.coneOuterGain, + distanceModel: typeof o.distanceModel !== "undefined" ? o.distanceModel : pa.distanceModel, + maxDistance: typeof o.maxDistance !== "undefined" ? o.maxDistance : pa.maxDistance, + refDistance: typeof o.refDistance !== "undefined" ? o.refDistance : pa.refDistance, + rolloffFactor: typeof o.rolloffFactor !== "undefined" ? o.rolloffFactor : pa.rolloffFactor, + panningModel: typeof o.panningModel !== "undefined" ? o.panningModel : pa.panningModel + }; + var panner = sound._panner; + if (panner) { + panner.coneInnerAngle = pa.coneInnerAngle; + panner.coneOuterAngle = pa.coneOuterAngle; + panner.coneOuterGain = pa.coneOuterGain; + panner.distanceModel = pa.distanceModel; + panner.maxDistance = pa.maxDistance; + panner.refDistance = pa.refDistance; + panner.rolloffFactor = pa.rolloffFactor; + panner.panningModel = pa.panningModel; + } else { + if (!sound._pos) { + sound._pos = self2._pos || [0, 0, -0.5]; + } + setupPanner(sound, "spatial"); + } + } + } + return self2; + }; + Sound.prototype.init = function(_super) { + return function() { + var self2 = this; + var parent = self2._parent; + self2._orientation = parent._orientation; + self2._stereo = parent._stereo; + self2._pos = parent._pos; + self2._pannerAttr = parent._pannerAttr; + _super.call(this); + if (self2._stereo) { + parent.stereo(self2._stereo); + } else if (self2._pos) { + parent.pos(self2._pos[0], self2._pos[1], self2._pos[2], self2._id); + } + }; + }(Sound.prototype.init); + Sound.prototype.reset = function(_super) { + return function() { + var self2 = this; + var parent = self2._parent; + self2._orientation = parent._orientation; + self2._stereo = parent._stereo; + self2._pos = parent._pos; + self2._pannerAttr = parent._pannerAttr; + if (self2._stereo) { + parent.stereo(self2._stereo); + } else if (self2._pos) { + parent.pos(self2._pos[0], self2._pos[1], self2._pos[2], self2._id); + } else if (self2._panner) { + self2._panner.disconnect(0); + self2._panner = void 0; + parent._refreshBuffer(self2); + } + return _super.call(this); + }; + }(Sound.prototype.reset); + var setupPanner = function(sound, type) { + type = type || "spatial"; + if (type === "spatial") { + sound._panner = Howler.ctx.createPanner(); + sound._panner.coneInnerAngle = sound._pannerAttr.coneInnerAngle; + sound._panner.coneOuterAngle = sound._pannerAttr.coneOuterAngle; + sound._panner.coneOuterGain = sound._pannerAttr.coneOuterGain; + sound._panner.distanceModel = sound._pannerAttr.distanceModel; + sound._panner.maxDistance = sound._pannerAttr.maxDistance; + sound._panner.refDistance = sound._pannerAttr.refDistance; + sound._panner.rolloffFactor = sound._pannerAttr.rolloffFactor; + sound._panner.panningModel = sound._pannerAttr.panningModel; + if (typeof sound._panner.positionX !== "undefined") { + sound._panner.positionX.setValueAtTime(sound._pos[0], Howler.ctx.currentTime); + sound._panner.positionY.setValueAtTime(sound._pos[1], Howler.ctx.currentTime); + sound._panner.positionZ.setValueAtTime(sound._pos[2], Howler.ctx.currentTime); + } else { + sound._panner.setPosition(sound._pos[0], sound._pos[1], sound._pos[2]); + } + if (typeof sound._panner.orientationX !== "undefined") { + sound._panner.orientationX.setValueAtTime(sound._orientation[0], Howler.ctx.currentTime); + sound._panner.orientationY.setValueAtTime(sound._orientation[1], Howler.ctx.currentTime); + sound._panner.orientationZ.setValueAtTime(sound._orientation[2], Howler.ctx.currentTime); + } else { + sound._panner.setOrientation(sound._orientation[0], sound._orientation[1], sound._orientation[2]); + } + } else { + sound._panner = Howler.ctx.createStereoPanner(); + sound._panner.pan.setValueAtTime(sound._stereo, Howler.ctx.currentTime); + } + sound._panner.connect(sound._node); + if (!sound._paused) { + sound._parent.pause(sound._id, true).play(sound._id, true); + } + }; + })(); + } +}); + +// node_modules/earcut/src/earcut.js +var require_earcut = __commonJS({ + "node_modules/earcut/src/earcut.js"(exports, module) { + "use strict"; + module.exports = earcut2; + module.exports.default = earcut2; + function earcut2(data, holeIndices, dim) { + dim = dim || 2; + var hasHoles = holeIndices && holeIndices.length, outerLen = hasHoles ? holeIndices[0] * dim : data.length, outerNode = linkedList(data, 0, outerLen, dim, true), triangles = []; + if (!outerNode || outerNode.next === outerNode.prev) + return triangles; + var minX, minY, maxX, maxY, x, y, invSize; + if (hasHoles) + outerNode = eliminateHoles(data, holeIndices, outerNode, dim); + if (data.length > 80 * dim) { + minX = maxX = data[0]; + minY = maxY = data[1]; + for (var i = dim; i < outerLen; i += dim) { + x = data[i]; + y = data[i + 1]; + if (x < minX) + minX = x; + if (y < minY) + minY = y; + if (x > maxX) + maxX = x; + if (y > maxY) + maxY = y; + } + invSize = Math.max(maxX - minX, maxY - minY); + invSize = invSize !== 0 ? 32767 / invSize : 0; + } + earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); + return triangles; + } + function linkedList(data, start, end, dim, clockwise) { + var i, last; + if (clockwise === signedArea(data, start, end, dim) > 0) { + for (i = start; i < end; i += dim) + last = insertNode(i, data[i], data[i + 1], last); + } else { + for (i = end - dim; i >= start; i -= dim) + last = insertNode(i, data[i], data[i + 1], last); + } + if (last && equals6(last, last.next)) { + removeNode(last); + last = last.next; + } + return last; + } + function filterPoints(start, end) { + if (!start) + return start; + if (!end) + end = start; + var p = start, again; + do { + again = false; + if (!p.steiner && (equals6(p, p.next) || area(p.prev, p, p.next) === 0)) { + removeNode(p); + p = end = p.prev; + if (p === p.next) + break; + again = true; + } else { + p = p.next; + } + } while (again || p !== end); + return end; + } + function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { + if (!ear) + return; + if (!pass && invSize) + indexCurve(ear, minX, minY, invSize); + var stop = ear, prev, next; + while (ear.prev !== ear.next) { + prev = ear.prev; + next = ear.next; + if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { + triangles.push(prev.i / dim | 0); + triangles.push(ear.i / dim | 0); + triangles.push(next.i / dim | 0); + removeNode(ear); + ear = next.next; + stop = next.next; + continue; + } + ear = next; + if (ear === stop) { + if (!pass) { + earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); + } else if (pass === 1) { + ear = cureLocalIntersections(filterPoints(ear), triangles, dim); + earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); + } else if (pass === 2) { + splitEarcut(ear, triangles, dim, minX, minY, invSize); + } + break; + } + } + } + function isEar(ear) { + var a = ear.prev, b = ear, c = ear.next; + if (area(a, b, c) >= 0) + return false; + var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + var x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy; + var p = c.next; + while (p !== a) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) + return false; + p = p.next; + } + return true; + } + function isEarHashed(ear, minX, minY, invSize) { + var a = ear.prev, b = ear, c = ear.next; + if (area(a, b, c) >= 0) + return false; + var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + var x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy; + var minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize); + var p = ear.prevZ, n = ear.nextZ; + while (p && p.z >= minZ && n && n.z <= maxZ) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) + return false; + p = p.prevZ; + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) + return false; + n = n.nextZ; + } + while (p && p.z >= minZ) { + if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) + return false; + p = p.prevZ; + } + while (n && n.z <= maxZ) { + if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) + return false; + n = n.nextZ; + } + return true; + } + function cureLocalIntersections(start, triangles, dim) { + var p = start; + do { + var a = p.prev, b = p.next.next; + if (!equals6(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { + triangles.push(a.i / dim | 0); + triangles.push(p.i / dim | 0); + triangles.push(b.i / dim | 0); + removeNode(p); + removeNode(p.next); + p = start = b; + } + p = p.next; + } while (p !== start); + return filterPoints(p); + } + function splitEarcut(start, triangles, dim, minX, minY, invSize) { + var a = start; + do { + var b = a.next.next; + while (b !== a.prev) { + if (a.i !== b.i && isValidDiagonal(a, b)) { + var c = splitPolygon(a, b); + a = filterPoints(a, a.next); + c = filterPoints(c, c.next); + earcutLinked(a, triangles, dim, minX, minY, invSize, 0); + earcutLinked(c, triangles, dim, minX, minY, invSize, 0); + return; + } + b = b.next; + } + a = a.next; + } while (a !== start); + } + function eliminateHoles(data, holeIndices, outerNode, dim) { + var queue = [], i, len4, start, end, list; + for (i = 0, len4 = holeIndices.length; i < len4; i++) { + start = holeIndices[i] * dim; + end = i < len4 - 1 ? holeIndices[i + 1] * dim : data.length; + list = linkedList(data, start, end, dim, false); + if (list === list.next) + list.steiner = true; + queue.push(getLeftmost(list)); + } + queue.sort(compareX); + for (i = 0; i < queue.length; i++) { + outerNode = eliminateHole(queue[i], outerNode); + } + return outerNode; + } + function compareX(a, b) { + return a.x - b.x; + } + function eliminateHole(hole, outerNode) { + var bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; + } + var bridgeReverse = splitPolygon(bridge, hole); + filterPoints(bridgeReverse, bridgeReverse.next); + return filterPoints(bridge, bridge.next); + } + function findHoleBridge(hole, outerNode) { + var p = outerNode, hx = hole.x, hy = hole.y, qx = -Infinity, m; + do { + if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { + var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); + if (x <= hx && x > qx) { + qx = x; + m = p.x < p.next.x ? p : p.next; + if (x === hx) + return m; + } + } + p = p.next; + } while (p !== outerNode); + if (!m) + return null; + var stop = m, mx = m.x, my = m.y, tanMin = Infinity, tan; + p = m; + do { + if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { + tan = Math.abs(hy - p.y) / (hx - p.x); + if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { + m = p; + tanMin = tan; + } + } + p = p.next; + } while (p !== stop); + return m; + } + function sectorContainsSector(m, p) { + return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; + } + function indexCurve(start, minX, minY, invSize) { + var p = start; + do { + if (p.z === 0) + p.z = zOrder(p.x, p.y, minX, minY, invSize); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; + } while (p !== start); + p.prevZ.nextZ = null; + p.prevZ = null; + sortLinked(p); + } + function sortLinked(list) { + var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; + do { + p = list; + list = null; + tail = null; + numMerges = 0; + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q.nextZ; + if (!q) + break; + } + qSize = inSize; + while (pSize > 0 || qSize > 0 && q) { + if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { + e = p; + p = p.nextZ; + pSize--; + } else { + e = q; + q = q.nextZ; + qSize--; + } + if (tail) + tail.nextZ = e; + else + list = e; + e.prevZ = tail; + tail = e; + } + p = q; + } + tail.nextZ = null; + inSize *= 2; + } while (numMerges > 1); + return list; + } + function zOrder(x, y, minX, minY, invSize) { + x = (x - minX) * invSize | 0; + y = (y - minY) * invSize | 0; + x = (x | x << 8) & 16711935; + x = (x | x << 4) & 252645135; + x = (x | x << 2) & 858993459; + x = (x | x << 1) & 1431655765; + y = (y | y << 8) & 16711935; + y = (y | y << 4) & 252645135; + y = (y | y << 2) & 858993459; + y = (y | y << 1) & 1431655765; + return x | y << 1; + } + function getLeftmost(start) { + var p = start, leftmost = start; + do { + if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) + leftmost = p; + p = p.next; + } while (p !== start); + return leftmost; + } + function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py); + } + function isValidDiagonal(a, b) { + return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges + (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors + equals6(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); + } + function area(p, q, r) { + return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); + } + function equals6(p1, p2) { + return p1.x === p2.x && p1.y === p2.y; + } + function intersects(p1, q1, p2, q2) { + var o1 = sign(area(p1, q1, p2)); + var o2 = sign(area(p1, q1, q2)); + var o3 = sign(area(p2, q2, p1)); + var o4 = sign(area(p2, q2, q1)); + if (o1 !== o2 && o3 !== o4) + return true; + if (o1 === 0 && onSegment(p1, p2, q1)) + return true; + if (o2 === 0 && onSegment(p1, q2, q1)) + return true; + if (o3 === 0 && onSegment(p2, p1, q2)) + return true; + if (o4 === 0 && onSegment(p2, q1, q2)) + return true; + return false; + } + function onSegment(p, q, r) { + return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); + } + function sign(num) { + return num > 0 ? 1 : num < 0 ? -1 : 0; + } + function intersectsPolygon(a, b) { + var p = a; + do { + if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) + return true; + p = p.next; + } while (p !== a); + return false; + } + function locallyInside(a, b) { + return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; + } + function middleInside(a, b) { + var p = a, inside = false, px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; + do { + if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) + inside = !inside; + p = p.next; + } while (p !== a); + return inside; + } + function splitPolygon(a, b) { + var a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; + a.next = b; + b.prev = a; + a2.next = an; + an.prev = a2; + b2.next = a2; + a2.prev = b2; + bp.next = b2; + b2.prev = bp; + return b2; + } + function insertNode(i, x, y, last) { + var p = new Node(i, x, y); + if (!last) { + p.prev = p; + p.next = p; + } else { + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; + } + return p; + } + function removeNode(p) { + p.next.prev = p.prev; + p.prev.next = p.next; + if (p.prevZ) + p.prevZ.nextZ = p.nextZ; + if (p.nextZ) + p.nextZ.prevZ = p.prevZ; + } + function Node(i, x, y) { + this.i = i; + this.x = x; + this.y = y; + this.prev = null; + this.next = null; + this.z = 0; + this.prevZ = null; + this.nextZ = null; + this.steiner = false; + } + earcut2.deviation = function(data, holeIndices, dim, triangles) { + var hasHoles = holeIndices && holeIndices.length; + var outerLen = hasHoles ? holeIndices[0] * dim : data.length; + var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim)); + if (hasHoles) { + for (var i = 0, len4 = holeIndices.length; i < len4; i++) { + var start = holeIndices[i] * dim; + var end = i < len4 - 1 ? holeIndices[i + 1] * dim : data.length; + polygonArea -= Math.abs(signedArea(data, start, end, dim)); + } + } + var trianglesArea = 0; + for (i = 0; i < triangles.length; i += 3) { + var a = triangles[i] * dim; + var b = triangles[i + 1] * dim; + var c = triangles[i + 2] * dim; + trianglesArea += Math.abs( + (data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1]) + ); + } + return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea); + }; + function signedArea(data, start, end, dim) { + var sum = 0; + for (var i = start, j = end - dim; i < end; i += dim) { + sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); + j = i; + } + return sum; + } + earcut2.flatten = function(data) { + var dim = data[0][0].length, result = { vertices: [], holes: [], dimensions: dim }, holeIndex = 0; + for (var i = 0; i < data.length; i++) { + for (var j = 0; j < data[i].length; j++) { + for (var d = 0; d < dim; d++) + result.vertices.push(data[i][j][d]); + } + if (i > 0) { + holeIndex += data[i - 1].length; + result.holes.push(holeIndex); + } + } + return result; + }; + } +}); + +// node_modules/@wonderlandengine/api/dist/index.js +var dist_exports = {}; +__export(dist_exports, { + APIVersion: () => APIVersion, + Alignment: () => Alignment, + Animation: () => Animation, + AnimationComponent: () => AnimationComponent, + AnimationState: () => AnimationState, + ArrayBufferSink: () => ArrayBufferSink, + ArrayBufferSource: () => ArrayBufferSource, + BitSet: () => BitSet, + BrokenComponent: () => BrokenComponent, + CBORReader: () => CBORReader, + CBORType: () => CBORType, + ChunkedSceneLoadSink: () => ChunkedSceneLoadSink, + Collider: () => Collider, + CollisionComponent: () => CollisionComponent, + CollisionEventType: () => CollisionEventType, + Component: () => Component3, + DefaultPropertyCloner: () => DefaultPropertyCloner, + DestroyedComponentInstance: () => DestroyedComponentInstance, + DestroyedObjectInstance: () => DestroyedObjectInstance, + DestroyedPrefabInstance: () => DestroyedPrefabInstance, + Emitter: () => Emitter, + Environment: () => Environment, + Font: () => Font, + ForceMode: () => ForceMode, + GLTFExtensions: () => GLTFExtensions, + I18N: () => I18N, + InputComponent: () => InputComponent, + InputType: () => InputType, + Justification: () => Justification, + LightComponent: () => LightComponent, + LightType: () => LightType, + LockAxis: () => LockAxis, + LogLevel: () => LogLevel, + LogTag: () => LogTag, + Logger: () => Logger, + Material: () => Material, + MaterialManager: () => MaterialManager, + MaterialParamType: () => MaterialParamType, + Mesh: () => Mesh, + MeshAttribute: () => MeshAttribute, + MeshAttributeAccessor: () => MeshAttributeAccessor, + MeshComponent: () => MeshComponent, + MeshIndexType: () => MeshIndexType, + MeshManager: () => MeshManager, + MeshSkinningType: () => MeshSkinningType, + MorphTargets: () => MorphTargets, + Object: () => Object3D2, + Object3D: () => Object3D2, + ParticleEffect: () => ParticleEffect, + ParticleEffectComponent: () => ParticleEffectComponent, + PhysXComponent: () => PhysXComponent, + Physics: () => Physics, + Prefab: () => Prefab, + PrefabGLTF: () => PrefabGLTF, + ProjectionType: () => ProjectionType, + Property: () => Property, + RayHit: () => RayHit, + Resource: () => Resource, + ResourceManager: () => ResourceManager, + RetainEmitter: () => RetainEmitter, + RootMotionMode: () => RootMotionMode, + Scene: () => Scene, + SceneResource: () => SceneResource, + SceneType: () => SceneType, + Shape: () => Shape, + Skin: () => Skin, + TextComponent: () => TextComponent, + TextEffect: () => TextEffect, + TextWrapMode: () => TextWrapMode, + Texture: () => Texture, + TextureManager: () => TextureManager, + Type: () => Type, + VerticalAlignment: () => VerticalAlignment, + ViewComponent: () => ViewComponent, + WASM: () => WASM, + WebXR: () => WebXR, + WonderlandEngine: () => WonderlandEngine, + XRSessionState: () => XRSessionState, + capitalizeFirstUTF8: () => capitalizeFirstUTF8, + checkRuntimeCompatibility: () => checkRuntimeCompatibility, + clamp: () => clamp, + createDestroyedProxy: () => createDestroyedProxy2, + decode: () => decode, + defaultPropertyCloner: () => defaultPropertyCloner, + enumerable: () => enumerable, + fetchStreamWithProgress: () => fetchStreamWithProgress, + fetchWithProgress: () => fetchWithProgress, + getBaseUrl: () => getBaseUrl, + getFilename: () => getFilename, + getType: () => getType, + inheritProperties: () => inheritProperties, + isImageLike: () => isImageLike, + isNumber: () => isNumber, + isString: () => isString, + isUndefined: () => isUndefined, + loadRuntime: () => loadRuntime, + math: () => math, + nativeProperty: () => nativeProperty, + onImageReady: () => onImageReady, + property: () => property, + timeout: () => timeout +}); +var __defProp2 = Object.defineProperty; +var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; +var __decorateClass = (decorators, target, key, kind) => { + for (var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc2(target, key) : target, i = decorators.length - 1, decorator; i >= 0; i--) + (decorator = decorators[i]) && (result = (kind ? decorator(target, key, result) : decorator(result)) || result); + return kind && result && __defProp2(target, key, result), result; +}; +var threads = () => (async (e) => { + try { + return typeof MessageChannel < "u" && new MessageChannel().port1.postMessage(new SharedArrayBuffer(1)), WebAssembly.validate(e); + } catch { + return false; + } +})(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 5, 4, 1, 3, 1, 1, 10, 11, 1, 9, 0, 65, 0, 254, 16, 2, 0, 26, 11])); +var Type = ((Type2) => (Type2[Type2.Native = 0] = "Native", Type2[Type2.Bool = 1] = "Bool", Type2[Type2.Int = 2] = "Int", Type2[Type2.Float = 3] = "Float", Type2[Type2.String = 4] = "String", Type2[Type2.Enum = 5] = "Enum", Type2[Type2.Object = 6] = "Object", Type2[Type2.Mesh = 7] = "Mesh", Type2[Type2.Texture = 8] = "Texture", Type2[Type2.Material = 9] = "Material", Type2[Type2.Animation = 10] = "Animation", Type2[Type2.Skin = 11] = "Skin", Type2[Type2.Color = 12] = "Color", Type2[Type2.Vector2 = 13] = "Vector2", Type2[Type2.Vector3 = 14] = "Vector3", Type2[Type2.Vector4 = 15] = "Vector4", Type2[Type2.Array = 16] = "Array", Type2[Type2.Record = 17] = "Record", Type2[Type2.ParticleEffect = 18] = "ParticleEffect", Type2[Type2.Count = 19] = "Count", Type2))(Type || {}); +var DefaultPropertyCloner = class { + clone(type, value) { + switch (type) { + case 12: + case 13: + case 14: + case 15: + case 16: + return value.slice(); + case 17: { + if (!value || !value.constructor) + return value; + let RecordClass = value.constructor, result = new RecordClass(); + for (let key in RecordClass.Properties) { + let prop = RecordClass.Properties[key]; + result[key] = this.clone(prop.type, value[key]); + } + return result; + } + default: + return value; + } + } +}; +var defaultPropertyCloner = new DefaultPropertyCloner(); +var Property = { bool(defaultValue = false) { + return { type: 1, default: defaultValue }; +}, int(defaultValue = 0) { + return { type: 2, default: defaultValue }; +}, float(defaultValue = 0) { + return { type: 3, default: defaultValue }; +}, string(defaultValue = "") { + return { type: 4, default: defaultValue }; +}, enum(values, defaultValue) { + return { type: 5, values, default: defaultValue }; +}, object(opts) { + return { type: 6, default: null, required: opts?.required ?? false }; +}, mesh(opts) { + return { type: 7, default: null, required: opts?.required ?? false }; +}, texture(opts) { + return { type: 8, default: null, required: opts?.required ?? false }; +}, material(opts) { + return { type: 9, default: null, required: opts?.required ?? false }; +}, animation(opts) { + return { type: 10, default: null, required: opts?.required ?? false }; +}, skin(opts) { + return { type: 11, default: null, required: opts?.required ?? false }; +}, particleEffect(opts) { + return { type: 18, default: null, required: opts?.required ?? false }; +}, color(r = 0, g = 0, b = 0, a = 1) { + return { type: 12, default: [r, g, b, a] }; +}, vector2(x = 0, y = 0) { + return { type: 13, default: [x, y] }; +}, vector3(x = 0, y = 0, z = 0) { + return { type: 14, default: [x, y, z] }; +}, vector4(x = 0, y = 0, z = 0, w = 0) { + return { type: 15, default: [x, y, z, w] }; +}, record(definition) { + return { type: 17, record: definition, default: new definition() }; +}, array(element) { + return { type: 16, element, default: void 0 }; +} }; +function propertyDecorator(data) { + return function(target, propertyKey) { + let ctor = target.constructor; + ctor.Properties = ctor.hasOwnProperty("Properties") ? ctor.Properties : {}, ctor.Properties[propertyKey] = data; + }; +} +function enumerable() { + return function(_, __, descriptor) { + descriptor.enumerable = true; + }; +} +function nativeProperty() { + return function(target, propertyKey, descriptor) { + enumerable()(target, propertyKey, descriptor), propertyDecorator({ type: 0 })(target, propertyKey); + }; +} +var property = {}; +for (let name in Property) + property[name] = (...args) => { + let functor = Property[name]; + return propertyDecorator(functor(...args)); + }; +function isString(value) { + return value === "" ? true : value && (typeof value == "string" || value.constructor === String); +} +function isNumber(value) { + return value == null ? false : typeof value == "number" || value.constructor === Number; +} +function isImageLike(value) { + return value instanceof HTMLImageElement || value instanceof HTMLVideoElement || value instanceof HTMLCanvasElement; +} +var Emitter = class { + _listeners = []; + _notifying = false; + _transactions = []; + add(listener, opts = {}) { + let { once = false, id = void 0 } = opts, data = { id, once, callback: listener }; + return this._notifying ? (this._transactions.push({ type: 1, data }), this) : (this._listeners.push(data), this); + } + push(...listeners) { + for (let cb of listeners) + this.add(cb); + return this; + } + once(listener) { + return this.add(listener, { once: true }); + } + remove(listener) { + if (this._notifying) + return this._transactions.push({ type: 2, data: listener }), this; + let listeners = this._listeners; + for (let i = 0; i < listeners.length; ++i) { + let target = listeners[i]; + (target.callback === listener || target.id === listener) && listeners.splice(i--, 1); + } + return this; + } + has(listener) { + let listeners = this._listeners; + for (let i = 0; i < listeners.length; ++i) { + let target = listeners[i]; + if (target.callback === listener || target.id === listener) + return true; + } + return false; + } + notify(...data) { + let listeners = this._listeners; + this._notifying = true; + for (let i = 0; i < listeners.length; ++i) { + let listener = listeners[i]; + listener.once && listeners.splice(i--, 1); + try { + listener.callback(...data); + } catch (e) { + console.error(e); + } + } + this._notifying = false, this._flushTransactions(); + } + notifyUnsafe(...data) { + let listeners = this._listeners; + for (let i = 0; i < listeners.length; ++i) { + let listener = listeners[i]; + listener.once && listeners.splice(i--, 1), listener.callback(...data); + } + this._flushTransactions(); + } + promise() { + return new Promise((res, _) => { + this.once((...args) => { + args.length > 1 ? res(args) : res(args[0]); + }); + }); + } + get listenerCount() { + return this._listeners.length; + } + get isEmpty() { + return this.listenerCount === 0; + } + _flushTransactions() { + let listeners = this._listeners; + for (let transaction of this._transactions) + transaction.type === 1 ? listeners.push(transaction.data) : this.remove(transaction.data); + this._transactions.length = 0; + } +}; +var RetainEmitterUndefined = {}; +var RetainEmitter = class extends Emitter { + _event = RetainEmitterUndefined; + _reset; + add(listener, opts) { + let immediate = opts?.immediate ?? true; + return this._event !== RetainEmitterUndefined && immediate && listener(...this._event), super.add(listener, opts), this; + } + once(listener, immediate) { + return this.add(listener, { once: true, immediate }); + } + notify(...data) { + this._event = data, super.notify(...data); + } + notifyUnsafe(...data) { + this._event = data, super.notifyUnsafe(...data); + } + reset() { + return this._event = RetainEmitterUndefined, this; + } + get data() { + return this.isDataRetained ? this._event : void 0; + } + get isDataRetained() { + return this._event !== RetainEmitterUndefined; + } +}; +function createDestroyedProxy(host, type) { + return new Proxy({}, { get(_, param) { + if (param === "isDestroyed") + return true; + throw new Error(`Cannot read '${param}' of destroyed '${type.name}' resource from ${host}`); + }, set(_, param) { + throw new Error(`Cannot write '${param}' of destroyed '${type.name}' resource from ${host}`); + } }); +} +var Resource = class { + _index = -1; + _id = -1; + _engine; + constructor(engine2, index) { + this._engine = engine2, this._index = index, this._id = index; + } + get engine() { + return this._engine; + } + get index() { + return this._index; + } + equals(other) { + return other ? this._index === other._index : false; + } + get isDestroyed() { + return this._index <= 0; + } +}; +var SceneResource = class _SceneResource { + static _pack(scene, index) { + return scene << 22 | index; + } + _index = -1; + _id = -1; + _scene; + constructor(scene, index) { + this._scene = scene, this._index = index, this._id = _SceneResource._pack(scene._index, index); + } + equals(other) { + return other ? this._id === other._id : false; + } + get scene() { + return this._scene; + } + get engine() { + return this._scene.engine; + } + get index() { + return this._index; + } + get isDestroyed() { + return this._id <= 0; + } +}; +var ResourceManager = class { + _host; + _cache = []; + _template; + _destructor = null; + _engine; + constructor(host, Class) { + this._host = host, this._template = Class, this._engine = host.engine ?? host; + } + wrap(index) { + return index <= 0 ? null : this._cache[index] ?? (this._cache[index] = new this._template(this._host, index)); + } + get(index) { + return this._cache[index] ?? null; + } + get allocatedCount() { + return this._cache.length; + } + get count() { + let count = 0; + for (let res of this._cache) + res && res.index >= 0 && ++count; + return count; + } + get engine() { + return this._engine; + } + _destroy(instance) { + let index = instance.index; + instance._index = -1, instance._id = -1, this._cache[index] = null, this.engine.erasePrototypeOnDestroy && (this._destructor || (this._destructor = createDestroyedProxy(this._host, this._template)), Object.setPrototypeOf(instance, this._destructor)); + } + _clear() { + if (this.engine.erasePrototypeOnDestroy) { + for (let i = 0; i < this._cache.length; ++i) { + let instance = this._cache[i]; + instance && this._destroy(instance); + } + this._cache.length = 0; + } + } +}; +function decode(data, tagger = (_, value) => value, options = {}) { + let reader = new CBORReader(data); + reader.tagger = tagger, options.dictionary && (reader.dictionary = options.dictionary); + let ret = reader.decodeItem(); + if (reader.offset !== data.byteLength) + throw new Error("CBORError: Remaining bytes"); + return ret; +} +var CBORType = ((CBORType2) => (CBORType2[CBORType2.Array = 0] = "Array", CBORType2[CBORType2.Record = 1] = "Record", CBORType2[CBORType2.Constant = 2] = "Constant", CBORType2[CBORType2.Native = 3] = "Native", CBORType2))(CBORType || {}); +function getType(typeInfo) { + let majorType = typeInfo >> 5, additionalInformation = typeInfo & 31; + switch (majorType) { + case 4: + return 0; + case 5: + return 1; + case 7: + return 2; + } + return 3; +} +function isUndefined(type, length5) { + return type === 2 && length5 === 23; +} +var CBORReader = class { + dataView; + data; + offset; + tagger = (_, value) => value; + dictionary = "object"; + constructor(data) { + this.dataView = new DataView(data.buffer, data.byteOffset, data.byteLength), this.data = data, this.offset = 0; + } + readTypeInfo() { + return this.readUint8(); + } + readArrayLength(typeInfo) { + let majorType = typeInfo >> 5, additionalInformation = typeInfo & 31, length5 = this.readLength(additionalInformation); + if (length5 < 0 && (majorType < 2 || 6 < majorType)) + throw new Error("CBORError: Invalid length"); + return length5; + } + readItem(typeInfo, inputLen = null) { + let majorType = typeInfo >> 5, additionalInformation = typeInfo & 31, i; + if (majorType === 7) + switch (additionalInformation) { + case 25: + return this.readFloat16(); + case 26: + return this.readFloat32(); + case 27: + return this.readFloat64(); + } + let length5 = inputLen === null ? this.readLength(additionalInformation) : inputLen; + if (length5 < 0 && (majorType < 2 || 6 < majorType)) + throw new Error("CBORError: Invalid length"); + switch (majorType) { + case 0: + return length5; + case 1: + return typeof length5 == "number" ? -1 - length5 : -1n - length5; + case 2: { + if (length5 < 0) { + let elements = [], fullArrayLength = 0; + for (; (length5 = this.readIndefiniteStringLength(majorType)) >= 0; ) + fullArrayLength += length5, elements.push(this.readArrayBuffer(length5)); + let fullArray = new Uint8Array(fullArrayLength), fullArrayOffset = 0; + for (i = 0; i < elements.length; ++i) + fullArray.set(elements[i], fullArrayOffset), fullArrayOffset += elements[i].length; + return fullArray; + } + return this.readArrayBuffer(length5).slice(); + } + case 3: { + let utf16data = []; + if (length5 < 0) + for (; (length5 = this.readIndefiniteStringLength(majorType)) >= 0; ) + this.appendUtf16Data(utf16data, length5); + else + this.appendUtf16Data(utf16data, length5); + let string = "", DECODE_CHUNK_SIZE = 8192; + for (i = 0; i < utf16data.length; i += DECODE_CHUNK_SIZE) + string += String.fromCharCode.apply(null, utf16data.slice(i, i + DECODE_CHUNK_SIZE)); + return string; + } + case 4: { + let retArray; + if (length5 < 0) + for (retArray = []; !this.readBreak(); ) + retArray.push(this.decodeItem()); + else + for (retArray = new Array(length5), i = 0; i < length5; ++i) + retArray[i] = this.decodeItem(); + return retArray; + } + case 5: { + if (this.dictionary === "map") { + let retMap = /* @__PURE__ */ new Map(); + for (i = 0; i < length5 || length5 < 0 && !this.readBreak(); ++i) { + let key = this.decodeItem(); + if (retMap.has(key)) + throw new Error("CBORError: Duplicate key encountered"); + retMap.set(key, this.decodeItem()); + } + return retMap; + } + let retObject = {}; + for (i = 0; i < length5 || length5 < 0 && !this.readBreak(); ++i) { + let key = this.decodeItem(); + if (Object.prototype.hasOwnProperty.call(retObject, key)) + throw new Error("CBORError: Duplicate key encountered"); + retObject[key] = this.decodeItem(); + } + return retObject; + } + case 6: { + let value = this.decodeItem(), tag = length5; + if (value instanceof Uint8Array) + switch (tag) { + case 2: + case 3: + let num = value.reduce((acc, n) => (acc << 8n) + BigInt(n), 0n); + return tag == 3 && (num = -1n - num), num; + case 64: + return value; + case 72: + return new Int8Array(value.buffer); + case 69: + return new Uint16Array(value.buffer); + case 77: + return new Int16Array(value.buffer); + case 70: + return new Uint32Array(value.buffer); + case 78: + return new Int32Array(value.buffer); + case 71: + return new BigUint64Array(value.buffer); + case 79: + return new BigInt64Array(value.buffer); + case 85: + return new Float32Array(value.buffer); + case 86: + return new Float64Array(value.buffer); + } + return this.tagger(tag, value); + } + case 7: + switch (length5) { + case 20: + return false; + case 21: + return true; + case 22: + return null; + case 23: + return; + default: + return length5; + } + } + } + decodeItem() { + let initialByte = this.readUint8(); + return this.readItem(initialByte); + } + readArrayBuffer(length5) { + return this.commitRead(length5, this.data.subarray(this.offset, this.offset + length5)); + } + readFloat16() { + let POW_2_24 = 5960464477539063e-23, tempArrayBuffer = new ArrayBuffer(4), tempDataView = new DataView(tempArrayBuffer), value = this.readUint16(), sign = value & 32768, exponent = value & 31744, fraction = value & 1023; + if (exponent === 31744) + exponent = 261120; + else if (exponent !== 0) + exponent += 114688; + else if (fraction !== 0) + return (sign ? -1 : 1) * fraction * POW_2_24; + return tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13), tempDataView.getFloat32(0); + } + readFloat32() { + return this.commitRead(4, this.dataView.getFloat32(this.offset)); + } + readFloat64() { + return this.commitRead(8, this.dataView.getFloat64(this.offset)); + } + readUint8() { + return this.commitRead(1, this.data[this.offset]); + } + readUint16() { + return this.commitRead(2, this.dataView.getUint16(this.offset)); + } + readUint32() { + return this.commitRead(4, this.dataView.getUint32(this.offset)); + } + readUint64() { + return this.commitRead(8, this.dataView.getBigUint64(this.offset)); + } + readBreak() { + return this.data[this.offset] !== 255 ? false : (this.offset += 1, true); + } + readLength(additionalInformation) { + if (additionalInformation < 24) + return additionalInformation; + if (additionalInformation === 24) + return this.readUint8(); + if (additionalInformation === 25) + return this.readUint16(); + if (additionalInformation === 26) + return this.readUint32(); + if (additionalInformation === 27) { + let integer = this.readUint64(); + return integer <= Number.MAX_SAFE_INTEGER ? Number(integer) : integer; + } + if (additionalInformation === 31) + return -1; + throw new Error("CBORError: Invalid length encoding"); + } + readIndefiniteStringLength(majorType) { + let initialByte = this.readUint8(); + if (initialByte === 255) + return -1; + let length5 = this.readLength(initialByte & 31); + if (length5 < 0 || initialByte >> 5 !== majorType) + throw new Error("CBORError: Invalid indefinite length element"); + return Number(length5); + } + appendUtf16Data(utf16data, length5) { + for (let i = 0; i < length5; ++i) { + let value = this.readUint8(); + value & 128 && (value < 224 ? (value = (value & 31) << 6 | this.readUint8() & 63, length5 -= 1) : value < 240 ? (value = (value & 15) << 12 | (this.readUint8() & 63) << 6 | this.readUint8() & 63, length5 -= 2) : (value = (value & 7) << 18 | (this.readUint8() & 63) << 12 | (this.readUint8() & 63) << 6 | this.readUint8() & 63, length5 -= 3)), value < 65536 ? utf16data.push(value) : (value -= 65536, utf16data.push(55296 | value >> 10), utf16data.push(56320 | value & 1023)); + } + } + commitRead(length5, value) { + return this.offset += length5, value; + } +}; +var ComponentManagers = class { + animation = -1; + collision = -1; + js = -1; + physx = -1; + view = -1; + _cache = []; + _constructors; + _nativeManagers = /* @__PURE__ */ new Map(); + _scene; + constructor(scene) { + this._scene = scene; + let wasm = this._scene.engine.wasm, native = [AnimationComponent, CollisionComponent, InputComponent, LightComponent, MeshComponent, PhysXComponent, TextComponent, ViewComponent, ParticleEffectComponent]; + this._cache = new Array(native.length), this._constructors = new Array(native.length); + for (let Class of native) { + let ptr2 = wasm.tempUTF8(Class.TypeName), manager = wasm._wl_scene_get_component_manager_index(scene._index, ptr2); + this._constructors[manager] = Class, this._cache[manager] = [], this._nativeManagers.set(Class.TypeName, manager); + } + this.animation = this._nativeManagers.get(AnimationComponent.TypeName), this.collision = this._nativeManagers.get(CollisionComponent.TypeName), this.physx = this._nativeManagers.get(PhysXComponent.TypeName), this.view = this._nativeManagers.get(ViewComponent.TypeName); + let ptr = wasm.tempUTF8("js"); + this.js = wasm._wl_scene_get_component_manager_index(scene._index, ptr), this._cache[this.js] = []; + } + createJs(index, id, type, object) { + let ctor = this._scene.engine.wasm._componentTypes[type]; + if (!ctor) + throw new Error(`Type index ${type} isn't registered`); + let log = this._scene.engine.log, component = null; + try { + component = new ctor(this._scene, this.js, id); + } catch (e) { + log.error(2, `Exception during instantiation of component ${ctor.TypeName}`), log.error(2, e), component = new BrokenComponent(this._scene); + } + component._object = this._scene.wrap(object); + try { + component.resetProperties(); + } catch (e) { + log.error(2, `Exception during ${component.type} resetProperties() on object ${component.object.name}`), log.error(2, e); + } + return this._scene._jsComponents[index] = component, this._cache[this.js][id] = component, component; + } + components(type, active) { + return this.componentsFromTypename(type.TypeName, active); + } + componentsFromTypename(typename, active) { + let wasm = this._scene.engine.wasm, manager = this.getNativeManager(typename) ?? this.js, subTypeIndex = manager === this.js ? wasm._componentTypeIndices[typename] ?? 0 : 0, maxRead = wasm._tempMemSize / 4, components = [], read = 0, offset2 = 0; + for (; read = wasm._wl_scene_get_components(this._scene._index, manager, subTypeIndex, active, offset2, maxRead, wasm._tempMem); ) { + for (let i = 0; i < read; ++i) { + let id = wasm._tempMemUint32[i]; + components.push(this.wrapAny(manager, id)); + } + offset2 += maxRead; + } + return components; + } + componentAt(type, index) { + let wasm = this._scene.engine.wasm, manager = this.getNativeManager(type.TypeName), id = wasm._wl_scene_get_component(this._scene._index, manager, index); + return this.wrapAny(manager, id); + } + get(manager, id) { + return this._cache[manager][id] ?? null; + } + wrapAnimation(id) { + return this.wrapNative(this.animation, id); + } + wrapCollision(id) { + return this.wrapNative(this.collision, id); + } + wrapView(id) { + return this.wrapNative(this.view, id); + } + wrapPhysx(id) { + return this.wrapNative(this.physx, id); + } + wrapNative(manager, id) { + if (id < 0) + return null; + let cache = this._cache[manager]; + if (cache[id]) + return cache[id]; + let scene = this._scene, Class = this._constructors[manager], component = new Class(scene, manager, id); + return cache[id] = component, component; + } + wrapAny(manager, id) { + if (id < 0) + return null; + if (manager === this.js) { + let found = this._cache[this.js][id]; + if (!found) + throw new Error("JS components must always be cached"); + return found.constructor !== BrokenComponent ? found : null; + } + return this.wrapNative(manager, id); + } + getNativeManager(name) { + let manager = this._nativeManagers.get(name); + return manager !== void 0 ? manager : null; + } + destroy(instance) { + let localId = instance._localId, manager = instance._manager; + instance._id = -1, instance._localId = -1, instance._manager = -1, this._scene.engine.erasePrototypeOnDestroy && instance && Object.setPrototypeOf(instance, DestroyedComponentInstance), this._cache[manager][localId] = null; + } + get managersCount() { + return this._scene.engine.wasm._wl_scene_get_component_manager_count(this._scene._index); + } +}; +function resetComponentProperties(record) { + let properties = record.constructor.Properties; + if (properties) + for (let name in properties) { + let property2 = properties[name], cloner = property2.cloner ?? defaultPropertyCloner; + record[name] = cloner.clone(property2.type, property2.default); + } +} +function setupComponentClass(ctor) { + inheritProperties(ctor); + for (let name in ctor.Properties) { + let prop = ctor.Properties[name], record = null; + switch (prop.type) { + case 17: + record = prop.record; + break; + case 16: + record = prop.element?.record; + break; + default: + break; + } + record && !record._propertyOrder && setupComponentClass(record); + } + _setupDefaults(ctor), _setPropertyOrder(ctor); +} +var ComponentPropertyDecoder = class { + scene; + offsets; + constructor(scene, offsets) { + this.scene = scene, this.offsets = offsets; + } + decode(cbor, component) { + let ctor = component.constructor; + ctor._propertyOrder || setupComponentClass(ctor); + let typeInfo = cbor.readTypeInfo(); + if (ctor === BrokenComponent) + return cbor.readItem(typeInfo); + if (getType(typeInfo) !== 0) + return this._error(`${component} parameters not encoded as an array.`), cbor.readItem(typeInfo); + let paramNames = ctor._propertyOrder, count = cbor.readArrayLength(typeInfo); + if (count !== paramNames.length) + return this._error(`${component} has ${count} parameters encoded, but expected ${paramNames.length}`), cbor.readItem(typeInfo, count); + for (let j = 0; j < count; ++j) { + let name = paramNames[j], property2 = ctor.Properties[name]; + component[name] = this.decodeProperty(cbor, name, property2); + } + } + decodeProperty(cbor, name, property2) { + let typeInfo = cbor.readTypeInfo(); + if (property2.type === 17) + return this.decodeRecordProperty(cbor, name, property2, typeInfo); + if (property2.type === 16) + return this.decodeArrayProperty(cbor, name, property2, typeInfo); + let value = cbor.readItem(typeInfo); + if (value === void 0) + return value = (property2.cloner ?? defaultPropertyCloner).clone(property2.type, property2.default), value; + typeof value == "number" && (value += this.offsets[property2.type]); + let engine2 = this.scene.engine; + switch (property2.type) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 13: + case 14: + case 15: + return value; + case 6: { + let wasm = engine2.wasm; + return value ? this.scene.wrap(wasm._wl_object_id(this.scene._index, value)) : null; + } + case 7: + return engine2.meshes.wrap(value); + case 8: + return engine2.textures.wrap(value); + case 9: + return engine2.materials.wrap(value); + case 18: + return engine2.particleEffects.wrap(value); + case 10: + return this.scene.animations.wrap(value); + case 11: + return this.scene.skins.wrap(value); + case 12: { + let max2 = (1 << value.BYTES_PER_ELEMENT * 8) - 1; + return Float32Array.from(value, (f, _) => f / max2); + } + } + } + decodeRecordProperty(cbor, name, property2, typeInfo) { + let cborType = getType(typeInfo); + if (cborType !== 0 && cborType !== 2) + return this._error(`Record parameter '${name}' not serialized as an array`), cbor.readItem(typeInfo), null; + let record = property2.record; + if (!record) + return this._error(`Record parameter '${name}' .record is undefined`), cbor.readItem(typeInfo), null; + record._propertyOrder || setupComponentClass(record); + let propertyOrder = record._propertyOrder, count = cbor.readArrayLength(typeInfo); + if (isUndefined(cborType, count)) + return (property2.cloner ?? defaultPropertyCloner).clone(property2.type, property2.default); + let result = new record(); + if (count !== propertyOrder.length) { + let propsCount = propertyOrder.length; + return this._error(`Record parameter ${name} has ${count} sub-parameters encoded, but expected ${propsCount}`), cbor.readItem(typeInfo, count), result; + } + let order = record._propertyOrder; + for (let i = 0; i < order.length; ++i) { + let key = order[i], prop = record.Properties[key]; + result[key] = this.decodeProperty(cbor, key, prop); + } + return result; + } + decodeArrayProperty(cbor, name, property2, typeInfo) { + let log = this.scene.engine.log, cborType = getType(typeInfo); + if (cborType !== 0 && cborType !== 2) + return this._error(`Array parameter '${name}' not serialized as an array`), cbor.readItem(typeInfo), []; + if (!property2.element) + return this._error(`Array parameter '${name}' .element property is undefined`), cbor.readItem(typeInfo), []; + let count = cbor.readArrayLength(typeInfo); + if (isUndefined(cborType, count)) + return []; + let result = new Array(count); + for (let i = 0; i < count; ++i) + result[i] = this.decodeProperty(cbor, name, property2.element); + return result; + } + _error(msg) { + this.scene.engine.log.error(0, msg); + } +}; +var _componentDefaults = /* @__PURE__ */ new Map([[1, false], [2, 0], [3, 0], [4, ""], [5, void 0], [6, null], [7, null], [8, null], [9, null], [10, null], [11, null], [18, null], [12, Float32Array.from([0, 0, 0, 1])], [13, Float32Array.from([0, 0])], [14, Float32Array.from([0, 0, 0])], [15, Float32Array.from([0, 0, 0, 0])], [17, void 0], [16, []]]); +function _setupDefaults(ctor) { + for (let name in ctor.Properties) { + let p = ctor.Properties[name]; + if (p.type === 5) + p.values?.length ? (typeof p.default != "number" && (p.default = p.values.indexOf(p.default)), (p.default < 0 || p.default >= p.values.length) && (p.default = 0)) : p.default = void 0; + else if ((p.type === 12 || p.type === 13 || p.type === 14 || p.type === 15) && Array.isArray(p.default)) + p.default = Float32Array.from(p.default); + else if (p.type === 17 && p.record) + p.default = new p.record(), resetComponentProperties(p.default); + else if (p.default === void 0) { + let cloner = p.cloner ?? defaultPropertyCloner; + p.default = cloner.clone(p.type, _componentDefaults.get(p.type)); + } + ctor.prototype[name] = p.default; + } +} +function _setPropertyOrder(ctor) { + ctor._propertyOrder = ctor.hasOwnProperty("Properties") ? Object.keys(ctor.Properties).sort() : []; +} +var FetchProgressTransformer = class { + #progress = 0; + #callback; + #totalSize; + constructor(callback, totalSize = 0) { + this.#callback = callback, this.#totalSize = totalSize; + } + transform(chunk, controller) { + controller.enqueue(chunk), this.#progress += chunk.length, this.#totalSize > 0 && this.#callback(this.#progress, this.#totalSize); + } + flush() { + this.#callback(this.#progress, this.#progress); + } +}; +var ArrayBufferSink = class { + #buffer; + #offset = 0; + constructor(size = 0) { + this.#buffer = new Uint8Array(size); + } + get arrayBuffer() { + let arrayBuffer = this.#buffer.buffer; + return this.#offset < arrayBuffer.byteLength ? arrayBuffer.slice(0, this.#offset) : arrayBuffer; + } + write(chunk) { + let newLength = this.#offset + chunk.length; + if (newLength > this.#buffer.length) { + let newBuffer = new Uint8Array(Math.max(this.#buffer.length * 1.5, newLength)); + newBuffer.set(this.#buffer), this.#buffer = newBuffer; + } + this.#buffer.set(chunk, this.#offset), this.#offset = newLength; + } +}; +var ArrayBufferSource = class { + #buffer; + constructor(buffer) { + this.#buffer = buffer; + } + start(controller) { + this.#buffer.byteLength > 0 && controller.enqueue(new Uint8Array(this.#buffer)), controller.close(); + } +}; +async function fetchWithProgress(path, onProgress, signal) { + let res = await fetch(path, { signal }); + if (!res.ok) + throw res.statusText; + if (!onProgress || !res.body) + return res.arrayBuffer(); + let size = Number(res.headers.get("Content-Length") ?? 0); + Number.isNaN(size) && (size = 0); + let sink = new ArrayBufferSink(size); + return await res.body.pipeThrough(new TransformStream(new FetchProgressTransformer(onProgress, size))).pipeTo(new WritableStream(sink)), sink.arrayBuffer; +} +async function fetchStreamWithProgress(path, onProgress, signal) { + let res = await fetch(path, { signal }); + if (!res.ok) + throw res.statusText; + let body = res.body ?? new ReadableStream(), size = Number(res.headers.get("Content-Length") ?? 0); + return Number.isNaN(size) && (size = 0), onProgress ? body.pipeThrough(new TransformStream(new FetchProgressTransformer(onProgress, size))) : body; +} +function getBaseUrl(url) { + return url.substring(0, url.lastIndexOf("/")); +} +function getFilename(url) { + url.endsWith("/") && (url = url.substring(0, url.lastIndexOf("/"))); + let lastSlash = url.lastIndexOf("/"); + return lastSlash < 0 ? url : url.substring(lastSlash + 1); +} +function onImageReady(image) { + return new Promise((res, rej) => { + if (image instanceof HTMLVideoElement) { + if (image.readyState >= 2) { + res(image); + return; + } + image.addEventListener("loadeddata", () => { + image.readyState >= 2 && res(image); + }, { once: true }); + return; + } else if (!(image instanceof HTMLImageElement) || image.complete) { + res(image); + return; + } + image.addEventListener("load", () => res(image), { once: true }), image.addEventListener("error", rej, { once: true }); + }); +} +var Prefab = class _Prefab { + static makeUrlLoadOptions(options) { + return isString(options) ? { url: options } : options; + } + static async loadBuffer(options, progress) { + let opts = _Prefab.makeUrlLoadOptions(options), buffer = await fetchWithProgress(opts.url, progress, opts.signal), baseURL = getBaseUrl(opts.url), filename = getFilename(opts.url); + return { ...opts, buffer, baseURL, filename }; + } + static async loadStream(options, progress) { + let opts = _Prefab.makeUrlLoadOptions(options), stream = await fetchStreamWithProgress(opts.url, progress, opts.signal), baseURL = getBaseUrl(opts.url), filename = getFilename(opts.url); + return { ...opts, stream, baseURL, filename }; + } + static validateBufferOptions(options) { + let { buffer, baseURL, filename = "scene.bin" } = options; + if (!buffer) + throw new Error("missing 'buffer' in options"); + if (!isString(baseURL)) + throw new Error("missing 'baseURL' in options"); + let url = baseURL ? `${baseURL}/${filename}` : filename; + return { buffer, baseURL, url }; + } + static validateStreamOptions(options) { + let { stream, baseURL, filename = "scene.bin" } = options; + if (!stream) + throw new Error("missing 'stream' in options"); + if (!isString(baseURL)) + throw new Error("missing 'baseURL' in options"); + let url = baseURL ? `${baseURL}/${filename}` : filename; + return { stream, baseURL, url }; + } + _index; + _engine; + _components; + _jsComponents = []; + _pxCallbacks = /* @__PURE__ */ new Map(); + _animations; + _skins; + _objectCache = []; + _pendingDestroy = 0; + constructor(engine2, index) { + this._engine = engine2, this._index = index, this._components = new ComponentManagers(this), this._animations = new ResourceManager(this, Animation), this._skins = new ResourceManager(this, Skin); + } + addChild() { + return this.wrap(0).addChild(); + } + addObject(parent = null) { + if (parent?.markedDestroyed) + throw new Error(`Failed to add object. ${parent} is marked as destroyed.`); + return this.assertOrigin(parent), (parent ?? this.wrap(0)).addChild(); + } + addObjects(count, parent = null, componentCountHint = 0) { + let parentId = parent ? parent._id : 0; + this.engine.wasm.requireTempMem(count * 2); + let actualCount = this.engine.wasm._wl_scene_add_objects(this._index, parentId, count, componentCountHint || 0, this.engine.wasm._tempMem, this.engine.wasm._tempMemSize >> 1), ids = this.engine.wasm._tempMemUint16.subarray(0, actualCount), wrapper = this.wrap.bind(this); + return Array.from(ids, wrapper); + } + reserveObjects(objectCount, componentCountPerType) { + let wasm = this.engine.wasm; + if (!componentCountPerType) + return; + let countsPerTypeIndex = wasm._tempMemInt, managerCount = this._components.managersCount; + for (let i = 0; i < managerCount; ++i) + countsPerTypeIndex[i] = 0; + let names = Object.keys(componentCountPerType); + for (let name of names) { + let count = componentCountPerType[name], nativeIndex = this._components.getNativeManager(name); + countsPerTypeIndex[nativeIndex !== null ? nativeIndex : this._components.js] += count; + } + wasm._wl_scene_reserve_objects(this._index, objectCount, wasm._tempMem); + } + getChildren(out = new Array(this.childrenCount)) { + return this.wrap(0).getChildren(out); + } + getComponents(typeOrClass) { + let typename = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName; + return this._components.componentsFromTypename(typename, false); + } + getActiveComponents(typeOrClass) { + let typename = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName; + return this._components.componentsFromTypename(typename, true); + } + get children() { + return this.wrap(0).children; + } + get childrenCount() { + let root = this.wrap(0); + return this.engine.wasm._wl_object_get_children_count(root._id); + } + findByName(name, recursive = false) { + return this.wrap(0).findByName(name, recursive); + } + findByNameDirect(name) { + return this.wrap(0).findByNameDirect(name); + } + findByNameRecursive(name) { + return this.wrap(0).findByNameRecursive(name); + } + wrap(objectId) { + let cache = this._objectCache; + return cache[objectId] || (cache[objectId] = new Object3D2(this, objectId)); + } + destroy() { + if (this._pendingDestroy > 0) + throw new Error("It's forbidden to destroy a scene from onDestroy()."); + this._pxCallbacks.clear(), this.engine._destroyScene(this); + } + get isActive() { + return !!this.engine.wasm._wl_scene_active(this._index); + } + get baseURL() { + let wasm = this.engine.wasm, ptr = wasm._wl_scene_get_baseURL(this._index); + return ptr ? wasm.UTF8ToString(ptr) : ""; + } + get filename() { + let wasm = this.engine.wasm, ptr = wasm._wl_scene_get_filename(this._index); + return ptr ? wasm.UTF8ToString(ptr) : ""; + } + get animations() { + return this._animations; + } + get skins() { + return this._skins; + } + get engine() { + return this._engine; + } + get isDestroyed() { + return this._index < 0; + } + toString() { + return this.isDestroyed ? "Scene(destroyed)" : `Scene('${this.filename}', ${this._index})`; + } + assertOrigin(other) { + if (other && other.scene !== this) + throw new Error(`Attempt to use ${other} from ${other.scene} in ${this}`); + } + _initialize() { + this.engine.wasm._wl_scene_initialize(this._index); + } + _destroyObject(localId) { + let instance = this._objectCache[localId]; + instance && (instance._id = -1, instance._localId = -1, this.engine.erasePrototypeOnDestroy && instance && Object.setPrototypeOf(instance, DestroyedObjectInstance), this._objectCache[localId] = null); + } + _destroyComponent(manager, id) { + let component = this._components.get(manager, id); + ++this._pendingDestroy, component?._triggerOnDestroy(), --this._pendingDestroy; + } +}; +function timeout(time) { + return new Promise((res) => setTimeout(res, time)); +} +function clamp(val, min2, max2) { + return Math.max(Math.min(max2, val), min2); +} +function capitalizeFirstUTF8(str5) { + return `${str5[0].toUpperCase()}${str5.substring(1)}`; +} +function createDestroyedProxy2(type) { + return new Proxy({}, { get(_, param) { + if (param === "isDestroyed") + return true; + throw new Error(`Cannot read '${param}' of destroyed ${type}`); + }, set(_, param) { + throw new Error(`Cannot write '${param}' of destroyed ${type}`); + } }); +} +var LogTag = ((LogTag2) => (LogTag2[LogTag2.Engine = 0] = "Engine", LogTag2[LogTag2.Scene = 1] = "Scene", LogTag2[LogTag2.Component = 2] = "Component", LogTag2))(LogTag || {}); +var Collider = ((Collider2) => (Collider2[Collider2.Sphere = 0] = "Sphere", Collider2[Collider2.AxisAlignedBox = 1] = "AxisAlignedBox", Collider2[Collider2.Box = 2] = "Box", Collider2))(Collider || {}); +var Alignment = ((Alignment2) => (Alignment2[Alignment2.Left = 0] = "Left", Alignment2[Alignment2.Center = 1] = "Center", Alignment2[Alignment2.Right = 2] = "Right", Alignment2))(Alignment || {}); +var VerticalAlignment = ((VerticalAlignment2) => (VerticalAlignment2[VerticalAlignment2.Line = 0] = "Line", VerticalAlignment2[VerticalAlignment2.Middle = 1] = "Middle", VerticalAlignment2[VerticalAlignment2.Top = 2] = "Top", VerticalAlignment2[VerticalAlignment2.Bottom = 3] = "Bottom", VerticalAlignment2))(VerticalAlignment || {}); +var Justification = VerticalAlignment; +var TextEffect = ((TextEffect2) => (TextEffect2[TextEffect2.None = 0] = "None", TextEffect2[TextEffect2.Outline = 1] = "Outline", TextEffect2[TextEffect2.Shadow = 2] = "Shadow", TextEffect2))(TextEffect || {}); +var TextWrapMode = ((TextWrapMode2) => (TextWrapMode2[TextWrapMode2.None = 0] = "None", TextWrapMode2[TextWrapMode2.Soft = 1] = "Soft", TextWrapMode2[TextWrapMode2.Hard = 2] = "Hard", TextWrapMode2[TextWrapMode2.Clip = 3] = "Clip", TextWrapMode2))(TextWrapMode || {}); +var InputType = ((InputType3) => (InputType3[InputType3.Head = 0] = "Head", InputType3[InputType3.EyeLeft = 1] = "EyeLeft", InputType3[InputType3.EyeRight = 2] = "EyeRight", InputType3[InputType3.ControllerLeft = 3] = "ControllerLeft", InputType3[InputType3.ControllerRight = 4] = "ControllerRight", InputType3[InputType3.RayLeft = 5] = "RayLeft", InputType3[InputType3.RayRight = 6] = "RayRight", InputType3))(InputType || {}); +var ProjectionType = ((ProjectionType2) => (ProjectionType2[ProjectionType2.Perspective = 0] = "Perspective", ProjectionType2[ProjectionType2.Orthographic = 1] = "Orthographic", ProjectionType2))(ProjectionType || {}); +var LightType = ((LightType2) => (LightType2[LightType2.Point = 0] = "Point", LightType2[LightType2.Spot = 1] = "Spot", LightType2[LightType2.Sun = 2] = "Sun", LightType2))(LightType || {}); +var AnimationState = ((AnimationState2) => (AnimationState2[AnimationState2.Playing = 0] = "Playing", AnimationState2[AnimationState2.Paused = 1] = "Paused", AnimationState2[AnimationState2.Stopped = 2] = "Stopped", AnimationState2))(AnimationState || {}); +var RootMotionMode = ((RootMotionMode2) => (RootMotionMode2[RootMotionMode2.None = 0] = "None", RootMotionMode2[RootMotionMode2.ApplyToOwner = 1] = "ApplyToOwner", RootMotionMode2[RootMotionMode2.Script = 2] = "Script", RootMotionMode2))(RootMotionMode || {}); +var ForceMode = ((ForceMode2) => (ForceMode2[ForceMode2.Force = 0] = "Force", ForceMode2[ForceMode2.Impulse = 1] = "Impulse", ForceMode2[ForceMode2.VelocityChange = 2] = "VelocityChange", ForceMode2[ForceMode2.Acceleration = 3] = "Acceleration", ForceMode2))(ForceMode || {}); +var CollisionEventType = ((CollisionEventType2) => (CollisionEventType2[CollisionEventType2.Touch = 0] = "Touch", CollisionEventType2[CollisionEventType2.TouchLost = 1] = "TouchLost", CollisionEventType2[CollisionEventType2.TriggerTouch = 2] = "TriggerTouch", CollisionEventType2[CollisionEventType2.TriggerTouchLost = 3] = "TriggerTouchLost", CollisionEventType2))(CollisionEventType || {}); +var Shape = ((Shape2) => (Shape2[Shape2.None = 0] = "None", Shape2[Shape2.Sphere = 1] = "Sphere", Shape2[Shape2.Capsule = 2] = "Capsule", Shape2[Shape2.Box = 3] = "Box", Shape2[Shape2.Plane = 4] = "Plane", Shape2[Shape2.ConvexMesh = 5] = "ConvexMesh", Shape2[Shape2.TriangleMesh = 6] = "TriangleMesh", Shape2))(Shape || {}); +var MeshAttribute = ((MeshAttribute2) => (MeshAttribute2[MeshAttribute2.Position = 0] = "Position", MeshAttribute2[MeshAttribute2.Tangent = 1] = "Tangent", MeshAttribute2[MeshAttribute2.Normal = 2] = "Normal", MeshAttribute2[MeshAttribute2.TextureCoordinate = 3] = "TextureCoordinate", MeshAttribute2[MeshAttribute2.Color = 4] = "Color", MeshAttribute2[MeshAttribute2.JointId = 5] = "JointId", MeshAttribute2[MeshAttribute2.JointWeight = 6] = "JointWeight", MeshAttribute2[MeshAttribute2.SecondaryTextureCoordinate = 7] = "SecondaryTextureCoordinate", MeshAttribute2))(MeshAttribute || {}); +var DestroyedObjectInstance = createDestroyedProxy2("object"); +var DestroyedComponentInstance = createDestroyedProxy2("component"); +var DestroyedPrefabInstance = createDestroyedProxy2("prefab/scene"); +function isMeshShape(shape) { + return shape === 5 || shape === 6; +} +function isBaseComponentClass(value) { + return !!value && value.hasOwnProperty("_isBaseComponent") && value._isBaseComponent; +} +var UP_VECTOR = [0, 1, 0]; +var SQRT_3 = Math.sqrt(3); +var _a; +var Component3 = (_a = class { + static _pack(scene, id) { + return scene << 22 | id; + } + static _inheritProperties() { + inheritProperties(this); + } + _manager; + _id; + _localId; + _object; + _scene; + constructor(scene, manager = -1, id = -1) { + this._scene = scene, this._manager = manager, this._localId = id, this._id = _a._pack(scene._index, id), this._object = null; + } + get scene() { + return this._scene; + } + get engine() { + return this._scene.engine; + } + get type() { + return this.constructor.TypeName; + } + get object() { + if (!this._object) { + let objectId = this.engine.wasm._wl_component_get_object(this._manager, this._id); + this._object = this._scene.wrap(objectId); + } + return this._object; + } + set active(active) { + this.engine.wasm._wl_component_setActive(this._manager, this._id, active); + } + get active() { + return this.markedActive && this._scene.isActive; + } + get markedActive() { + return this.engine.wasm._wl_component_isActive(this._manager, this._id) != 0; + } + copy(src) { + let properties = this.constructor.Properties; + if (!properties) + return this; + for (let name in properties) { + let property2 = properties[name], value = src[name]; + if (value === void 0) + continue; + let cloner = property2.cloner ?? defaultPropertyCloner; + this[name] = cloner.clone(property2.type, value); + } + return this; + } + destroy() { + let manager = this._manager; + manager < 0 || this._id < 0 || this.engine.wasm._wl_component_remove(manager, this._id); + } + equals(otherComponent) { + return otherComponent ? this._manager === otherComponent._manager && this._id === otherComponent._id : false; + } + resetProperties() { + return resetComponentProperties(this), this; + } + reset() { + return this.resetProperties(); + } + validateProperties() { + let ctor = this.constructor; + if (ctor.Properties) { + for (let name in ctor.Properties) + if (ctor.Properties[name].required && !this[name]) + throw new Error(`Property '${name}' is required but was not initialized`); + } + } + toString() { + return this.isDestroyed ? "Component(destroyed)" : `Component('${this.type}', ${this._localId})`; + } + get isDestroyed() { + return this._id < 0; + } + _copy(src, offsetsPtr, copyInfoPtr) { + let wasm = this.engine.wasm, offsets = wasm.HEAPU32, offsetsStart = offsetsPtr >>> 2, copyInfoStart = copyInfoPtr >>> 1, srcRootIndex = wasm.HEAPU16[copyInfoStart], srcRootSize = wasm.HEAPU16[copyInfoStart + 1], dstRootIndex = wasm.HEAPU16[copyInfoStart + 2], destScene = this._scene, ctor = this.constructor; + for (let name in ctor.Properties) { + let value = src[name]; + if (value === null) { + this[name] = null; + continue; + } + let prop = ctor.Properties[name], offset2 = offsets[offsetsStart + prop.type], retargeted = null; + switch (prop.type) { + case 6: { + let index = wasm._wl_object_index(value._id) + offset2, dist2 = index - srcRootIndex; + dist2 >= 0 && dist2 <= srcRootSize && (index = dstRootIndex + dist2); + let id = wasm._wl_object_id(destScene._index, index); + retargeted = destScene.wrap(id); + break; + } + case 10: + retargeted = destScene.animations.wrap(offset2 + value._index); + break; + case 11: + retargeted = destScene.skins.wrap(offset2 + value._index); + break; + default: + retargeted = (prop.cloner ?? defaultPropertyCloner).clone(prop.type, value); + break; + } + this[name] = retargeted; + } + return this; + } + _triggerInit() { + if (this.init) + try { + this.init(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} init() on object ${this.object.name}`), this.engine.log.error(2, e); + } + let oldActivate = this.onActivate; + this.onActivate = function() { + this.onActivate = oldActivate; + let failed = false; + try { + this.validateProperties(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} validateProperties() on object ${this.object.name}`), this.engine.log.error(2, e), failed = true; + } + try { + this.start?.(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} start() on object ${this.object.name}`), this.engine.log.error(2, e), failed = true; + } + if (failed) { + this.active = false; + return; + } + if (this.onActivate) + try { + this.onActivate(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} onActivate() on object ${this.object.name}`), this.engine.log.error(2, e); + } + }; + } + _triggerUpdate(dt) { + if (this.update) + try { + this.update(dt); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} update() on object ${this.object.name}`), this.engine.log.error(2, e), this.engine.wasm._deactivate_component_on_error && (this.active = false); + } + } + _triggerOnActivate() { + if (this.onActivate) + try { + this.onActivate(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} onActivate() on object ${this.object.name}`), this.engine.log.error(2, e); + } + } + _triggerOnDeactivate() { + if (this.onDeactivate) + try { + this.onDeactivate(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} onDeactivate() on object ${this.object.name}`), this.engine.log.error(2, e); + } + } + _triggerOnDestroy() { + try { + this.onDestroy && this.onDestroy(); + } catch (e) { + this.engine.log.error(2, `Exception during ${this.type} onDestroy() on object ${this.object.name}`), this.engine.log.error(2, e); + } + this._scene._components.destroy(this); + } +}, __publicField(_a, "_isBaseComponent", true), __publicField(_a, "_propertyOrder", []), __publicField(_a, "TypeName"), __publicField(_a, "Properties"), __publicField(_a, "InheritProperties"), __publicField(_a, "onRegister"), _a); +var _a2; +var BrokenComponent = (_a2 = class extends Component3 { +}, __publicField(_a2, "TypeName", "__broken-component__"), _a2); +function inheritProperties(target) { + let chain = [], curr = target; + for (; curr && !isBaseComponentClass(curr); ) { + let comp = curr; + if (!(comp.hasOwnProperty("InheritProperties") ? comp.InheritProperties : true)) + break; + comp.hasOwnProperty("Properties") && chain.push(comp), curr = Object.getPrototypeOf(curr); + } + if (!chain.length || chain.length === 1 && chain[0] === target) + return; + let merged = {}; + for (let i = chain.length - 1; i >= 0; --i) + Object.assign(merged, chain[i].Properties); + target.Properties = merged; +} +var _a3; +var CollisionComponent = (_a3 = class extends Component3 { + getExtents(out = new Float32Array(3)) { + let wasm = this.engine.wasm, ptr = wasm._wl_collision_component_get_extents(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out; + } + get collider() { + return this.engine.wasm._wl_collision_component_get_collider(this._id); + } + set collider(collider) { + this.engine.wasm._wl_collision_component_set_collider(this._id, collider); + } + get extents() { + let wasm = this.engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_collision_component_get_extents(this._id), 3); + } + set extents(extents) { + let wasm = this.engine.wasm, ptr = wasm._wl_collision_component_get_extents(this._id) / 4; + wasm.HEAPF32[ptr] = extents[0], wasm.HEAPF32[ptr + 1] = extents[1], wasm.HEAPF32[ptr + 2] = extents[2]; + } + get radius() { + let wasm = this.engine.wasm; + if (this.collider === 0) + return wasm.HEAPF32[wasm._wl_collision_component_get_extents(this._id) >> 2]; + let extents = new Float32Array(wasm.HEAPF32.buffer, wasm._wl_collision_component_get_extents(this._id), 3), x2 = extents[0] * extents[0], y2 = extents[1] * extents[1], z2 = extents[2] * extents[2]; + return Math.sqrt(x2 + y2 + z2) / 2; + } + set radius(radius) { + let length5 = this.collider === 0 ? radius : 2 * radius / SQRT_3; + this.extents.set([length5, length5, length5]); + } + get group() { + return this.engine.wasm._wl_collision_component_get_group(this._id); + } + set group(group) { + this.engine.wasm._wl_collision_component_set_group(this._id, group); + } + queryOverlaps() { + let count = this.engine.wasm._wl_collision_component_query_overlaps(this._id, this.engine.wasm._tempMem, this.engine.wasm._tempMemSize >> 1), overlaps = new Array(count); + for (let i = 0; i < count; ++i) { + let id = this.engine.wasm._tempMemUint16[i]; + overlaps[i] = this._scene._components.wrapCollision(id); + } + return overlaps; + } +}, __publicField(_a3, "TypeName", "collision"), _a3); +__decorateClass([nativeProperty()], CollisionComponent.prototype, "collider", 1), __decorateClass([nativeProperty()], CollisionComponent.prototype, "extents", 1), __decorateClass([nativeProperty()], CollisionComponent.prototype, "group", 1); +var _a4; +var TextComponent = (_a4 = class extends Component3 { + get alignment() { + return this.engine.wasm._wl_text_component_get_horizontal_alignment(this._id); + } + set alignment(alignment) { + this.engine.wasm._wl_text_component_set_horizontal_alignment(this._id, alignment); + } + get verticalAlignment() { + return this.engine.wasm._wl_text_component_get_vertical_alignment(this._id); + } + set verticalAlignment(verticalAlignment) { + this.engine.wasm._wl_text_component_set_vertical_alignment(this._id, verticalAlignment); + } + get justification() { + return this.verticalAlignment; + } + set justification(justification) { + this.verticalAlignment = justification; + } + get justified() { + return !!this.engine.wasm._wl_text_component_get_justified(this._id); + } + set justified(justified) { + this.engine.wasm._wl_text_component_set_justified(this._id, justified); + } + get characterSpacing() { + return this.engine.wasm._wl_text_component_get_character_spacing(this._id); + } + set characterSpacing(spacing) { + this.engine.wasm._wl_text_component_set_character_spacing(this._id, spacing); + } + get lineSpacing() { + return this.engine.wasm._wl_text_component_get_line_spacing(this._id); + } + set lineSpacing(spacing) { + this.engine.wasm._wl_text_component_set_line_spacing(this._id, spacing); + } + get effect() { + return this.engine.wasm._wl_text_component_get_effect(this._id); + } + set effect(effect) { + this.engine.wasm._wl_text_component_set_effect(this._id, effect); + } + get effectOffset() { + return this.getEffectOffset(); + } + set effectOffset(offset2) { + this.setEffectOffset(offset2); + } + getEffectOffset(out = new Float32Array(2)) { + let wasm = this.engine.wasm; + return wasm._wl_text_component_get_effectOffset(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out; + } + setEffectOffset(offset2) { + let wasm = this.engine.wasm; + wasm._tempMemFloat.set(offset2), wasm._wl_text_component_set_effectOffset(this._id, wasm._tempMem); + } + get wrapMode() { + return this.engine.wasm._wl_text_component_get_wrapMode(this._id); + } + set wrapMode(wrapMode) { + this.engine.wasm._wl_text_component_set_wrapMode(this._id, wrapMode); + } + get wrapWidth() { + return this.engine.wasm._wl_text_component_get_wrapWidth(this._id); + } + set wrapWidth(width) { + this.engine.wasm._wl_text_component_set_wrapWidth(this._id, width); + } + get text() { + let wasm = this.engine.wasm, ptr = wasm._wl_text_component_get_text(this._id); + return wasm.UTF8ToString(ptr); + } + set text(text) { + let wasm = this.engine.wasm; + wasm._wl_text_component_set_text(this._id, wasm.tempUTF8(text.toString())); + } + set material(material) { + let matIndex = material ? material._id : 0; + this.engine.wasm._wl_text_component_set_material(this._id, matIndex); + } + get material() { + let index = this.engine.wasm._wl_text_component_get_material(this._id); + return this.engine.materials.wrap(index); + } + getBoundingBoxForText(text, out = new Float32Array(4)) { + let wasm = this.engine.wasm, textPtr = wasm.tempUTF8(text, 4 * 4); + return this.engine.wasm._wl_text_component_get_boundingBox(this._id, textPtr, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out[3] = wasm._tempMemFloat[3], out; + } + getBoundingBox(out = new Float32Array(4)) { + let wasm = this.engine.wasm; + return this.engine.wasm._wl_text_component_get_boundingBox(this._id, 0, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out[3] = wasm._tempMemFloat[3], out; + } +}, __publicField(_a4, "TypeName", "text"), _a4); +__decorateClass([nativeProperty()], TextComponent.prototype, "alignment", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "verticalAlignment", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "justification", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "justified", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "characterSpacing", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "lineSpacing", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "effect", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "effectOffset", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "wrapMode", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "wrapWidth", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "text", 1), __decorateClass([nativeProperty()], TextComponent.prototype, "material", 1); +var _a5; +var ViewComponent = (_a5 = class extends Component3 { + get projectionType() { + return this.engine.wasm._wl_view_component_get_projectionType(this._id); + } + set projectionType(type) { + this.engine.wasm._wl_view_component_set_projectionType(this._id, type); + } + getProjectionMatrix(out = new Float32Array(16)) { + let wasm = this.engine.wasm; + wasm.requireTempMem(16 * 4), wasm._wl_view_component_get_projectionMatrix(this._id, wasm._tempMem); + for (let i = 0; i < 16; ++i) + out[i] = wasm._tempMemFloat[i]; + return out; + } + get projectionMatrix() { + return this.getProjectionMatrix(); + } + _setProjectionMatrix(v) { + let wasm = this._scene.engine.wasm; + wasm.requireTempMem(16 * 4), wasm._tempMemFloat.set(v), wasm._wl_view_component_set_projectionMatrix(this._id, wasm._tempMem); + } + _generateProjectionMatrix() { + this._scene.engine.wasm._wl_view_component_generate_projectionMatrix(this._id); + } + get near() { + return this.engine.wasm._wl_view_component_get_near(this._id); + } + set near(near) { + this.engine.wasm._wl_view_component_set_near(this._id, near), this.engine.xr && this.scene.isActive && this.active && this.engine.webxr.updateProjectionParams(near, this.far); + } + get far() { + return this.engine.wasm._wl_view_component_get_far(this._id); + } + set far(far) { + this.engine.wasm._wl_view_component_set_far(this._id, far), this.engine.xr && this.scene.isActive && this.active && this.engine.webxr.updateProjectionParams(this.near, far); + } + get fov() { + return this.engine.wasm._wl_view_component_get_fov(this._id); + } + set fov(fov) { + this.engine.wasm._wl_view_component_set_fov(this._id, fov); + } + getViewport(out = new Int32Array(4)) { + let wasm = this.engine.wasm, ptr = wasm._wl_view_component_get_viewport(this._id); + for (let i = 0; i < 4; ++i) + out[i] = wasm.HEAPF32[ptr + i]; + return out; + } + get viewport() { + return this.getViewport(); + } + _setViewport(x, y, width, height) { + this._scene.engine.wasm._wl_view_component_set_viewport(this._id, x, y, width, height); + } + get extent() { + return this.engine.wasm._wl_view_component_get_extent(this._id); + } + set extent(extent) { + this.engine.wasm._wl_view_component_set_extent(this._id, extent); + } +}, __publicField(_a5, "TypeName", "view"), _a5); +__decorateClass([nativeProperty()], ViewComponent.prototype, "projectionType", 1), __decorateClass([enumerable()], ViewComponent.prototype, "projectionMatrix", 1), __decorateClass([nativeProperty()], ViewComponent.prototype, "near", 1), __decorateClass([nativeProperty()], ViewComponent.prototype, "far", 1), __decorateClass([nativeProperty()], ViewComponent.prototype, "fov", 1), __decorateClass([enumerable()], ViewComponent.prototype, "viewport", 1), __decorateClass([nativeProperty()], ViewComponent.prototype, "extent", 1); +var _a6; +var InputComponent = (_a6 = class extends Component3 { + get inputType() { + return this.engine.wasm._wl_input_component_get_type(this._id); + } + set inputType(type) { + this.engine.wasm._wl_input_component_set_type(this._id, type); + } + get xrInputSource() { + let xr = this.engine.xr; + if (!xr) + return null; + for (let inputSource of xr.session.inputSources) + if (inputSource.handedness == this.handedness) + return inputSource; + return null; + } + get handedness() { + let inputType = this.inputType; + return inputType == 4 || inputType == 6 || inputType == 2 ? "right" : inputType == 3 || inputType == 5 || inputType == 1 ? "left" : null; + } +}, __publicField(_a6, "TypeName", "input"), _a6); +__decorateClass([nativeProperty()], InputComponent.prototype, "inputType", 1), __decorateClass([enumerable()], InputComponent.prototype, "xrInputSource", 1), __decorateClass([enumerable()], InputComponent.prototype, "handedness", 1); +var _a7; +var LightComponent = (_a7 = class extends Component3 { + getColor(out = new Float32Array(3)) { + let wasm = this.engine.wasm, ptr = wasm._wl_light_component_get_color(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out; + } + setColor(c) { + let wasm = this.engine.wasm, ptr = wasm._wl_light_component_get_color(this._id) / 4; + wasm.HEAPF32[ptr] = c[0], wasm.HEAPF32[ptr + 1] = c[1], wasm.HEAPF32[ptr + 2] = c[2]; + } + get color() { + let wasm = this.engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_light_component_get_color(this._id), 3); + } + set color(c) { + this.color.set(c); + } + get lightType() { + return this.engine.wasm._wl_light_component_get_type(this._id); + } + set lightType(t) { + this.engine.wasm._wl_light_component_set_type(this._id, t); + } + get intensity() { + return this.engine.wasm._wl_light_component_get_intensity(this._id); + } + set intensity(intensity) { + this.engine.wasm._wl_light_component_set_intensity(this._id, intensity); + } + get outerAngle() { + return this.engine.wasm._wl_light_component_get_outerAngle(this._id); + } + set outerAngle(angle2) { + this.engine.wasm._wl_light_component_set_outerAngle(this._id, angle2); + } + get innerAngle() { + return this.engine.wasm._wl_light_component_get_innerAngle(this._id); + } + set innerAngle(angle2) { + this.engine.wasm._wl_light_component_set_innerAngle(this._id, angle2); + } + get shadows() { + return !!this.engine.wasm._wl_light_component_get_shadows(this._id); + } + set shadows(b) { + this.engine.wasm._wl_light_component_set_shadows(this._id, b); + } + get shadowRange() { + return this.engine.wasm._wl_light_component_get_shadowRange(this._id); + } + set shadowRange(range) { + this.engine.wasm._wl_light_component_set_shadowRange(this._id, range); + } + get shadowBias() { + return this.engine.wasm._wl_light_component_get_shadowBias(this._id); + } + set shadowBias(bias) { + this.engine.wasm._wl_light_component_set_shadowBias(this._id, bias); + } + get shadowNormalBias() { + return this.engine.wasm._wl_light_component_get_shadowNormalBias(this._id); + } + set shadowNormalBias(bias) { + this.engine.wasm._wl_light_component_set_shadowNormalBias(this._id, bias); + } + get shadowTexelSize() { + return this.engine.wasm._wl_light_component_get_shadowTexelSize(this._id); + } + set shadowTexelSize(size) { + this.engine.wasm._wl_light_component_set_shadowTexelSize(this._id, size); + } + get cascadeCount() { + return this.engine.wasm._wl_light_component_get_cascadeCount(this._id); + } + set cascadeCount(count) { + this.engine.wasm._wl_light_component_set_cascadeCount(this._id, count); + } +}, __publicField(_a7, "TypeName", "light"), _a7); +__decorateClass([nativeProperty()], LightComponent.prototype, "color", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "lightType", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "intensity", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "outerAngle", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "innerAngle", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "shadows", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "shadowRange", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "shadowBias", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "shadowNormalBias", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "shadowTexelSize", 1), __decorateClass([nativeProperty()], LightComponent.prototype, "cascadeCount", 1); +var _a8; +var AnimationComponent = (_a8 = class extends Component3 { + onEvent = new Emitter(); + set animation(anim) { + this.scene.assertOrigin(anim), this.engine.wasm._wl_animation_component_set_animation(this._id, anim ? anim._id : 0); + } + get animation() { + let index = this.engine.wasm._wl_animation_component_get_animation(this._id); + return this._scene.animations.wrap(index); + } + set playCount(playCount) { + this.engine.wasm._wl_animation_component_set_playCount(this._id, playCount); + } + get playCount() { + return this.engine.wasm._wl_animation_component_get_playCount(this._id); + } + set speed(speed) { + this.engine.wasm._wl_animation_component_set_speed(this._id, speed); + } + get speed() { + return this.engine.wasm._wl_animation_component_get_speed(this._id); + } + get state() { + return this.engine.wasm._wl_animation_component_state(this._id); + } + get rootMotionMode() { + return this.engine.wasm._wl_animation_component_get_rootMotionMode(this._id); + } + set rootMotionMode(mode) { + this.engine.wasm._wl_animation_component_set_rootMotionMode(this._id, mode); + } + get iteration() { + return this.engine.wasm._wl_animation_component_get_iteration(this._id); + } + get position() { + return this.engine.wasm._wl_animation_component_get_position(this._id); + } + get duration() { + return this.engine.wasm._wl_animation_component_get_duration(this._id); + } + play() { + this.engine.wasm._wl_animation_component_play(this._id); + } + stop() { + this.engine.wasm._wl_animation_component_stop(this._id); + } + pause() { + this.engine.wasm._wl_animation_component_pause(this._id); + } + getFloatParameter(name) { + let wasm = this.engine.wasm, index = wasm._wl_animation_component_getGraphParamIndex(this._id, wasm.tempUTF8(name)); + if (index === -1) + throw Error(`Missing parameter '${name}'`); + return wasm._wl_animation_component_getGraphParamValue(this._id, index, wasm._tempMem), wasm._tempMemFloat[0]; + } + setFloatParameter(name, value) { + let wasm = this.engine.wasm, index = wasm._wl_animation_component_getGraphParamIndex(this._id, wasm.tempUTF8(name)); + if (index === -1) + throw Error(`Missing parameter '${name}'`); + wasm._tempMemFloat[0] = value, wasm._wl_animation_component_setGraphParamValue(this._id, index, wasm._tempMem); + } + getRootMotionTranslation(out = new Float32Array(3)) { + let wasm = this.engine.wasm; + return wasm._wl_animation_component_get_rootMotion_translation(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + getRootMotionRotation(out = new Float32Array(3)) { + let wasm = this.engine.wasm; + return wasm._wl_animation_component_get_rootMotion_rotation(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } +}, __publicField(_a8, "TypeName", "animation"), _a8); +__decorateClass([nativeProperty()], AnimationComponent.prototype, "animation", 1), __decorateClass([nativeProperty()], AnimationComponent.prototype, "playCount", 1), __decorateClass([nativeProperty()], AnimationComponent.prototype, "speed", 1), __decorateClass([enumerable()], AnimationComponent.prototype, "state", 1), __decorateClass([nativeProperty(), enumerable()], AnimationComponent.prototype, "rootMotionMode", 1); +var _a9; +var MeshComponent = (_a9 = class extends Component3 { + set material(material) { + this.engine.wasm._wl_mesh_component_set_material(this._id, material ? material._id : 0); + } + get material() { + let index = this.engine.wasm._wl_mesh_component_get_material(this._id); + return this.engine.materials.wrap(index); + } + get mesh() { + let index = this.engine.wasm._wl_mesh_component_get_mesh(this._id); + return this.engine.meshes.wrap(index); + } + set mesh(mesh) { + this.engine.wasm._wl_mesh_component_set_mesh(this._id, mesh?._id ?? 0); + } + get skin() { + let index = this.engine.wasm._wl_mesh_component_get_skin(this._id); + return this._scene.skins.wrap(index); + } + set skin(skin) { + this.scene.assertOrigin(skin), this.engine.wasm._wl_mesh_component_set_skin(this._id, skin ? skin._id : 0); + } + get morphTargets() { + let index = this.engine.wasm._wl_mesh_component_get_morph_targets(this._id); + return this.engine.morphTargets.wrap(index); + } + set morphTargets(morphTargets) { + this.engine.wasm._wl_mesh_component_set_morph_targets(this._id, morphTargets?._id ?? 0); + } + get morphTargetWeights() { + return this.getMorphTargetWeights(); + } + set morphTargetWeights(weights) { + this.setMorphTargetWeights(weights); + } + getMorphTargetWeights(out) { + let wasm = this.engine.wasm, count = wasm._wl_mesh_component_get_morph_target_weights(this._id, wasm._tempMem); + out || (out = new Float32Array(count)); + for (let i = 0; i < count; ++i) + out[i] = wasm._tempMemFloat[i]; + return out; + } + getMorphTargetWeight(target) { + let count = this.morphTargets?.count ?? 0; + if (target >= count) + throw new Error(`Index ${target} is out of bounds for ${count} targets`); + return this.engine.wasm._wl_mesh_component_get_morph_target_weight(this._id, target); + } + setMorphTargetWeights(weights) { + let count = this.morphTargets?.count ?? 0; + if (weights.length !== count) + throw new Error(`Expected ${count} weights but got ${weights.length}`); + let wasm = this.engine.wasm; + wasm._tempMemFloat.set(weights), wasm._wl_mesh_component_set_morph_target_weights(this._id, wasm._tempMem, weights.length); + } + setMorphTargetWeight(target, weight) { + let count = this.morphTargets?.count ?? 0; + if (target >= count) + throw new Error(`Index ${target} is out of bounds for ${count} targets`); + this.engine.wasm._wl_mesh_component_set_morph_target_weight(this._id, target, weight); + } +}, __publicField(_a9, "TypeName", "mesh"), _a9); +__decorateClass([nativeProperty()], MeshComponent.prototype, "material", 1), __decorateClass([nativeProperty()], MeshComponent.prototype, "mesh", 1), __decorateClass([nativeProperty()], MeshComponent.prototype, "skin", 1), __decorateClass([nativeProperty()], MeshComponent.prototype, "morphTargets", 1), __decorateClass([nativeProperty()], MeshComponent.prototype, "morphTargetWeights", 1); +var _a10; +var ParticleEffectComponent = (_a10 = class extends Component3 { + get particleEffect() { + let index = this.engine.wasm._wl_particleEffect_component_get_particleEffect(this._id); + return this.engine.particleEffects.wrap(index); + } + set particleEffect(particleEffect) { + this.engine.wasm._wl_particleEffect_component_set_particleEffect(this._id, particleEffect?._id ?? 0); + } +}, __publicField(_a10, "TypeName", "particle-effect"), _a10); +__decorateClass([nativeProperty()], ParticleEffectComponent.prototype, "particleEffect", 1); +var LockAxis = ((LockAxis2) => (LockAxis2[LockAxis2.None = 0] = "None", LockAxis2[LockAxis2.X = 1] = "X", LockAxis2[LockAxis2.Y = 2] = "Y", LockAxis2[LockAxis2.Z = 4] = "Z", LockAxis2))(LockAxis || {}); +var _a11; +var PhysXComponent = (_a11 = class extends Component3 { + getTranslationOffset(out = new Float32Array(3)) { + let wasm = this.engine.wasm; + return wasm._wl_physx_component_get_offsetTranslation(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + getRotationOffset(out = new Float32Array(4)) { + let wasm = this.engine.wasm, ptr = wasm._wl_physx_component_get_offsetTransform(this._id) >> 2; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out[3] = wasm.HEAPF32[ptr + 3], out; + } + getExtents(out = new Float32Array(3)) { + let wasm = this.engine.wasm, ptr = wasm._wl_physx_component_get_extents(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out; + } + getLinearVelocity(out = new Float32Array(3)) { + let wasm = this.engine.wasm, tempMemFloat = wasm._tempMemFloat; + return wasm._wl_physx_component_get_linearVelocity(this._id, wasm._tempMem), out[0] = tempMemFloat[0], out[1] = tempMemFloat[1], out[2] = tempMemFloat[2], out; + } + getAngularVelocity(out = new Float32Array(3)) { + let wasm = this.engine.wasm, tempMemFloat = wasm._tempMemFloat; + return wasm._wl_physx_component_get_angularVelocity(this._id, wasm._tempMem), out[0] = tempMemFloat[0], out[1] = tempMemFloat[1], out[2] = tempMemFloat[2], out; + } + set static(b) { + this.engine.wasm._wl_physx_component_set_static(this._id, b); + } + get static() { + return !!this.engine.wasm._wl_physx_component_get_static(this._id); + } + get translationOffset() { + return this.getTranslationOffset(); + } + set translationOffset(offset2) { + this.engine.wasm._wl_physx_component_set_offsetTranslation(this._id, offset2[0], offset2[1], offset2[2]); + } + get rotationOffset() { + return this.getRotationOffset(); + } + set rotationOffset(offset2) { + this.engine.wasm._wl_physx_component_set_offsetRotation(this._id, offset2[0], offset2[1], offset2[2], offset2[3]); + } + set kinematic(b) { + this.engine.wasm._wl_physx_component_set_kinematic(this._id, b); + } + get kinematic() { + return !!this.engine.wasm._wl_physx_component_get_kinematic(this._id); + } + set gravity(b) { + this.engine.wasm._wl_physx_component_set_gravity(this._id, b); + } + get gravity() { + return !!this.engine.wasm._wl_physx_component_get_gravity(this._id); + } + set simulate(b) { + this.engine.wasm._wl_physx_component_set_simulate(this._id, b); + } + get simulate() { + return !!this.engine.wasm._wl_physx_component_get_simulate(this._id); + } + set allowSimulation(b) { + this.engine.wasm._wl_physx_component_set_allowSimulation(this._id, b); + } + get allowSimulation() { + return !!this.engine.wasm._wl_physx_component_get_allowSimulation(this._id); + } + set allowQuery(b) { + this.engine.wasm._wl_physx_component_set_allowQuery(this._id, b); + } + get allowQuery() { + return !!this.engine.wasm._wl_physx_component_get_allowQuery(this._id); + } + set trigger(b) { + this.engine.wasm._wl_physx_component_set_trigger(this._id, b); + } + get trigger() { + return !!this.engine.wasm._wl_physx_component_get_trigger(this._id); + } + set shape(s) { + this.engine.wasm._wl_physx_component_set_shape(this._id, s); + } + get shape() { + return this.engine.wasm._wl_physx_component_get_shape(this._id); + } + set shapeData(d) { + d == null || !isMeshShape(this.shape) || this.engine.wasm._wl_physx_component_set_shape_data(this._id, d.index); + } + get shapeData() { + return isMeshShape(this.shape) ? { index: this.engine.wasm._wl_physx_component_get_shape_data(this._id) } : null; + } + set extents(e) { + this.extents.set(e); + } + get extents() { + let wasm = this.engine.wasm, ptr = wasm._wl_physx_component_get_extents(this._id); + return new Float32Array(wasm.HEAPF32.buffer, ptr, 3); + } + get staticFriction() { + return this.engine.wasm._wl_physx_component_get_staticFriction(this._id); + } + set staticFriction(v) { + this.engine.wasm._wl_physx_component_set_staticFriction(this._id, v); + } + get dynamicFriction() { + return this.engine.wasm._wl_physx_component_get_dynamicFriction(this._id); + } + set dynamicFriction(v) { + this.engine.wasm._wl_physx_component_set_dynamicFriction(this._id, v); + } + get bounciness() { + return this.engine.wasm._wl_physx_component_get_bounciness(this._id); + } + set bounciness(v) { + this.engine.wasm._wl_physx_component_set_bounciness(this._id, v); + } + get linearDamping() { + return this.engine.wasm._wl_physx_component_get_linearDamping(this._id); + } + set linearDamping(v) { + this.engine.wasm._wl_physx_component_set_linearDamping(this._id, v); + } + get angularDamping() { + return this.engine.wasm._wl_physx_component_get_angularDamping(this._id); + } + set angularDamping(v) { + this.engine.wasm._wl_physx_component_set_angularDamping(this._id, v); + } + set linearVelocity(v) { + this.engine.wasm._wl_physx_component_set_linearVelocity(this._id, v[0], v[1], v[2]); + } + get linearVelocity() { + let wasm = this.engine.wasm; + return wasm._wl_physx_component_get_linearVelocity(this._id, wasm._tempMem), new Float32Array(wasm.HEAPF32.buffer, wasm._tempMem, 3); + } + set angularVelocity(v) { + this.engine.wasm._wl_physx_component_set_angularVelocity(this._id, v[0], v[1], v[2]); + } + get angularVelocity() { + let wasm = this.engine.wasm; + return wasm._wl_physx_component_get_angularVelocity(this._id, wasm._tempMem), new Float32Array(wasm.HEAPF32.buffer, wasm._tempMem, 3); + } + set groupsMask(flags) { + this.engine.wasm._wl_physx_component_set_groupsMask(this._id, flags); + } + get groupsMask() { + return this.engine.wasm._wl_physx_component_get_groupsMask(this._id); + } + set blocksMask(flags) { + this.engine.wasm._wl_physx_component_set_blocksMask(this._id, flags); + } + get blocksMask() { + return this.engine.wasm._wl_physx_component_get_blocksMask(this._id); + } + set linearLockAxis(lock) { + this.engine.wasm._wl_physx_component_set_linearLockAxis(this._id, lock); + } + get linearLockAxis() { + return this.engine.wasm._wl_physx_component_get_linearLockAxis(this._id); + } + set angularLockAxis(lock) { + this.engine.wasm._wl_physx_component_set_angularLockAxis(this._id, lock); + } + get angularLockAxis() { + return this.engine.wasm._wl_physx_component_get_angularLockAxis(this._id); + } + set mass(m) { + this.engine.wasm._wl_physx_component_set_mass(this._id, m); + } + get mass() { + return this.engine.wasm._wl_physx_component_get_mass(this._id); + } + set massSpaceInteriaTensor(v) { + this.engine.wasm._wl_physx_component_set_massSpaceInertiaTensor(this._id, v[0], v[1], v[2]); + } + set sleepOnActivate(flag) { + this.engine.wasm._wl_physx_component_set_sleepOnActivate(this._id, flag); + } + get sleepOnActivate() { + return !!this.engine.wasm._wl_physx_component_get_sleepOnActivate(this._id); + } + addForce(f, m = 0, localForce = false, p, local = false) { + let wasm = this.engine.wasm; + if (!p) { + wasm._wl_physx_component_addForce(this._id, f[0], f[1], f[2], m, localForce); + return; + } + wasm._wl_physx_component_addForceAt(this._id, f[0], f[1], f[2], m, localForce, p[0], p[1], p[2], local); + } + addTorque(f, m = 0) { + this.engine.wasm._wl_physx_component_addTorque(this._id, f[0], f[1], f[2], m); + } + onCollision(callback) { + return this.onCollisionWith(this, callback); + } + onCollisionWith(otherComp, callback) { + let callbacks = this.scene._pxCallbacks; + return callbacks.has(this._id) || callbacks.set(this._id, []), callbacks.get(this._id).push(callback), this.engine.wasm._wl_physx_component_addCallback(this._id, otherComp._id); + } + removeCollisionCallback(callbackId) { + let r = this.engine.wasm._wl_physx_component_removeCallback(this._id, callbackId), callbacks = this.scene._pxCallbacks; + r && callbacks.get(this._id).splice(-r); + } +}, __publicField(_a11, "TypeName", "physx"), _a11); +__decorateClass([nativeProperty()], PhysXComponent.prototype, "static", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "translationOffset", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "rotationOffset", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "kinematic", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "gravity", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "simulate", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "allowSimulation", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "allowQuery", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "trigger", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "shape", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "shapeData", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "extents", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "staticFriction", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "dynamicFriction", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "bounciness", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "linearDamping", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "angularDamping", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "linearVelocity", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "angularVelocity", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "groupsMask", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "blocksMask", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "linearLockAxis", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "angularLockAxis", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "mass", 1), __decorateClass([nativeProperty()], PhysXComponent.prototype, "sleepOnActivate", 1); +var Physics = class { + _hit; + _engine; + _rayHit; + constructor(engine2) { + this._engine = engine2; + let wasm = engine2.wasm; + this._rayHit = wasm._malloc(4 * (3 * 4 + 3 * 4 + 4 + 2) + 4), this._hit = new RayHit(engine2.scene, this._rayHit), wasm._wl_physx_set_collision_callback(wasm.addFunction((a, index, type, b) => { + let physxA = this._engine.scene._components.wrapPhysx(a), physxB = this._engine.scene._components.wrapPhysx(b), callback = this._engine.scene._pxCallbacks.get(physxA._id)[index]; + callback(type, physxB); + }, "viiii")); + } + rayCast(o, d, groupMask, maxDistance = 100) { + let scene = this._engine.scene._index; + return this._engine.wasm._wl_physx_ray_cast(scene, o[0], o[1], o[2], d[0], d[1], d[2], groupMask, maxDistance, this._rayHit), this._hit; + } + get engine() { + return this._engine; + } +}; +var MeshIndexType = ((MeshIndexType2) => (MeshIndexType2[MeshIndexType2.UnsignedByte = 1] = "UnsignedByte", MeshIndexType2[MeshIndexType2.UnsignedShort = 2] = "UnsignedShort", MeshIndexType2[MeshIndexType2.UnsignedInt = 4] = "UnsignedInt", MeshIndexType2))(MeshIndexType || {}); +var MeshSkinningType = ((MeshSkinningType2) => (MeshSkinningType2[MeshSkinningType2.None = 0] = "None", MeshSkinningType2[MeshSkinningType2.FourJoints = 1] = "FourJoints", MeshSkinningType2[MeshSkinningType2.EightJoints = 2] = "EightJoints", MeshSkinningType2))(MeshSkinningType || {}); +var Mesh = class extends Resource { + constructor(engine2, params) { + if (!isNumber(params)) { + let mesh = engine2.meshes.create(params); + return super(engine2, mesh._index), mesh; + } + super(engine2, params); + } + get vertexCount() { + return this.engine.wasm._wl_mesh_get_vertexCount(this._id); + } + get indexData() { + let wasm = this.engine.wasm, tempMem = wasm._tempMem, ptr = wasm._wl_mesh_get_indexData(this._id, tempMem, tempMem + 4); + if (ptr === null) + return null; + let indexCount = wasm.HEAPU32[tempMem / 4]; + switch (wasm.HEAPU32[tempMem / 4 + 1]) { + case 1: + return new Uint8Array(wasm.HEAPU8.buffer, ptr, indexCount); + case 2: + return new Uint16Array(wasm.HEAPU16.buffer, ptr, indexCount); + case 4: + return new Uint32Array(wasm.HEAPU32.buffer, ptr, indexCount); + } + return null; + } + update() { + this.engine.wasm._wl_mesh_update(this._id); + } + getBoundingSphere(out = new Float32Array(4)) { + let wasm = this.engine.wasm; + return this.engine.wasm._wl_mesh_get_boundingSphere(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out[3] = wasm._tempMemFloat[3], out; + } + attribute(attr) { + if (typeof attr != "number") + throw new TypeError("Expected number, but got " + typeof attr); + let wasm = this.engine.wasm, tempMemUint32 = wasm._tempMemUint32; + if (wasm._wl_mesh_get_attribute(this._id, attr, wasm._tempMem), tempMemUint32[0] == 255) + return null; + let arraySize = tempMemUint32[5]; + return new MeshAttributeAccessor(this.engine, { attribute: tempMemUint32[0], offset: tempMemUint32[1], stride: tempMemUint32[2], formatSize: tempMemUint32[3], componentCount: tempMemUint32[4], arraySize: arraySize || 1, length: this.vertexCount, bufferType: attr !== 5 ? Float32Array : Uint16Array }); + } + destroy() { + this.engine.wasm._wl_mesh_destroy(this._id), this.engine.meshes._destroy(this); + } + toString() { + return this.isDestroyed ? "Mesh(destroyed)" : `Mesh(${this._index})`; + } +}; +var MeshAttributeAccessor = class { + length = 0; + _engine; + _attribute = -1; + _offset = 0; + _stride = 0; + _formatSize = 0; + _componentCount = 0; + _arraySize = 1; + _bufferType; + _tempBufferGetter; + constructor(engine2, options) { + this._engine = engine2, this._attribute = options.attribute, this._offset = options.offset, this._stride = options.stride, this._formatSize = options.formatSize, this._componentCount = options.componentCount, this._arraySize = options.arraySize, this._bufferType = options.bufferType, this.length = options.length; + let wasm = this._engine.wasm; + this._tempBufferGetter = this._bufferType === Float32Array ? wasm.getTempBufferF32.bind(wasm) : wasm.getTempBufferU16.bind(wasm); + } + createArray(count = 1) { + return count = count > this.length ? this.length : count, new this._bufferType(count * this._componentCount * this._arraySize); + } + get(index, out = this.createArray()) { + if (out.length % this._componentCount !== 0) + throw new Error(`out.length, ${out.length} is not a multiple of the attribute vector components, ${this._componentCount}`); + let componentCount = this._componentCount * this._arraySize, len4 = Math.min(out.length, componentCount * this.length), dest = this._tempBufferGetter(len4), elementSize = this._bufferType.BYTES_PER_ELEMENT, destSize = elementSize * len4, srcFormatSize = this._formatSize * this._arraySize, destFormatSize = this._componentCount * elementSize * this._arraySize; + this.engine.wasm._wl_mesh_get_attribute_values(this._attribute, srcFormatSize, this._offset + index * this._stride, this._stride, destFormatSize, dest.byteOffset, destSize); + for (let i = 0; i < len4; ++i) + out[i] = dest[i]; + return out; + } + set(i, v) { + if (v.length % this._componentCount !== 0) + throw new Error(`out.length, ${v.length} is not a multiple of the attribute vector components, ${this._componentCount}`); + let componentCount = this._componentCount * this._arraySize, len4 = Math.min(v.length, componentCount * this.length), elementSize = this._bufferType.BYTES_PER_ELEMENT, srcSize = elementSize * len4, srcFormatSize = componentCount * elementSize, destFormatSize = this._formatSize * this._arraySize, wasm = this.engine.wasm; + if (v.buffer != wasm.HEAPU8.buffer) { + let dest = this._tempBufferGetter(len4); + dest.set(v), v = dest; + } + return wasm._wl_mesh_set_attribute_values(this._attribute, srcFormatSize, v.byteOffset, srcSize, destFormatSize, this._offset + i * this._stride, this._stride), this; + } + get engine() { + return this._engine; + } +}; +var Font = class extends Resource { + get emHeight() { + return this.engine.wasm._wl_font_get_emHeight(this._id); + } + get capHeight() { + return this.engine.wasm._wl_font_get_capHeight(this._id); + } + get xHeight() { + return this.engine.wasm._wl_font_get_xHeight(this._id); + } + get outlineSize() { + return this.engine.wasm._wl_font_get_outlineSize(this._id); + } +}; +var ParticleEffect = class extends Resource { + clone() { + let index = this.engine.wasm._wl_particleEffect_clone(this._id); + return this.engine.particleEffects.wrap(index); + } +}; +var temp2d = null; +var Texture = class extends Resource { + constructor(engine2, param) { + if (isImageLike(param)) { + let texture = engine2.textures.create(param); + return super(engine2, texture._index), texture; + } + super(engine2, param); + } + get valid() { + return !this.isDestroyed && this.width !== 0 && this.height !== 0; + } + get id() { + return this.index; + } + update() { + let image = this._imageIndex; + !this.valid || !image || this.engine.wasm._wl_image_markDirty(image); + } + get width() { + let element = this.htmlElement; + if (element) + return element.videoWidth ?? element.width; + let wasm = this.engine.wasm; + return wasm._wl_image_size(this._imageIndex, wasm._tempMem), wasm._tempMemUint32[0]; + } + get height() { + let element = this.htmlElement; + if (element) + return element.videoHeight ?? element.height; + let wasm = this.engine.wasm; + return wasm._wl_image_size(this._imageIndex, wasm._tempMem), wasm._tempMemUint32[1]; + } + get htmlElement() { + let image = this._imageIndex; + if (!image) + return null; + let wasm = this.engine.wasm, jsImageIndex = wasm._wl_image_get_jsImage_index(image); + return wasm._images[jsImageIndex]; + } + updateSubImage(srcX, srcY, srcWidth, srcHeight, dstX = srcX, dstY = srcY, content) { + if (this.isDestroyed) + return false; + let image = this._imageIndex; + if (!image) + return false; + let img = content ?? this.htmlElement; + if (!img) + return false; + let isImageBitmap = img instanceof ImageBitmap, flipY = !isImageBitmap; + if (srcX || srcY) { + if (!temp2d) { + let canvas2 = document.createElement("canvas"), ctx = canvas2.getContext("2d"); + if (!ctx) + throw new Error("Texture.updateSubImage(): Failed to obtain CanvasRenderingContext2D."); + temp2d = { canvas: canvas2, ctx }; + } + temp2d.canvas.width = srcWidth, temp2d.canvas.height = srcHeight, temp2d.ctx.drawImage(img, srcX, isImageBitmap ? img.height - srcY - srcHeight : srcY, srcWidth, srcHeight, 0, 0, srcWidth, srcHeight), img = temp2d.canvas; + } + let wasm = this.engine.wasm; + wasm._images[0] = img; + let width = srcWidth - Math.max(srcWidth + dstX - this.width, 0), height = srcHeight - Math.max(srcHeight + dstY - this.height, 0), dstReversedY = this.height - dstY - height, ret = wasm._wl_renderer_updateImage(image, 0, width, height, dstX, dstReversedY, flipY); + return wasm._images[0] = null, !!ret; + } + destroy() { + let wasm = this.engine.wasm, img = wasm._images[this._imageIndex]; + img instanceof ImageBitmap && img.close(), wasm._wl_texture_destroy(this._id), this.engine.textures._destroy(this); + } + toString() { + return this.isDestroyed ? "Texture(destroyed)" : `Texture(${this._index})`; + } + get _imageIndex() { + return this.engine.wasm._wl_texture_get_image_index(this._id); + } +}; +var Animation = class extends SceneResource { + constructor(host = WL, index) { + let scene = host instanceof Prefab ? host : host.scene; + super(scene, index); + } + get duration() { + return this.engine.wasm._wl_animation_get_duration(this._id); + } + get trackCount() { + return this.engine.wasm._wl_animation_get_trackCount(this._id); + } + retarget(newTargets) { + let wasm = this.engine.wasm; + if (newTargets instanceof Skin) { + let index2 = wasm._wl_animation_retargetToSkin(this._id, newTargets._id); + return this._scene.animations.wrap(index2); + } + if (newTargets.length != this.trackCount) + throw Error("Expected " + this.trackCount.toString() + " targets, but got " + newTargets.length.toString()); + let ptr = wasm._malloc(2 * newTargets.length); + for (let i = 0; i < newTargets.length; ++i) { + let object3d = newTargets[i]; + this.scene.assertOrigin(object3d), wasm.HEAPU16[(ptr >>> 1) + i] = newTargets[i].objectId; + } + let index = wasm._wl_animation_retarget(this._id, ptr); + return wasm._free(ptr), this._scene.animations.wrap(index); + } + toString() { + return this.isDestroyed ? "Animation(destroyed)" : `Animation(${this._index})`; + } +}; +var Object3D2 = class { + _id = -1; + _localId = -1; + _scene; + _engine; + constructor(scene, id) { + scene = scene instanceof Prefab ? scene : scene.scene, this._localId = id, this._id = scene._index << 22 | id, this._scene = scene, this._engine = scene.engine; + } + get name() { + let wasm = this._engine.wasm; + return wasm.UTF8ToString(wasm._wl_object_name(this._id)); + } + set name(newName) { + let wasm = this._engine.wasm; + wasm._wl_object_set_name(this._id, wasm.tempUTF8(newName)); + } + get parent() { + let p = this._engine.wasm._wl_object_parent(this._id); + return p === 0 ? null : this._scene.wrap(p); + } + get children() { + return this.getChildren(); + } + get childrenCount() { + return this._engine.wasm._wl_object_get_children_count(this._id); + } + set parent(newParent) { + if (this.markedDestroyed) { + let strThis = this.toString(), strParent = newParent || "root"; + throw new Error(`Failed to attach ${strThis} to ${strParent}. ${strThis} is marked as destroyed.`); + } else if (newParent?.markedDestroyed) { + let strParent = newParent.toString(); + throw new Error(`Failed to attach ${this} to ${strParent}. ${strParent} is marked as destroyed.`); + } + this.scene.assertOrigin(newParent), this._engine.wasm._wl_object_set_parent(this._id, newParent == null ? 0 : newParent._id); + } + get objectId() { + return this._localId; + } + get scene() { + return this._scene; + } + get engine() { + return this._engine; + } + addChild() { + let objectId = this.engine.wasm._wl_scene_add_object(this.scene._index, this._id); + return this.scene.wrap(objectId); + } + clone(parent = null) { + this.scene.assertOrigin(parent); + let id = this._engine.wasm._wl_object_clone(this._id, parent ? parent._id : 0); + return this._scene.wrap(id); + } + getChildren(out = new Array(this.childrenCount)) { + let childrenCount = this.childrenCount; + if (childrenCount === 0) + return out; + let wasm = this._engine.wasm; + wasm.requireTempMem(childrenCount * 2), this._engine.wasm._wl_object_get_children(this._id, wasm._tempMem, wasm._tempMemSize >> 1); + for (let i = 0; i < childrenCount; ++i) + out[i] = this._scene.wrap(wasm._tempMemUint16[i]); + return out; + } + resetTransform() { + return this._engine.wasm._wl_object_reset_translation_rotation(this._id), this._engine.wasm._wl_object_reset_scaling(this._id), this; + } + resetPositionRotation() { + return this._engine.wasm._wl_object_reset_translation_rotation(this._id), this; + } + resetTranslationRotation() { + return this.resetPositionRotation(); + } + resetRotation() { + return this._engine.wasm._wl_object_reset_rotation(this._id), this; + } + resetPosition() { + return this._engine.wasm._wl_object_reset_translation(this._id), this; + } + resetTranslation() { + return this.resetPosition(); + } + resetScaling() { + return this._engine.wasm._wl_object_reset_scaling(this._id), this; + } + translate(v) { + return this.translateLocal(v); + } + translateLocal(v) { + return this._engine.wasm._wl_object_translate(this._id, v[0], v[1], v[2]), this; + } + translateObject(v) { + return this._engine.wasm._wl_object_translate_obj(this._id, v[0], v[1], v[2]), this; + } + translateWorld(v) { + return this._engine.wasm._wl_object_translate_world(this._id, v[0], v[1], v[2]), this; + } + rotateAxisAngleDeg(a, d) { + return this.rotateAxisAngleDegLocal(a, d), this; + } + rotateAxisAngleDegLocal(a, d) { + return this._engine.wasm._wl_object_rotate_axis_angle(this._id, a[0], a[1], a[2], d), this; + } + rotateAxisAngleRad(a, d) { + return this.rotateAxisAngleRadLocal(a, d); + } + rotateAxisAngleRadLocal(a, d) { + return this._engine.wasm._wl_object_rotate_axis_angle_rad(this._id, a[0], a[1], a[2], d), this; + } + rotateAxisAngleDegObject(a, d) { + return this._engine.wasm._wl_object_rotate_axis_angle_obj(this._id, a[0], a[1], a[2], d), this; + } + rotateAxisAngleRadObject(a, d) { + return this._engine.wasm._wl_object_rotate_axis_angle_rad_obj(this._id, a[0], a[1], a[2], d), this; + } + rotate(q) { + return this.rotateLocal(q), this; + } + rotateLocal(q) { + return this._engine.wasm._wl_object_rotate_quat(this._id, q[0], q[1], q[2], q[3]), this; + } + rotateObject(q) { + return this._engine.wasm._wl_object_rotate_quat_obj(this._id, q[0], q[1], q[2], q[3]), this; + } + scale(v) { + return this.scaleLocal(v), this; + } + scaleLocal(v) { + return this._engine.wasm._wl_object_scale(this._id, v[0], v[1], v[2]), this; + } + getPositionLocal(out = new Float32Array(3)) { + let wasm = this._engine.wasm; + return wasm._wl_object_get_translation_local(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + getTranslationLocal(out = new Float32Array(3)) { + return this.getPositionLocal(out); + } + getPositionWorld(out = new Float32Array(3)) { + let wasm = this._engine.wasm; + return wasm._wl_object_get_translation_world(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + getTranslationWorld(out = new Float32Array(3)) { + return this.getPositionWorld(out); + } + setPositionLocal(v) { + return this._engine.wasm._wl_object_set_translation_local(this._id, v[0], v[1], v[2]), this; + } + setTranslationLocal(v) { + return this.setPositionLocal(v); + } + setPositionWorld(v) { + return this._engine.wasm._wl_object_set_translation_world(this._id, v[0], v[1], v[2]), this; + } + setTranslationWorld(v) { + return this.setPositionWorld(v); + } + getScalingLocal(out = new Float32Array(3)) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_scaling_local(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out; + } + setScalingLocal(v) { + return this._engine.wasm._wl_object_set_scaling_local(this._id, v[0], v[1], v[2]), this; + } + getScalingWorld(out = new Float32Array(3)) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_scaling_world(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out; + } + setScalingWorld(v) { + return this._engine.wasm._wl_object_set_scaling_world(this._id, v[0], v[1], v[2]), this; + } + getRotationLocal(out = new Float32Array(4)) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_trans_local(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out[3] = wasm.HEAPF32[ptr + 3], out; + } + setRotationLocal(v) { + return this._engine.wasm._wl_object_set_rotation_local(this._id, v[0], v[1], v[2], v[3]), this; + } + getRotationWorld(out = new Float32Array(4)) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_trans_world(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out[3] = wasm.HEAPF32[ptr + 3], out; + } + setRotationWorld(v) { + return this._engine.wasm._wl_object_set_rotation_world(this._id, v[0], v[1], v[2], v[3]), this; + } + getTransformLocal(out = new Float32Array(8)) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_trans_local(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out[3] = wasm.HEAPF32[ptr + 3], out[4] = wasm.HEAPF32[ptr + 4], out[5] = wasm.HEAPF32[ptr + 5], out[6] = wasm.HEAPF32[ptr + 6], out[7] = wasm.HEAPF32[ptr + 7], out; + } + setTransformLocal(v) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_trans_local(this._id) / 4; + return wasm.HEAPF32[ptr] = v[0], wasm.HEAPF32[ptr + 1] = v[1], wasm.HEAPF32[ptr + 2] = v[2], wasm.HEAPF32[ptr + 3] = v[3], wasm.HEAPF32[ptr + 4] = v[4], wasm.HEAPF32[ptr + 5] = v[5], wasm.HEAPF32[ptr + 6] = v[6], wasm.HEAPF32[ptr + 7] = v[7], this.setDirty(), this; + } + getTransformWorld(out = new Float32Array(8)) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_trans_world(this._id) / 4; + return out[0] = wasm.HEAPF32[ptr], out[1] = wasm.HEAPF32[ptr + 1], out[2] = wasm.HEAPF32[ptr + 2], out[3] = wasm.HEAPF32[ptr + 3], out[4] = wasm.HEAPF32[ptr + 4], out[5] = wasm.HEAPF32[ptr + 5], out[6] = wasm.HEAPF32[ptr + 6], out[7] = wasm.HEAPF32[ptr + 7], out; + } + setTransformWorld(v) { + let wasm = this._engine.wasm, ptr = wasm._wl_object_trans_world(this._id) / 4; + return wasm.HEAPF32[ptr] = v[0], wasm.HEAPF32[ptr + 1] = v[1], wasm.HEAPF32[ptr + 2] = v[2], wasm.HEAPF32[ptr + 3] = v[3], wasm.HEAPF32[ptr + 4] = v[4], wasm.HEAPF32[ptr + 5] = v[5], wasm.HEAPF32[ptr + 6] = v[6], wasm.HEAPF32[ptr + 7] = v[7], this._engine.wasm._wl_object_trans_world_to_local(this._id), this; + } + get transformLocal() { + let wasm = this._engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_object_trans_local(this._id), 8); + } + set transformLocal(t) { + this.transformLocal.set(t), this.setDirty(); + } + get transformWorld() { + let wasm = this._engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_object_trans_world(this._id), 8); + } + set transformWorld(t) { + this.transformWorld.set(t), this._engine.wasm._wl_object_trans_world_to_local(this._id); + } + get scalingLocal() { + let wasm = this._engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_object_scaling_local(this._id), 3); + } + set scalingLocal(s) { + this.scalingLocal.set(s), this.setDirty(); + } + get scalingWorld() { + let wasm = this._engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_object_scaling_world(this._id), 3); + } + set scalingWorld(s) { + this.scalingWorld.set(s), this._engine.wasm._wl_object_scaling_world_to_local(this._id); + } + get rotationLocal() { + return this.transformLocal.subarray(0, 4); + } + get rotationWorld() { + return this.transformWorld.subarray(0, 4); + } + set rotationLocal(r) { + this._engine.wasm._wl_object_set_rotation_local(this._id, r[0], r[1], r[2], r[3]); + } + set rotationWorld(r) { + this._engine.wasm._wl_object_set_rotation_world(this._id, r[0], r[1], r[2], r[3]); + } + getForward(out) { + return this.getForwardWorld(out); + } + getForwardWorld(out) { + return out[0] = 0, out[1] = 0, out[2] = -1, this.transformVectorWorld(out), out; + } + getUp(out) { + return this.getUpWorld(out); + } + getUpWorld(out) { + return out[0] = 0, out[1] = 1, out[2] = 0, this.transformVectorWorld(out), out; + } + getRight(out) { + return this.getRightWorld(out); + } + getRightWorld(out) { + return out[0] = 1, out[1] = 0, out[2] = 0, this.transformVectorWorld(out), out; + } + transformVectorWorld(out, v = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = v[0], wasm._tempMemFloat[1] = v[1], wasm._tempMemFloat[2] = v[2], wasm._wl_object_transformVectorWorld(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformVectorLocal(out, v = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = v[0], wasm._tempMemFloat[1] = v[1], wasm._tempMemFloat[2] = v[2], wasm._wl_object_transformVectorLocal(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformPointWorld(out, p = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = p[0], wasm._tempMemFloat[1] = p[1], wasm._tempMemFloat[2] = p[2], wasm._wl_object_transformPointWorld(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformPointLocal(out, p = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = p[0], wasm._tempMemFloat[1] = p[1], wasm._tempMemFloat[2] = p[2], wasm._wl_object_transformPointLocal(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformVectorInverseWorld(out, v = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = v[0], wasm._tempMemFloat[1] = v[1], wasm._tempMemFloat[2] = v[2], wasm._wl_object_transformVectorInverseWorld(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformVectorInverseLocal(out, v = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = v[0], wasm._tempMemFloat[1] = v[1], wasm._tempMemFloat[2] = v[2], wasm._wl_object_transformVectorInverseLocal(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformPointInverseWorld(out, p = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat[0] = p[0], wasm._tempMemFloat[1] = p[1], wasm._tempMemFloat[2] = p[2], wasm._wl_object_transformPointInverseWorld(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + transformPointInverseLocal(out, p = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat.set(p), wasm._wl_object_transformPointInverseLocal(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + toWorldSpaceTransform(out, q = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat.set(q), wasm._wl_object_toWorldSpaceTransform(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out[3] = wasm._tempMemFloat[3], out[4] = wasm._tempMemFloat[4], out[5] = wasm._tempMemFloat[5], out[6] = wasm._tempMemFloat[6], out[7] = wasm._tempMemFloat[7], out; + } + toLocalSpaceTransform(out, q = out) { + let p = this.parent; + return p ? (p.toObjectSpaceTransform(out, q), out) : (out !== q && (out[0] = q[0], out[1] = q[1], out[2] = q[2], out[3] = q[3], out[4] = q[4], out[5] = q[5], out[6] = q[6], out[7] = q[7]), out); + } + toObjectSpaceTransform(out, q = out) { + let wasm = this._engine.wasm; + return wasm._tempMemFloat.set(q), wasm._wl_object_toObjectSpaceTransform(this._id, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out[3] = wasm._tempMemFloat[3], out[4] = wasm._tempMemFloat[4], out[5] = wasm._tempMemFloat[5], out[6] = wasm._tempMemFloat[6], out[7] = wasm._tempMemFloat[7], out; + } + lookAt(p, up = UP_VECTOR) { + return this._engine.wasm._wl_object_lookAt(this._id, p[0], p[1], p[2], up[0], up[1], up[2]), this; + } + destroy() { + this._id < 0 || this.engine.wasm._wl_object_remove(this._id); + } + setDirty() { + this._engine.wasm._wl_object_set_dirty(this._id); + } + set active(b) { + let comps = this.getComponents(); + for (let c of comps) + c.active = b; + } + getComponent(typeOrClass, index = 0) { + let wasm = this._engine.wasm, type = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName, scene = this._scene, componentType = wasm._wl_scene_get_component_manager_index(scene._index, wasm.tempUTF8(type)); + if (componentType < 0) { + let typeIndex = wasm._componentTypeIndices[type]; + if (typeIndex === void 0) + return null; + let jsIndex = wasm._wl_get_js_component_index(this._id, typeIndex, index); + if (jsIndex < 0) + return null; + let component = this._scene._jsComponents[jsIndex]; + return component.constructor !== BrokenComponent ? component : null; + } + let componentId = wasm._wl_get_component_id(this._id, componentType, index); + return scene._components.wrapNative(componentType, componentId); + } + getComponents(typeOrClass) { + let wasm = this._engine.wasm, scene = this._scene, manager = null, type = null; + if (typeOrClass) { + type = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName; + let nativeManager = scene._components.getNativeManager(type); + manager = nativeManager !== null ? nativeManager : scene._components.js; + } + let components = [], maxComps = Math.floor(wasm._tempMemSize / 3 * 2), componentsCount = wasm._wl_object_get_components(this._id, wasm._tempMem, maxComps), offset2 = 2 * componentsCount; + wasm._wl_object_get_component_types(this._id, wasm._tempMem + offset2, maxComps); + for (let i = 0; i < componentsCount; ++i) { + let t = wasm._tempMemUint8[i + offset2], componentId = wasm._tempMemUint16[i]; + if (manager !== null && t !== manager) + continue; + let comp = this._scene._components.wrapAny(t, componentId); + comp && (type && type !== comp.constructor.TypeName || components.push(comp)); + } + return components; + } + addComponent(typeOrClass, params) { + if (this.markedDestroyed) + throw new Error(`Failed to add component. ${this} is marked as destroyed.`); + let wasm = this._engine.wasm, type = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName, nativeManager = this._scene._components.getNativeManager(type), isNative = nativeManager !== null, manager = isNative ? nativeManager : this._scene._components.js, componentId = -1; + if (isNative) + componentId = wasm._wl_object_add_component(this._id, manager); + else { + if (!(type in wasm._componentTypeIndices)) + throw new TypeError("Unknown component type '" + type + "'"); + componentId = wasm._wl_object_add_js_component(this._id, wasm._componentTypeIndices[type]); + } + let component = this._scene._components.wrapAny(manager, componentId); + return params !== void 0 && component.copy(params), isNative || component._triggerInit(), (!params || !("active" in params && !params.active)) && (component.active = true), component; + } + findByName(name, recursive = false) { + return recursive ? this.findByNameRecursive(name) : this.findByNameDirect(name); + } + findByNameDirect(name) { + let wasm = this._engine.wasm, id = this._id, maxCount = (wasm._tempMemSize >> 2) - 2, buffer = wasm._tempMemUint16; + buffer[maxCount] = 0, buffer[maxCount + 1] = 0; + let bufferPtr = wasm._tempMem, indexPtr = bufferPtr + maxCount * 2, childCountPtr = bufferPtr + maxCount * 2 + 2, namePtr = wasm.tempUTF8(name, (maxCount + 2) * 2), result = [], read = 0; + for (; read = wasm._wl_object_findByName(id, namePtr, indexPtr, childCountPtr, bufferPtr, maxCount); ) + for (let i = 0; i < read; ++i) + result.push(this._scene.wrap(buffer[i])); + return result; + } + findByNameRecursive(name) { + let wasm = this._engine.wasm, id = this._id, maxCount = (wasm._tempMemSize >> 2) - 1, buffer = wasm._tempMemUint16; + buffer[maxCount] = 0; + let bufferPtr = wasm._tempMem, indexPtr = bufferPtr + maxCount * 2, namePtr = wasm.tempUTF8(name, (maxCount + 1) * 2), read = 0, result = []; + for (; read = wasm._wl_object_findByNameRecursive(id, namePtr, indexPtr, bufferPtr, maxCount); ) + for (let i = 0; i < read; ++i) + result.push(this._scene.wrap(buffer[i])); + return result; + } + get changed() { + return !!this._engine.wasm._wl_object_is_changed(this._id); + } + get isDestroyed() { + return this._id < 0; + } + get markedDestroyed() { + return !!this.engine.wasm._wl_object_markedDestroyed(this._id); + } + equals(otherObject) { + return otherObject ? this._id == otherObject._id : false; + } + toString() { + return this.isDestroyed ? "Object3D(destroyed)" : `Object3D('${this.name}', ${this._localId})`; + } +}; +var Skin = class extends SceneResource { + get jointCount() { + return this.engine.wasm._wl_skin_get_joint_count(this._id); + } + get jointIds() { + let wasm = this.engine.wasm; + return new Uint16Array(wasm.HEAPU16.buffer, wasm._wl_skin_joint_ids(this._id), this.jointCount); + } + get inverseBindTransforms() { + let wasm = this.engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_skin_inverse_bind_transforms(this._id), 8 * this.jointCount); + } + get inverseBindScalings() { + let wasm = this.engine.wasm; + return new Float32Array(wasm.HEAPF32.buffer, wasm._wl_skin_inverse_bind_scalings(this._id), 3 * this.jointCount); + } +}; +var MorphTargets = class extends Resource { + get count() { + return this.engine.wasm._wl_morph_targets_get_target_count(this._id); + } + getTargetName(target) { + if (target >= this.count) + throw new Error(`Index ${target} is out of bounds for ${this.count} targets`); + let wasm = this.engine.wasm; + return wasm.UTF8ToString(wasm._wl_morph_targets_get_target_name(this._id, target)); + } + getTargetIndex(name) { + let wasm = this.engine.wasm, index = wasm._wl_morph_targets_get_target_index(this._id, wasm.tempUTF8(name)); + if (index === -1) + throw Error(`Missing target '${name}'`); + return index; + } +}; +var RayHit = class { + _scene; + _ptr; + constructor(scene, ptr) { + if (ptr & 3) + throw new Error("Misaligned pointer: please report a bug"); + this._scene = scene, this._ptr = ptr; + } + getLocations(out) { + out = out ?? Array.from({ length: this.hitCount }, () => new Float32Array(3)); + let wasm = this.engine.wasm, alignedPtr = this._ptr / 4; + for (let i = 0; i < this.hitCount; ++i) { + let locationPtr = alignedPtr + 3 * i; + out[i][0] = wasm.HEAPF32[locationPtr], out[i][1] = wasm.HEAPF32[locationPtr + 1], out[i][2] = wasm.HEAPF32[locationPtr + 2]; + } + return out; + } + getNormals(out) { + out = out ?? Array.from({ length: this.hitCount }, () => new Float32Array(3)); + let wasm = this.engine.wasm, alignedPtr = (this._ptr + 48) / 4; + for (let i = 0; i < this.hitCount; ++i) { + let normalPtr = alignedPtr + 3 * i; + out[i][0] = wasm.HEAPF32[normalPtr], out[i][1] = wasm.HEAPF32[normalPtr + 1], out[i][2] = wasm.HEAPF32[normalPtr + 2]; + } + return out; + } + getDistances(out = new Float32Array(this.hitCount)) { + let wasm = this.engine.wasm, alignedPtr = (this._ptr + 48 * 2) / 4; + for (let i = 0; i < this.hitCount; ++i) { + let distancePtr = alignedPtr + i; + out[i] = wasm.HEAPF32[distancePtr]; + } + return out; + } + getObjects(out = new Array(this.hitCount)) { + let HEAPU16 = this.engine.wasm.HEAPU16, alignedPtr = this._ptr + (48 * 2 + 16) >> 1; + for (let i = 0; i < this.hitCount; ++i) + out[i] = this._scene.wrap(HEAPU16[alignedPtr + i]); + return out; + } + get engine() { + return this._scene.engine; + } + get locations() { + return this.getLocations(); + } + get normals() { + return this.getNormals(); + } + get distances() { + return this.getDistances(); + } + get objects() { + let objects = [null, null, null, null]; + return this.getObjects(objects); + } + get hitCount() { + return Math.min(this.engine.wasm.HEAPU32[this._ptr / 4 + 30], 4); + } +}; +var math = class { + static cubicHermite(out, a, b, c, d, f, engine2 = WL) { + let wasm = engine2.wasm; + wasm._tempMemFloat.subarray(0).set(a), wasm._tempMemFloat.subarray(4).set(b), wasm._tempMemFloat.subarray(8).set(c), wasm._tempMemFloat.subarray(12).set(d); + let isQuat = a.length == 4; + return wasm._wl_math_cubicHermite(wasm._tempMem + 4 * 16, wasm._tempMem + 4 * 0, wasm._tempMem + 4 * 4, wasm._tempMem + 4 * 8, wasm._tempMem + 4 * 12, f, isQuat), out[0] = wasm._tempMemFloat[16], out[1] = wasm._tempMemFloat[17], out[2] = wasm._tempMemFloat[18], isQuat && (out[3] = wasm._tempMemFloat[19]), out; + } +}; +var I18N = class { + onLanguageChanged = new Emitter(); + _engine; + _prevLanguageIndex = -1; + constructor(engine2) { + this._engine = engine2; + } + set language(code) { + this.setLanguage(code); + } + get language() { + let wasm = this._engine.wasm, code = wasm._wl_i18n_currentLanguage(); + return code === 0 ? null : wasm.UTF8ToString(code); + } + get currentIndex() { + return this._engine.wasm._wl_i18n_currentLanguageIndex(); + } + get previousIndex() { + return this._prevLanguageIndex; + } + async setLanguage(code) { + if (code == null) + return Promise.resolve(this.currentIndex); + let wasm = this._engine.wasm; + this._prevLanguageIndex = this.currentIndex, wasm._wl_i18n_setLanguage(wasm.tempUTF8(code)); + let scene = this.engine.scene, filename = wasm.UTF8ToString(wasm._wl_i18n_languageFile(this.currentIndex)), url = `${scene.baseURL}/locale/${filename}`; + return await scene._downloadDependency(url), this.onLanguageChanged.notify(this._prevLanguageIndex, this.currentIndex), this.currentIndex; + } + translate(term) { + let wasm = this._engine.wasm, translation = wasm._wl_i18n_translate(wasm.tempUTF8(term)); + return translation === 0 ? null : wasm.UTF8ToString(translation); + } + languageCount() { + return this._engine.wasm._wl_i18n_languageCount(); + } + languageIndex(code) { + let wasm = this._engine.wasm; + return wasm._wl_i18n_languageIndex(wasm.tempUTF8(code)); + } + languageCode(index) { + let wasm = this._engine.wasm, code = wasm._wl_i18n_languageCode(index); + return code === 0 ? null : wasm.UTF8ToString(code); + } + languageName(index) { + let wasm = this._engine.wasm, name = wasm._wl_i18n_languageName(index); + return name === 0 ? null : wasm.UTF8ToString(name); + } + get engine() { + return this._engine; + } +}; +var Environment = class { + _scene; + constructor(scene) { + this._scene = scene; + } + get intensity() { + return this._scene.engine.wasm._wl_scene_environment_get_intensity(this._scene._index); + } + set intensity(intensity) { + this._scene.engine.wasm._wl_scene_environment_set_intensity(this._scene._index, intensity); + } + getTint(out = new Float32Array(3)) { + let wasm = this._scene.engine.wasm; + return wasm._wl_scene_environment_get_tint(this._scene._index, wasm._tempMem), out[0] = wasm._tempMemFloat[0], out[1] = wasm._tempMemFloat[1], out[2] = wasm._tempMemFloat[2], out; + } + get tint() { + return this.getTint(); + } + setTint(v) { + this._scene.engine.wasm._wl_scene_environment_set_tint(this._scene._index, v[0], v[1], v[2]); + } + set tint(v) { + this.setTint(v); + } + getCoefficients(out = new Float32Array(3 * 9)) { + let wasm = this._scene.engine.wasm; + wasm.requireTempMem(3 * 9 * 4), wasm._wl_scene_environment_get_coefficients(this._scene._index, wasm._tempMem); + for (let i = 0; i < 3 * 9; ++i) + out[i] = wasm._tempMemFloat[i]; + return out; + } + get coefficients() { + return this.getCoefficients(); + } + setCoefficients(v) { + let count = v.length / 3; + count > 9 ? count = 9 : count > 4 && count < 9 ? count = 4 : count > 1 && count < 4 && (count = 1); + let wasm = this._scene.engine.wasm; + wasm._tempMemFloat.set(v), wasm._wl_scene_environment_set_coefficients(this._scene._index, wasm._tempMem, count); + } + set coefficients(v) { + this.setCoefficients(v); + } +}; +var APIVersion = { major: 1, minor: 4, patch: 2, rc: 0 }; +var MaterialParamType = ((MaterialParamType2) => (MaterialParamType2[MaterialParamType2.UnsignedInt = 0] = "UnsignedInt", MaterialParamType2[MaterialParamType2.Int = 1] = "Int", MaterialParamType2[MaterialParamType2.HalfFloat = 2] = "HalfFloat", MaterialParamType2[MaterialParamType2.Float = 3] = "Float", MaterialParamType2[MaterialParamType2.Sampler = 4] = "Sampler", MaterialParamType2[MaterialParamType2.Font = 5] = "Font", MaterialParamType2))(MaterialParamType || {}); +var Material = class extends Resource { + constructor(engine2, params) { + if (typeof params != "number") { + if (!params?.pipeline) + throw new Error("Missing parameter 'pipeline'"); + let template = engine2.materials.getTemplate(params.pipeline), material = new template(); + return super(engine2, material._index), material; + } + super(engine2, params); + } + hasParameter(name) { + let parameters = this.constructor.Parameters; + return parameters && parameters.has(name); + } + get shader() { + return this.pipeline; + } + get pipeline() { + let wasm = this.engine.wasm; + return wasm.UTF8ToString(wasm._wl_material_get_pipeline(this._id)); + } + clone() { + let index = this.engine.wasm._wl_material_clone(this._id); + return this.engine.materials.wrap(index); + } + toString() { + return this.isDestroyed ? "Material(destroyed)" : `Material('${this.pipeline}', ${this._index})`; + } + static wrap(engine2, index) { + return engine2.materials.wrap(index); + } +}; +var MaterialManager = class extends ResourceManager { + _materialTemplates = []; + constructor(engine2) { + super(engine2, Material), this._cacheDefinitions(); + } + wrap(index) { + if (index <= 0) + return null; + let cached = this._cache[index]; + if (cached) + return cached; + let definition = this.engine.wasm._wl_material_get_definition(index), Template = this._materialTemplates[definition], material = new Template(index); + return this._wrapInstance(material); + } + getTemplate(pipeline) { + let wasm = this.engine.wasm, index = wasm._wl_get_material_definition_index(wasm.tempUTF8(pipeline)); + if (!index) + throw new Error(`Pipeline '${pipeline}' doesn't exist in the scene`); + return this._materialTemplates[index]; + } + _wrapInstance(instance) { + if (this._cache[instance.index] = instance, !this.engine.legacyMaterialSupport) + return instance; + let proxy = new Proxy(instance, { get(target, prop) { + if (!target.hasParameter(prop)) + return target[prop]; + let name = `get${capitalizeFirstUTF8(prop)}`; + return target[name](); + }, set(target, prop, value) { + if (!target.hasParameter(prop)) + return target[prop] = value, true; + let name = `set${capitalizeFirstUTF8(prop)}`; + return target[name](value), true; + } }); + return this._cache[instance.index] = proxy, proxy; + } + _cacheDefinitions() { + let count = this.engine.wasm._wl_get_material_definition_count(); + for (let i = 1; i < count; ++i) + this._materialTemplates[i] = this._createMaterialTemplate(i); + } + _createMaterialTemplate(definitionIndex) { + let engine2 = this.engine, template = class extends Material { + static Parameters = /* @__PURE__ */ new Set(); + constructor(index) { + return index = index ?? engine2.wasm._wl_material_create(definitionIndex), super(engine2, index), engine2.materials._wrapInstance(this); + } + }, wasm = this.engine.wasm, nbParams = wasm._wl_material_definition_get_param_count(definitionIndex); + for (let index = 0; index < nbParams; ++index) { + let name = wasm.UTF8ToString(wasm._wl_material_definition_get_param_name(definitionIndex, index)); + template.Parameters.add(name); + let t = wasm._wl_material_definition_get_param_type(definitionIndex, index), type = t & 255, componentCount = t >> 8 & 255, capitalized = capitalizeFirstUTF8(name), getterId = `get${capitalized}`, setterId = `set${capitalized}`, templateProto = template.prototype; + switch (type) { + case 0: + templateProto[getterId] = uint32Getter(index, componentCount), templateProto[setterId] = uint32Setter(index); + break; + case 1: + templateProto[getterId] = int32Getter(index, componentCount), templateProto[setterId] = uint32Setter(index); + break; + case 2: + case 3: + templateProto[getterId] = float32Getter(index, componentCount), templateProto[setterId] = float32Setter(index, componentCount); + break; + case 4: + templateProto[getterId] = samplerGetter(index), templateProto[setterId] = samplerSetter(index); + break; + case 5: + templateProto[getterId] = fontGetter(index); + break; + } + } + return template; + } +}; +function uint32Getter(index, count) { + return count === 1 ? function() { + let wasm = this.engine.wasm; + return wasm._wl_material_get_param_value(this._id, index, wasm._tempMem), wasm._tempMemUint32[0]; + } : function(out = new Uint32Array(count)) { + let wasm = this.engine.wasm; + wasm._wl_material_get_param_value(this._id, index, wasm._tempMem); + for (let i = 0; i < out.length; ++i) + out[i] = wasm._tempMemUint32[i]; + return out; + }; +} +function uint32Setter(index) { + return function(value) { + this.engine.wasm._wl_material_set_param_value_uint(this._id, index, value); + }; +} +function int32Getter(index, count) { + return count === 1 ? function() { + let wasm = this.engine.wasm; + return wasm._wl_material_get_param_value(this._id, index, wasm._tempMem), wasm._tempMemInt[0]; + } : function(out = new Int32Array(count)) { + let wasm = this.engine.wasm; + wasm._wl_material_get_param_value(this._id, index, wasm._tempMem); + for (let i = 0; i < out.length; ++i) + out[i] = wasm._tempMemInt[i]; + return out; + }; +} +function float32Getter(index, count) { + return count === 1 ? function() { + let wasm = this.engine.wasm; + return wasm._wl_material_get_param_value(this._id, index, wasm._tempMem), wasm._tempMemFloat[0]; + } : function(out = new Float32Array(count)) { + let wasm = this.engine.wasm; + wasm._wl_material_get_param_value(this._id, index, wasm._tempMem); + for (let i = 0; i < out.length; ++i) + out[i] = wasm._tempMemFloat[i]; + return out; + }; +} +function float32Setter(index, count) { + return function(value) { + let wasm = this.engine.wasm, actualCount = 1; + if (typeof value == "number") + wasm._tempMemFloat[0] = value; + else { + actualCount = value.length; + for (let i = 0; i < actualCount; ++i) + wasm._tempMemFloat[i] = value[i]; + } + if (actualCount < count) + throw new Error(`Expected an array ${count} values, got ${actualCount}`); + wasm._wl_material_set_param_value_float(this._id, index, wasm._tempMem, count); + }; +} +function samplerGetter(index) { + return function() { + let wasm = this.engine.wasm; + return wasm._wl_material_get_param_value(this._id, index, wasm._tempMem), this.engine.textures.wrap(wasm._tempMemInt[0]); + }; +} +function samplerSetter(index) { + return function(value) { + this.engine.wasm._wl_material_set_param_value_uint(this._id, index, value?._id ?? 0); + }; +} +function fontGetter(index) { + return function() { + let wasm = this.engine.wasm; + return wasm._wl_material_get_param_value(this._id, index, wasm._tempMem), this.engine.fonts.wrap(wasm._tempMemInt[0]); + }; +} +var MeshManager = class extends ResourceManager { + constructor(engine2) { + super(engine2, Mesh); + } + create(params) { + if (!params.vertexCount) + throw new Error("Missing parameter 'vertexCount'"); + let wasm = this.engine.wasm, indexData = 0, indexType = 0, indexDataSize = 0; + if (params.indexData) + switch (indexType = params.indexType || 2, indexDataSize = params.indexData.length * indexType, indexData = wasm._malloc(indexDataSize), indexType) { + case 1: + wasm.HEAPU8.set(params.indexData, indexData); + break; + case 2: + wasm.HEAPU16.set(params.indexData, indexData >> 1); + break; + case 4: + wasm.HEAPU32.set(params.indexData, indexData >> 2); + break; + } + let { skinningType = 0 } = params, index = wasm._wl_mesh_create(indexData, indexDataSize, indexType, params.vertexCount, skinningType), instance = new Mesh(this._host, index); + return this._cache[instance.index] = instance, instance; + } +}; +function needsFlipY(image) { + return image instanceof ImageBitmap ? 0 : 1; +} +var TextureManager = class extends ResourceManager { + constructor(engine2) { + super(engine2, Texture); + } + create(image) { + let wasm = this.engine.wasm, jsImageIndex = wasm._images.length; + if (wasm._images.push(image), image instanceof HTMLImageElement && !image.complete) + throw new Error("image must be ready to create a texture"); + let width = image.videoWidth ?? image.width, height = image.videoHeight ?? image.height, imageIndex = wasm._wl_image_create(jsImageIndex); + wasm._wl_image_markReady(imageIndex, width, height, needsFlipY(image)); + let index = wasm._wl_texture_create(imageIndex), instance = new Texture(this.engine, index); + return this._cache[instance.index] = instance, instance; + } + load(filename, crossOrigin) { + let image = new Image(); + return image.crossOrigin = crossOrigin ?? image.crossOrigin, image.src = filename, new Promise((resolve, reject) => { + image.onload = () => { + resolve(this.create(image)); + }, image.onerror = function() { + reject("Failed to load image. Not found or no read access"); + }; + }); + } +}; +var XRSessionState = class { + #webxr; + sessionMode; + session; + frame = null; + constructor(webxr, mode, session) { + this.#webxr = webxr, this.sessionMode = mode, this.session = session; + } + referenceSpaceForType(type) { + return this.#webxr.referenceSpaceForType(type); + } + set currentReferenceSpace(refSpace) { + this.#webxr.currentReferenceSpace = refSpace; + } + get currentReferenceSpace() { + return this.#webxr.currentReferenceSpace; + } + get currentReferenceSpaceType() { + return this.#webxr.currentReferenceSpaceType; + } + get baseLayer() { + return this.#webxr.baseLayer; + } + get framebuffers() { + return this.#webxr.framebuffers; + } +}; +var WebXR = class { + #wasm; + engine; + sessionState = null; + colorFormat = 32856; + depthFormat = 35056; + textureType = "texture"; + onSessionEnd = new Emitter(); + onSessionStart = new RetainEmitter(); + onSessionFirstFrame = new Emitter(); + arSupported = false; + vrSupported = false; + baseLayer = null; + framebufferScaleFactor = 1; + _webglBinding = null; + _initXR = false; + _inXR = false; + _refSpaces = { viewer: void 0, local: void 0, "local-floor": void 0, "bounded-floor": void 0, unbounded: void 0 }; + _refSpace = null; + _refSpaceType = null; + _fbo = 0; + _allowLayers = false; + _requestAnimationFrameId = null; + _initialReferenceSpaceType = null; + _tempPosition = new Float32Array(3); + _tempRotation = new Float32Array(4); + _tempPose(transform) { + let p = transform.position; + this._tempPosition[0] = p.x, this._tempPosition[1] = p.y, this._tempPosition[2] = p.z; + let o = transform.orientation; + this._tempRotation[0] = o.x, this._tempRotation[1] = o.y, this._tempRotation[2] = o.z, this._tempRotation[3] = o.w; + } + constructor(engine2) { + this.engine = engine2, this.#wasm = engine2.wasm; + } + checkXRSupport() { + if (typeof navigator > "u") + return Promise.resolve(false); + if (!navigator.xr) { + let isLocalhost = location.hostname === "localhost" || location.hostname === "127.0.0.1", missingHTTPS = location.protocol !== "https:" && !isLocalhost; + return console.warn(missingHTTPS ? "WebXR is only supported with HTTPS or on localhost!" : "WebXR unsupported in this browser."), Promise.resolve(false); + } + let vrPromise = navigator.xr.isSessionSupported("immersive-vr").then((supported) => this.vrSupported = supported).catch(() => this.vrSupported = false), arPromise = navigator.xr.isSessionSupported("immersive-ar").then((supported) => this.arSupported = supported).catch(() => this.arSupported = false); + return Promise.all([vrPromise, arPromise]).then(() => this.vrSupported || this.arSupported); + } + async init(framebufferScaleFactor, offerSessionOptions) { + if (!await this.checkXRSupport()) + return; + this.framebufferScaleFactor = framebufferScaleFactor; + let onXRStart = () => { + this._initialReferenceSpaceType = this.currentReferenceSpaceType; + let newSpace = this._refSpaces.local ?? this._refSpaces.viewer; + this.currentReferenceSpace = newSpace; + }; + if (this.onSessionStart.add(onXRStart), this.engine.onLoadingScreenEnd.once(() => { + this.onSessionStart.remove(onXRStart), this._initialReferenceSpaceType && (this.currentReferenceSpace = this._refSpaces[this._initialReferenceSpaceType] ?? this._refSpaces.viewer, this._initialReferenceSpaceType = null); + }), offerSessionOptions !== null) { + let mode = offerSessionOptions.mode; + mode == "auto" && (this.vrSupported ? mode = "immersive-vr" : this.arSupported ? mode = "immersive-ar" : mode = "inline"); + let offerSession = () => { + this.offerSession(mode, offerSessionOptions.features, offerSessionOptions.optionalFeatures).then((s) => s.addEventListener("end", offerSession)).catch(console.warn); + }; + offerSession(); + } + } + referenceSpaceForType(type) { + return this._refSpaces[type] ?? null; + } + set currentReferenceSpace(refSpace) { + this._refSpace = refSpace, this._refSpaceType = null; + for (let type of Object.keys(this._refSpaces)) + this._refSpaces[type] === refSpace && (this._refSpaceType = type); + } + get currentReferenceSpace() { + return this._refSpace; + } + get currentReferenceSpaceType() { + return this._refSpaceType; + } + get framebuffers() { + return Array.isArray(this._fbo) ? this._fbo.map((id) => this.#wasm.GL.framebuffers[id]) : [this.#wasm.GL.framebuffers[this._fbo]]; + } + updateViewState(inXR) { + let scene = this.engine.scene; + inXR = inXR ?? this._inXR, scene.mainView && (scene.mainView.active = !inXR), scene.leftEyeView && scene.rightEyeView && (scene.leftEyeView.active = inXR, scene.rightEyeView.active = inXR); + } + updateProjectionParams(near, far) { + if (!this.sessionState) + return; + let scene = this.engine.scene; + near ??= scene.leftEyeView.near, far ??= scene.leftEyeView.far; + let reverseZ = !!this.engine.isReverseZEnabled; + this.sessionState.session.updateRenderState({ depthNear: reverseZ ? far : near, depthFar: reverseZ ? near : far }); + } + requestSession(mode, features, optionalFeatures = []) { + let options = this.sessionOptions(features, optionalFeatures); + return navigator.xr.requestSession(mode, options).then(async (s) => (await this.engine.canvas.getContext("webgl2").makeXRCompatible(), this.startSession(s, mode), s)); + } + offerSession(mode, features, optionalFeatures = []) { + if (!navigator.xr.offerSession) + return Promise.reject("WebXR offerSession() unsupported in this browser."); + let options = this.sessionOptions(features, optionalFeatures); + return navigator.xr.offerSession(mode, options).then(async (s) => (await this.engine.canvas.getContext("webgl2").makeXRCompatible(), this.startSession(s, mode), s)); + } + async startSession(session, mode) { + if (session == this.sessionState?.session) + return; + this.sessionState = new XRSessionState(this, mode, session), session.addEventListener("end", () => { + this.endSession(); + }); + let gl = this.engine.canvas.getContext("webgl2"), binding = null; + !("CustomWebXRPolyfill" in window) && "XRWebGLBinding" in window && (binding = this._webglBinding = new XRWebGLBinding(session, gl)); + let useLayers = this._allowLayers && binding; + if (useLayers) { + let layer = this.baseLayer = binding.createProjectionLayer({ scaleFactor: this.framebufferScaleFactor, colorFormat: this.colorFormat, depthFormat: this.depthFormat, textureType: this.textureType ? this.textureType : "texture-array" }); + session.updateRenderState({ layers: [layer] }); + } else { + let layer = this.baseLayer = new XRWebGLLayer(session, gl, { framebufferScaleFactor: this.framebufferScaleFactor }); + session.updateRenderState({ baseLayer: layer }); + } + let promises = []; + for (let type of Object.keys(this._refSpaces)) + promises.push(session.requestReferenceSpace(type).then((refSpace) => (this._refSpaces[type] = refSpace, refSpace))); + await Promise.allSettled(promises).then((results) => { + for (let i = results.length - 1; i >= 0; --i) { + let result = results[i]; + if (result.status !== "rejected") { + this._refSpaceType = Object.keys(this._refSpaces)[i], this._refSpace = result.value; + break; + } + } + }); + let isVR = mode == "immersive-vr", scene = this.engine.scene; + if (isVR && (!scene.leftEyeView || !scene.rightEyeView)) { + console.error("sessionStart(): VR sessions require 1 classic view, a left and a right view"); + return; + } + this.updateViewState(true), this.updateProjectionParams(void 0, void 0), this.onSessionStart.notify(session, this.sessionState.sessionMode), this.#wasm._wl_reset_context(), console.log(`WebXR ${isVR ? "VR" : "AR"} session started`), this.sessionState.session.addEventListener("visibilitychange", (event) => { + switch (event.session.visibilityState) { + case "visible": + this.#wasm._wl_xr_focus(); + case "visible-blurred": + this.#wasm._wl_xr_blur(); + case "hidden": + this.#wasm._wl_xr_hide(); + } + }), this._initXR = true, this._requestAnimationFrameId = session.requestAnimationFrame(useLayers ? this.nextFrameLayers.bind(this) : this.nextFrameSingle.bind(this)); + } + sessionOptions(requiredFeatures, optionalFeatures = []) { + this._allowLayers && !optionalFeatures.includes("layers") && optionalFeatures.push("layers"); + let params = { requiredFeatures, optionalFeatures }; + return (requiredFeatures.includes("depth-sensing") || optionalFeatures.includes("depth-sensing")) && (params.depthSensing = { usagePreference: ["gpu-optimized", "cpu-optimized"], dataFormatPreference: ["float32", "luminance-alpha"] }), params; + } + endSession() { + this.sessionState.session.cancelAnimationFrame(this._requestAnimationFrameId), this._requestAnimationFrameId = null, this.sessionState = null, this.#wasm._wl_reset_context(), this.onSessionStart instanceof RetainEmitter && this.onSessionStart.reset(), this.onSessionEnd.notify(), console.log("WebXR session ended"), this._inXR = false, this.updateViewState(false); + let scene = this.engine.scene; + this.#wasm._wl_xr_exit(), scene.leftEyeView._generateProjectionMatrix(), scene.rightEyeView._generateProjectionMatrix(), this.#wasm._wl_application_redraw(); + } + nextFrame(time, frame) { + this.sessionState.frame = frame; + let session = frame.session, pose = frame.getViewerPose(this._refSpace); + if (!pose) + return; + let scene = this.engine.scene; + this._initXR && (scene.rightEyeView.active = pose.views.length > 1, this.#wasm._wl_xr_init(pose.views.length, !this.baseLayer), this._initXR = false, this._inXR = true, this.onSessionFirstFrame.notify(session, this.sessionState.sessionMode)), this._tempPose(pose.transform), scene._setInputTransformation(0, this._tempPosition, this._tempRotation); + for (let i = 0; i < pose.views.length && i < 2; ++i) { + let view = pose.views[i], viewIndex = view.eye == "right" ? 1 : 0; + this._tempPose(view.transform), scene._setInputTransformation(1 + viewIndex, this._tempPosition, this._tempRotation); + } + for (let inputSource of session.inputSources) { + let handedness = -1; + inputSource.handedness == "left" ? handedness = 0 : inputSource.handedness == "right" && (handedness = 1), this._updateInputComponent(frame, inputSource.gripSpace, 3 + handedness), this._updateInputComponent(frame, inputSource.targetRaySpace, 5 + handedness); + } + this.#wasm._wl_nextFrame(0); + } + nextFrameSingle(time, frame) { + let session = frame.session; + this.sessionState != null && (this._requestAnimationFrameId = session.requestAnimationFrame(this.nextFrameSingle.bind(this))); + let pose = frame.getViewerPose(this._refSpace); + if (!pose) + return; + let gl = this.engine.canvas.getContext("webgl2"), GL = this.#wasm.GL, glLayer = session.renderState.baseLayer; + if (glLayer.framebuffer) { + let id = this._fbo || GL.getNewId(GL.framebuffers); + glLayer.framebuffer.name = id, GL.framebuffers[id] = glLayer.framebuffer, this._fbo = id, gl.bindFramebuffer(gl.FRAMEBUFFER, GL.framebuffers[id]); + } + for (let i = 0; i < pose.views.length; ++i) { + let view = pose.views[i], viewport = glLayer.getViewport(view), destView; + view.eye == "left" ? destView = this.engine.scene.leftEyeView : view.eye == "right" ? destView = this.engine.scene.rightEyeView : destView = this.engine.scene._components.componentAt(ViewComponent, i), destView._setViewport(viewport.x, viewport.y, viewport.width, viewport.height), destView._setProjectionMatrix(view.projectionMatrix), this.#wasm._wl_view_component_remapProjectionMatrix(destView._id, this.engine.isReverseZEnabled, false), this.#wasm._wl_view_component_set_externalFramebuffer(destView._id, this._fbo); + } + this.nextFrame(time, frame); + } + nextFrameLayers(time, frame) { + let session = frame.session; + this.sessionState != null && (this._requestAnimationFrameId = session.requestAnimationFrame(this.nextFrameLayers.bind(this))); + let scene = this.engine.scene, pose = frame.getViewerPose(this._refSpace), layer = this.baseLayer, binding = this._webglBinding, gl = this.engine.canvas.getContext("webgl2"), GL = this.#wasm.GL, createFramebuffer = !this._fbo, textureArray = layer.textureArrayLength == 2; + if (createFramebuffer) { + if (textureArray) + this._fbo = [GL.getNewId(GL.framebuffers), GL.getNewId(GL.framebuffers)]; + else { + let id = GL.getNewId(GL.framebuffers); + this._fbo = [id, id]; + } + this._fbo.forEach((id) => { + let framebuffer = gl.createFramebuffer(); + GL.framebuffers[id] = framebuffer, framebuffer.name = id; + }); + } + let ids = this._fbo; + pose.views.forEach((view) => { + let viewIndex = view.eye == "right" ? 1 : 0, subImage = binding.getViewSubImage(layer, view), viewport = subImage.viewport, destView; + if (view.eye == "right" ? destView = this.engine.scene.rightEyeView : destView = this.engine.scene.leftEyeView, destView._setViewport(viewport.x, viewport.y, viewport.width, viewport.height), destView._setProjectionMatrix(view.projectionMatrix), this.#wasm._wl_view_component_remapProjectionMatrix(destView._id, this.engine.isReverseZEnabled, false), this.#wasm._wl_view_component_set_externalFramebuffer(destView._id, ids[viewIndex]), !createFramebuffer || !textureArray && viewIndex != 0) + return; + let colorTexture = subImage.colorTexture, colorImageId = colorTexture.name = colorTexture.name || GL.getNewId(GL.textures); + GL.textures[colorImageId] = colorTexture; + let depthStencilTexture = subImage.depthStencilTexture, depthStencilImageId = depthStencilTexture.name = depthStencilTexture.name || GL.getNewId(GL.textures); + GL.textures[depthStencilImageId] = depthStencilTexture, gl.bindFramebuffer(gl.FRAMEBUFFER, GL.framebuffers[ids[viewIndex]]), textureArray ? (gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, subImage.colorTexture, 0, subImage.imageIndex), gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, subImage.depthStencilTexture, 0, subImage.imageIndex)) : (gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, subImage.colorTexture, 0), gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, subImage.depthStencilTexture, 0)), gl.checkFramebufferStatus(gl.FRAMEBUFFER) || console.error("Target framebuffer for", view.eye, "eye is incomplete."); + }), this.nextFrame(time, frame); + } + _updateInputComponent(frame, space, inputType) { + if (!space) + return; + let pose = frame.getPose(space, this._refSpace); + !pose || Number.isNaN(pose.transform.matrix[0]) || (this._tempPose(pose.transform), this.engine.scene._setInputTransformation(inputType, this._tempPosition, this._tempRotation)); + } +}; +var GLTFExtensions = class { + objectCount; + root = {}; + mesh = {}; + node = {}; + constructor(count) { + this.objectCount = count; + } +}; +var PrefabGLTF = class extends Prefab { + extensions = null; + constructor(engine2, index) { + super(engine2, index), this.extensions = this._readExtensions(); + } + _processInstantiaton(dest, root, result) { + if (!this.extensions) + return null; + let wasm = this.engine.wasm, count = this.extensions.objectCount, idMapping = new Array(count), activeRootIndex = wasm._wl_object_index(root._id); + for (let i = 0; i < count; ++i) { + let mappedId = wasm._wl_glTF_scene_extensions_gltfIndex_to_id(this._index, dest._index, activeRootIndex, i); + idMapping[i] = mappedId; + } + let remapped = { mesh: {}, node: {}, idMapping }; + for (let gltfIndex in this.extensions.mesh) { + let id = idMapping[gltfIndex]; + remapped.mesh[id] = this.extensions.mesh[gltfIndex]; + } + for (let gltfIndex in this.extensions.node) { + let id = idMapping[gltfIndex]; + remapped.node[id] = this.extensions.node[gltfIndex]; + } + result.extensions = remapped; + } + _readExtensions() { + let wasm = this.engine.wasm, ptr = wasm._wl_glTF_scene_get_extensions(this._index); + if (!ptr) + return null; + let index = ptr / 4, data = wasm.HEAPU32, readString = () => { + let strPtr = data[index++], strLen = data[index++]; + return wasm.UTF8ViewToString(strPtr, strPtr + strLen); + }, objectCount = data[index++], extensions = new GLTFExtensions(objectCount), meshExtensionsSize = data[index++]; + for (let i = 0; i < meshExtensionsSize; ++i) { + let objectId = data[index++]; + extensions.mesh[objectId] = JSON.parse(readString()); + } + let nodeExtensionsSize = data[index++]; + for (let i = 0; i < nodeExtensionsSize; ++i) { + let objectId = data[index++]; + extensions.node[objectId] = JSON.parse(readString()); + } + let rootExtensionsStr = readString(); + return rootExtensionsStr && (extensions.root = JSON.parse(rootExtensionsStr)), extensions; + } +}; +var MAGIC_BIN = "WLEV"; +var SceneType = ((SceneType2) => (SceneType2[SceneType2.Prefab = 0] = "Prefab", SceneType2[SceneType2.Main = 1] = "Main", SceneType2[SceneType2.Dependency = 2] = "Dependency", SceneType2))(SceneType || {}); +var ChunkedSceneLoadSink = class { + #wasm; + #type; + #closeParameters; + #offset = 0; + #requested = 0; + #firstChunk = true; + _loadIndex = -1; + sceneIndex = -1; + #ptr = 0; + #bufferSize = 0; + constructor(engine2, type, url, ...closeParameters) { + this.#wasm = engine2.wasm, this.#type = type, this.#closeParameters = closeParameters, this._loadIndex = this.#wasm._wl_scene_create_chunked_start(this.#wasm.tempUTF8(url)), this.#requested = this.#wasm._wl_scene_create_chunked_buffer_size(this._loadIndex), this._resizeBuffer(this.#requested); + } + _resizeBuffer(size) { + this.#bufferSize > 0 && this.#wasm._free(this.#ptr), this.#bufferSize = size, this.#ptr = size ? this.#wasm._malloc(size) : 0; + } + _throwError(reason) { + throw this.abort(), new Error(reason); + } + write(blob) { + let read = 0; + for (; read < blob.length; ) { + let toRead = Math.min(blob.length - read, this.#bufferSize - this.#offset); + if (this.#wasm.HEAPU8.set(blob.subarray(read, read + toRead), this.#ptr + this.#offset), this.#offset += toRead, read += toRead, this.#requested > this.#offset) + continue; + let readSizePtr = this.#wasm._tempMem, requestedPtr = this.#wasm._tempMem + 4, success; + try { + success = this.#wasm._wl_scene_create_chunked_next(this._loadIndex, this.#ptr, this.#offset, readSizePtr, requestedPtr); + } catch { + success = false; + } + success || this._throwError("Chunk parsing failed"); + let sentSize = this.#offset, readSize = this.#wasm._tempMemUint32[0]; + this.#requested = this.#wasm._tempMemUint32[1]; + let extraSize = sentSize - readSize; + extraSize > this.#requested && this._throwError("Unexpected extra data"), readSize && (extraSize > 0 && this.#wasm.HEAPU8.copyWithin(this.#ptr, this.#ptr + readSize, this.#ptr + sentSize), this.#offset = extraSize, this.#firstChunk && (this._resizeBuffer(this.#wasm._wl_scene_create_chunked_buffer_size(this._loadIndex)), this.#firstChunk = false)); + } + } + close() { + switch (this.#requested > 0 && this._throwError("Unexpected end of data"), this._resizeBuffer(0), this.#type) { + case 0: + this.sceneIndex = this.#wasm._wl_scene_create_chunked_end_prefab(this._loadIndex); + break; + case 1: + this.#wasm._wl_scene_create_chunked_end_main(this._loadIndex), this.sceneIndex = 0; + break; + case 2: + [this.sceneIndex] = this.#closeParameters, this.#wasm._wl_scene_create_chunked_end_queued(this._loadIndex, this.sceneIndex); + break; + default: + this.#wasm._wl_scene_create_chunked_abort(this._loadIndex); + break; + } + this._loadIndex = -1; + } + abort() { + this._loadIndex !== -1 && (this.#wasm._wl_scene_create_chunked_abort(this._loadIndex), this._loadIndex = -1, this._resizeBuffer(0)); + } + get size() { + return this.#bufferSize; + } +}; +var Scene = class _Scene extends Prefab { + onPreRender = new Emitter(); + onPostRender = new Emitter(); + _rayHit; + _hit; + _environment; + constructor(engine2, index) { + super(engine2, index), this._rayHit = this.engine?.wasm._malloc(4 * (3 * 4 + 3 * 4 + 4 + 2) + 4), this._hit = new RayHit(this, this._rayHit), this._environment = new Environment(this); + } + instantiate(prefab) { + if (prefab._index === this._index) + throw new Error("Can't instantiate scene into itself"); + let id = this.engine.wasm._wl_scene_instantiate(prefab._index, this._index), result = { root: this.wrap(id) }; + if (prefab instanceof PrefabGLTF) { + let obj = this.wrap(id); + prefab._processInstantiaton(this, obj, result); + } + return result; + } + destroy() { + if (this.isActive) + throw new Error(`Attempt to destroy ${this}, but destroying the active scene is not supported`); + let wasm = this.engine.wasm, rayPtr = this._rayHit; + super.destroy(), wasm._free(rayPtr); + } + get views() { + return this._components.components(ViewComponent, false); + } + get activeViews() { + return this._components.components(ViewComponent, true); + } + get mainView() { + let id = this.engine.wasm._wl_scene_get_mainView(this._index); + return this._components.wrapView(id); + } + get leftEyeView() { + let id = this.engine.wasm._wl_scene_get_leftView(this._index); + return this._components.wrapView(id); + } + get rightEyeView() { + let id = this.engine.wasm._wl_scene_get_rightView(this._index); + return this._components.wrapView(id); + } + _setInputTransformation(type, position, orientation) { + let wasm = this.engine.wasm; + wasm.requireTempMem(4 * 2 * 4), wasm._tempMemFloat.set(position), wasm._tempMemFloat.set(orientation, 4), wasm._wl_input_set_transformation(this._index, type, wasm._tempMem); + } + rayCast(o, d, groupMask, maxDistance = 100) { + return this.engine.wasm._wl_scene_ray_cast(this._index, o[0], o[1], o[2], d[0], d[1], d[2], groupMask, this._rayHit, maxDistance), this._hit; + } + set clearColor(color) { + this.engine.wasm._wl_scene_set_clearColor(color[0], color[1], color[2], color[3]); + } + set colorClearEnabled(b) { + this.engine.wasm._wl_scene_enableColorClear(b); + } + async load(options) { + let engine2 = this.engine, dispatchReadyEvent = false, opts; + isString(options) ? opts = await _Scene.loadBuffer(options, (bytes, size) => { + this.engine.log.info(1, `Scene downloading: ${bytes} / ${size}`), this.engine.setLoadingProgress(bytes / size); + }) : (opts = options, dispatchReadyEvent = options.dispatchReadyEvent ?? false); + let scene = await engine2.loadMainSceneFromBuffer({ ...opts, dispatchReadyEvent }); + return engine2.onSceneLoaded.notify(), scene; + } + async append(file, options = {}) { + let { baseURL = isString(file) ? getBaseUrl(file) : this.baseURL } = options, buffer = isString(file) ? await fetchWithProgress(file) : file, data = new Uint8Array(buffer), scene = data.byteLength > MAGIC_BIN.length && data.subarray(0, MAGIC_BIN.length).every((value, i) => value === MAGIC_BIN.charCodeAt(i)) ? this.engine.loadPrefabFromBuffer({ buffer, baseURL }) : this.engine.loadGLTFFromBuffer({ buffer, baseURL, extensions: options.loadGltfExtensions }), result = this.instantiate(scene); + return scene instanceof PrefabGLTF ? scene.extensions ? { root: result.root, extensions: { ...result.extensions, root: scene.extensions.root } } : result.root : (scene.destroy(), result.root); + } + setLoadingProgress(percentage) { + this.engine.setLoadingProgress(percentage); + } + dispatchReadyEvent() { + document.dispatchEvent(new CustomEvent("wle-scene-ready", { detail: { filename: this.filename } })); + } + set skyMaterial(material) { + this.engine.wasm._wl_scene_set_sky_material(this._index, material?._id ?? 0); + } + get skyMaterial() { + let index = this.engine.wasm._wl_scene_get_sky_material(this._index); + return this.engine.materials.wrap(index); + } + get environment() { + return this._environment; + } + get componentsBundle() { + let wasm = this.engine.wasm, ptr = wasm._wl_scene_get_componentsBundle(this._index); + return ptr ? wasm.UTF8ToString(ptr) : null; + } + reset() { + } + async _downloadDependency(url) { + let sink = new ChunkedSceneLoadSink(this.engine, 2, url, this._index); + return (await fetchStreamWithProgress(url)).pipeTo(new WritableStream(sink)); + } + async _downloadDependencies() { + let wasm = this.engine.wasm, count = wasm._wl_scene_queued_bin_count(this._index); + if (!count) + return Promise.resolve(); + let urls = new Array(count).fill(0).map((_, i) => { + let ptr = wasm._wl_scene_queued_bin_path(this._index, i); + return wasm.UTF8ToString(ptr); + }); + return wasm._wl_scene_clear_queued_bin_list(this._index), Promise.all(urls.map((url) => this._downloadDependency(url))); + } +}; +function checkRuntimeCompatibility(version) { + let { major, minor } = version, majorDiff = major - APIVersion.major, minorDiff = minor - APIVersion.minor; + if (!majorDiff && !minorDiff) + return; + let error = `checkRuntimeCompatibility(): Version compatibility mismatch: + \u2192 API and runtime compatibility is enforced on a patch level (versions x.y.*) +`; + throw majorDiff < 0 || !majorDiff && minorDiff < 0 ? new Error(`${error} \u2192 Please use a Wonderland Engine editor version >= ${APIVersion.major}.${APIVersion.minor}.*`) : new Error(`${error} \u2192 Please use a new API version >= ${version.major}.${version.minor}.*`); +} +function reloadPage(engine2) { + let session = engine2.xr?.session ?? null; + if (!session) { + location.reload(); + return; + } + session.end().then(() => location.reload()); +} +var WonderlandEngine = class { + get onXRSessionEnd() { + return this.#webxr.onSessionEnd; + } + get onXRSessionStart() { + return this.#webxr.onSessionStart; + } + onResize = new Emitter(); + get arSupported() { + return this.#webxr.arSupported; + } + get vrSupported() { + return this.#webxr.vrSupported; + } + onLoadingScreenEnd = new RetainEmitter(); + onSceneLoaded = new Emitter(); + onSceneActivated = new Emitter(); + onHotReloadRequest = new Emitter(); + i18n = new I18N(this); + get xr() { + return this.#webxr.sessionState; + } + erasePrototypeOnDestroy = false; + legacyMaterialSupport = true; + autoHotReload = true; + loadUncompressedImagesAsBitmap = false; + _scenes = []; + _scene; + _mainSceneVersion = 0; + _textures; + _materials; + _meshes; + _morphTargets; + _fonts; + _particleEffects; + _uncompressedPromises = []; + #wasm; + #physics = null; + #webxr; + #resizeObserver = null; + #bundleCache = /* @__PURE__ */ new Set(); + constructor(wasm, loadingScreen, withRenderer) { + this.#wasm = wasm, this.#wasm._setEngine(this), this.#wasm._loadingScreen = loadingScreen; + let version = this.runtimeVersion, versionString = `${version.major}.${version.minor}.${version.patch}`; + version.rc > 0 && (versionString += `-rc${version.rc}`), this.log.info(0, "Wonderland Engine runtime version:", versionString), this.#webxr = new WebXR(this), checkRuntimeCompatibility(this.#wasm.runtimeVersion), this.#wasm._wl_set_error_callback(this.#wasm.addFunction((messagePtr) => { + throw new Error(this.#wasm.UTF8ToString(messagePtr)); + }, "vi")), this.#wasm._wl_application_create(), this.#physics = null, this.#wasm.withPhysX && (this.#physics = new Physics(this)), this._init(withRenderer), this._scene = this._reload(0), this.canvas.addEventListener("webglcontextlost", (e) => this.log.error(0, "Context lost:", e), false); + } + start() { + this.wasm._wl_application_start(); + } + registerComponent(...classes) { + for (let arg of classes) + this.wasm._registerComponent(arg); + } + async registerBundle(url, nocache = false) { + if (!nocache && this.#bundleCache.has(url)) + return; + this.#bundleCache.add(url), nocache && (url += `?t=${Date.now()}`); + let register = (await import(url)).default; + if (typeof register != "function") + throw new Error(`The bundle '${url}' doesn't have a default exported registration function`); + register(this); + } + setLoadingProgress(percentage) { + this.wasm._wl_set_loading_screen_progress(clamp(percentage, 0, 1)); + } + async switchTo(scene, opts = {}) { + this.wasm._wl_deactivate_activeScene(); + let previous = this.scene; + this._preactivate(scene), this.wasm._wl_scene_activate(scene._index), this.#webxr.updateViewState(void 0), this.#webxr.updateProjectionParams(void 0, void 0), this.onLoadingScreenEnd.isDataRetained || this.onLoadingScreenEnd.notify(); + let promise = scene._downloadDependencies(); + await this.i18n.setLanguage(this.i18n.languageCode(0)); + let { dispatchReadyEvent = false, waitForDependencies = false } = opts; + waitForDependencies && await promise, this.onSceneActivated.notify(previous, scene), dispatchReadyEvent && scene.dispatchReadyEvent(); + } + async loadMainScene(options, progress) { + progress ??= (bytes, size) => { + this.log.info(1, `Scene downloading: ${bytes} / ${size}`), this.setLoadingProgress(bytes / size); + }; + let opts = Prefab.makeUrlLoadOptions(options), { streamed = true, nocache = false } = opts; + if (streamed) { + let options2 = await Scene.loadStream(opts, progress), { stream, url } = Prefab.validateStreamOptions(options2); + return this._loadMainScene(stream, url, nocache, options2); + } else { + let options2 = await Scene.loadBuffer(opts, progress); + return this.loadMainSceneFromBuffer(options2); + } + } + async loadMainSceneFromBuffer(options) { + let { nocache = false } = options, { buffer, url } = Prefab.validateBufferOptions(options); + return this._loadMainScene(buffer, url, nocache, options); + } + async loadPrefab(options, progress) { + let opts = Prefab.makeUrlLoadOptions(options), { streamed = true } = opts; + if (streamed) { + let options2 = await Scene.loadStream(opts, progress), scene = await this._loadSceneFromStream(Prefab, options2); + return this._validateLoadedPrefab(scene), scene._initialize(), scene; + } else { + let options2 = await Scene.loadBuffer(opts, progress); + return this.loadPrefabFromBuffer(options2); + } + } + loadPrefabFromBuffer(options) { + let scene = this._loadSceneFromBuffer(Prefab, options); + return this._validateLoadedPrefab(scene), scene._initialize(), scene; + } + async loadScene(options, progress) { + let opts = Prefab.makeUrlLoadOptions(options), { streamed = true } = opts; + if (streamed) { + let options2 = await Scene.loadStream(opts, progress), scene = await this._loadSceneFromStream(Scene, options2); + return this._validateLoadedScene(scene), scene._initialize(), scene; + } else { + let options2 = await Scene.loadBuffer(opts, progress); + return this.loadSceneFromBuffer(options2); + } + } + async loadGLTF(opts, progress) { + let memOptions = await Scene.loadBuffer(opts, progress), options = isString(opts) ? memOptions : { ...opts, ...memOptions }; + return this.loadGLTFFromBuffer(options); + } + loadSceneFromBuffer(options) { + let scene = this._loadSceneFromBuffer(Scene, options); + return this._validateLoadedScene(scene), scene._initialize(), scene; + } + loadGLTFFromBuffer(options) { + Scene.validateBufferOptions(options); + let { buffer, extensions = false } = options, wasm = this.wasm; + if (!wasm._wl_glTF_scene_create) + throw new Error("Loading .gltf files requires `enableRuntimeGltf` to be checked in the editor Project Settings."); + let ptr = wasm.copyBufferToHeap(buffer); + try { + let index = wasm._wl_glTF_scene_create(extensions, ptr, buffer.byteLength), scene = new PrefabGLTF(this, index); + return this._scenes[scene._index] = scene, this.runtimeVersion.patch && this._loadUncompressedImages(scene._index), scene; + } finally { + wasm._free(ptr); + } + } + isRegistered(typeOrClass) { + return this.#wasm.isRegistered(isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName); + } + getComponentClass(typename) { + let index = this.wasm._componentTypeIndices[typename]; + return index === void 0 ? null : this.wasm._componentTypes[index]; + } + resize(width, height, devicePixelRatio = window.devicePixelRatio) { + width = width * devicePixelRatio, height = height * devicePixelRatio, this.canvas.width = width, this.canvas.height = height, this.wasm._wl_application_resize(width, height), this.onResize.notify(); + } + nextFrame(fixedDelta = 0) { + this.#wasm._wl_nextFrame(fixedDelta); + } + requestXRSession(mode, features, optionalFeatures = []) { + return this.#webxr.requestSession(mode, features, optionalFeatures); + } + offerXRSession(mode, features, optionalFeatures = []) { + return this.#webxr.offerSession(mode, features, optionalFeatures); + } + wrapObject(objectId) { + return this.scene.wrap(objectId); + } + toString() { + return "engine"; + } + get scene() { + return this._scene; + } + get wasm() { + return this.#wasm; + } + get webxr() { + return this.#webxr; + } + get canvas() { + return this.#wasm.canvas; + } + get xrSession() { + return this.xr?.session ?? null; + } + get xrFrame() { + return this.xr?.frame ?? null; + } + get xrBaseLayer() { + return this.xr?.baseLayer ?? null; + } + get xrFramebuffer() { + return this.xr?.framebuffers[0] ?? null; + } + get xrFramebufferScaleFactor() { + return this.#webxr.framebufferScaleFactor; + } + set xrFramebufferScaleFactor(value) { + this.#webxr.framebufferScaleFactor = value; + } + get physics() { + return this.#physics; + } + get textures() { + return this._textures; + } + get materials() { + return this._materials; + } + get meshes() { + return this._meshes; + } + get morphTargets() { + return this._morphTargets; + } + get fonts() { + return this._fonts; + } + get particleEffects() { + return this._particleEffects; + } + get images() { + let wasm = this.wasm, max2 = wasm._tempMemSize >> 1, count = wasm._wl_get_images(wasm._tempMem, max2), result = new Array(count); + for (let i = 0; i < count; ++i) { + let index = wasm._tempMemUint16[i]; + result[i] = wasm._images[index]; + } + return result; + } + get imagesPromise() { + let wasm = this.wasm, max2 = wasm._tempMemSize >> 1, count = wasm._wl_get_images(wasm._tempMem, max2), result = new Array(count); + for (let i = 0; i < count; ++i) { + let index = wasm._tempMemUint16[i], loading = this._uncompressedPromises[index]; + result[i] = (loading ?? Promise.resolve()).then(() => wasm._images[index]); + } + return Promise.all(result); + } + get isTextureStreamingIdle() { + return !!this.wasm._wl_renderer_streaming_idle(); + } + get isReverseZEnabled() { + return !!this.wasm._wl_renderer_isReverseZEnabled(); + } + set autoResizeCanvas(flag) { + if (!!this.#resizeObserver !== flag) { + if (!flag) { + this.#resizeObserver?.unobserve(this.canvas), this.#resizeObserver = null; + return; + } + this.#resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) + entry.target === this.canvas && this.resize(entry.contentRect.width, entry.contentRect.height); + }), this.#resizeObserver.observe(this.canvas); + } + } + get autoResizeCanvas() { + return this.#resizeObserver !== null; + } + get runtimeVersion() { + return this.#wasm.runtimeVersion; + } + get log() { + return this.#wasm._log; + } + _init(withRenderer) { + if (!this.#wasm._wl_application_init(withRenderer)) + throw new Error("Failed to initializing Wonderland runtime"); + return this.resize(this.canvas.clientWidth, this.canvas.clientHeight), true; + } + _reset() { + return this.wasm.reset(), this._scenes.length = 0, this._scene = this._reload(0), this.#bundleCache.clear(), this.switchTo(this._scene); + } + async _reloadRequest(filename) { + if (filename === null) { + reloadPage(this); + return; + } + try { + this.autoHotReload && await this.loadMainScene({ url: filename, nocache: true }); + } catch (e) { + console.error("Hot reload request failed to load main scene, reason:", e), setTimeout(() => reloadPage(this), 1e3); + return; + } + this.onHotReloadRequest.notify(filename); + } + _createEmpty() { + let index = this.wasm._wl_scene_create_empty(), scene = new Scene(this, index); + return this._scenes[index] = scene, scene; + } + _destroyScene(instance) { + this.wasm._wl_scene_destroy(instance._index); + let index = instance._index; + instance._index = -1, this.erasePrototypeOnDestroy && Object.setPrototypeOf(instance, DestroyedPrefabInstance), this._scenes[index] = null; + } + _reload(index) { + let scene = new Scene(this, index); + return this._scenes[index] = scene, this._textures = new TextureManager(this), this._materials = new MaterialManager(this), this._meshes = new MeshManager(this), this._morphTargets = new ResourceManager(this, MorphTargets), this._fonts = new ResourceManager(this, Font), this._particleEffects = new ResourceManager(this, ParticleEffect), this._uncompressedPromises.length = 0, scene; + } + async _loadMainScene(data, url, nocache, options) { + nocache && (url += `?t=${Date.now()}`), this.#wasm._wl_deactivate_activeScene(), ++this._mainSceneVersion; + for (let i = this._scenes.length - 1; i >= 0; --i) { + let scene = this._scenes[i]; + scene && scene.destroy(); + } + this._textures._clear(), this._materials._clear(), this._meshes._clear(), this._morphTargets._clear(), this._uncompressedPromises.length = 0; + let stream = data instanceof ReadableStream ? data : new ReadableStream(new ArrayBufferSource(data)), sink = new ChunkedSceneLoadSink(this, 1, url); + await stream.pipeTo(new WritableStream(sink)); + let mainScene = this._reload(sink.sceneIndex); + this._loadUncompressedImages(mainScene._index), this._preactivate(mainScene); + let componentsBundle = mainScene.componentsBundle; + if (componentsBundle) { + let bundleURL = new URL(componentsBundle, document.baseURI), url2 = nocache ? bundleURL.href.split("?")[0] : bundleURL.href; + await this.registerBundle(url2, nocache); + } + return mainScene._initialize(), await this.switchTo(mainScene, options), mainScene; + } + _loadSceneFromBuffer(PrefabClass, options) { + let { buffer, url } = Scene.validateBufferOptions(options), sink = new ChunkedSceneLoadSink(this, 0, url); + sink.write(new Uint8Array(buffer)), sink.close(); + let index = sink.sceneIndex, scene = new PrefabClass(this, index); + return this._scenes[index] = scene, this._loadUncompressedImages(scene._index), scene; + } + async _loadSceneFromStream(PrefabClass, options) { + let { stream, url } = Scene.validateStreamOptions(options), sink = new ChunkedSceneLoadSink(this, 0, url); + await stream.pipeTo(new WritableStream(sink)); + let index = sink.sceneIndex, scene = new PrefabClass(this, index); + return this._scenes[index] = scene, this._loadUncompressedImages(scene._index), scene; + } + _validateLoadedPrefab(scene) { + if (this.wasm._wl_scene_activatable(scene._index)) + throw this.wasm._wl_scene_destroy(scene._index), new Error("File is not a prefab. To load a scene, use loadScene() instead"); + } + _validateLoadedScene(scene) { + if (!this.wasm._wl_scene_activatable(scene._index)) + throw this.wasm._wl_scene_destroy(scene._index), new Error("File is not a scene. To load a prefab, use loadPrefab() instead"); + } + _preactivate(scene) { + this._scene = scene, this.physics && (this.physics._hit._scene = scene); + } + _loadUncompressedImages(sceneIndex) { + let mainSceneVersion = this._mainSceneVersion, wasm = this.wasm, bitmapOptions = { colorSpaceConversion: "none" }, imageCount = wasm._wl_image_count(); + for (let i = 0; i < imageCount; ++i) { + if (wasm._wl_image_originalScene(i) !== sceneIndex) + continue; + let jsImageIndex = wasm._wl_image_get_jsImage_index(i), image = wasm._images[jsImageIndex]; + if (!image) + continue; + let promise = onImageReady(image); + this.loadUncompressedImagesAsBitmap && (promise = promise.then((img) => createImageBitmap(img, bitmapOptions))), this._uncompressedPromises[jsImageIndex] = promise.then((img) => { + this._mainSceneVersion === mainSceneVersion && wasm._wl_image_markReady(i, img.width, img.height, needsFlipY(img)); + }).catch((e) => { + this._mainSceneVersion === mainSceneVersion && this.log.error(1, "Failed to load uncompressed image", e); + }); + } + } +}; +function assert(bit) { + if (bit >= 32) + throw new Error(`BitSet.enable(): Value ${bit} is over 31`); +} +var BitSet = class { + _bits = 0; + enable(...bits) { + for (let bit of bits) + assert(bit), this._bits |= 1 << bit >>> 0; + return this; + } + enableAll() { + return this._bits = -1, this; + } + disable(...bits) { + for (let bit of bits) + assert(bit), this._bits &= ~(1 << bit >>> 0); + return this; + } + disableAll() { + return this._bits = 0, this; + } + enabled(bit) { + return !!(this._bits & 1 << bit >>> 0); + } +}; +var LogLevel = ((LogLevel2) => (LogLevel2[LogLevel2.Info = 0] = "Info", LogLevel2[LogLevel2.Warn = 1] = "Warn", LogLevel2[LogLevel2.Error = 2] = "Error", LogLevel2))(LogLevel || {}); +var Logger = class { + levels = new BitSet(); + tags = new BitSet().enableAll(); + onLog = new Emitter(); + constructor(...levels) { + this.levels.enable(...levels); + } + info(tag, ...msg) { + return this.levels.enabled(0) && this.tags.enabled(tag) && console.log(...msg), this.onLog.notify(0, msg, tag), this; + } + warn(tag, ...msg) { + return this.levels.enabled(1) && this.tags.enabled(tag) && console.warn(...msg), this.onLog.notify(1, msg, tag), this; + } + error(tag, ...msg) { + return this.levels.enabled(2) && this.tags.enabled(tag) && console.error(...msg), this.onLog.notify(2, msg, tag), this; + } +}; +var WASM = class { + wasm = null; + canvas = null; + preinitializedWebGPUDevice = null; + UTF8ViewToString; + _log = new Logger(); + _deactivate_component_on_error = false; + _tempMem = null; + _tempMemSize = 0; + _tempMemFloat = null; + _tempMemInt = null; + _tempMemUint8 = null; + _tempMemUint32 = null; + _tempMemUint16 = null; + _loadingScreen = null; + _sceneLoadedCallback = []; + _images = [null]; + _components = null; + _componentTypes = []; + _componentTypeIndices = {}; + _engine = null; + _withPhysX = false; + _utf8Decoder = new TextDecoder("utf8"); + _brokenComponentIndex = 0; + constructor(threads2) { + threads2 ? this.UTF8ViewToString = (s, e) => s ? this._utf8Decoder.decode(this.HEAPU8.slice(s, e)) : "" : this.UTF8ViewToString = (s, e) => s ? this._utf8Decoder.decode(this.HEAPU8.subarray(s, e)) : "", this._brokenComponentIndex = this._registerComponent(BrokenComponent); + } + get runtimeVersion() { + let tempBuf = this._malloc(8), tempVersion = new Uint16Array(this.HEAP8.buffer, tempBuf, 4); + this._wl_runtime_version(tempBuf); + let version = { major: tempVersion[0], minor: tempVersion[1], patch: tempVersion[2], rc: tempVersion[3] }; + return this._free(tempBuf), version; + } + reset() { + this._wl_reset(), this._components = null, this._images.length = 1, this.allocateTempMemory(1024), this._componentTypes = [], this._componentTypeIndices = {}, this._brokenComponentIndex = this._registerComponent(BrokenComponent); + } + isRegistered(type) { + return type in this._componentTypeIndices; + } + _registerComponentLegacy(typeName, params, object) { + let ctor = class extends Component3 { + }; + return ctor.TypeName = typeName, ctor.Properties = params, Object.assign(ctor.prototype, object), this._registerComponent(ctor); + } + _registerComponent(ctor) { + if (!ctor.TypeName) + throw new Error("no name provided for component."); + if (!ctor.prototype._triggerInit) + throw new Error(`registerComponent(): Component ${ctor.TypeName} must extend Component`); + setupComponentClass(ctor); + let typeIndex = ctor.TypeName in this._componentTypeIndices ? this._componentTypeIndices[ctor.TypeName] : this._componentTypes.length; + return this._componentTypes[typeIndex] = ctor, this._componentTypeIndices[ctor.TypeName] = typeIndex, ctor === BrokenComponent || (this._log.info(0, "Registered component", ctor.TypeName, `(class ${ctor.name})`, "with index", typeIndex), ctor.onRegister && ctor.onRegister(this._engine)), typeIndex; + } + allocateTempMemory(size) { + this._log.info(0, "Allocating temp mem:", size), this._tempMemSize = size, this._tempMem && this._free(this._tempMem), this._tempMem = this._malloc(this._tempMemSize), this.updateTempMemory(); + } + requireTempMem(size) { + this._tempMemSize >= size || this.allocateTempMemory(Math.ceil(size / 1024) * 1024); + } + updateTempMemory() { + this._tempMemFloat = new Float32Array(this.HEAP8.buffer, this._tempMem, this._tempMemSize >> 2), this._tempMemInt = new Int32Array(this.HEAP8.buffer, this._tempMem, this._tempMemSize >> 2), this._tempMemUint32 = new Uint32Array(this.HEAP8.buffer, this._tempMem, this._tempMemSize >> 2), this._tempMemUint16 = new Uint16Array(this.HEAP8.buffer, this._tempMem, this._tempMemSize >> 1), this._tempMemUint8 = new Uint8Array(this.HEAP8.buffer, this._tempMem, this._tempMemSize); + } + getTempBufferU8(count) { + return this.requireTempMem(count), this._tempMemUint8; + } + getTempBufferU16(count) { + return this.requireTempMem(count * 2), this._tempMemUint16; + } + getTempBufferU32(count) { + return this.requireTempMem(count * 4), this._tempMemUint32; + } + getTempBufferI32(count) { + return this.requireTempMem(count * 4), this._tempMemInt; + } + getTempBufferF32(count) { + return this.requireTempMem(count * 4), this._tempMemFloat; + } + tempUTF8(str5, byteOffset = 0) { + let strLen = this.lengthBytesUTF8(str5) + 1; + this.requireTempMem(strLen + byteOffset); + let ptr = this._tempMem + byteOffset; + return this.stringToUTF8(str5, ptr, strLen), ptr; + } + copyBufferToHeap(buffer) { + let size = buffer.byteLength, ptr = this._malloc(size); + return this.HEAPU8.set(new Uint8Array(buffer), ptr), ptr; + } + get withPhysX() { + return this._withPhysX; + } + _setEngine(engine2) { + this._engine = engine2; + } + _wljs_reload(filenamePtr) { + let filename = filenamePtr ? this.UTF8ToString(filenamePtr) : null; + this._engine._reloadRequest(filename); + } + _wljs_init(withPhysX) { + this._withPhysX = withPhysX, this.allocateTempMemory(1024); + } + _wljs_scene_switch(index) { + let scene = this._engine._scenes[index]; + this._components = scene?._jsComponents ?? null; + } + _wljs_destroy_image(index) { + let img = this._images[index]; + img && (this._images[index] = null, img.src !== void 0 && (img.src = ""), img.onload !== void 0 && (img.onload = null), img.onerror !== void 0 && (img.onerror = null)); + } + _wljs_objects_markDestroyed(sceneIndex, idsPtr, count) { + let scene = this._engine._scenes[sceneIndex], start = idsPtr >>> 1; + for (let i = 0; i < count; ++i) { + let id = this.HEAPU16[start + i]; + scene._destroyObject(id); + } + } + _wljs_scene_initialize(sceneIndex, idsPtr, idsEnd, paramDataPtr, paramDataEndPtr, offsetsPtr, offsetsEndPtr) { + let cborEncoded = this.HEAPU8.subarray(paramDataPtr, paramDataEndPtr), offsets = this.HEAPU32.subarray(offsetsPtr >>> 2, offsetsEndPtr >>> 2), ids = this.HEAPU16.subarray(idsPtr >>> 1, idsEnd >>> 1), scene = this._engine._scenes[sceneIndex], components = scene._jsComponents, cbor = new CBORReader(cborEncoded), componentsCount = 0; + { + let typeInfo = cbor.readTypeInfo(); + if (getType(typeInfo) !== 0) { + this._log.error(0, "Parameters data must be an array"); + return; + } + componentsCount = cbor.readArrayLength(typeInfo); + } + if (componentsCount !== ids.length) { + this._log.error(0, `Parameters set for ${componentsCount} components, but expected ${ids.length}`); + return; + } + let decoder = new ComponentPropertyDecoder(scene, offsets); + for (let i = 0; i < componentsCount; ++i) { + let id = Component3._pack(sceneIndex, ids[i]), index = this._wl_get_js_component_index_for_id(id), component = components[index]; + decoder.decode(cbor, component); + } + } + _wljs_set_component_param_translation(scene, component, param, valuePtr, valueEndPtr) { + let comp = this._engine._scenes[scene]._jsComponents[component], value = this.UTF8ViewToString(valuePtr, valueEndPtr), paramName = comp.constructor._propertyOrder[param]; + comp[paramName] = value; + } + _wljs_get_component_type_index(namePtr, nameEndPtr) { + let typename = this.UTF8ViewToString(namePtr, nameEndPtr), index = this._componentTypeIndices[typename]; + return index === void 0 ? this._brokenComponentIndex : index; + } + _wljs_component_create(sceneIndex, index, id, type, object) { + this._engine._scenes[sceneIndex]._components.createJs(index, id, type, object); + } + _wljs_component_init(scene, component) { + this._engine._scenes[scene]._jsComponents[component]._triggerInit(); + } + _wljs_component_update(component, dt) { + this._components[component]._triggerUpdate(dt); + } + _wljs_component_onActivate(component) { + this._components[component]._triggerOnActivate(); + } + _wljs_component_onDeactivate(component) { + this._components[component]._triggerOnDeactivate(); + } + _wljs_component_markDestroyed(sceneIndex, manager, componentId) { + this._engine._scenes[sceneIndex]._destroyComponent(manager, componentId); + } + _wljs_swap(scene, a, b) { + let components = this._engine._scenes[scene]._jsComponents, componentA = components[a]; + components[a] = components[b], components[b] = componentA; + } + _wljs_copy(srcSceneIndex, srcIndex, dstSceneIndex, dstIndex, offsetsPtr, copyInfoPtr) { + let srcScene = this._engine._scenes[srcSceneIndex], destComp = this._engine._scenes[dstSceneIndex]._jsComponents[dstIndex], srcComp = srcScene._jsComponents[srcIndex]; + destComp._copy(srcComp, offsetsPtr, copyInfoPtr); + } + _wljs_trigger_animationEvent(componentId, namePtr, nameEndPtr) { + let comp = this._engine.scene._components.wrapAnimation(componentId), nameStr = this.UTF8ViewToString(namePtr, nameEndPtr); + comp.onEvent.notify(nameStr); + } +}; +var LOADING_SCREEN_PATH = "WonderlandRuntime-LoadingScreen.bin"; +function loadScript(scriptURL) { + return new Promise((resolve, reject) => { + let s = document.createElement("script"), node = document.body.appendChild(s); + s.onload = () => { + document.body.removeChild(node), resolve(); + }, s.onerror = (e) => { + document.body.removeChild(node), reject(e); + }, s.src = scriptURL; + }); +} +async function detectFeatures() { + let threadsSupported = await threads(); + return threadsSupported ? self.crossOriginIsolated ? console.log("WASM Threads is supported") : console.warn("WASM Threads is supported, but the page is not crossOriginIsolated, therefore thread support is disabled.") : console.warn("WASM Threads is not supported"), threadsSupported = threadsSupported && self.crossOriginIsolated, { threadsSupported }; +} +async function loadRuntime(runtime, options = {}) { + let baseURL = getBaseUrl(runtime), { threadsSupported } = await detectFeatures(), { threads: threads2 = threadsSupported, webgpu = false, physx = false, loader = false, renderer = true, xrFramebufferScaleFactor = 1, xrOfferSession = null, loadingScreen = baseURL ? `${baseURL}/${LOADING_SCREEN_PATH}` : LOADING_SCREEN_PATH, canvas: canvas2 = "canvas", logs = [0, 1, 2] } = options, variant = []; + loader && variant.push("loader"), physx && variant.push("physx"), threads2 && variant.push("threads"), webgpu && variant.push("webgpu"); + let variantStr = variant.join("-"), filename = runtime; + variantStr && (filename = `${filename}-${variantStr}`); + let download = function(filename2, errorMessage) { + return fetch(filename2).then((r) => r.ok ? r.arrayBuffer() : Promise.reject(errorMessage)).catch((_) => Promise.reject(errorMessage)); + }, [wasmData, loadingScreenData] = await Promise.all([download(`${filename}.wasm`, `Failed to fetch runtime .wasm file: ${filename}`), download(loadingScreen, "Failed to fetch loading screen file")]), canvasElement = document.getElementById(canvas2); + if (!canvasElement) + throw new Error(`loadRuntime(): Failed to find canvas with id '${canvas2}'`); + if (!(canvasElement instanceof HTMLCanvasElement)) + throw new Error(`loadRuntime(): HTML element '${canvas2}' must be a canvas`); + let wasm = new WASM(threads2); + wasm.wasm = wasmData, wasm.canvas = canvasElement, wasm._log.levels.enable(...logs), window._WL || (window._WL = { runtimes: {} }); + let runtimes = window._WL.runtimes, runtimeGlobalId = variantStr || "default"; + if (runtimes[runtimeGlobalId] || (await loadScript(`${filename}.js`), runtimes[runtimeGlobalId] = window.instantiateWonderlandRuntime, window.instantiateWonderlandRuntime = void 0), await runtimes[runtimeGlobalId](wasm), webgpu) { + let WebGPU = wasm.WebGPU, adapter = await navigator.gpu.requestAdapter(), adapterId = WebGPU.mgrAdapter.create(adapter), desc = { requiredFeatures: ["texture-compression-bc", "depth32float-stencil8"] }, device = await adapter.requestDevice(desc), deviceId = WebGPU.mgrDevice.create(adapter); + wasm.preinitializedWebGPUDevice = device, canvasElement.getContext("webgpu").configure({ device, format: navigator.gpu.getPreferredCanvasFormat(), alphaMode: "premultiplied" }); + } + let engine2 = new WonderlandEngine(wasm, loadingScreenData, renderer); + return await engine2.webxr.init(xrFramebufferScaleFactor, xrOfferSession), engine2.autoResizeCanvas = true, engine2.start(), engine2; +} + +// node_modules/@wonderlandengine/components/dist/8thwall-camera.js +var ARCamera8thwall = class extends Component3 { + /* 8thwall camera pipeline module name */ + name = "wonderland-engine-8thwall-camera"; + started = false; + view = null; + // cache camera + position = [0, 0, 0]; + // cache 8thwall cam position + rotation = [0, 0, 0, -1]; + // cache 8thwall cam rotation + glTextureRenderer = null; + // cache XR8.GlTextureRenderer.pipelineModule + promptForDeviceMotion() { + return new Promise(async (resolve, reject) => { + window.dispatchEvent(new Event("8thwall-request-user-interaction")); + window.addEventListener("8thwall-safe-to-request-permissions", async () => { + try { + const motionEvent = await DeviceMotionEvent.requestPermission(); + resolve(motionEvent); + } catch (exception) { + reject(exception); + } + }); + }); + } + async getPermissions() { + if (DeviceMotionEvent && DeviceMotionEvent.requestPermission) { + try { + const result = await DeviceMotionEvent.requestPermission(); + if (result !== "granted") { + throw new Error("MotionEvent"); + } + } catch (exception) { + if (exception.name === "NotAllowedError") { + const motionEvent = await this.promptForDeviceMotion(); + if (motionEvent !== "granted") { + throw new Error("MotionEvent"); + } + } else { + throw new Error("MotionEvent"); + } + } + } + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: true + }); + stream.getTracks().forEach((track) => { + track.stop(); + }); + } catch (exception) { + throw new Error("Camera"); + } + } + init() { + this.view = this.object.getComponent("view"); + this.onUpdate = this.onUpdate.bind(this); + this.onAttach = this.onAttach.bind(this); + this.onException = this.onException.bind(this); + this.onCameraStatusChange = this.onCameraStatusChange.bind(this); + } + async start() { + this.view = this.object.getComponent("view"); + if (!this.useCustomUIOverlays) { + OverlaysHandler.init(); + } + try { + await this.getPermissions(); + } catch (error) { + window.dispatchEvent(new CustomEvent("8thwall-permission-fail", { detail: error })); + return; + } + await this.waitForXR8(); + XR8.XrController.configure({ + disableWorldTracking: false + }); + this.glTextureRenderer = XR8.GlTextureRenderer.pipelineModule(); + XR8.addCameraPipelineModules([ + this.glTextureRenderer, + XR8.XrController.pipelineModule(), + this + ]); + const config = { + cameraConfig: { + direction: XR8.XrConfig.camera().BACK + }, + canvas: Module.canvas, + allowedDevices: XR8.XrConfig.device().ANY, + ownRunLoop: false + }; + XR8.run(config); + } + /** + * @private + * 8thwall pipeline function + */ + onAttach(params) { + this.started = true; + this.engine.scene.colorClearEnabled = false; + const gl = Module.ctx; + const rot = this.object.rotationWorld; + const pos = this.object.getTranslationWorld([]); + this.position = Array.from(pos); + this.rotation = Array.from(rot); + XR8.XrController.updateCameraProjectionMatrix({ + origin: { x: pos[0], y: pos[1], z: pos[2] }, + facing: { x: rot[0], y: rot[1], z: rot[2], w: rot[3] }, + cam: { + pixelRectWidth: Module.canvas.width, + pixelRectHeight: Module.canvas.height, + nearClipPlane: this.view.near, + farClipPlane: this.view.far + } + }); + this.engine.scene.onPreRender.push(() => { + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + XR8.runPreRender(Date.now()); + XR8.runRender(); + }); + this.engine.scene.onPostRender.push(() => { + XR8.runPostRender(Date.now()); + }); + } + /** + * @private + * 8thwall pipeline function + */ + onCameraStatusChange(e) { + if (e && e.status === "failed") { + this.onException(new Error(`Camera failed with status: ${e.status}`)); + } + } + /** + * @private + * 8thwall pipeline function + */ + onUpdate(e) { + if (!e.processCpuResult.reality) + return; + const { rotation, position, intrinsics } = e.processCpuResult.reality; + this.rotation[0] = rotation.x; + this.rotation[1] = rotation.y; + this.rotation[2] = rotation.z; + this.rotation[3] = rotation.w; + this.position[0] = position.x; + this.position[1] = position.y; + this.position[2] = position.z; + if (intrinsics) { + const projectionMatrix = this.view.projectionMatrix; + for (let i = 0; i < 16; i++) { + if (Number.isFinite(intrinsics[i])) { + projectionMatrix[i] = intrinsics[i]; + } + } + } + if (position && rotation) { + this.object.rotationWorld = this.rotation; + this.object.setTranslationWorld(this.position); + } + } + /** + * @private + * 8thwall pipeline function + */ + onException(error) { + console.error("8thwall exception:", error); + window.dispatchEvent(new CustomEvent("8thwall-error", { detail: error })); + } + waitForXR8() { + return new Promise((resolve, _rej) => { + if (window.XR8) { + resolve(); + } else { + window.addEventListener("xrloaded", () => resolve()); + } + }); + } +}; +__publicField(ARCamera8thwall, "TypeName", "8thwall-camera"); +__publicField(ARCamera8thwall, "Properties", { + /** Override the WL html overlays for handling camera/motion permissions and error handling */ + useCustomUIOverlays: { type: Type.Bool, default: false } +}); +var OverlaysHandler = { + init: function() { + this.handleRequestUserInteraction = this.handleRequestUserInteraction.bind(this); + this.handlePermissionFail = this.handlePermissionFail.bind(this); + this.handleError = this.handleError.bind(this); + window.addEventListener("8thwall-request-user-interaction", this.handleRequestUserInteraction); + window.addEventListener("8thwall-permission-fail", this.handlePermissionFail); + window.addEventListener("8thwall-error", this.handleError); + }, + handleRequestUserInteraction: function() { + const overlay = this.showOverlay(requestPermissionOverlay); + window.addEventListener("8thwall-safe-to-request-permissions", () => { + overlay.remove(); + }); + }, + handlePermissionFail: function(_reason) { + this.showOverlay(failedPermissionOverlay); + }, + handleError: function(_error) { + this.showOverlay(runtimeErrorOverlay); + }, + showOverlay: function(htmlContent) { + const overlay = document.createElement("div"); + overlay.innerHTML = htmlContent; + document.body.appendChild(overlay); + return overlay; + } +}; +var requestPermissionOverlay = ` + + +
+
This app requires to use your camera and motion sensors
+ + +
`; +var failedPermissionOverlay = ` + + +
+
Failed to grant permissions. Reset the the permissions and refresh the page.
+ + +
`; +var runtimeErrorOverlay = ` + + +
+
Error has occurred. Please reload the page
+ + +
`; + +// node_modules/@wonderlandengine/components/dist/utils/webxr.js +var tempVec = new Float32Array(3); +var tempQuat = new Float32Array(4); +function setXRRigidTransformLocal(o, transform) { + const r = transform.orientation; + tempQuat[0] = r.x; + tempQuat[1] = r.y; + tempQuat[2] = r.z; + tempQuat[3] = r.w; + const t = transform.position; + tempVec[0] = t.x; + tempVec[1] = t.y; + tempVec[2] = t.z; + o.resetTranslationRotation(); + o.transformLocal.set(tempQuat); + o.translate(tempVec); +} + +// node_modules/@wonderlandengine/components/dist/anchor.js +var __decorate = function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if (d = decorators[i]) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var tempVec3 = new Float32Array(3); +var tempQuat2 = new Float32Array(4); +var _anchors, _addAnchor, addAnchor_fn, _removeAnchor, removeAnchor_fn, _getFrame, getFrame_fn, _createAnchor, createAnchor_fn, _onAddAnchor, onAddAnchor_fn, _onRestoreAnchor, onRestoreAnchor_fn, _onCreate, onCreate_fn; +var _Anchor = class extends Component3 { + constructor() { + super(...arguments); + __privateAdd(this, _getFrame); + __privateAdd(this, _createAnchor); + __privateAdd(this, _onAddAnchor); + __privateAdd(this, _onRestoreAnchor); + __privateAdd(this, _onCreate); + __publicField(this, "persist", false); + /** Unique identifier to load a persistent anchor from, or empty/null if unknown */ + __publicField(this, "uuid", null); + /** The xrAnchor, if created */ + __publicField(this, "xrAnchor", null); + /** Emits events when the anchor is created either by being restored or newly created */ + __publicField(this, "onCreate", new Emitter()); + /** Whether the anchor is currently being tracked */ + __publicField(this, "visible", false); + /** Emits an event when this anchor starts tracking */ + __publicField(this, "onTrackingFound", new Emitter()); + /** Emits an event when this anchor stops tracking */ + __publicField(this, "onTrackingLost", new Emitter()); + /** XRFrame to use for creating the anchor */ + __publicField(this, "xrFrame", null); + /** XRHitTestResult to use for creating the anchor */ + __publicField(this, "xrHitResult", null); + } + /** Retrieve all anchors of the current scene */ + static getAllAnchors() { + return __privateGet(_Anchor, _anchors); + } + /** + * Create a new anchor + * + * @param o Object to attach the component to + * @param params Parameters for the anchor component + * @param frame XRFrame to use for anchor cration, if null, will use the current frame if available + * @param hitResult Optional hit-test result to create the anchor with + * @returns Promise for the newly created anchor component + */ + static create(o, params, frame, hitResult) { + const a = o.addComponent(_Anchor, { ...params, active: false }); + if (a === null) + return null; + a.xrHitResult = hitResult ?? null; + a.xrFrame = frame ?? null; + a.onCreate.once(() => (a.xrFrame = null, a.xrHitResult = null)); + a.active = true; + return a.onCreate.promise(); + } + start() { + if (this.uuid && this.engine.xr) { + this.persist = true; + if (this.engine.xr.session.restorePersistentAnchor === void 0) { + console.warn("anchor: Persistent anchors are not supported by your client. Ignoring persist property."); + } + this.engine.xr.session.restorePersistentAnchor(this.uuid).then(__privateMethod(this, _onRestoreAnchor, onRestoreAnchor_fn).bind(this)); + } else if (__privateMethod(this, _getFrame, getFrame_fn).call(this)) { + __privateMethod(this, _createAnchor, createAnchor_fn).call(this).then(__privateMethod(this, _onAddAnchor, onAddAnchor_fn).bind(this)); + } else { + throw new Error("Anchors can only be created during the XR frame in an active XR session"); + } + } + update() { + if (!this.xrAnchor || !this.engine.xr) + return; + const pose = this.engine.xr.frame.getPose(this.xrAnchor.anchorSpace, this.engine.xr.currentReferenceSpace); + const visible = !!pose; + if (visible != this.visible) { + this.visible = visible; + (visible ? this.onTrackingFound : this.onTrackingLost).notify(this); + } + if (pose) { + setXRRigidTransformLocal(this.object, pose.transform); + } + } + onDestroy() { + var _a12; + __privateMethod(_a12 = _Anchor, _removeAnchor, removeAnchor_fn).call(_a12, this); + } +}; +var Anchor = _Anchor; +_anchors = new WeakMap(); +_addAnchor = new WeakSet(); +addAnchor_fn = function(anchor) { + __privateGet(_Anchor, _anchors).push(anchor); +}; +_removeAnchor = new WeakSet(); +removeAnchor_fn = function(anchor) { + const index = __privateGet(_Anchor, _anchors).indexOf(anchor); + if (index < 0) + return; + __privateGet(_Anchor, _anchors).splice(index, 1); +}; +_getFrame = new WeakSet(); +getFrame_fn = function() { + return this.xrFrame || this.engine.xr.frame; +}; +_createAnchor = new WeakSet(); +createAnchor_fn = async function() { + if (!__privateMethod(this, _getFrame, getFrame_fn).call(this).createAnchor) { + throw new Error("Cannot create anchor - anchors not supported, did you enable the 'anchors' WebXR feature?"); + } + if (this.xrHitResult) { + if (this.xrHitResult.createAnchor === void 0) { + throw new Error("Requested anchor on XRHitTestResult, but WebXR hit-test feature is not available."); + } + return this.xrHitResult.createAnchor(); + } else { + this.object.getTranslationWorld(tempVec3); + tempQuat2.set(this.object.rotationWorld); + const rotation = tempQuat2; + const anchorPose = new XRRigidTransform({ x: tempVec3[0], y: tempVec3[1], z: tempVec3[2] }, { x: rotation[0], y: rotation[1], z: rotation[2], w: rotation[3] }); + return __privateMethod(this, _getFrame, getFrame_fn).call(this)?.createAnchor(anchorPose, this.engine.xr.currentReferenceSpace); + } +}; +_onAddAnchor = new WeakSet(); +onAddAnchor_fn = function(anchor) { + if (!anchor) + return; + if (this.persist) { + if (anchor.requestPersistentHandle !== void 0) { + anchor.requestPersistentHandle().then((uuid) => { + var _a12; + this.uuid = uuid; + __privateMethod(this, _onCreate, onCreate_fn).call(this, anchor); + __privateMethod(_a12 = _Anchor, _addAnchor, addAnchor_fn).call(_a12, this); + }); + return; + } else { + console.warn("anchor: Persistent anchors are not supported by your client. Ignoring persist property."); + } + } + __privateMethod(this, _onCreate, onCreate_fn).call(this, anchor); +}; +_onRestoreAnchor = new WeakSet(); +onRestoreAnchor_fn = function(anchor) { + __privateMethod(this, _onCreate, onCreate_fn).call(this, anchor); +}; +_onCreate = new WeakSet(); +onCreate_fn = function(anchor) { + this.xrAnchor = anchor; + this.onCreate.notify(this); +}; +__privateAdd(Anchor, _addAnchor); +__privateAdd(Anchor, _removeAnchor); +__publicField(Anchor, "TypeName", "anchor"); +/* Static management of all anchors */ +__privateAdd(Anchor, _anchors, []); +__decorate([ + property.bool(false) +], Anchor.prototype, "persist", void 0); +__decorate([ + property.string() +], Anchor.prototype, "uuid", void 0); + +// node_modules/@wonderlandengine/components/dist/cursor-target.js +var CursorTarget = class extends Component3 { + /** Emitter for events when the target is hovered */ + onHover = new Emitter(); + /** Emitter for events when the target is unhovered */ + onUnhover = new Emitter(); + /** Emitter for events when the target is clicked */ + onClick = new Emitter(); + /** Emitter for events when the cursor moves on the target */ + onMove = new Emitter(); + /** Emitter for events when the user pressed the select button on the target */ + onDown = new Emitter(); + /** Emitter for events when the user unpressed the select button on the target */ + onUp = new Emitter(); + /** + * @deprecated Use the emitter instead. + * + * @example + * this.onHover.add(f); + */ + addHoverFunction(f) { + this.onHover.add(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * this.onHover.remove(f); + */ + removeHoverFunction(f) { + this.onHover.remove(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * this.onUnhover.add(f); + */ + addUnHoverFunction(f) { + this.onUnhover.add(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * this.onUnhover.remove(f); + */ + removeUnHoverFunction(f) { + this.onUnhover.remove(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * this.onClick.add(f); + */ + addClickFunction(f) { + this.onClick.add(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onClick.remove(f); + */ + removeClickFunction(f) { + this.onClick.remove(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onMove.add(f); + */ + addMoveFunction(f) { + this.onMove.add(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onMove.remove(f); + */ + removeMoveFunction(f) { + this.onMove.remove(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onDown.add(f); + */ + addDownFunction(f) { + this.onDown.add(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onDown.remove(f); + */ + removeDownFunction(f) { + this.onDown.remove(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onUp.add(f); + */ + addUpFunction(f) { + this.onUp.add(f); + } + /** + * @deprecated Use the emitter instead. + * + * @example + * component.onUp.remove(f); + */ + removeUpFunction(f) { + this.onUp.remove(f); + } +}; +__publicField(CursorTarget, "TypeName", "cursor-target"); +__publicField(CursorTarget, "Properties", {}); + +// node_modules/gl-matrix/esm/common.js +var EPSILON = 1e-6; +var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; +var RANDOM = Math.random; +var degree = Math.PI / 180; +if (!Math.hypot) + Math.hypot = function() { + var y = 0, i = arguments.length; + while (i--) { + y += arguments[i] * arguments[i]; + } + return Math.sqrt(y); + }; + +// node_modules/gl-matrix/esm/mat3.js +function create() { + var out = new ARRAY_TYPE(9); + if (ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + out[0] = 1; + out[4] = 1; + out[8] = 1; + return out; +} + +// node_modules/gl-matrix/esm/mat4.js +var mat4_exports = {}; +__export(mat4_exports, { + add: () => add, + adjoint: () => adjoint, + clone: () => clone, + copy: () => copy, + create: () => create2, + determinant: () => determinant, + equals: () => equals, + exactEquals: () => exactEquals, + frob: () => frob, + fromQuat: () => fromQuat, + fromQuat2: () => fromQuat2, + fromRotation: () => fromRotation, + fromRotationTranslation: () => fromRotationTranslation, + fromRotationTranslationScale: () => fromRotationTranslationScale, + fromRotationTranslationScaleOrigin: () => fromRotationTranslationScaleOrigin, + fromScaling: () => fromScaling, + fromTranslation: () => fromTranslation, + fromValues: () => fromValues, + fromXRotation: () => fromXRotation, + fromYRotation: () => fromYRotation, + fromZRotation: () => fromZRotation, + frustum: () => frustum, + getRotation: () => getRotation, + getScaling: () => getScaling, + getTranslation: () => getTranslation, + identity: () => identity, + invert: () => invert, + lookAt: () => lookAt, + mul: () => mul, + multiply: () => multiply, + multiplyScalar: () => multiplyScalar, + multiplyScalarAndAdd: () => multiplyScalarAndAdd, + ortho: () => ortho, + orthoNO: () => orthoNO, + orthoZO: () => orthoZO, + perspective: () => perspective, + perspectiveFromFieldOfView: () => perspectiveFromFieldOfView, + perspectiveNO: () => perspectiveNO, + perspectiveZO: () => perspectiveZO, + rotate: () => rotate, + rotateX: () => rotateX, + rotateY: () => rotateY, + rotateZ: () => rotateZ, + scale: () => scale, + set: () => set, + str: () => str, + sub: () => sub, + subtract: () => subtract, + targetTo: () => targetTo, + translate: () => translate, + transpose: () => transpose +}); +function create2() { + var out = new ARRAY_TYPE(16); + if (ARRAY_TYPE != Float32Array) { + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + } + out[0] = 1; + out[5] = 1; + out[10] = 1; + out[15] = 1; + return out; +} +function clone(a) { + var out = new ARRAY_TYPE(16); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +} +function copy(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +} +function fromValues(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { + var out = new ARRAY_TYPE(16); + out[0] = m00; + out[1] = m01; + out[2] = m02; + out[3] = m03; + out[4] = m10; + out[5] = m11; + out[6] = m12; + out[7] = m13; + out[8] = m20; + out[9] = m21; + out[10] = m22; + out[11] = m23; + out[12] = m30; + out[13] = m31; + out[14] = m32; + out[15] = m33; + return out; +} +function set(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { + out[0] = m00; + out[1] = m01; + out[2] = m02; + out[3] = m03; + out[4] = m10; + out[5] = m11; + out[6] = m12; + out[7] = m13; + out[8] = m20; + out[9] = m21; + out[10] = m22; + out[11] = m23; + out[12] = m30; + out[13] = m31; + out[14] = m32; + out[15] = m33; + return out; +} +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function transpose(out, a) { + if (out === a) { + var a01 = a[1], a02 = a[2], a03 = a[3]; + var a12 = a[6], a13 = a[7]; + var a23 = a[11]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; + } else { + out[0] = a[0]; + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a[1]; + out[5] = a[5]; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a[2]; + out[9] = a[6]; + out[10] = a[10]; + out[11] = a[14]; + out[12] = a[3]; + out[13] = a[7]; + out[14] = a[11]; + out[15] = a[15]; + } + return out; +} +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; + var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; + var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; + var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + var b00 = a00 * a11 - a01 * a10; + var b01 = a00 * a12 - a02 * a10; + var b02 = a00 * a13 - a03 * a10; + var b03 = a01 * a12 - a02 * a11; + var b04 = a01 * a13 - a03 * a11; + var b05 = a02 * a13 - a03 * a12; + var b06 = a20 * a31 - a21 * a30; + var b07 = a20 * a32 - a22 * a30; + var b08 = a20 * a33 - a23 * a30; + var b09 = a21 * a32 - a22 * a31; + var b10 = a21 * a33 - a23 * a31; + var b11 = a22 * a33 - a23 * a32; + var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + if (!det) { + return null; + } + det = 1 / 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; +} +function adjoint(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; + var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; + var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; + var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + out[0] = a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22); + out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22)); + out[2] = a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12); + out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12)); + out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22)); + out[5] = a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22); + out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12)); + out[7] = a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12); + out[8] = a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21); + out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21)); + out[10] = a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11); + out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11)); + out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21)); + out[13] = a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21); + out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11)); + out[15] = a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11); + return out; +} +function determinant(a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; + var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; + var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; + var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + var b00 = a00 * a11 - a01 * a10; + var b01 = a00 * a12 - a02 * a10; + var b02 = a00 * a13 - a03 * a10; + var b03 = a01 * a12 - a02 * a11; + var b04 = a01 * a13 - a03 * a11; + var b05 = a02 * a13 - a03 * a12; + var b06 = a20 * a31 - a21 * a30; + var b07 = a20 * a32 - a22 * a30; + var b08 = a20 * a33 - a23 * a30; + var b09 = a21 * a32 - a22 * a31; + var b10 = a21 * a33 - a23 * a31; + var b11 = a22 * a33 - a23 * a32; + return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; +} +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]; + var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]; + var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]; + var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = b[4]; + b1 = b[5]; + b2 = b[6]; + b3 = b[7]; + out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = b[8]; + b1 = b[9]; + b2 = b[10]; + b3 = b[11]; + out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + b0 = b[12]; + b1 = b[13]; + b2 = b[14]; + b3 = b[15]; + out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + return out; +} +function translate(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + var a00, a01, a02, a03; + var a10, a11, a12, a13; + var 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 scale(out, a, v) { + var x = v[0], y = v[1], z = v[2]; + out[0] = a[0] * x; + out[1] = a[1] * x; + out[2] = a[2] * x; + out[3] = a[3] * x; + out[4] = a[4] * y; + out[5] = a[5] * y; + out[6] = a[6] * y; + out[7] = a[7] * y; + out[8] = a[8] * z; + out[9] = a[9] * z; + out[10] = a[10] * z; + out[11] = a[11] * z; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + return out; +} +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2]; + var len4 = Math.hypot(x, y, z); + var s, c, t; + var a00, a01, a02, a03; + var a10, a11, a12, a13; + var a20, a21, a22, a23; + var b00, b01, b02; + var b10, b11, b12; + var b20, b21, b22; + if (len4 < EPSILON) { + return null; + } + len4 = 1 / len4; + x *= len4; + y *= len4; + z *= len4; + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + a00 = a[0]; + a01 = a[1]; + a02 = a[2]; + a03 = a[3]; + a10 = a[4]; + a11 = a[5]; + a12 = a[6]; + a13 = a[7]; + a20 = a[8]; + a21 = a[9]; + a22 = a[10]; + a23 = a[11]; + b00 = x * x * t + c; + b01 = y * x * t + z * s; + b02 = z * x * t - y * s; + b10 = x * y * t - z * s; + b11 = y * y * t + c; + b12 = z * y * t + x * s; + b20 = x * z * t + y * s; + b21 = y * z * t - x * s; + b22 = z * z * t + c; + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + if (a !== out) { + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +} +function rotateX(out, a, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + var a10 = a[4]; + var a11 = a[5]; + var a12 = a[6]; + var a13 = a[7]; + var a20 = a[8]; + var a21 = a[9]; + var a22 = a[10]; + var a23 = a[11]; + if (a !== out) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; + return out; +} +function rotateY(out, a, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + var a00 = a[0]; + var a01 = a[1]; + var a02 = a[2]; + var a03 = a[3]; + var a20 = a[8]; + var a21 = a[9]; + var a22 = a[10]; + var a23 = a[11]; + if (a !== out) { + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + out[0] = a00 * c - a20 * s; + out[1] = a01 * c - a21 * s; + out[2] = a02 * c - a22 * s; + out[3] = a03 * c - a23 * s; + out[8] = a00 * s + a20 * c; + out[9] = a01 * s + a21 * c; + out[10] = a02 * s + a22 * c; + out[11] = a03 * s + a23 * c; + return out; +} +function rotateZ(out, a, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + var a00 = a[0]; + var a01 = a[1]; + var a02 = a[2]; + var a03 = a[3]; + var a10 = a[4]; + var a11 = a[5]; + var a12 = a[6]; + var a13 = a[7]; + if (a !== out) { + out[8] = a[8]; + out[9] = a[9]; + out[10] = a[10]; + out[11] = a[11]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + out[0] = a00 * c + a10 * s; + out[1] = a01 * c + a11 * s; + out[2] = a02 * c + a12 * s; + out[3] = a03 * c + a13 * s; + out[4] = a10 * c - a00 * s; + out[5] = a11 * c - a01 * s; + out[6] = a12 * c - a02 * s; + out[7] = a13 * c - a03 * s; + return out; +} +function fromTranslation(out, v) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; +} +function fromScaling(out, v) { + out[0] = v[0]; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = v[1]; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = v[2]; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function fromRotation(out, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2]; + var len4 = Math.hypot(x, y, z); + var s, c, t; + if (len4 < EPSILON) { + return null; + } + len4 = 1 / len4; + x *= len4; + y *= len4; + z *= len4; + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + out[0] = x * x * t + c; + out[1] = y * x * t + z * s; + out[2] = z * x * t - y * s; + out[3] = 0; + out[4] = x * y * t - z * s; + out[5] = y * y * t + c; + out[6] = z * y * t + x * s; + out[7] = 0; + out[8] = x * z * t + y * s; + out[9] = y * z * t - x * s; + out[10] = z * z * t + c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function fromXRotation(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = c; + out[6] = s; + out[7] = 0; + out[8] = 0; + out[9] = -s; + out[10] = c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function fromYRotation(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + out[0] = c; + out[1] = 0; + out[2] = -s; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = s; + out[9] = 0; + out[10] = c; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function fromZRotation(out, rad) { + var s = Math.sin(rad); + var c = Math.cos(rad); + out[0] = c; + out[1] = s; + out[2] = 0; + out[3] = 0; + out[4] = -s; + out[5] = c; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function fromRotationTranslation(out, q, v) { + var x = q[0], y = q[1], z = q[2], w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var xy = x * y2; + var xz = x * z2; + var yy = y * y2; + var yz = y * z2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var 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 fromQuat2(out, a) { + var translation = new ARRAY_TYPE(3); + var bx = -a[0], by = -a[1], bz = -a[2], bw = a[3], ax = a[4], ay = a[5], az = a[6], aw = a[7]; + var magnitude = bx * bx + by * by + bz * bz + bw * bw; + if (magnitude > 0) { + translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude; + translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude; + translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude; + } else { + translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2; + translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2; + translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2; + } + fromRotationTranslation(out, a, translation); + return out; +} +function getTranslation(out, mat) { + out[0] = mat[12]; + out[1] = mat[13]; + out[2] = mat[14]; + return out; +} +function getScaling(out, mat) { + var m11 = mat[0]; + var m12 = mat[1]; + var m13 = mat[2]; + var m21 = mat[4]; + var m22 = mat[5]; + var m23 = mat[6]; + var m31 = mat[8]; + var m32 = mat[9]; + var m33 = mat[10]; + out[0] = Math.hypot(m11, m12, m13); + out[1] = Math.hypot(m21, m22, m23); + out[2] = Math.hypot(m31, m32, m33); + return out; +} +function getRotation(out, mat) { + var scaling = new ARRAY_TYPE(3); + getScaling(scaling, mat); + var is1 = 1 / scaling[0]; + var is2 = 1 / scaling[1]; + var is3 = 1 / scaling[2]; + var sm11 = mat[0] * is1; + var sm12 = mat[1] * is2; + var sm13 = mat[2] * is3; + var sm21 = mat[4] * is1; + var sm22 = mat[5] * is2; + var sm23 = mat[6] * is3; + var sm31 = mat[8] * is1; + var sm32 = mat[9] * is2; + var sm33 = mat[10] * is3; + var trace = sm11 + sm22 + sm33; + var S = 0; + if (trace > 0) { + S = Math.sqrt(trace + 1) * 2; + out[3] = 0.25 * S; + out[0] = (sm23 - sm32) / S; + out[1] = (sm31 - sm13) / S; + out[2] = (sm12 - sm21) / S; + } else if (sm11 > sm22 && sm11 > sm33) { + S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2; + out[3] = (sm23 - sm32) / S; + out[0] = 0.25 * S; + out[1] = (sm12 + sm21) / S; + out[2] = (sm31 + sm13) / S; + } else if (sm22 > sm33) { + S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2; + out[3] = (sm31 - sm13) / S; + out[0] = (sm12 + sm21) / S; + out[1] = 0.25 * S; + out[2] = (sm23 + sm32) / S; + } else { + S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2; + out[3] = (sm12 - sm21) / S; + out[0] = (sm31 + sm13) / S; + out[1] = (sm23 + sm32) / S; + out[2] = 0.25 * S; + } + return out; +} +function fromRotationTranslationScale(out, q, v, s) { + var x = q[0], y = q[1], z = q[2], w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var xy = x * y2; + var xz = x * z2; + var yy = y * y2; + var yz = y * z2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + var sx = s[0]; + var sy = s[1]; + var sz = s[2]; + out[0] = (1 - (yy + zz)) * sx; + out[1] = (xy + wz) * sx; + out[2] = (xz - wy) * sx; + out[3] = 0; + out[4] = (xy - wz) * sy; + out[5] = (1 - (xx + zz)) * sy; + out[6] = (yz + wx) * sy; + out[7] = 0; + out[8] = (xz + wy) * sz; + out[9] = (yz - wx) * sz; + out[10] = (1 - (xx + yy)) * sz; + out[11] = 0; + out[12] = v[0]; + out[13] = v[1]; + out[14] = v[2]; + out[15] = 1; + return out; +} +function fromRotationTranslationScaleOrigin(out, q, v, s, o) { + var x = q[0], y = q[1], z = q[2], w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var xy = x * y2; + var xz = x * z2; + var yy = y * y2; + var yz = y * z2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + var sx = s[0]; + var sy = s[1]; + var sz = s[2]; + var ox = o[0]; + var oy = o[1]; + var oz = o[2]; + var out0 = (1 - (yy + zz)) * sx; + var out1 = (xy + wz) * sx; + var out2 = (xz - wy) * sx; + var out4 = (xy - wz) * sy; + var out5 = (1 - (xx + zz)) * sy; + var out6 = (yz + wx) * sy; + var out8 = (xz + wy) * sz; + var out9 = (yz - wx) * sz; + var out10 = (1 - (xx + yy)) * sz; + out[0] = out0; + out[1] = out1; + out[2] = out2; + out[3] = 0; + out[4] = out4; + out[5] = out5; + out[6] = out6; + out[7] = 0; + out[8] = out8; + out[9] = out9; + out[10] = out10; + out[11] = 0; + out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz); + out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz); + out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz); + out[15] = 1; + return out; +} +function fromQuat(out, q) { + var x = q[0], y = q[1], z = q[2], w = q[3]; + var x2 = x + x; + var y2 = y + y; + var z2 = z + z; + var xx = x * x2; + var yx = y * x2; + var yy = y * y2; + var zx = z * x2; + var zy = z * y2; + var zz = z * z2; + var wx = w * x2; + var wy = w * y2; + var wz = w * z2; + out[0] = 1 - yy - zz; + out[1] = yx + wz; + out[2] = zx - wy; + out[3] = 0; + out[4] = yx - wz; + out[5] = 1 - xx - zz; + out[6] = zy + wx; + out[7] = 0; + out[8] = zx + wy; + out[9] = zy - wx; + out[10] = 1 - xx - yy; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +} +function frustum(out, left, right, bottom, top, near, far) { + var rl = 1 / (right - left); + var tb = 1 / (top - bottom); + var nf = 1 / (near - far); + out[0] = near * 2 * rl; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = near * 2 * tb; + out[6] = 0; + out[7] = 0; + out[8] = (right + left) * rl; + out[9] = (top + bottom) * tb; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = far * near * 2 * nf; + out[15] = 0; + return out; +} +function perspectiveNO(out, fovy, aspect, near, far) { + var f = 1 / Math.tan(fovy / 2), nf; + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[15] = 0; + if (far != null && far !== Infinity) { + nf = 1 / (near - far); + out[10] = (far + near) * nf; + out[14] = 2 * far * near * nf; + } else { + out[10] = -1; + out[14] = -2 * near; + } + return out; +} +var perspective = perspectiveNO; +function perspectiveZO(out, fovy, aspect, near, far) { + var f = 1 / Math.tan(fovy / 2), nf; + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[15] = 0; + if (far != null && far !== Infinity) { + nf = 1 / (near - far); + out[10] = far * nf; + out[14] = far * near * nf; + } else { + out[10] = -1; + out[14] = -near; + } + return out; +} +function perspectiveFromFieldOfView(out, fov, near, far) { + var upTan = Math.tan(fov.upDegrees * Math.PI / 180); + var downTan = Math.tan(fov.downDegrees * Math.PI / 180); + var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180); + var rightTan = Math.tan(fov.rightDegrees * Math.PI / 180); + var xScale = 2 / (leftTan + rightTan); + var yScale = 2 / (upTan + downTan); + out[0] = xScale; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = yScale; + out[6] = 0; + out[7] = 0; + out[8] = -((leftTan - rightTan) * xScale * 0.5); + out[9] = (upTan - downTan) * yScale * 0.5; + out[10] = far / (near - far); + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = far * near / (near - far); + out[15] = 0; + return out; +} +function orthoNO(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right); + var bt = 1 / (bottom - top); + var 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 ortho = orthoNO; +function orthoZO(out, left, right, bottom, top, near, far) { + var lr = 1 / (left - right); + var bt = 1 / (bottom - top); + var 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] = nf; + out[11] = 0; + out[12] = (left + right) * lr; + out[13] = (top + bottom) * bt; + out[14] = near * nf; + out[15] = 1; + return out; +} +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len4; + var eyex = eye[0]; + var eyey = eye[1]; + var eyez = eye[2]; + var upx = up[0]; + var upy = up[1]; + var upz = up[2]; + var centerx = center[0]; + var centery = center[1]; + var centerz = center[2]; + if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) { + return identity(out); + } + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + len4 = 1 / Math.hypot(z0, z1, z2); + z0 *= len4; + z1 *= len4; + z2 *= len4; + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len4 = Math.hypot(x0, x1, x2); + if (!len4) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len4 = 1 / len4; + x0 *= len4; + x1 *= len4; + x2 *= len4; + } + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + len4 = Math.hypot(y0, y1, y2); + if (!len4) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len4 = 1 / len4; + y0 *= len4; + y1 *= len4; + y2 *= len4; + } + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + return out; +} +function targetTo(out, eye, target, up) { + var eyex = eye[0], eyey = eye[1], eyez = eye[2], upx = up[0], upy = up[1], upz = up[2]; + var z0 = eyex - target[0], z1 = eyey - target[1], z2 = eyez - target[2]; + var len4 = z0 * z0 + z1 * z1 + z2 * z2; + if (len4 > 0) { + len4 = 1 / Math.sqrt(len4); + z0 *= len4; + z1 *= len4; + z2 *= len4; + } + var x0 = upy * z2 - upz * z1, x1 = upz * z0 - upx * z2, x2 = upx * z1 - upy * z0; + len4 = x0 * x0 + x1 * x1 + x2 * x2; + if (len4 > 0) { + len4 = 1 / Math.sqrt(len4); + x0 *= len4; + x1 *= len4; + x2 *= len4; + } + out[0] = x0; + out[1] = x1; + out[2] = x2; + out[3] = 0; + out[4] = z1 * x2 - z2 * x1; + out[5] = z2 * x0 - z0 * x2; + out[6] = z0 * x1 - z1 * x0; + out[7] = 0; + out[8] = z0; + out[9] = z1; + out[10] = z2; + out[11] = 0; + out[12] = eyex; + out[13] = eyey; + out[14] = eyez; + out[15] = 1; + return out; +} +function str(a) { + return "mat4(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ", " + a[9] + ", " + a[10] + ", " + a[11] + ", " + a[12] + ", " + a[13] + ", " + a[14] + ", " + a[15] + ")"; +} +function frob(a) { + return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); +} +function add(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + out[8] = a[8] + b[8]; + out[9] = a[9] + b[9]; + out[10] = a[10] + b[10]; + out[11] = a[11] + b[11]; + out[12] = a[12] + b[12]; + out[13] = a[13] + b[13]; + out[14] = a[14] + b[14]; + out[15] = a[15] + b[15]; + return out; +} +function subtract(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + out[3] = a[3] - b[3]; + out[4] = a[4] - b[4]; + out[5] = a[5] - b[5]; + out[6] = a[6] - b[6]; + out[7] = a[7] - b[7]; + out[8] = a[8] - b[8]; + out[9] = a[9] - b[9]; + out[10] = a[10] - b[10]; + out[11] = a[11] - b[11]; + out[12] = a[12] - b[12]; + out[13] = a[13] - b[13]; + out[14] = a[14] - b[14]; + out[15] = a[15] - b[15]; + return out; +} +function multiplyScalar(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + out[4] = a[4] * b; + out[5] = a[5] * b; + out[6] = a[6] * b; + out[7] = a[7] * b; + out[8] = a[8] * b; + out[9] = a[9] * b; + out[10] = a[10] * b; + out[11] = a[11] * b; + out[12] = a[12] * b; + out[13] = a[13] * b; + out[14] = a[14] * b; + out[15] = a[15] * b; + return out; +} +function multiplyScalarAndAdd(out, a, b, scale6) { + out[0] = a[0] + b[0] * scale6; + out[1] = a[1] + b[1] * scale6; + out[2] = a[2] + b[2] * scale6; + out[3] = a[3] + b[3] * scale6; + out[4] = a[4] + b[4] * scale6; + out[5] = a[5] + b[5] * scale6; + out[6] = a[6] + b[6] * scale6; + out[7] = a[7] + b[7] * scale6; + out[8] = a[8] + b[8] * scale6; + out[9] = a[9] + b[9] * scale6; + out[10] = a[10] + b[10] * scale6; + out[11] = a[11] + b[11] * scale6; + out[12] = a[12] + b[12] * scale6; + out[13] = a[13] + b[13] * scale6; + out[14] = a[14] + b[14] * scale6; + out[15] = a[15] + b[15] * scale6; + return out; +} +function exactEquals(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] && a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15]; +} +function equals(a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7]; + var a8 = a[8], a9 = a[9], a10 = a[10], a11 = a[11]; + var a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + var b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7]; + var b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11]; + var b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= EPSILON * Math.max(1, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= EPSILON * Math.max(1, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= EPSILON * Math.max(1, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= EPSILON * Math.max(1, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= EPSILON * Math.max(1, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= EPSILON * Math.max(1, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= EPSILON * Math.max(1, Math.abs(a15), Math.abs(b15)); +} +var mul = multiply; +var sub = subtract; + +// node_modules/gl-matrix/esm/quat.js +var quat_exports = {}; +__export(quat_exports, { + add: () => add4, + calculateW: () => calculateW, + clone: () => clone4, + conjugate: () => conjugate, + copy: () => copy4, + create: () => create5, + dot: () => dot3, + equals: () => equals4, + exactEquals: () => exactEquals4, + exp: () => exp, + fromEuler: () => fromEuler, + fromMat3: () => fromMat3, + fromValues: () => fromValues4, + getAngle: () => getAngle, + getAxisAngle: () => getAxisAngle, + identity: () => identity2, + invert: () => invert2, + len: () => len2, + length: () => length3, + lerp: () => lerp3, + ln: () => ln, + mul: () => mul3, + multiply: () => multiply3, + normalize: () => normalize3, + pow: () => pow, + random: () => random2, + rotateX: () => rotateX3, + rotateY: () => rotateY3, + rotateZ: () => rotateZ3, + rotationTo: () => rotationTo, + scale: () => scale4, + set: () => set4, + setAxes: () => setAxes, + setAxisAngle: () => setAxisAngle, + slerp: () => slerp, + sqlerp: () => sqlerp, + sqrLen: () => sqrLen2, + squaredLength: () => squaredLength3, + str: () => str3 +}); + +// node_modules/gl-matrix/esm/vec3.js +var vec3_exports = {}; +__export(vec3_exports, { + add: () => add2, + angle: () => angle, + bezier: () => bezier, + ceil: () => ceil, + clone: () => clone2, + copy: () => copy2, + create: () => create3, + cross: () => cross, + dist: () => dist, + distance: () => distance, + div: () => div, + divide: () => divide, + dot: () => dot, + equals: () => equals2, + exactEquals: () => exactEquals2, + floor: () => floor, + forEach: () => forEach, + fromValues: () => fromValues2, + hermite: () => hermite, + inverse: () => inverse, + len: () => len, + length: () => length, + lerp: () => lerp, + max: () => max, + min: () => min, + mul: () => mul2, + multiply: () => multiply2, + negate: () => negate, + normalize: () => normalize, + random: () => random, + rotateX: () => rotateX2, + rotateY: () => rotateY2, + rotateZ: () => rotateZ2, + round: () => round, + scale: () => scale2, + scaleAndAdd: () => scaleAndAdd, + set: () => set2, + sqrDist: () => sqrDist, + sqrLen: () => sqrLen, + squaredDistance: () => squaredDistance, + squaredLength: () => squaredLength, + str: () => str2, + sub: () => sub2, + subtract: () => subtract2, + transformMat3: () => transformMat3, + transformMat4: () => transformMat4, + transformQuat: () => transformQuat, + zero: () => zero +}); +function create3() { + var out = new ARRAY_TYPE(3); + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + return out; +} +function clone2(a) { + var out = new ARRAY_TYPE(3); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +} +function length(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return Math.hypot(x, y, z); +} +function fromValues2(x, y, z) { + var out = new ARRAY_TYPE(3); + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} +function copy2(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + return out; +} +function set2(out, x, y, z) { + out[0] = x; + out[1] = y; + out[2] = z; + return out; +} +function add2(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + return out; +} +function subtract2(out, a, b) { + out[0] = a[0] - b[0]; + out[1] = a[1] - b[1]; + out[2] = a[2] - b[2]; + return out; +} +function multiply2(out, a, b) { + out[0] = a[0] * b[0]; + out[1] = a[1] * b[1]; + out[2] = a[2] * b[2]; + return out; +} +function divide(out, a, b) { + out[0] = a[0] / b[0]; + out[1] = a[1] / b[1]; + out[2] = a[2] / b[2]; + return out; +} +function ceil(out, a) { + out[0] = Math.ceil(a[0]); + out[1] = Math.ceil(a[1]); + out[2] = Math.ceil(a[2]); + return out; +} +function floor(out, a) { + out[0] = Math.floor(a[0]); + out[1] = Math.floor(a[1]); + out[2] = Math.floor(a[2]); + return out; +} +function min(out, a, b) { + out[0] = Math.min(a[0], b[0]); + out[1] = Math.min(a[1], b[1]); + out[2] = Math.min(a[2], b[2]); + return out; +} +function max(out, a, b) { + out[0] = Math.max(a[0], b[0]); + out[1] = Math.max(a[1], b[1]); + out[2] = Math.max(a[2], b[2]); + return out; +} +function round(out, a) { + out[0] = Math.round(a[0]); + out[1] = Math.round(a[1]); + out[2] = Math.round(a[2]); + return out; +} +function scale2(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + return out; +} +function scaleAndAdd(out, a, b, scale6) { + out[0] = a[0] + b[0] * scale6; + out[1] = a[1] + b[1] * scale6; + out[2] = a[2] + b[2] * scale6; + return out; +} +function distance(a, b) { + var x = b[0] - a[0]; + var y = b[1] - a[1]; + var z = b[2] - a[2]; + return Math.hypot(x, y, z); +} +function squaredDistance(a, b) { + var x = b[0] - a[0]; + var y = b[1] - a[1]; + var z = b[2] - a[2]; + return x * x + y * y + z * z; +} +function squaredLength(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + return x * x + y * y + z * z; +} +function negate(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + return out; +} +function inverse(out, a) { + out[0] = 1 / a[0]; + out[1] = 1 / a[1]; + out[2] = 1 / a[2]; + return out; +} +function normalize(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var len4 = x * x + y * y + z * z; + if (len4 > 0) { + len4 = 1 / Math.sqrt(len4); + } + out[0] = a[0] * len4; + out[1] = a[1] * len4; + out[2] = a[2] * len4; + return out; +} +function dot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} +function cross(out, a, b) { + var ax = a[0], ay = a[1], az = a[2]; + var bx = b[0], by = b[1], bz = b[2]; + out[0] = ay * bz - az * by; + out[1] = az * bx - ax * bz; + out[2] = ax * by - ay * bx; + return out; +} +function lerp(out, a, b, t) { + var ax = a[0]; + var ay = a[1]; + var az = a[2]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + return out; +} +function hermite(out, a, b, c, d, t) { + var factorTimes2 = t * t; + var factor1 = factorTimes2 * (2 * t - 3) + 1; + var factor2 = factorTimes2 * (t - 2) + t; + var factor3 = factorTimes2 * (t - 1); + var factor4 = factorTimes2 * (3 - 2 * t); + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + return out; +} +function bezier(out, a, b, c, d, t) { + var inverseFactor = 1 - t; + var inverseFactorTimesTwo = inverseFactor * inverseFactor; + var factorTimes2 = t * t; + var factor1 = inverseFactorTimesTwo * inverseFactor; + var factor2 = 3 * t * inverseFactorTimesTwo; + var factor3 = 3 * factorTimes2 * inverseFactor; + var factor4 = factorTimes2 * t; + out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4; + out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4; + out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4; + return out; +} +function random(out, scale6) { + scale6 = scale6 || 1; + var r = RANDOM() * 2 * Math.PI; + var z = RANDOM() * 2 - 1; + var zScale = Math.sqrt(1 - z * z) * scale6; + out[0] = Math.cos(r) * zScale; + out[1] = Math.sin(r) * zScale; + out[2] = z * scale6; + return out; +} +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + var w = m[3] * x + m[7] * y + m[11] * z + m[15]; + w = w || 1; + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; + return out; +} +function transformMat3(out, a, m) { + var x = a[0], y = a[1], z = a[2]; + out[0] = x * m[0] + y * m[3] + z * m[6]; + out[1] = x * m[1] + y * m[4] + z * m[7]; + out[2] = x * m[2] + y * m[5] + z * m[8]; + return out; +} +function transformQuat(out, a, q) { + var qx = q[0], qy = q[1], qz = q[2], qw = q[3]; + var x = a[0], y = a[1], z = a[2]; + var uvx = qy * z - qz * y, uvy = qz * x - qx * z, uvz = qx * y - qy * x; + var uuvx = qy * uvz - qz * uvy, uuvy = qz * uvx - qx * uvz, uuvz = qx * uvy - qy * uvx; + var w2 = qw * 2; + uvx *= w2; + uvy *= w2; + uvz *= w2; + uuvx *= 2; + uuvy *= 2; + uuvz *= 2; + out[0] = x + uvx + uuvx; + out[1] = y + uvy + uuvy; + out[2] = z + uvz + uuvz; + return out; +} +function rotateX2(out, a, b, rad) { + var p = [], r = []; + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; + r[0] = p[0]; + r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad); + r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + return out; +} +function rotateY2(out, a, b, rad) { + var p = [], r = []; + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; + r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad); + r[1] = p[1]; + r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad); + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + return out; +} +function rotateZ2(out, a, b, rad) { + var p = [], r = []; + p[0] = a[0] - b[0]; + p[1] = a[1] - b[1]; + p[2] = a[2] - b[2]; + r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad); + r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad); + r[2] = p[2]; + out[0] = r[0] + b[0]; + out[1] = r[1] + b[1]; + out[2] = r[2] + b[2]; + return out; +} +function angle(a, b) { + var ax = a[0], ay = a[1], az = a[2], bx = b[0], by = b[1], bz = b[2], mag1 = Math.sqrt(ax * ax + ay * ay + az * az), mag2 = Math.sqrt(bx * bx + by * by + bz * bz), mag = mag1 * mag2, cosine = mag && dot(a, b) / mag; + return Math.acos(Math.min(Math.max(cosine, -1), 1)); +} +function zero(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + return out; +} +function str2(a) { + return "vec3(" + a[0] + ", " + a[1] + ", " + a[2] + ")"; +} +function exactEquals2(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2]; +} +function equals2(a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2]; + var b0 = b[0], b1 = b[1], b2 = b[2]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)); +} +var sub2 = subtract2; +var mul2 = multiply2; +var div = divide; +var dist = distance; +var sqrDist = squaredDistance; +var len = length; +var sqrLen = squaredLength; +var forEach = function() { + var vec = create3(); + return function(a, stride, offset2, count, fn, arg) { + var i, l; + if (!stride) { + stride = 3; + } + if (!offset2) { + offset2 = 0; + } + if (count) { + l = Math.min(count * stride + offset2, a.length); + } else { + l = a.length; + } + for (i = offset2; i < l; i += stride) { + vec[0] = a[i]; + vec[1] = a[i + 1]; + vec[2] = a[i + 2]; + fn(vec, vec, arg); + a[i] = vec[0]; + a[i + 1] = vec[1]; + a[i + 2] = vec[2]; + } + return a; + }; +}(); + +// node_modules/gl-matrix/esm/vec4.js +function create4() { + var out = new ARRAY_TYPE(4); + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + } + return out; +} +function clone3(a) { + var out = new ARRAY_TYPE(4); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} +function fromValues3(x, y, z, w) { + var out = new ARRAY_TYPE(4); + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +} +function copy3(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; +} +function set3(out, x, y, z, w) { + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = w; + return out; +} +function add3(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + return out; +} +function scale3(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + return out; +} +function length2(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + return Math.hypot(x, y, z, w); +} +function squaredLength2(a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + return x * x + y * y + z * z + w * w; +} +function normalize2(out, a) { + var x = a[0]; + var y = a[1]; + var z = a[2]; + var w = a[3]; + var len4 = x * x + y * y + z * z + w * w; + if (len4 > 0) { + len4 = 1 / Math.sqrt(len4); + } + out[0] = x * len4; + out[1] = y * len4; + out[2] = z * len4; + out[3] = w * len4; + return out; +} +function dot2(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; +} +function lerp2(out, a, b, t) { + var ax = a[0]; + var ay = a[1]; + var az = a[2]; + var aw = a[3]; + out[0] = ax + t * (b[0] - ax); + out[1] = ay + t * (b[1] - ay); + out[2] = az + t * (b[2] - az); + out[3] = aw + t * (b[3] - aw); + return out; +} +function exactEquals3(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3]; +} +function equals3(a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3)); +} +var forEach2 = function() { + var vec = create4(); + return function(a, stride, offset2, count, fn, arg) { + var i, l; + if (!stride) { + stride = 4; + } + if (!offset2) { + offset2 = 0; + } + if (count) { + l = Math.min(count * stride + offset2, a.length); + } else { + l = a.length; + } + for (i = offset2; i < l; i += stride) { + vec[0] = a[i]; + vec[1] = a[i + 1]; + vec[2] = a[i + 2]; + vec[3] = a[i + 3]; + fn(vec, vec, arg); + a[i] = vec[0]; + a[i + 1] = vec[1]; + a[i + 2] = vec[2]; + a[i + 3] = vec[3]; + } + return a; + }; +}(); + +// node_modules/gl-matrix/esm/quat.js +function create5() { + var out = new ARRAY_TYPE(4); + if (ARRAY_TYPE != Float32Array) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + } + out[3] = 1; + return out; +} +function identity2(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; +} +function setAxisAngle(out, axis, rad) { + rad = rad * 0.5; + var s = Math.sin(rad); + out[0] = s * axis[0]; + out[1] = s * axis[1]; + out[2] = s * axis[2]; + out[3] = Math.cos(rad); + return out; +} +function getAxisAngle(out_axis, q) { + var rad = Math.acos(q[3]) * 2; + var s = Math.sin(rad / 2); + if (s > EPSILON) { + out_axis[0] = q[0] / s; + out_axis[1] = q[1] / s; + out_axis[2] = q[2] / s; + } else { + out_axis[0] = 1; + out_axis[1] = 0; + out_axis[2] = 0; + } + return rad; +} +function getAngle(a, b) { + var dotproduct = dot3(a, b); + return Math.acos(2 * dotproduct * dotproduct - 1); +} +function multiply3(out, a, b) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3]; + var bx = b[0], by = b[1], bz = b[2], bw = b[3]; + out[0] = ax * bw + aw * bx + ay * bz - az * by; + out[1] = ay * bw + aw * by + az * bx - ax * bz; + out[2] = az * bw + aw * bz + ax * by - ay * bx; + out[3] = aw * bw - ax * bx - ay * by - az * bz; + return out; +} +function rotateX3(out, a, rad) { + rad *= 0.5; + var ax = a[0], ay = a[1], az = a[2], aw = a[3]; + var bx = Math.sin(rad), bw = Math.cos(rad); + out[0] = ax * bw + aw * bx; + out[1] = ay * bw + az * bx; + out[2] = az * bw - ay * bx; + out[3] = aw * bw - ax * bx; + return out; +} +function rotateY3(out, a, rad) { + rad *= 0.5; + var ax = a[0], ay = a[1], az = a[2], aw = a[3]; + var by = Math.sin(rad), bw = Math.cos(rad); + out[0] = ax * bw - az * by; + out[1] = ay * bw + aw * by; + out[2] = az * bw + ax * by; + out[3] = aw * bw - ay * by; + return out; +} +function rotateZ3(out, a, rad) { + rad *= 0.5; + var ax = a[0], ay = a[1], az = a[2], aw = a[3]; + var bz = Math.sin(rad), bw = Math.cos(rad); + out[0] = ax * bw + ay * bz; + out[1] = ay * bw - ax * bz; + out[2] = az * bw + aw * bz; + out[3] = aw * bw - az * bz; + return out; +} +function calculateW(out, a) { + var x = a[0], y = a[1], z = a[2]; + out[0] = x; + out[1] = y; + out[2] = z; + out[3] = Math.sqrt(Math.abs(1 - x * x - y * y - z * z)); + return out; +} +function exp(out, a) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + var r = Math.sqrt(x * x + y * y + z * z); + var et = Math.exp(w); + var s = r > 0 ? et * Math.sin(r) / r : 0; + out[0] = x * s; + out[1] = y * s; + out[2] = z * s; + out[3] = et * Math.cos(r); + return out; +} +function ln(out, a) { + var x = a[0], y = a[1], z = a[2], w = a[3]; + var r = Math.sqrt(x * x + y * y + z * z); + var t = r > 0 ? Math.atan2(r, w) / r : 0; + out[0] = x * t; + out[1] = y * t; + out[2] = z * t; + out[3] = 0.5 * Math.log(x * x + y * y + z * z + w * w); + return out; +} +function pow(out, a, b) { + ln(out, a); + scale4(out, out, b); + exp(out, out); + return out; +} +function slerp(out, a, b, t) { + var ax = a[0], ay = a[1], az = a[2], aw = a[3]; + var bx = b[0], by = b[1], bz = b[2], bw = b[3]; + var omega, cosom, sinom, scale0, scale1; + cosom = ax * bx + ay * by + az * bz + aw * bw; + if (cosom < 0) { + cosom = -cosom; + bx = -bx; + by = -by; + bz = -bz; + bw = -bw; + } + if (1 - cosom > EPSILON) { + omega = Math.acos(cosom); + sinom = Math.sin(omega); + scale0 = Math.sin((1 - t) * omega) / sinom; + scale1 = Math.sin(t * omega) / sinom; + } else { + scale0 = 1 - t; + scale1 = t; + } + out[0] = scale0 * ax + scale1 * bx; + out[1] = scale0 * ay + scale1 * by; + out[2] = scale0 * az + scale1 * bz; + out[3] = scale0 * aw + scale1 * bw; + return out; +} +function random2(out) { + var u1 = RANDOM(); + var u2 = RANDOM(); + var u3 = RANDOM(); + var sqrt1MinusU1 = Math.sqrt(1 - u1); + var sqrtU1 = Math.sqrt(u1); + out[0] = sqrt1MinusU1 * Math.sin(2 * Math.PI * u2); + out[1] = sqrt1MinusU1 * Math.cos(2 * Math.PI * u2); + out[2] = sqrtU1 * Math.sin(2 * Math.PI * u3); + out[3] = sqrtU1 * Math.cos(2 * Math.PI * u3); + return out; +} +function invert2(out, a) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + var dot5 = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3; + var invDot = dot5 ? 1 / dot5 : 0; + out[0] = -a0 * invDot; + out[1] = -a1 * invDot; + out[2] = -a2 * invDot; + out[3] = a3 * invDot; + return out; +} +function conjugate(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + return out; +} +function fromMat3(out, m) { + var fTrace = m[0] + m[4] + m[8]; + var fRoot; + if (fTrace > 0) { + fRoot = Math.sqrt(fTrace + 1); + out[3] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[0] = (m[5] - m[7]) * fRoot; + out[1] = (m[6] - m[2]) * fRoot; + out[2] = (m[1] - m[3]) * fRoot; + } else { + var i = 0; + if (m[4] > m[0]) + i = 1; + if (m[8] > m[i * 3 + i]) + i = 2; + var j = (i + 1) % 3; + var k = (i + 2) % 3; + fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1); + out[i] = 0.5 * fRoot; + fRoot = 0.5 / fRoot; + out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot; + out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot; + out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot; + } + return out; +} +function fromEuler(out, x, y, z) { + var halfToRad = 0.5 * Math.PI / 180; + x *= halfToRad; + y *= halfToRad; + z *= halfToRad; + var sx = Math.sin(x); + var cx = Math.cos(x); + var sy = Math.sin(y); + var cy = Math.cos(y); + var sz = Math.sin(z); + var cz = Math.cos(z); + out[0] = sx * cy * cz - cx * sy * sz; + out[1] = cx * sy * cz + sx * cy * sz; + out[2] = cx * cy * sz - sx * sy * cz; + out[3] = cx * cy * cz + sx * sy * sz; + return out; +} +function str3(a) { + return "quat(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")"; +} +var clone4 = clone3; +var fromValues4 = fromValues3; +var copy4 = copy3; +var set4 = set3; +var add4 = add3; +var mul3 = multiply3; +var scale4 = scale3; +var dot3 = dot2; +var lerp3 = lerp2; +var length3 = length2; +var len2 = length3; +var squaredLength3 = squaredLength2; +var sqrLen2 = squaredLength3; +var normalize3 = normalize2; +var exactEquals4 = exactEquals3; +var equals4 = equals3; +var rotationTo = function() { + var tmpvec3 = create3(); + var xUnitVec3 = fromValues2(1, 0, 0); + var yUnitVec3 = fromValues2(0, 1, 0); + return function(out, a, b) { + var dot5 = dot(a, b); + if (dot5 < -0.999999) { + cross(tmpvec3, xUnitVec3, a); + if (len(tmpvec3) < 1e-6) + cross(tmpvec3, yUnitVec3, a); + normalize(tmpvec3, tmpvec3); + setAxisAngle(out, tmpvec3, Math.PI); + return out; + } else if (dot5 > 0.999999) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + return out; + } else { + cross(tmpvec3, a, b); + out[0] = tmpvec3[0]; + out[1] = tmpvec3[1]; + out[2] = tmpvec3[2]; + out[3] = 1 + dot5; + return normalize3(out, out); + } + }; +}(); +var sqlerp = function() { + var temp1 = create5(); + var temp2 = create5(); + return function(out, a, b, c, d, t) { + slerp(temp1, a, d, t); + slerp(temp2, b, c, t); + slerp(out, temp1, temp2, 2 * t * (1 - t)); + return out; + }; +}(); +var setAxes = function() { + var matr = create(); + return function(out, view, right, up) { + matr[0] = right[0]; + matr[3] = right[1]; + matr[6] = right[2]; + matr[1] = up[0]; + matr[4] = up[1]; + matr[7] = up[2]; + matr[2] = -view[0]; + matr[5] = -view[1]; + matr[8] = -view[2]; + return normalize3(out, fromMat3(out, matr)); + }; +}(); + +// node_modules/gl-matrix/esm/quat2.js +var quat2_exports = {}; +__export(quat2_exports, { + add: () => add5, + clone: () => clone5, + conjugate: () => conjugate2, + copy: () => copy5, + create: () => create6, + dot: () => dot4, + equals: () => equals5, + exactEquals: () => exactEquals5, + fromMat4: () => fromMat4, + fromRotation: () => fromRotation2, + fromRotationTranslation: () => fromRotationTranslation2, + fromRotationTranslationValues: () => fromRotationTranslationValues, + fromTranslation: () => fromTranslation2, + fromValues: () => fromValues5, + getDual: () => getDual, + getReal: () => getReal, + getTranslation: () => getTranslation2, + identity: () => identity3, + invert: () => invert3, + len: () => len3, + length: () => length4, + lerp: () => lerp4, + mul: () => mul4, + multiply: () => multiply4, + normalize: () => normalize4, + rotateAroundAxis: () => rotateAroundAxis, + rotateByQuatAppend: () => rotateByQuatAppend, + rotateByQuatPrepend: () => rotateByQuatPrepend, + rotateX: () => rotateX4, + rotateY: () => rotateY4, + rotateZ: () => rotateZ4, + scale: () => scale5, + set: () => set5, + setDual: () => setDual, + setReal: () => setReal, + sqrLen: () => sqrLen3, + squaredLength: () => squaredLength4, + str: () => str4, + translate: () => translate2 +}); +function create6() { + var dq = new ARRAY_TYPE(8); + if (ARRAY_TYPE != Float32Array) { + dq[0] = 0; + dq[1] = 0; + dq[2] = 0; + dq[4] = 0; + dq[5] = 0; + dq[6] = 0; + dq[7] = 0; + } + dq[3] = 1; + return dq; +} +function clone5(a) { + var dq = new ARRAY_TYPE(8); + dq[0] = a[0]; + dq[1] = a[1]; + dq[2] = a[2]; + dq[3] = a[3]; + dq[4] = a[4]; + dq[5] = a[5]; + dq[6] = a[6]; + dq[7] = a[7]; + return dq; +} +function fromValues5(x1, y1, z1, w1, x2, y2, z2, w2) { + var dq = new ARRAY_TYPE(8); + dq[0] = x1; + dq[1] = y1; + dq[2] = z1; + dq[3] = w1; + dq[4] = x2; + dq[5] = y2; + dq[6] = z2; + dq[7] = w2; + return dq; +} +function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) { + var dq = new ARRAY_TYPE(8); + dq[0] = x1; + dq[1] = y1; + dq[2] = z1; + dq[3] = w1; + var ax = x2 * 0.5, ay = y2 * 0.5, az = z2 * 0.5; + dq[4] = ax * w1 + ay * z1 - az * y1; + dq[5] = ay * w1 + az * x1 - ax * z1; + dq[6] = az * w1 + ax * y1 - ay * x1; + dq[7] = -ax * x1 - ay * y1 - az * z1; + return dq; +} +function fromRotationTranslation2(out, q, t) { + var ax = t[0] * 0.5, ay = t[1] * 0.5, az = t[2] * 0.5, bx = q[0], by = q[1], bz = q[2], bw = q[3]; + out[0] = bx; + out[1] = by; + out[2] = bz; + out[3] = bw; + out[4] = ax * bw + ay * bz - az * by; + out[5] = ay * bw + az * bx - ax * bz; + out[6] = az * bw + ax * by - ay * bx; + out[7] = -ax * bx - ay * by - az * bz; + return out; +} +function fromTranslation2(out, t) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = t[0] * 0.5; + out[5] = t[1] * 0.5; + out[6] = t[2] * 0.5; + out[7] = 0; + return out; +} +function fromRotation2(out, q) { + out[0] = q[0]; + out[1] = q[1]; + out[2] = q[2]; + out[3] = q[3]; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + return out; +} +function fromMat4(out, a) { + var outer = create5(); + getRotation(outer, a); + var t = new ARRAY_TYPE(3); + getTranslation(t, a); + fromRotationTranslation2(out, outer, t); + return out; +} +function copy5(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[5]; + out[6] = a[6]; + out[7] = a[7]; + return out; +} +function identity3(out) { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + return out; +} +function set5(out, x1, y1, z1, w1, x2, y2, z2, w2) { + out[0] = x1; + out[1] = y1; + out[2] = z1; + out[3] = w1; + out[4] = x2; + out[5] = y2; + out[6] = z2; + out[7] = w2; + return out; +} +var getReal = copy4; +function getDual(out, a) { + out[0] = a[4]; + out[1] = a[5]; + out[2] = a[6]; + out[3] = a[7]; + return out; +} +var setReal = copy4; +function setDual(out, q) { + out[4] = q[0]; + out[5] = q[1]; + out[6] = q[2]; + out[7] = q[3]; + return out; +} +function getTranslation2(out, a) { + var ax = a[4], ay = a[5], az = a[6], aw = a[7], bx = -a[0], by = -a[1], bz = -a[2], bw = a[3]; + out[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2; + out[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2; + out[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2; + return out; +} +function translate2(out, a, v) { + var ax1 = a[0], ay1 = a[1], az1 = a[2], aw1 = a[3], bx1 = v[0] * 0.5, by1 = v[1] * 0.5, bz1 = v[2] * 0.5, ax2 = a[4], ay2 = a[5], az2 = a[6], aw2 = a[7]; + out[0] = ax1; + out[1] = ay1; + out[2] = az1; + out[3] = aw1; + out[4] = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2; + out[5] = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2; + out[6] = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2; + out[7] = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2; + return out; +} +function rotateX4(out, a, rad) { + var bx = -a[0], by = -a[1], bz = -a[2], bw = a[3], ax = a[4], ay = a[5], az = a[6], aw = a[7], ax1 = ax * bw + aw * bx + ay * bz - az * by, ay1 = ay * bw + aw * by + az * bx - ax * bz, az1 = az * bw + aw * bz + ax * by - ay * bx, aw1 = aw * bw - ax * bx - ay * by - az * bz; + rotateX3(out, a, rad); + bx = out[0]; + by = out[1]; + bz = out[2]; + bw = out[3]; + out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + return out; +} +function rotateY4(out, a, rad) { + var bx = -a[0], by = -a[1], bz = -a[2], bw = a[3], ax = a[4], ay = a[5], az = a[6], aw = a[7], ax1 = ax * bw + aw * bx + ay * bz - az * by, ay1 = ay * bw + aw * by + az * bx - ax * bz, az1 = az * bw + aw * bz + ax * by - ay * bx, aw1 = aw * bw - ax * bx - ay * by - az * bz; + rotateY3(out, a, rad); + bx = out[0]; + by = out[1]; + bz = out[2]; + bw = out[3]; + out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + return out; +} +function rotateZ4(out, a, rad) { + var bx = -a[0], by = -a[1], bz = -a[2], bw = a[3], ax = a[4], ay = a[5], az = a[6], aw = a[7], ax1 = ax * bw + aw * bx + ay * bz - az * by, ay1 = ay * bw + aw * by + az * bx - ax * bz, az1 = az * bw + aw * bz + ax * by - ay * bx, aw1 = aw * bw - ax * bx - ay * by - az * bz; + rotateZ3(out, a, rad); + bx = out[0]; + by = out[1]; + bz = out[2]; + bw = out[3]; + out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + return out; +} +function rotateByQuatAppend(out, a, q) { + var qx = q[0], qy = q[1], qz = q[2], qw = q[3], ax = a[0], ay = a[1], az = a[2], aw = a[3]; + out[0] = ax * qw + aw * qx + ay * qz - az * qy; + out[1] = ay * qw + aw * qy + az * qx - ax * qz; + out[2] = az * qw + aw * qz + ax * qy - ay * qx; + out[3] = aw * qw - ax * qx - ay * qy - az * qz; + ax = a[4]; + ay = a[5]; + az = a[6]; + aw = a[7]; + out[4] = ax * qw + aw * qx + ay * qz - az * qy; + out[5] = ay * qw + aw * qy + az * qx - ax * qz; + out[6] = az * qw + aw * qz + ax * qy - ay * qx; + out[7] = aw * qw - ax * qx - ay * qy - az * qz; + return out; +} +function rotateByQuatPrepend(out, q, a) { + var qx = q[0], qy = q[1], qz = q[2], qw = q[3], bx = a[0], by = a[1], bz = a[2], bw = a[3]; + out[0] = qx * bw + qw * bx + qy * bz - qz * by; + out[1] = qy * bw + qw * by + qz * bx - qx * bz; + out[2] = qz * bw + qw * bz + qx * by - qy * bx; + out[3] = qw * bw - qx * bx - qy * by - qz * bz; + bx = a[4]; + by = a[5]; + bz = a[6]; + bw = a[7]; + out[4] = qx * bw + qw * bx + qy * bz - qz * by; + out[5] = qy * bw + qw * by + qz * bx - qx * bz; + out[6] = qz * bw + qw * bz + qx * by - qy * bx; + out[7] = qw * bw - qx * bx - qy * by - qz * bz; + return out; +} +function rotateAroundAxis(out, a, axis, rad) { + if (Math.abs(rad) < EPSILON) { + return copy5(out, a); + } + var axisLength = Math.hypot(axis[0], axis[1], axis[2]); + rad = rad * 0.5; + var s = Math.sin(rad); + var bx = s * axis[0] / axisLength; + var by = s * axis[1] / axisLength; + var bz = s * axis[2] / axisLength; + var bw = Math.cos(rad); + var ax1 = a[0], ay1 = a[1], az1 = a[2], aw1 = a[3]; + out[0] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by; + out[1] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz; + out[2] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx; + out[3] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz; + var ax = a[4], ay = a[5], az = a[6], aw = a[7]; + out[4] = ax * bw + aw * bx + ay * bz - az * by; + out[5] = ay * bw + aw * by + az * bx - ax * bz; + out[6] = az * bw + aw * bz + ax * by - ay * bx; + out[7] = aw * bw - ax * bx - ay * by - az * bz; + return out; +} +function add5(out, a, b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + return out; +} +function multiply4(out, a, b) { + var ax0 = a[0], ay0 = a[1], az0 = a[2], aw0 = a[3], bx1 = b[4], by1 = b[5], bz1 = b[6], bw1 = b[7], ax1 = a[4], ay1 = a[5], az1 = a[6], aw1 = a[7], bx0 = b[0], by0 = b[1], bz0 = b[2], bw0 = b[3]; + out[0] = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0; + out[1] = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0; + out[2] = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0; + out[3] = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0; + out[4] = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 - az1 * by0; + out[5] = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 - ax1 * bz0; + out[6] = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 - ay1 * bx0; + out[7] = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 - ax1 * bx0 - ay1 * by0 - az1 * bz0; + return out; +} +var mul4 = multiply4; +function scale5(out, a, b) { + out[0] = a[0] * b; + out[1] = a[1] * b; + out[2] = a[2] * b; + out[3] = a[3] * b; + out[4] = a[4] * b; + out[5] = a[5] * b; + out[6] = a[6] * b; + out[7] = a[7] * b; + return out; +} +var dot4 = dot3; +function lerp4(out, a, b, t) { + var mt = 1 - t; + if (dot4(a, b) < 0) + t = -t; + out[0] = a[0] * mt + b[0] * t; + out[1] = a[1] * mt + b[1] * t; + out[2] = a[2] * mt + b[2] * t; + out[3] = a[3] * mt + b[3] * t; + out[4] = a[4] * mt + b[4] * t; + out[5] = a[5] * mt + b[5] * t; + out[6] = a[6] * mt + b[6] * t; + out[7] = a[7] * mt + b[7] * t; + return out; +} +function invert3(out, a) { + var sqlen = squaredLength4(a); + out[0] = -a[0] / sqlen; + out[1] = -a[1] / sqlen; + out[2] = -a[2] / sqlen; + out[3] = a[3] / sqlen; + out[4] = -a[4] / sqlen; + out[5] = -a[5] / sqlen; + out[6] = -a[6] / sqlen; + out[7] = a[7] / sqlen; + return out; +} +function conjugate2(out, a) { + out[0] = -a[0]; + out[1] = -a[1]; + out[2] = -a[2]; + out[3] = a[3]; + out[4] = -a[4]; + out[5] = -a[5]; + out[6] = -a[6]; + out[7] = a[7]; + return out; +} +var length4 = length3; +var len3 = length4; +var squaredLength4 = squaredLength3; +var sqrLen3 = squaredLength4; +function normalize4(out, a) { + var magnitude = squaredLength4(a); + if (magnitude > 0) { + magnitude = Math.sqrt(magnitude); + var a0 = a[0] / magnitude; + var a1 = a[1] / magnitude; + var a2 = a[2] / magnitude; + var a3 = a[3] / magnitude; + var b0 = a[4]; + var b1 = a[5]; + var b2 = a[6]; + var b3 = a[7]; + var a_dot_b = a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3; + out[0] = a0; + out[1] = a1; + out[2] = a2; + out[3] = a3; + out[4] = (b0 - a0 * a_dot_b) / magnitude; + out[5] = (b1 - a1 * a_dot_b) / magnitude; + out[6] = (b2 - a2 * a_dot_b) / magnitude; + out[7] = (b3 - a3 * a_dot_b) / magnitude; + } + return out; +} +function str4(a) { + return "quat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ")"; +} +function exactEquals5(a, b) { + return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7]; +} +function equals5(a, b) { + var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7]; + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7]; + return Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1, Math.abs(a7), Math.abs(b7)); +} + +// node_modules/@wonderlandengine/components/dist/hit-test-location.js +var __decorate2 = function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if (d = decorators[i]) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var HitTestLocation = class extends Component3 { + tempScaling = new Float32Array(3); + visible = false; + xrHitTestSource = null; + /** Reference space for creating the hit test when the session starts */ + xrReferenceSpace = null; + /** + * For maintaining backwards compatibility: Whether to scale the object to 0 and back. + * @deprecated Use onHitLost and onHitFound instead. + */ + scaleObject = true; + /** Emits an event when the hit test switches from visible to invisible */ + onHitLost = new Emitter(); + /** Emits an event when the hit test switches from invisible to visible */ + onHitFound = new Emitter(); + onSessionStartCallback = null; + onSessionEndCallback = null; + start() { + this.onSessionStartCallback = this.onXRSessionStart.bind(this); + this.onSessionEndCallback = this.onXRSessionEnd.bind(this); + if (this.scaleObject) { + this.tempScaling.set(this.object.scalingLocal); + this.object.scale([0, 0, 0]); + this.onHitLost.add(() => { + this.tempScaling.set(this.object.scalingLocal); + this.object.scale([0, 0, 0]); + }); + this.onHitFound.add(() => { + this.object.scalingLocal.set(this.tempScaling); + this.object.setDirty(); + }); + } + } + onActivate() { + this.engine.onXRSessionStart.add(this.onSessionStartCallback); + this.engine.onXRSessionEnd.add(this.onSessionEndCallback); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this.onSessionStartCallback); + this.engine.onXRSessionEnd.remove(this.onSessionEndCallback); + } + update() { + const wasVisible = this.visible; + if (this.xrHitTestSource) { + const frame = this.engine.xrFrame; + if (!frame) + return; + let hitTestResults = frame.getHitTestResults(this.xrHitTestSource); + if (hitTestResults.length > 0) { + let pose = hitTestResults[0].getPose(this.engine.xr.currentReferenceSpace); + this.visible = !!pose; + if (pose) { + setXRRigidTransformLocal(this.object, pose.transform); + } + } else { + this.visible = false; + } + } + if (this.visible != wasVisible) { + (this.visible ? this.onHitFound : this.onHitLost).notify(this); + } + } + getHitTestResults(frame = this.engine.xr?.frame ?? null) { + if (!frame) + return []; + if (!this.xrHitTestSource) + return []; + return frame.getHitTestResults(this.xrHitTestSource); + } + onXRSessionStart(session) { + if (session.requestHitTestSource === void 0) { + console.error("hit-test-location: hit test feature not available. Deactivating component."); + this.active = false; + return; + } + session.requestHitTestSource({ + space: this.xrReferenceSpace ?? this.engine.xr.referenceSpaceForType("viewer") + }).then((hitTestSource) => { + this.xrHitTestSource = hitTestSource; + }).catch(console.error); + } + onXRSessionEnd() { + if (!this.xrHitTestSource) + return; + this.xrHitTestSource.cancel(); + this.xrHitTestSource = null; + } +}; +__publicField(HitTestLocation, "TypeName", "hit-test-location"); +__decorate2([ + property.bool(true) +], HitTestLocation.prototype, "scaleObject", void 0); + +// node_modules/@wonderlandengine/components/dist/cursor.js +var __decorate3 = function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if (d = decorators[i]) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var tempVec2 = new Float32Array(3); +var CursorTargetEmitters = class { + /** Emitter for events when the target is hovered */ + onHover = new Emitter(); + /** Emitter for events when the target is unhovered */ + onUnhover = new Emitter(); + /** Emitter for events when the target is clicked */ + onClick = new Emitter(); + /** Emitter for events when the cursor moves on the target */ + onMove = new Emitter(); + /** Emitter for events when the user pressed the select button on the target */ + onDown = new Emitter(); + /** Emitter for events when the user unpressed the select button on the target */ + onUp = new Emitter(); +}; +var Cursor = class extends Component3 { + static onRegister(engine2) { + engine2.registerComponent(HitTestLocation); + } + _collisionMask = 0; + _onDeactivateCallbacks = []; + _input = null; + _origin = new Float32Array(3); + _cursorObjScale = new Float32Array(3); + _direction = new Float32Array(3); + _projectionMatrix = new Float32Array(16); + _viewComponent = null; + _isDown = false; + _lastIsDown = false; + _arTouchDown = false; + _lastPointerPos = new Float32Array(2); + _lastCursorPosOnTarget = new Float32Array(3); + _cursorRayScale = new Float32Array(3); + _hitTestLocation = null; + _hitTestObject = null; + _onSessionStartCallback = null; + /** + * Whether the cursor (and cursorObject) is visible, i.e. pointing at an object + * that matches the collision group + */ + visible = true; + /** Maximum distance for the cursor's ray cast */ + maxDistance = 100; + /** Currently hovered object */ + hoveringObject = null; + /** CursorTarget component of the currently hovered object */ + hoveringObjectTarget = null; + /** Whether the cursor is hovering reality via hit-test */ + hoveringReality = false; + /** + * Global target lets you receive global cursor events on any object. + */ + globalTarget = new CursorTargetEmitters(); + /** + * Hit test target lets you receive cursor events for "reality", if + * `useWebXRHitTest` is set to `true`. + * + * @example + * ```js + * cursor.hitTestTarget.onClick.add((hit, cursor) => { + * // User clicked on reality + * }); + * ``` + */ + hitTestTarget = new CursorTargetEmitters(); + /** World position of the cursor */ + cursorPos = new Float32Array(3); + /** Collision group for the ray cast. Only objects in this group will be affected by this cursor. */ + collisionGroup = 1; + /** (optional) Object that visualizes the cursor's ray. */ + cursorRayObject = null; + /** Axis along which to scale the `cursorRayObject`. */ + cursorRayScalingAxis = 2; + /** (optional) Object that visualizes the cursor's hit location. */ + cursorObject = null; + /** Handedness for VR cursors to accept trigger events only from respective controller. */ + handedness = 0; + /** Mode for raycasting, whether to use PhysX or simple collision components */ + rayCastMode = 0; + /** Whether to set the CSS style of the mouse cursor on desktop */ + styleCursor = true; + /** + * Use WebXR hit-test if available. + * + * Attaches a hit-test-location component to the cursorObject, which will be used + * by the cursor to send events to the hitTestTarget with HitTestResult. + */ + useWebXRHitTest = false; + _onViewportResize = () => { + if (!this._viewComponent) + return; + mat4_exports.invert(this._projectionMatrix, this._viewComponent.projectionMatrix); + }; + start() { + this._collisionMask = 1 << this.collisionGroup; + if (this.handedness == 0) { + const inputComp = this.object.getComponent("input"); + if (!inputComp) { + console.warn("cursor component on object", this.object.name, 'was configured with handedness "input component", but object has no input component.'); + } else { + this.handedness = inputComp.handedness || "none"; + this._input = inputComp; + } + } else { + this.handedness = ["left", "right", "none"][this.handedness - 1]; + } + this._viewComponent = this.object.getComponent(ViewComponent); + if (this.useWebXRHitTest) { + this._hitTestObject = this.engine.scene.addObject(this.object); + this._hitTestLocation = this._hitTestObject.addComponent(HitTestLocation, { + scaleObject: false + }) ?? null; + } + this._onSessionStartCallback = this.setupVREvents.bind(this); + } + onActivate() { + this.engine.onXRSessionStart.add(this._onSessionStartCallback); + this.engine.onResize.add(this._onViewportResize); + this._setCursorVisibility(true); + if (this._viewComponent != null) { + const canvas2 = this.engine.canvas; + const onClick = this.onClick.bind(this); + const onPointerMove = this.onPointerMove.bind(this); + const onPointerDown = this.onPointerDown.bind(this); + const onPointerUp = this.onPointerUp.bind(this); + canvas2.addEventListener("click", onClick); + canvas2.addEventListener("pointermove", onPointerMove); + canvas2.addEventListener("pointerdown", onPointerDown); + canvas2.addEventListener("pointerup", onPointerUp); + this._onDeactivateCallbacks.push(() => { + canvas2.removeEventListener("click", onClick); + canvas2.removeEventListener("pointermove", onPointerMove); + canvas2.removeEventListener("pointerdown", onPointerDown); + canvas2.removeEventListener("pointerup", onPointerUp); + }); + } + this._onViewportResize(); + } + _setCursorRayTransform(hitPosition) { + if (!this.cursorRayObject) + return; + const dist2 = vec3_exports.dist(this._origin, hitPosition); + this.cursorRayObject.setTranslationLocal([0, 0, -dist2 / 2]); + if (this.cursorRayScalingAxis != 4) { + this.cursorRayObject.resetScaling(); + this._cursorRayScale[this.cursorRayScalingAxis] = dist2 / 2; + this.cursorRayObject.scale(this._cursorRayScale); + } + } + _setCursorVisibility(visible) { + if (this.visible == visible) + return; + this.visible = visible; + if (!this.cursorObject) + return; + if (visible) { + this.cursorObject.setScalingWorld(this._cursorObjScale); + } else { + this.cursorObject.getScalingLocal(this._cursorObjScale); + this.cursorObject.scale([0, 0, 0]); + } + } + update() { + if (this.engine.xr && this._arTouchDown && this._input && this.engine.xr.session.inputSources[0].handedness === "none" && this.engine.xr.session.inputSources[0].gamepad) { + const p = this.engine.xr.session.inputSources[0].gamepad.axes; + this._direction[0] = p[0]; + this._direction[1] = -p[1]; + this._direction[2] = -1; + this.applyTransformAndProjectDirection(); + } else if (this.engine.xr && this._input && this._input.xrInputSource) { + this._direction[0] = 0; + this._direction[1] = 0; + this._direction[2] = -1; + this.applyTransformToDirection(); + } else if (this._viewComponent) { + this.updateDirection(); + } + this.rayCast(null, this.engine.xr?.frame); + if (this.cursorObject) { + if (this.hoveringObject && (this.cursorPos[0] != 0 || this.cursorPos[1] != 0 || this.cursorPos[2] != 0)) { + this._setCursorVisibility(true); + this.cursorObject.setTranslationWorld(this.cursorPos); + this._setCursorRayTransform(this.cursorPos); + } else { + this._setCursorVisibility(false); + } + } + } + /* Returns the hovered cursor target, if available */ + notify(event, originalEvent) { + const target = this.hoveringObject; + if (target) { + const cursorTarget = this.hoveringObjectTarget; + if (cursorTarget) + cursorTarget[event].notify(target, this, originalEvent ?? void 0); + this.globalTarget[event].notify(target, this, originalEvent ?? void 0); + } + } + hoverBehaviour(rayHit, hitTestResult, doClick, originalEvent) { + const hit = !this.hoveringReality && rayHit.hitCount > 0 ? rayHit.objects[0] : null; + if (hit) { + if (!this.hoveringObject || !this.hoveringObject.equals(hit)) { + if (this.hoveringObject) { + this.notify("onUnhover", originalEvent); + } + this.hoveringObject = hit; + this.hoveringObjectTarget = this.hoveringObject.getComponent(CursorTarget); + if (this.styleCursor) + this.engine.canvas.style.cursor = "pointer"; + this.notify("onHover", originalEvent); + } + } else if (this.hoveringObject) { + this.notify("onUnhover", originalEvent); + this.hoveringObject = null; + this.hoveringObjectTarget = null; + if (this.styleCursor) + this.engine.canvas.style.cursor = "default"; + } + if (this.hoveringObject) { + if (this._isDown !== this._lastIsDown) { + this.notify(this._isDown ? "onDown" : "onUp", originalEvent); + } + if (doClick) + this.notify("onClick", originalEvent); + } else if (this.hoveringReality) { + if (this._isDown !== this._lastIsDown) { + (this._isDown ? this.hitTestTarget.onDown : this.hitTestTarget.onUp).notify(hitTestResult, this, originalEvent ?? void 0); + } + if (doClick) + this.hitTestTarget.onClick.notify(hitTestResult, this, originalEvent ?? void 0); + } + if (hit) { + if (this.hoveringObject) { + this.hoveringObject.transformPointInverseWorld(tempVec2, this.cursorPos); + } else { + tempVec2.set(this.cursorPos); + } + if (!vec3_exports.equals(this._lastCursorPosOnTarget, tempVec2)) { + this.notify("onMove", originalEvent); + this._lastCursorPosOnTarget.set(tempVec2); + } + } else if (this.hoveringReality) { + if (!vec3_exports.equals(this._lastCursorPosOnTarget, this.cursorPos)) { + this.hitTestTarget.onMove.notify(hitTestResult, this, originalEvent ?? void 0); + this._lastCursorPosOnTarget.set(this.cursorPos); + } + } else { + this._lastCursorPosOnTarget.set(this.cursorPos); + } + this._lastIsDown = this._isDown; + } + /** + * Setup event listeners on session object + * @param s WebXR session + * + * Sets up 'select' and 'end' events. + */ + setupVREvents(s) { + if (!s) + console.error("setupVREvents called without a valid session"); + const onSelect = this.onSelect.bind(this); + s.addEventListener("select", onSelect); + const onSelectStart = this.onSelectStart.bind(this); + s.addEventListener("selectstart", onSelectStart); + const onSelectEnd = this.onSelectEnd.bind(this); + s.addEventListener("selectend", onSelectEnd); + this._onDeactivateCallbacks.push(() => { + if (!this.engine.xrSession) + return; + s.removeEventListener("select", onSelect); + s.removeEventListener("selectstart", onSelectStart); + s.removeEventListener("selectend", onSelectEnd); + }); + this._onViewportResize(); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this._onSessionStartCallback); + this.engine.onResize.remove(this._onViewportResize); + this._setCursorVisibility(false); + if (this.hoveringObject) + this.notify("onUnhover", null); + if (this.cursorRayObject) + this.cursorRayObject.scale([0, 0, 0]); + for (const f of this._onDeactivateCallbacks) + f(); + this._onDeactivateCallbacks.length = 0; + } + onDestroy() { + this._hitTestObject?.destroy(); + } + /** 'select' event listener */ + onSelect(e) { + if (e.inputSource.handedness != this.handedness) + return; + this.rayCast(e, e.frame, true); + } + /** 'selectstart' event listener */ + onSelectStart(e) { + this._arTouchDown = true; + if (e.inputSource.handedness == this.handedness) { + this._isDown = true; + this.rayCast(e, e.frame); + } + } + /** 'selectend' event listener */ + onSelectEnd(e) { + this._arTouchDown = false; + if (e.inputSource.handedness == this.handedness) { + this._isDown = false; + this.rayCast(e, e.frame); + } + } + /** 'pointermove' event listener */ + onPointerMove(e) { + if (!e.isPrimary) + return; + this.updateMousePos(e); + this.rayCast(e, null); + } + /** 'click' event listener */ + onClick(e) { + this.updateMousePos(e); + this.rayCast(e, null, true); + } + /** 'pointerdown' event listener */ + onPointerDown(e) { + if (!e.isPrimary || e.button !== 0) + return; + this.updateMousePos(e); + this._isDown = true; + this.rayCast(e); + } + /** 'pointerup' event listener */ + onPointerUp(e) { + if (!e.isPrimary || e.button !== 0) + return; + this.updateMousePos(e); + this._isDown = false; + this.rayCast(e); + } + /** + * Update mouse position in non-VR mode and raycast for new position + * @returns @ref WL.RayHit for new position. + */ + updateMousePos(e) { + this._lastPointerPos[0] = e.clientX; + this._lastPointerPos[1] = e.clientY; + this.updateDirection(); + } + updateDirection() { + const bounds = this.engine.canvas.getBoundingClientRect(); + const left = this._lastPointerPos[0] / bounds.width; + const top = this._lastPointerPos[1] / bounds.height; + this._direction[0] = left * 2 - 1; + this._direction[1] = -top * 2 + 1; + this._direction[2] = -1; + this.applyTransformAndProjectDirection(); + } + applyTransformAndProjectDirection() { + vec3_exports.transformMat4(this._direction, this._direction, this._projectionMatrix); + vec3_exports.normalize(this._direction, this._direction); + this.applyTransformToDirection(); + } + applyTransformToDirection() { + vec3_exports.transformQuat(this._direction, this._direction, this.object.transformWorld); + this.object.getTranslationWorld(this._origin); + } + rayCast(originalEvent, frame = null, doClick = false) { + const rayHit = this.rayCastMode == 0 ? this.engine.scene.rayCast(this._origin, this._direction, this._collisionMask) : this.engine.physics.rayCast(this._origin, this._direction, this._collisionMask, this.maxDistance); + let hitResultDistance = Infinity; + let hitTestResult = null; + if (this._hitTestLocation?.visible) { + this._hitTestObject.getTranslationWorld(this.cursorPos); + hitResultDistance = vec3_exports.distance(this.object.getTranslationWorld(tempVec2), this.cursorPos); + hitTestResult = this._hitTestLocation?.getHitTestResults(frame)[0]; + } + let hoveringReality = false; + if (rayHit.hitCount > 0) { + const d = rayHit.distances[0]; + if (hitResultDistance >= d) { + this.cursorPos.set(rayHit.locations[0]); + } else { + hoveringReality = true; + } + } else if (hitResultDistance < Infinity) { + } else { + this.cursorPos.fill(0); + } + if (hoveringReality && !this.hoveringReality) { + this.hitTestTarget.onHover.notify(hitTestResult, this); + } else if (!hoveringReality && this.hoveringReality) { + this.hitTestTarget.onUnhover.notify(hitTestResult, this); + } + this.hoveringReality = hoveringReality; + this.hoverBehaviour(rayHit, hitTestResult, doClick, originalEvent); + return rayHit; + } +}; +__publicField(Cursor, "TypeName", "cursor"); +/* Dependencies is deprecated, but we keep it here for compatibility + * with 1.0.0-rc2 until 1.0.0 is released */ +__publicField(Cursor, "Dependencies", [HitTestLocation]); +__decorate3([ + property.int(1) +], Cursor.prototype, "collisionGroup", void 0); +__decorate3([ + property.object() +], Cursor.prototype, "cursorRayObject", void 0); +__decorate3([ + property.enum(["x", "y", "z", "none"], "z") +], Cursor.prototype, "cursorRayScalingAxis", void 0); +__decorate3([ + property.object() +], Cursor.prototype, "cursorObject", void 0); +__decorate3([ + property.enum(["input component", "left", "right", "none"], "input component") +], Cursor.prototype, "handedness", void 0); +__decorate3([ + property.enum(["collision", "physx"], "collision") +], Cursor.prototype, "rayCastMode", void 0); +__decorate3([ + property.bool(true) +], Cursor.prototype, "styleCursor", void 0); +__decorate3([ + property.bool(false) +], Cursor.prototype, "useWebXRHitTest", void 0); + +// node_modules/@wonderlandengine/components/dist/debug-object.js +var __decorate4 = function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if (d = decorators[i]) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var DebugObject = class extends Component3 { + /** A second object to print the name of */ + obj = null; + start() { + let origin = new Float32Array(3); + quat2_exports.getTranslation(origin, this.object.transformWorld); + console.log("Debug object:", this.object.name); + console.log("Other object:", this.obj?.name); + console.log(" translation", origin); + console.log(" transformWorld", this.object.transformWorld); + console.log(" transformLocal", this.object.transformLocal); + } +}; +__publicField(DebugObject, "TypeName", "debug-object"); +__decorate4([ + property.object() +], DebugObject.prototype, "obj", void 0); + +// node_modules/@wonderlandengine/components/dist/finger-cursor.js +var FingerCursor = class extends Component3 { + init() { + this.lastTarget = null; + } + start() { + this.tip = this.object.getComponent("collision"); + } + update() { + const overlaps = this.tip.queryOverlaps(); + let overlapFound = null; + for (let i = 0; i < overlaps.length; ++i) { + const o = overlaps[i].object; + const target = o.getComponent("cursor-target"); + if (target) { + if (!target.equals(this.lastTarget)) { + target.onHover(o, this); + target.onClick(o, this); + } + overlapFound = target; + break; + } + } + if (!overlapFound) { + if (this.lastTarget) + this.lastTarget.onUnhover(this.lastTarget.object, this); + this.lastTarget = null; + return; + } else { + this.lastTarget = overlapFound; + } + } +}; +__publicField(FingerCursor, "TypeName", "finger-cursor"); +__publicField(FingerCursor, "Properties", {}); + +// node_modules/@wonderlandengine/components/dist/fixed-foveation.js +var FixedFoveation = class extends Component3 { + start() { + this.onSessionStartCallback = this.setFixedFoveation.bind(this); + } + onActivate() { + this.engine.onXRSessionStart.add(this.onSessionStartCallback); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this.onSessionStartCallback); + } + setFixedFoveation() { + this.engine.xr.baseLayer.fixedFoveation = this.fixedFoveation; + } +}; +__publicField(FixedFoveation, "TypeName", "fixed-foveation"); +__publicField(FixedFoveation, "Properties", { + /** Amount to apply from 0 (none) to 1 (full) */ + fixedFoveation: { type: Type.Float, default: 0.5 } +}); + +// node_modules/@wonderlandengine/components/dist/hand-tracking.js +var ORDERED_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 invTranslation = new Float32Array(3); +var invRotation = new Float32Array(4); +var HandTracking = class extends Component3 { + init() { + this.handedness = ["left", "right"][this.handedness]; + } + joints = {}; + session = null; + /* Whether last update had a hand pose */ + hasPose = false; + _childrenActive = true; + start() { + if (!("XRHand" in window)) { + console.warn("WebXR Hand Tracking not supported by this browser."); + this.active = false; + return; + } + if (this.handSkin) { + let skin = this.handSkin; + let jointIds = skin.jointIds; + this.joints[ORDERED_JOINTS[0]] = this.engine.wrapObject(jointIds[0]); + for (let j = 0; j < jointIds.length; ++j) { + let joint = this.engine.wrapObject(jointIds[j]); + this.joints[joint.name] = joint; + } + return; + } + const jointObjects = this.engine.scene.addObjects(ORDERED_JOINTS.length, this.object.parent, ORDERED_JOINTS.length); + for (let j = 0; j < ORDERED_JOINTS.length; ++j) { + let joint = jointObjects[j]; + joint.addComponent(MeshComponent, { + mesh: this.jointMesh, + material: this.jointMaterial + }); + this.joints[ORDERED_JOINTS[j]] = joint; + } + } + update(dt) { + if (!this.session) { + if (this.engine.xr) + this.setupVREvents(this.engine.xr.session); + } + if (!this.session) + return; + this.hasPose = false; + if (this.session && this.session.inputSources) { + for (let i = 0; i < this.session.inputSources.length; ++i) { + const inputSource = this.session.inputSources[i]; + if (!inputSource || !inputSource.hand || inputSource.handedness != this.handedness) + continue; + this.hasPose = true; + const wristSpace = inputSource.hand.get("wrist"); + if (wristSpace !== null) { + const p = this.engine.xr.frame.getJointPose(wristSpace, this.engine.xr.currentReferenceSpace); + if (p) { + setXRRigidTransformLocal(this.object, p.transform); + } + } + this.object.getRotationLocal(invRotation); + quat_exports.conjugate(invRotation, invRotation); + this.object.getTranslationLocal(invTranslation); + for (let j = 0; j < ORDERED_JOINTS.length; ++j) { + const jointName = ORDERED_JOINTS[j]; + const joint = this.joints[jointName]; + if (joint === null) + continue; + let jointPose = null; + const jointSpace = inputSource.hand.get(jointName); + if (jointSpace !== null) { + jointPose = this.engine.xr.frame.getJointPose(jointSpace, this.engine.xr.currentReferenceSpace); + } + if (jointPose !== null) { + if (this.handSkin) { + joint.resetTranslationRotation(); + joint.translate([ + jointPose.transform.position.x - invTranslation[0], + jointPose.transform.position.y - invTranslation[1], + jointPose.transform.position.z - invTranslation[2] + ]); + joint.rotate(invRotation); + joint.rotateObject([ + jointPose.transform.orientation.x, + jointPose.transform.orientation.y, + jointPose.transform.orientation.z, + jointPose.transform.orientation.w + ]); + } else { + setXRRigidTransformLocal(joint, jointPose.transform); + const r = jointPose.radius || 7e-3; + joint.setScalingLocal([r, r, r]); + } + } + } + } + } + if (!this.hasPose && this._childrenActive) { + this._childrenActive = false; + if (this.deactivateChildrenWithoutPose) { + this.setChildrenActive(false); + } + if (this.controllerToDeactivate) { + this.controllerToDeactivate.active = true; + this.setChildrenActive(true, this.controllerToDeactivate); + } + } else if (this.hasPose && !this._childrenActive) { + this._childrenActive = true; + if (this.deactivateChildrenWithoutPose) { + this.setChildrenActive(true); + } + if (this.controllerToDeactivate) { + this.controllerToDeactivate.active = false; + this.setChildrenActive(false, this.controllerToDeactivate); + } + } + } + setChildrenActive(active, object) { + object = object || this.object; + const children = object.children; + for (const o of children) { + o.active = active; + this.setChildrenActive(active, o); + } + } + isGrabbing() { + const indexTipPos = [0, 0, 0]; + quat2_exports.getTranslation(indexTipPos, this.joints["index-finger-tip"].transformLocal); + const thumbTipPos = [0, 0, 0]; + quat2_exports.getTranslation(thumbTipPos, this.joints["thumb-tip"].transformLocal); + return vec3_exports.sqrDist(thumbTipPos, indexTipPos) < 1e-3; + } + setupVREvents(s) { + this.session = s; + } +}; +__publicField(HandTracking, "TypeName", "hand-tracking"); +__publicField(HandTracking, "Properties", { + /** Handedness determining whether to receive tracking input from right or left hand */ + handedness: { type: Type.Enum, default: "left", values: ["left", "right"] }, + /** (optional) Mesh to use to visualize joints */ + jointMesh: { type: Type.Mesh, default: null }, + /** Material to use for display. Applied to either the spawned skinned mesh or the joint spheres. */ + jointMaterial: { type: Type.Material, default: null }, + /** (optional) Skin to apply tracked joint poses to. If not present, joint spheres will be used for display instead. */ + handSkin: { type: Type.Skin, default: null }, + /** Deactivate children if no pose was tracked */ + deactivateChildrenWithoutPose: { type: Type.Bool, default: true }, + /** Controller objects to activate including children if no pose is available */ + controllerToDeactivate: { type: Type.Object } +}); + +// node_modules/@wonderlandengine/components/dist/howler-audio-listener.js +var import_howler = __toESM(require_howler(), 1); +var HowlerAudioListener = class extends Component3 { + init() { + this.origin = new Float32Array(3); + this.fwd = new Float32Array(3); + this.up = new Float32Array(3); + } + update() { + if (!this.spatial) + return; + this.object.getTranslationWorld(this.origin); + this.object.getForward(this.fwd); + this.object.getUp(this.up); + Howler.pos(this.origin[0], this.origin[1], this.origin[2]); + Howler.orientation(this.fwd[0], this.fwd[1], this.fwd[2], this.up[0], this.up[1], this.up[2]); + } +}; +__publicField(HowlerAudioListener, "TypeName", "howler-audio-listener"); +__publicField(HowlerAudioListener, "Properties", { + /** Whether audio should be spatialized/positional. */ + spatial: { type: Type.Bool, default: true } +}); + +// node_modules/@wonderlandengine/components/dist/howler-audio-source.js +var import_howler2 = __toESM(require_howler(), 1); +var HowlerAudioSource = class extends Component3 { + start() { + this.audio = new Howl({ + src: [this.src], + loop: this.loop, + volume: this.volume, + autoplay: this.autoplay + }); + this.lastPlayedAudioId = null; + this.origin = new Float32Array(3); + this.lastOrigin = new Float32Array(3); + if (this.spatial && this.autoplay) { + this.updatePosition(); + this.play(); + } + } + update() { + if (!this.spatial || !this.lastPlayedAudioId) + return; + this.object.getTranslationWorld(this.origin); + if (Math.abs(this.lastOrigin[0] - this.origin[0]) > 5e-3 || Math.abs(this.lastOrigin[1] - this.origin[1]) > 5e-3 || Math.abs(this.lastOrigin[2] - this.origin[2]) > 5e-3) { + this.updatePosition(); + } + } + updatePosition() { + this.audio.pos(this.origin[0], this.origin[1], this.origin[2], this.lastPlayedAudioId); + this.lastOrigin.set(this.origin); + } + play() { + if (this.lastPlayedAudioId) + this.audio.stop(this.lastPlayedAudioId); + this.lastPlayedAudioId = this.audio.play(); + if (this.spatial) + this.updatePosition(); + } + stop() { + if (!this.lastPlayedAudioId) + return; + this.audio.stop(this.lastPlayedAudioId); + this.lastPlayedAudioId = null; + } + onDeactivate() { + this.stop(); + } +}; +__publicField(HowlerAudioSource, "TypeName", "howler-audio-source"); +__publicField(HowlerAudioSource, "Properties", { + /** Volume */ + volume: { type: Type.Float, default: 1 }, + /** Whether audio should be spatialized/positional */ + spatial: { type: Type.Bool, default: true }, + /** Whether to loop the sound */ + loop: { type: Type.Bool, default: false }, + /** Whether to start playing automatically */ + autoplay: { type: Type.Bool, default: false }, + /** URL to a sound file to play */ + src: { type: Type.String, default: "" } +}); + +// node_modules/@wonderlandengine/components/dist/utils/utils.js +function setFirstMaterialTexture(mat, texture, customTextureProperty) { + if (customTextureProperty !== "auto") { + mat[customTextureProperty] = texture; + return true; + } + const shader = mat.shader; + if (shader === "Flat Opaque Textured") { + mat.flatTexture = texture; + return true; + } else if (shader === "Phong Opaque Textured" || shader === "Foliage" || shader === "Phong Normalmapped" || shader === "Phong Lightmapped") { + mat.diffuseTexture = texture; + return true; + } else if (shader === "Particle") { + mat.mainTexture = texture; + return true; + } else if (shader === "DistanceFieldVector") { + mat.vectorTexture = texture; + return true; + } else if (shader === "Background" || shader === "Sky") { + mat.texture = texture; + return true; + } else if (shader === "Physical Opaque Textured") { + mat.albedoTexture = texture; + return true; + } + return false; +} + +// node_modules/@wonderlandengine/components/dist/image-texture.js +var ImageTexture = class extends Component3 { + start() { + if (!this.material) { + throw Error("image-texture: material property not set"); + } + this.engine.textures.load(this.url, "anonymous").then((texture) => { + const mat = this.material; + if (!setFirstMaterialTexture(mat, texture, this.textureProperty)) { + console.error("Shader", mat.shader, "not supported by image-texture"); + } + }).catch(console.err); + } +}; +__publicField(ImageTexture, "TypeName", "image-texture"); +__publicField(ImageTexture, "Properties", { + /** URL to download the image from */ + url: Property.string(), + /** Material to apply the video texture to */ + material: Property.material(), + /** Name of the texture property to set */ + textureProperty: Property.string("auto") +}); + +// node_modules/@wonderlandengine/components/dist/mouse-look.js +var MouseLookComponent = class extends Component3 { + init() { + this.currentRotationY = 0; + this.currentRotationX = 0; + this.origin = new Float32Array(3); + this.parentOrigin = new Float32Array(3); + this.rotationX = 0; + this.rotationY = 0; + } + start() { + document.addEventListener("mousemove", (e) => { + if (this.active && (this.mouseDown || !this.requireMouseDown)) { + this.rotationY = -this.sensitity * e.movementX / 100; + this.rotationX = -this.sensitity * e.movementY / 100; + this.currentRotationX += this.rotationX; + this.currentRotationY += this.rotationY; + this.currentRotationX = Math.min(1.507, this.currentRotationX); + this.currentRotationX = Math.max(-1.507, this.currentRotationX); + this.object.getTranslationWorld(this.origin); + const parent = this.object.parent; + if (parent !== null) { + parent.getTranslationWorld(this.parentOrigin); + vec3_exports.sub(this.origin, this.origin, this.parentOrigin); + } + this.object.resetTranslationRotation(); + this.object.rotateAxisAngleRad([1, 0, 0], this.currentRotationX); + this.object.rotateAxisAngleRad([0, 1, 0], this.currentRotationY); + this.object.translate(this.origin); + } + }); + const canvas2 = this.engine.canvas; + if (this.pointerLockOnClick) { + canvas2.addEventListener("mousedown", () => { + canvas2.requestPointerLock = canvas2.requestPointerLock || canvas2.mozRequestPointerLock || canvas2.webkitRequestPointerLock; + canvas2.requestPointerLock(); + }); + } + if (this.requireMouseDown) { + if (this.mouseButtonIndex == 2) { + canvas2.addEventListener("contextmenu", (e) => { + e.preventDefault(); + }, false); + } + canvas2.addEventListener("mousedown", (e) => { + if (e.button == this.mouseButtonIndex) { + this.mouseDown = true; + document.body.style.cursor = "grabbing"; + if (e.button == 1) { + e.preventDefault(); + return false; + } + } + }); + canvas2.addEventListener("mouseup", (e) => { + if (e.button == this.mouseButtonIndex) { + this.mouseDown = false; + document.body.style.cursor = "initial"; + } + }); + } + } +}; +__publicField(MouseLookComponent, "TypeName", "mouse-look"); +__publicField(MouseLookComponent, "Properties", { + /** Mouse look sensitivity */ + sensitity: { type: Type.Float, default: 0.25 }, + /** Require a mouse button to be pressed to control view. + * Otherwise view will allways follow mouse movement */ + requireMouseDown: { type: Type.Bool, default: true }, + /** If "moveOnClick" is enabled, mouse button which should + * be held down to control view */ + mouseButtonIndex: { type: Type.Int }, + /** Enables pointer lock on "mousedown" event on canvas */ + pointerLockOnClick: { type: Type.Bool, default: false } +}); + +// node_modules/@wonderlandengine/components/dist/player-height.js +var PlayerHeight = class extends Component3 { + start() { + this.object.resetTranslationRotation(); + this.object.translate([0, this.height, 0]); + this.onSessionStartCallback = this.onXRSessionStart.bind(this); + this.onSessionEndCallback = this.onXRSessionEnd.bind(this); + } + onActivate() { + this.engine.onXRSessionStart.add(this.onSessionStartCallback); + this.engine.onXRSessionEnd.add(this.onSessionEndCallback); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this.onSessionStartCallback); + this.engine.onXRSessionEnd.remove(this.onSessionEndCallback); + } + onXRSessionStart() { + if (!["local", "viewer"].includes(this.engine.xr.currentReferenceSpace)) { + this.object.resetTranslationRotation(); + } + } + onXRSessionEnd() { + if (!["local", "viewer"].includes(this.engine.xr.currentReferenceSpace)) { + this.object.resetTranslationRotation(); + this.object.translate([0, this.height, 0]); + } + } +}; +__publicField(PlayerHeight, "TypeName", "player-height"); +__publicField(PlayerHeight, "Properties", { + height: { type: Type.Float, default: 1.75 } +}); + +// node_modules/@wonderlandengine/components/dist/target-framerate.js +var TargetFramerate = class extends Component3 { + start() { + this.onSessionStartCallback = this.setTargetFramerate.bind(this); + } + onActivate() { + this.engine.onXRSessionStart.add(this.onSessionStartCallback); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this.onSessionStartCallback); + } + setTargetFramerate(s) { + if (s.supportedFrameRates && s.updateTargetFrameRate) { + const a = this.engine.xr.session.supportedFrameRates; + a.sort((a2, b) => Math.abs(a2 - this.framerate) - Math.abs(b - this.framerate)); + this.engine.xr.session.updateTargetFrameRate(a[0]); + } + } +}; +__publicField(TargetFramerate, "TypeName", "target-framerate"); +__publicField(TargetFramerate, "Properties", { + framerate: { type: Type.Float, default: 90 } +}); + +// node_modules/@wonderlandengine/components/dist/teleport.js +var TeleportComponent = class extends Component3 { + init() { + this._prevThumbstickAxis = new Float32Array(2); + this._tempVec = new Float32Array(3); + this._tempVec0 = new Float32Array(3); + this._currentIndicatorRotation = 0; + this.input = this.object.getComponent("input"); + if (!this.input) { + console.error(this.object.name, "generic-teleport-component.js: input component is required on the object"); + return; + } + if (!this.teleportIndicatorMeshObject) { + console.error(this.object.name, "generic-teleport-component.js: Teleport indicator mesh is missing"); + return; + } + if (!this.camRoot) { + console.error(this.object.name, "generic-teleport-component.js: camRoot not set"); + return; + } + this.isIndicating = false; + this.indicatorHidden = true; + this.hitSpot = new Float32Array(3); + this._hasHit = false; + this._extraRotation = 0; + this._currentStickAxes = new Float32Array(2); + } + start() { + if (this.cam) { + this.isMouseIndicating = false; + canvas.addEventListener("mousedown", this.onMouseDown.bind(this)); + canvas.addEventListener("mouseup", this.onMouseUp.bind(this)); + } + if (this.handedness == 0) { + const inputComp = this.object.getComponent("input"); + if (!inputComp) { + console.warn("teleport component on object", this.object.name, 'was configured with handedness "input component", but object has no input component.'); + } else { + this.handedness = inputComp.handedness; + this.input = inputComp; + } + } else { + this.handedness = ["left", "right"][this.handedness - 1]; + } + this.onSessionStartCallback = this.setupVREvents.bind(this); + this.teleportIndicatorMeshObject.active = false; + } + onActivate() { + this.engine.onXRSessionStart.add(this.onSessionStartCallback); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this.onSessionStartCallback); + } + /* Get current camera Y rotation */ + _getCamRotation() { + this.eyeLeft.getForward(this._tempVec); + this._tempVec[1] = 0; + vec3_exports.normalize(this._tempVec, this._tempVec); + return Math.atan2(this._tempVec[0], this._tempVec[2]); + } + update() { + let inputLength = 0; + if (this.gamepad && this.gamepad.axes) { + this._currentStickAxes[0] = this.gamepad.axes[2]; + this._currentStickAxes[1] = this.gamepad.axes[3]; + inputLength = Math.abs(this._currentStickAxes[0]) + Math.abs(this._currentStickAxes[1]); + } + if (!this.isIndicating && this._prevThumbstickAxis[1] >= this.thumbstickActivationThreshhold && this._currentStickAxes[1] < this.thumbstickActivationThreshhold) { + this.isIndicating = true; + } else if (this.isIndicating && inputLength < this.thumbstickDeactivationThreshhold) { + this.isIndicating = false; + this.teleportIndicatorMeshObject.active = false; + if (this._hasHit) { + this._teleportPlayer(this.hitSpot, this._extraRotation); + } + } + if (this.isIndicating && this.teleportIndicatorMeshObject && this.input) { + const origin = this._tempVec0; + this.object.getPositionWorld(origin); + const direction2 = this.object.getForwardWorld(this._tempVec); + let rayHit = this.rayHit = this.rayCastMode == 0 ? this.engine.scene.rayCast(origin, direction2, 1 << this.floorGroup) : this.engine.physics.rayCast(origin, direction2, 1 << this.floorGroup, this.maxDistance); + if (rayHit.hitCount > 0) { + this.indicatorHidden = false; + this._extraRotation = Math.PI + Math.atan2(this._currentStickAxes[0], this._currentStickAxes[1]); + this._currentIndicatorRotation = this._getCamRotation() + (this._extraRotation - Math.PI); + this.teleportIndicatorMeshObject.resetPositionRotation(); + this.teleportIndicatorMeshObject.rotateAxisAngleRad([0, 1, 0], this._currentIndicatorRotation); + this.teleportIndicatorMeshObject.translate(rayHit.locations[0]); + this.teleportIndicatorMeshObject.translate([ + 0, + this.indicatorYOffset, + 0 + ]); + this.teleportIndicatorMeshObject.active = true; + this.hitSpot.set(rayHit.locations[0]); + this._hasHit = true; + } else { + if (!this.indicatorHidden) { + this.teleportIndicatorMeshObject.active = false; + this.indicatorHidden = true; + } + this._hasHit = false; + } + } else if (this.teleportIndicatorMeshObject && this.isMouseIndicating) { + this.onMousePressed(); + } + this._prevThumbstickAxis.set(this._currentStickAxes); + } + setupVREvents(s) { + this.session = s; + s.addEventListener("end", function() { + this.gamepad = null; + this.session = null; + }.bind(this)); + if (s.inputSources && s.inputSources.length) { + for (let i = 0; i < s.inputSources.length; i++) { + let inputSource = s.inputSources[i]; + if (inputSource.handedness == this.handedness) { + this.gamepad = inputSource.gamepad; + } + } + } + s.addEventListener("inputsourceschange", function(e) { + if (e.added && e.added.length) { + for (let i = 0; i < e.added.length; i++) { + let inputSource = e.added[i]; + if (inputSource.handedness == this.handedness) { + this.gamepad = inputSource.gamepad; + } + } + } + }.bind(this)); + } + onMouseDown() { + this.isMouseIndicating = true; + } + onMouseUp() { + this.isMouseIndicating = false; + this.teleportIndicatorMeshObject.active = false; + if (this._hasHit) { + this._teleportPlayer(this.hitSpot, 0); + } + } + onMousePressed() { + let origin = [0, 0, 0]; + this.cam.getPositionWorld(origin); + const direction2 = this.cam.getForward(this._tempVec); + let rayHit = this.rayHit = this.rayCastMode == 0 ? this.engine.scene.rayCast(origin, direction2, 1 << this.floorGroup) : this.engine.physics.rayCast(origin, direction2, 1 << this.floorGroup, this.maxDistance); + if (rayHit.hitCount > 0) { + this.indicatorHidden = false; + direction2[1] = 0; + vec3_exports.normalize(direction2, direction2); + this._currentIndicatorRotation = -Math.sign(direction2[2]) * Math.acos(direction2[0]) - Math.PI * 0.5; + this.teleportIndicatorMeshObject.resetPositionRotation(); + this.teleportIndicatorMeshObject.rotateAxisAngleRad([0, 1, 0], this._currentIndicatorRotation); + this.teleportIndicatorMeshObject.translate(rayHit.locations[0]); + this.teleportIndicatorMeshObject.active = true; + this.hitSpot = rayHit.locations[0]; + this._hasHit = true; + } else { + if (!this.indicatorHidden) { + this.teleportIndicatorMeshObject.active = false; + this.indicatorHidden = true; + } + this._hasHit = false; + } + } + _teleportPlayer(newPosition, rotationToAdd) { + this.camRoot.rotateAxisAngleRad([0, 1, 0], rotationToAdd); + const p = this._tempVec; + const p1 = this._tempVec0; + if (this.session) { + this.eyeLeft.getPositionWorld(p); + this.eyeRight.getPositionWorld(p1); + vec3_exports.add(p, p, p1); + vec3_exports.scale(p, p, 0.5); + } else { + this.cam.getPositionWorld(p); + } + this.camRoot.getPositionWorld(p1); + vec3_exports.sub(p, p1, p); + p[0] += newPosition[0]; + p[1] = newPosition[1]; + p[2] += newPosition[2]; + this.camRoot.setPositionWorld(p); + } +}; +__publicField(TeleportComponent, "TypeName", "teleport"); +__publicField(TeleportComponent, "Properties", { + /** Object that will be placed as indiciation forwhere the player will teleport to. */ + teleportIndicatorMeshObject: { type: Type.Object }, + /** Root of the player, the object that will be positioned on teleportation. */ + camRoot: { type: Type.Object }, + /** Non-vr camera for use outside of VR */ + cam: { type: Type.Object }, + /** Left eye for use in VR*/ + eyeLeft: { type: Type.Object }, + /** Right eye for use in VR*/ + eyeRight: { type: Type.Object }, + /** Handedness for VR cursors to accept trigger events only from respective controller. */ + handedness: { + type: Type.Enum, + values: ["input component", "left", "right", "none"], + default: "input component" + }, + /** Collision group of valid "floor" objects that can be teleported on */ + floorGroup: { type: Type.Int, default: 1 }, + /** How far the thumbstick needs to be pushed to have the teleport target indicator show up */ + thumbstickActivationThreshhold: { type: Type.Float, default: -0.7 }, + /** How far the thumbstick needs to be released to execute the teleport */ + thumbstickDeactivationThreshhold: { type: Type.Float, default: 0.3 }, + /** Offset to apply to the indicator object, e.g. to avoid it from Z-fighting with the floor */ + indicatorYOffset: { type: Type.Float, default: 0.01 }, + /** Mode for raycasting, whether to use PhysX or simple collision components */ + rayCastMode: { + type: Type.Enum, + values: ["collision", "physx"], + default: "collision" + }, + /** Max distance for PhysX raycast */ + maxDistance: { type: Type.Float, default: 100 } +}); + +// node_modules/@wonderlandengine/components/dist/trail.js +var direction = vec3_exports.create(); +var offset = vec3_exports.create(); +var normal = vec3_exports.create(); +var Trail = class extends Component3 { + init() { + this.points = new Array(this.segments + 1); + for (let i = 0; i < this.points.length; ++i) { + this.points[i] = vec3_exports.create(); + } + this.currentPointOffset = 0; + this.up = [0, 1, 0]; + this.timeTillNext = this.interval; + } + start() { + this.trailContainer = this.engine.scene.addObject(); + this.meshComp = this.trailContainer.addComponent("mesh"); + this.meshComp.material = this.material; + const vertexCount = 2 * this.points.length; + this.indexData = new Uint32Array(6 * this.segments); + for (let i = 0, v = 0; i < vertexCount - 2; i += 2, v += 6) { + this.indexData.subarray(v, v + 6).set([i + 1, i + 0, i + 2, i + 2, i + 3, i + 1]); + } + this.mesh = new Mesh(this.engine, { + vertexCount, + indexData: this.indexData, + indexType: MeshIndexType.UnsignedInt + }); + this.meshComp.mesh = this.mesh; + } + updateVertices() { + const positions = this.mesh.attribute(MeshAttribute.Position); + const texCoords = this.mesh.attribute(MeshAttribute.TextureCoordinate); + const normals = this.mesh.attribute(MeshAttribute.Normal); + vec3_exports.set(direction, 0, 0, 0); + for (let i = 0; i < this.points.length; ++i) { + const curr = this.points[(this.currentPointIndex + i + 1) % this.points.length]; + const next = this.points[(this.currentPointIndex + i + 2) % this.points.length]; + if (i !== this.points.length - 1) { + vec3_exports.sub(direction, next, curr); + } + vec3_exports.cross(offset, this.up, direction); + vec3_exports.normalize(offset, offset); + const timeFraction = 1 - this.timeTillNext / this.interval; + const fraction = (i - timeFraction) / this.segments; + vec3_exports.scale(offset, offset, (this.taper ? fraction : 1) * this.width / 2); + positions.set(i * 2, [ + curr[0] - offset[0], + curr[1] - offset[1], + curr[2] - offset[2] + ]); + positions.set(i * 2 + 1, [ + curr[0] + offset[0], + curr[1] + offset[1], + curr[2] + offset[2] + ]); + if (normals) { + vec3_exports.cross(normal, direction, offset); + vec3_exports.normalize(normal, normal); + normals.set(i * 2, normal); + normals.set(i * 2 + 1, normal); + } + if (texCoords) { + texCoords.set(i * 2, [0, fraction]); + texCoords.set(i * 2 + 1, [1, fraction]); + } + } + this.mesh.update(); + } + resetTrail() { + this.object.getTranslationWorld(this.points[0]); + for (let i = 1; i < this.points.length; ++i) { + vec3_exports.copy(this.points[i], this.points[0]); + } + this.currentPointIndex = 0; + this.timeTillNext = this.interval; + } + update(dt) { + this.timeTillNext -= dt; + if (dt > this.resetThreshold) { + this.resetTrail(); + } + if (this.timeTillNext < 0) { + this.currentPointIndex = (this.currentPointIndex + 1) % this.points.length; + this.timeTillNext = this.timeTillNext % this.interval + this.interval; + } + this.object.getTranslationWorld(this.points[this.currentPointIndex]); + this.updateVertices(); + } + onActivate() { + this.resetTrail(); + } + onDestroy() { + this.trailContainer.destroy(); + this.mesh.destroy(); + } +}; +__publicField(Trail, "TypeName", "trail"); +__publicField(Trail, "Properties", { + /** The material to apply to the trail mesh */ + material: { type: Type.Material }, + /** The number of segments in the trail mesh */ + segments: { type: Type.Int, default: 50 }, + /** The time interval before recording a new point */ + interval: { type: Type.Float, default: 0.1 }, + /** The width of the trail (in world space) */ + width: { type: Type.Float, default: 1 }, + /** Whether or not the trail should taper off */ + taper: { type: Type.Bool, default: true }, + /** + * The maximum delta time in seconds, above which the trail resets. + * This prevents the trail from jumping around when updates happen + * infrequently (e.g. when the tab doesn't have focus). + */ + resetThreshold: { type: Type.Float, default: 0.5 } +}); + +// node_modules/@wonderlandengine/components/dist/two-joint-ik-solver.js +Math.clamp = function(v, a, b) { + return Math.max(a, Math.min(v, b)); +}; +var twoJointIK = function() { + let ta = new Float32Array(3); + let ca = new Float32Array(3); + let ba = new Float32Array(3); + let ab = new Float32Array(3); + let cb = new Float32Array(3); + let axis0 = new Float32Array(3); + let axis1 = new Float32Array(3); + let temp = new Float32Array(4); + let r0 = new Float32Array(4); + let r1 = new Float32Array(4); + let r2 = new Float32Array(4); + return function(a_lr, b_lr, a, b, c, t, eps, a_gr, b_gr, helper) { + vec3_exports.sub(ba, b, a); + const lab = vec3_exports.length(ba); + vec3_exports.sub(ta, b, c); + const lcb = vec3_exports.length(ta); + vec3_exports.sub(ta, t, a); + const lat = Math.clamp(vec3_exports.length(ta), eps, lab + lcb - eps); + vec3_exports.sub(ca, c, a); + vec3_exports.sub(ab, a, b); + vec3_exports.sub(cb, c, b); + vec3_exports.normalize(ca, ca); + vec3_exports.normalize(ba, ba); + vec3_exports.normalize(ab, ab); + vec3_exports.normalize(cb, cb); + vec3_exports.normalize(ta, ta); + const ac_ab_0 = Math.acos(Math.clamp(vec3_exports.dot(ca, ba), -1, 1)); + const ba_bc_0 = Math.acos(Math.clamp(vec3_exports.dot(ab, cb), -1, 1)); + const ac_at_0 = Math.acos(Math.clamp(vec3_exports.dot(ca, ta), -1, 1)); + const ac_ab_1 = Math.acos(Math.clamp((lcb * lcb - lab * lab - lat * lat) / (-2 * lab * lat), -1, 1)); + const ba_bc_1 = Math.acos(Math.clamp((lat * lat - lab * lab - lcb * lcb) / (-2 * lab * lcb), -1, 1)); + vec3_exports.sub(ca, c, a); + vec3_exports.sub(ba, b, a); + vec3_exports.sub(ta, t, a); + vec3_exports.cross(axis0, ca, ba); + vec3_exports.cross(axis1, ca, ta); + if (helper) { + vec3_exports.sub(ba, helper, b); + vec3_exports.transformQuat(ba, [0, 0, -1], b_gr); + } else { + vec3_exports.sub(ba, b, a); + } + const l = vec3_exports.length(axis0); + if (l == 0) { + axis0.set([1, 0, 0]); + } else { + vec3_exports.scale(axis0, axis0, 1 / l); + } + vec3_exports.normalize(axis1, axis1); + quat_exports.conjugate(a_gr, a_gr); + quat_exports.setAxisAngle(r0, vec3_exports.transformQuat(temp, axis0, a_gr), ac_ab_1 - ac_ab_0); + quat_exports.setAxisAngle(r2, vec3_exports.transformQuat(temp, axis1, a_gr), ac_at_0); + quat_exports.mul(a_lr, a_lr, quat_exports.mul(temp, r0, r2)); + quat_exports.normalize(a_lr, a_lr); + quat_exports.conjugate(b_gr, b_gr); + quat_exports.setAxisAngle(r1, vec3_exports.transformQuat(temp, axis0, b_gr), ba_bc_1 - ba_bc_0); + quat_exports.mul(b_lr, b_lr, r1); + quat_exports.normalize(b_lr, b_lr); + }; +}(); +var TwoJointIkSolver = class extends Component3 { + init() { + this.pos = new Float32Array(3 * 7); + this.p = [ + this.pos.subarray(0, 3), + this.pos.subarray(3, 6), + this.pos.subarray(6, 9), + this.pos.subarray(9, 12), + this.pos.subarray(12, 15), + this.pos.subarray(15, 18), + this.pos.subarray(18, 21) + ]; + } + update() { + const p = this.p; + this.root.getTranslationWorld(p[0]); + this.middle.getTranslationWorld(p[1]); + this.end.getTranslationWorld(p[2]); + this.target.getTranslationWorld(p[3]); + const tla = p[4]; + const tlb = p[5]; + this.root.getTranslationLocal(tla); + this.middle.getTranslationLocal(tlb); + if (this.helper) + this.helper.getTranslationWorld(p[6]); + twoJointIK(this.root.transformLocal, this.middle.transformLocal, p[0], p[1], p[2], p[3], 0.01, this.root.transformWorld.subarray(0, 4), this.middle.transformWorld.subarray(0, 4), this.helper ? p[6] : null); + this.root.setTranslationLocal(tla); + this.middle.setTranslationLocal(tlb); + this.root.setDirty(); + this.middle.setDirty(); + } +}; +__publicField(TwoJointIkSolver, "TypeName", "two-joint-ik-solver"); +__publicField(TwoJointIkSolver, "Properties", { + /** Root bone, never moves */ + root: { type: Type.Object }, + /** Bone attached to the root */ + middle: { type: Type.Object }, + /** Bone attached to the middle */ + end: { type: Type.Object }, + /** Target the joins should reach for */ + target: { type: Type.Object }, + /** Helper object to use to determine joint rotation axis */ + helper: { type: Type.Object } +}); + +// node_modules/@wonderlandengine/components/dist/video-texture.js +var VideoTexture = class extends Component3 { + init() { + if (!this.material) { + throw Error("video-texture: material property not set"); + } + this.loaded = false; + this.frameUpdateRequested = true; + } + start() { + this.video = document.createElement("video"); + this.video.src = this.url; + this.video.crossOrigin = "anonymous"; + this.video.playsInline = true; + this.video.loop = this.loop; + this.video.muted = this.muted; + this.video.addEventListener("playing", () => { + this.loaded = true; + }); + if (this.autoplay) { + const playAfterUserGesture = () => { + this.video.play(); + window.removeEventListener("click", playAfterUserGesture); + window.removeEventListener("touchstart", playAfterUserGesture); + }; + window.addEventListener("click", playAfterUserGesture); + window.addEventListener("touchstart", playAfterUserGesture); + } + } + applyTexture() { + const mat = this.material; + const shader = mat.shader; + const texture = this.texture = new Texture(this.engine, this.video); + if (!setFirstMaterialTexture(mat, texture, this.textureProperty)) { + console.error("Shader", shader, "not supported by video-texture"); + } + if ("requestVideoFrameCallback" in this.video) { + this.video.requestVideoFrameCallback(this.updateVideo.bind(this)); + } else { + this.video.addEventListener("timeupdate", () => { + this.frameUpdateRequested = true; + }); + } + } + update(dt) { + if (this.loaded && this.frameUpdateRequested) { + if (this.texture) { + this.texture.update(); + } else { + this.applyTexture(); + } + this.frameUpdateRequested = false; + } + } + updateVideo() { + this.frameUpdateRequested = true; + this.video.requestVideoFrameCallback(this.updateVideo.bind(this)); + } +}; +__publicField(VideoTexture, "TypeName", "video-texture"); +__publicField(VideoTexture, "Properties", { + /** URL to download video from */ + url: Property.string(), + /** Material to apply the video texture to */ + material: Property.material(), + /** Whether to loop the video */ + loop: Property.bool(true), + /** Whether to automatically start playing the video */ + autoplay: Property.bool(true), + /** Whether to mute sound */ + muted: Property.bool(true), + /** Name of the texture property to set */ + textureProperty: Property.string("auto") +}); + +// node_modules/@wonderlandengine/components/dist/vr-mode-active-switch.js +var VrModeActiveSwitch = class extends Component3 { + start() { + this.components = []; + this.getComponents(this.object); + this.onXRSessionEnd(); + this.onSessionStartCallback = this.onXRSessionStart.bind(this); + this.onSessionEndCallback = this.onXRSessionEnd.bind(this); + } + onActivate() { + this.engine.onXRSessionStart.add(this.onSessionStartCallback); + this.engine.onXRSessionEnd.add(this.onSessionEndCallback); + } + onDeactivate() { + this.engine.onXRSessionStart.remove(this.onSessionStartCallback); + this.engine.onXRSessionEnd.remove(this.onSessionEndCallback); + } + getComponents(obj) { + const comps = obj.getComponents().filter((c) => c.type !== "vr-mode-active-switch"); + this.components = this.components.concat(comps); + if (this.affectChildren) { + let children = obj.children; + for (let i = 0; i < children.length; ++i) { + this.getComponents(children[i]); + } + } + } + setComponentsActive(active) { + const comps = this.components; + for (let i = 0; i < comps.length; ++i) { + comps[i].active = active; + } + } + onXRSessionStart() { + this.setComponentsActive(this.activateComponents == 0); + } + onXRSessionEnd() { + this.setComponentsActive(this.activateComponents != 0); + } +}; +__publicField(VrModeActiveSwitch, "TypeName", "vr-mode-active-switch"); +__publicField(VrModeActiveSwitch, "Properties", { + /** When components should be active: In VR or when not in VR */ + activateComponents: { + type: Type.Enum, + values: ["in VR", "in non-VR"], + default: "in VR" + }, + /** Whether child object's components should be affected */ + affectChildren: { type: Type.Bool, default: true } +}); + +// node_modules/@wonderlandengine/components/dist/plane-detection.js +var import_earcut = __toESM(require_earcut(), 1); +var __decorate5 = function(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if (d = decorators[i]) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var tempVec32 = new Float32Array(3); +function extentsFromContour(out, points) { + if (points.length == 0) + return out; + let absMaxX = Math.abs(points[0].x); + let absMaxZ = Math.abs(points[0].z); + for (let i = 1; i < points.length; ++i) { + absMaxX = Math.max(absMaxX, Math.abs(points[i].x)); + absMaxZ = Math.max(absMaxZ, Math.abs(points[i].z)); + } + out[0] = absMaxX; + out[1] = 0; + out[2] = absMaxZ; +} +function planeMeshFromContour(engine2, points, meshToUpdate = null) { + const vertexCount = points.length; + const vertices = new Float32Array(vertexCount * 2); + for (let i = 0, d = 0; i < vertexCount; ++i, d += 2) { + vertices[d] = points[i].x; + vertices[d + 1] = points[i].z; + } + const triangles = (0, import_earcut.default)(vertices); + const mesh = meshToUpdate || new Mesh(engine2, { + vertexCount, + /* Assumption here that we will never have more than 256 points + * in the detected plane meshes! */ + indexType: MeshIndexType.UnsignedByte, + indexData: triangles + }); + if (mesh.vertexCount !== vertexCount) { + console.warn("vertexCount of meshToUpdate did not match required vertexCount"); + return mesh; + } + const positions = mesh.attribute(MeshAttribute.Position); + const textureCoords = mesh.attribute(MeshAttribute.TextureCoordinate); + const normals = mesh.attribute(MeshAttribute.Normal); + tempVec32[1] = 0; + for (let i = 0, s = 0; i < vertexCount; ++i, s += 2) { + tempVec32[0] = vertices[s]; + tempVec32[2] = vertices[s + 1]; + positions.set(i, tempVec32); + } + textureCoords?.set(0, vertices); + if (normals) { + tempVec32[0] = 0; + tempVec32[1] = 1; + tempVec32[2] = 0; + for (let i = 0; i < vertexCount; ++i) { + normals.set(i, tempVec32); + } + } + if (meshToUpdate) + mesh.update(); + return mesh; +} +var _planeLost, planeLost_fn, _planeFound, planeFound_fn, _planeUpdate, planeUpdate_fn, _planeUpdatePose, planeUpdatePose_fn; +var PlaneDetection = class extends Component3 { + constructor() { + super(...arguments); + __privateAdd(this, _planeLost); + __privateAdd(this, _planeFound); + __privateAdd(this, _planeUpdate); + __privateAdd(this, _planeUpdatePose); + /** + * Material to assign to created plane meshes or `null` if meshes should not be created. + */ + __publicField(this, "planeMaterial", null); + /** + * Collision mask to assign to newly created collision components or a negative value if + * collision components should not be created. + */ + __publicField(this, "collisionMask", -1); + /** Map of all planes and their last updated timestamps */ + __publicField(this, "planes", /* @__PURE__ */ new Map()); + /** Objects generated for each XRPlane */ + __publicField(this, "planeObjects", /* @__PURE__ */ new Map()); + /** Called when a plane starts tracking */ + __publicField(this, "onPlaneFound", new Emitter()); + /** Called when a plane stops tracking */ + __publicField(this, "onPlaneLost", new Emitter()); + } + update() { + if (!this.engine.xr?.frame) + return; + if (this.engine.xr.frame.detectedPlanes === void 0) { + console.error("plane-detection: WebXR feature not available."); + this.active = false; + return; + } + const detectedPlanes = this.engine.xr.frame.detectedPlanes; + for (const [plane, _] of this.planes) { + if (!detectedPlanes.has(plane)) { + __privateMethod(this, _planeLost, planeLost_fn).call(this, plane); + } + } + detectedPlanes.forEach((plane) => { + if (this.planes.has(plane)) { + if (plane.lastChangedTime > this.planes.get(plane)) { + __privateMethod(this, _planeUpdate, planeUpdate_fn).call(this, plane); + } + } else { + __privateMethod(this, _planeFound, planeFound_fn).call(this, plane); + } + __privateMethod(this, _planeUpdatePose, planeUpdatePose_fn).call(this, plane); + }); + } +}; +_planeLost = new WeakSet(); +planeLost_fn = function(plane) { + this.planes.delete(plane); + const o = this.planeObjects.get(plane); + this.onPlaneLost.notify(plane, o); + if (o.objectId > 0) + o.destroy(); +}; +_planeFound = new WeakSet(); +planeFound_fn = function(plane) { + this.planes.set(plane, plane.lastChangedTime); + const o = this.engine.scene.addObject(this.object); + this.planeObjects.set(plane, o); + if (this.planeMaterial) { + o.addComponent(MeshComponent, { + mesh: planeMeshFromContour(this.engine, plane.polygon), + material: this.planeMaterial + }); + } + if (this.collisionMask >= 0) { + extentsFromContour(tempVec32, plane.polygon); + tempVec32[1] = 0.025; + o.addComponent(CollisionComponent, { + group: this.collisionMask, + collider: Collider.Box, + extents: tempVec32 + }); + } + this.onPlaneFound.notify(plane, o); +}; +_planeUpdate = new WeakSet(); +planeUpdate_fn = function(plane) { + this.planes.set(plane, plane.lastChangedTime); + const planeMesh = this.planeObjects.get(plane).getComponent(MeshComponent); + if (!planeMesh) + return; + planeMeshFromContour(this.engine, plane.polygon, planeMesh.mesh); +}; +_planeUpdatePose = new WeakSet(); +planeUpdatePose_fn = function(plane) { + const o = this.planeObjects.get(plane); + const pose = this.engine.xr.frame.getPose(plane.planeSpace, this.engine.xr.currentReferenceSpace); + if (!pose) { + o.active = false; + return; + } + setXRRigidTransformLocal(o, pose.transform); +}; +__publicField(PlaneDetection, "TypeName", "plane-detection"); +__decorate5([ + property.material() +], PlaneDetection.prototype, "planeMaterial", void 0); +__decorate5([ + property.int() +], PlaneDetection.prototype, "collisionMask", void 0); + +// node_modules/@wonderlandengine/components/dist/vrm.js +var VRM_ROLL_AXES = { + X: [1, 0, 0], + Y: [0, 1, 0], + Z: [0, 0, 1] +}; +var VRM_AIM_AXES = { + PositiveX: [1, 0, 0], + NegativeX: [-1, 0, 0], + PositiveY: [0, 1, 0], + NegativeY: [0, -1, 0], + PositiveZ: [0, 0, 1], + NegativeZ: [0, 0, -1] +}; +var Vrm = class extends Component3 { + /** Meta information about the VRM model */ + meta = null; + /** The humanoid bones of the VRM model */ + bones = { + /* Torso */ + hips: null, + spine: null, + chest: null, + upperChest: null, + neck: null, + /* Head */ + head: null, + leftEye: null, + rightEye: null, + jaw: null, + /* Legs */ + leftUpperLeg: null, + leftLowerLeg: null, + leftFoot: null, + leftToes: null, + rightUpperLeg: null, + rightLowerLeg: null, + rightFoot: null, + rightToes: null, + /* Arms */ + leftShoulder: null, + leftUpperArm: null, + leftLowerArm: null, + leftHand: null, + rightShoulder: null, + rightUpperArm: null, + rightLowerArm: null, + rightHand: null, + /* Fingers */ + leftThumbMetacarpal: null, + leftThumbProximal: null, + leftThumbDistal: null, + leftIndexProximal: null, + leftIndexIntermediate: null, + leftIndexDistal: null, + leftMiddleProximal: null, + leftMiddleIntermediate: null, + leftMiddleDistal: null, + leftRingProximal: null, + leftRingIntermediate: null, + leftRingDistal: null, + leftLittleProximal: null, + leftLittleIntermediate: null, + leftLittleDistal: null, + rightThumbMetacarpal: null, + rightThumbProximal: null, + rightThumbDistal: null, + rightIndexProximal: null, + rightIndexIntermediate: null, + rightIndexDistal: null, + rightMiddleProximal: null, + rightMiddleIntermediate: null, + rightMiddleDistal: null, + rightRingProximal: null, + rightRingIntermediate: null, + rightRingDistal: null, + rightLittleProximal: null, + rightLittleIntermediate: null, + rightLittleDistal: null + }; + /** Rotations of the bones in the rest pose (T-pose) */ + restPose = {}; + /* All node constraints, ordered to deal with dependencies */ + _nodeConstraints = []; + /* VRMC_springBone chains */ + _springChains = []; + /* Spherical colliders for spring bones */ + _sphereColliders = []; + /* Capsule shaped colliders for spring bones */ + _capsuleColliders = []; + /* Indicates which meshes are rendered in first/third person views */ + _firstPersonAnnotations = []; + /* Contains details for (bone type) lookAt behaviour */ + _lookAt = null; + /* Whether or not the VRM component has been initialized with `initializeVrm` */ + _initialized = false; + init() { + this._tempV3 = vec3_exports.create(); + this._tempV3A = vec3_exports.create(); + this._tempV3B = vec3_exports.create(); + this._tempQuat = quat_exports.create(); + this._tempQuatA = quat_exports.create(); + this._tempQuatB = quat_exports.create(); + this._tempMat4A = mat4_exports.create(); + this._tempQuat2 = quat2_exports.create(); + this._tailToShape = vec3_exports.create(); + this._headToTail = vec3_exports.create(); + this._inertia = vec3_exports.create(); + this._stiffness = vec3_exports.create(); + this._external = vec3_exports.create(); + this._rightVector = vec3_exports.set(vec3_exports.create(), 1, 0, 0); + this._upVector = vec3_exports.set(vec3_exports.create(), 0, 1, 0); + this._forwardVector = vec3_exports.set(vec3_exports.create(), 0, 0, 1); + this._identityQuat = quat_exports.identity(quat_exports.create()); + this._rad2deg = 180 / Math.PI; + } + start() { + if (!this.src) { + console.error("vrm: src property not set"); + return; + } + this.engine.scene.append(this.src, { loadGltfExtensions: true }).then(({ root, extensions }) => { + root.children.forEach((child) => child.parent = this.object); + this._initializeVrm(extensions); + root.destroy(); + }); + } + /** + * Parses the VRM glTF extensions and initializes the vrm component. + * @param {GLTFExtensions} extensions The glTF extensions for the VRM model + */ + _initializeVrm(extensions) { + if (this._initialized) { + throw Error("VRM component has already been initialized"); + } + const VRMC_vrm = extensions.root["VRMC_vrm"]; + if (!VRMC_vrm) { + throw Error("Missing VRM extensions"); + } + if (VRMC_vrm.specVersion !== "1.0") { + throw Error(`Unsupported VRM version, only 1.0 is supported, but encountered '${VRMC_vrm.specVersion}'`); + } + this.meta = VRMC_vrm.meta; + this._parseHumanoid(VRMC_vrm.humanoid, extensions); + if (VRMC_vrm.firstPerson) { + this._parseFirstPerson(VRMC_vrm.firstPerson, extensions); + } + if (VRMC_vrm.lookAt) { + this._parseLookAt(VRMC_vrm.lookAt); + } + this._findAndParseNodeConstraints(extensions); + const springBone = extensions.root["VRMC_springBone"]; + if (springBone) { + this._parseAndInitializeSpringBones(springBone, extensions); + } + this._initialized = true; + } + _parseHumanoid(humanoid, extensions) { + for (const boneName in humanoid.humanBones) { + if (!(boneName in this.bones)) { + console.warn(`Unrecognized bone '${boneName}'`); + continue; + } + const node = humanoid.humanBones[boneName].node; + const objectId = extensions.idMapping[node]; + this.bones[boneName] = this.engine.wrapObject(objectId); + this.restPose[boneName] = quat_exports.copy(quat_exports.create(), this.bones[boneName].rotationLocal); + } + } + _parseFirstPerson(firstPerson, extensions) { + for (const meshAnnotation of firstPerson.meshAnnotations) { + const annotation = { + node: this.engine.wrapObject(extensions.idMapping[meshAnnotation.node]), + firstPerson: true, + thirdPerson: true + }; + switch (meshAnnotation.type) { + case "firstPersonOnly": + annotation.thirdPerson = false; + break; + case "thirdPersonOnly": + annotation.firstPerson = false; + break; + case "both": + break; + case "auto": + console.warn("First person mesh annotation type 'auto' is not supported, treating as 'both'!"); + break; + default: + console.error(`Invalid mesh annotation type '${meshAnnotation.type}'`); + break; + } + this._firstPersonAnnotations.push(annotation); + } + } + _parseLookAt(lookAt2) { + if (lookAt2.type !== "bone") { + console.warn(`Unsupported lookAt type '${lookAt2.type}', only 'bone' is supported`); + return; + } + const parseRangeMap = (rangeMap) => { + return { + inputMaxValue: rangeMap.inputMaxValue, + outputScale: rangeMap.outputScale + }; + }; + this._lookAt = { + offsetFromHeadBone: lookAt2.offsetFromHeadBone || [0, 0, 0], + horizontalInner: parseRangeMap(lookAt2.rangeMapHorizontalInner), + horizontalOuter: parseRangeMap(lookAt2.rangeMapHorizontalOuter), + verticalDown: parseRangeMap(lookAt2.rangeMapVerticalDown), + verticalUp: parseRangeMap(lookAt2.rangeMapVerticalUp) + }; + } + _findAndParseNodeConstraints(extensions) { + const traverse = (object) => { + const nodeExtensions = extensions.node[object.objectId]; + if (nodeExtensions && "VRMC_node_constraint" in nodeExtensions) { + const nodeConstraintExtension = nodeExtensions["VRMC_node_constraint"]; + const constraint = nodeConstraintExtension.constraint; + let type, axis; + if ("roll" in constraint) { + type = "roll"; + axis = VRM_ROLL_AXES[constraint.roll.rollAxis]; + } else if ("aim" in constraint) { + type = "aim"; + axis = VRM_AIM_AXES[constraint.aim.aimAxis]; + } else if ("rotation" in constraint) { + type = "rotation"; + } + if (type) { + const source = this.engine.wrapObject(extensions.idMapping[constraint[type].source]); + this._nodeConstraints.push({ + type, + source, + destination: object, + axis, + weight: constraint[type].weight, + /* Rest pose */ + destinationRestLocalRotation: quat_exports.copy(quat_exports.create(), object.rotationLocal), + sourceRestLocalRotation: quat_exports.copy(quat_exports.create(), source.rotationLocal), + sourceRestLocalRotationInv: quat_exports.invert(quat_exports.create(), source.rotationLocal) + }); + } else { + console.warn("Unrecognized or invalid VRMC_node_constraint, ignoring it"); + } + } + for (const child of object.children) { + traverse(child); + } + }; + traverse(this.object); + } + _parseAndInitializeSpringBones(springBone, extensions) { + const colliders = (springBone.colliders || []).map((collider, i) => { + const shapeType = "capsule" in collider.shape ? "capsule" : "sphere"; + return { + id: i, + object: this.engine.wrapObject(extensions.idMapping[collider.node]), + shape: { + isCapsule: shapeType === "capsule", + radius: collider.shape[shapeType].radius, + offset: collider.shape[shapeType].offset, + tail: collider.shape[shapeType].tail + }, + cache: { + head: vec3_exports.create(), + tail: vec3_exports.create() + } + }; + }); + this._sphereColliders = colliders.filter((c) => !c.shape.isCapsule); + this._capsuleColliders = colliders.filter((c) => c.shape.isCapsule); + const colliderGroups = (springBone.colliderGroups || []).map((group) => ({ + name: group.name, + colliders: group.colliders.map((c) => colliders[c]) + })); + for (const spring of springBone.springs) { + const joints = []; + for (const joint of spring.joints) { + const springJoint = { + hitRadius: 0, + stiffness: 1, + gravityPower: 0, + gravityDir: [0, -1, 0], + dragForce: 0.5, + node: null, + state: null + }; + Object.assign(springJoint, joint); + springJoint.node = this.engine.wrapObject(extensions.idMapping[springJoint.node]); + joints.push(springJoint); + } + const springChainColliders = (spring.colliderGroups || []).flatMap((cg) => colliderGroups[cg].colliders); + this._springChains.push({ + name: spring.name, + center: spring.center ? this.engine.wrapObject(extensions.idMapping[spring.center]) : null, + joints, + sphereColliders: springChainColliders.filter((c) => !c.shape.isCapsule), + capsuleColliders: springChainColliders.filter((c) => c.shape.isCapsule) + }); + } + for (const springChain of this._springChains) { + for (let i = 0; i < springChain.joints.length - 1; ++i) { + const springBoneJoint = springChain.joints[i]; + const childSpringBoneJoint = springChain.joints[i + 1]; + const springBonePosition = springBoneJoint.node.getTranslationWorld(vec3_exports.create()); + const childSpringBonePosition = childSpringBoneJoint.node.getTranslationWorld(vec3_exports.create()); + const boneDirection = vec3_exports.subtract(this._tempV3A, springBonePosition, childSpringBonePosition); + const state2 = { + prevTail: childSpringBonePosition, + currentTail: vec3_exports.copy(vec3_exports.create(), childSpringBonePosition), + initialLocalRotation: quat_exports.copy(quat_exports.create(), springBoneJoint.node.rotationLocal), + initialLocalTransformInvert: quat2_exports.invert(quat2_exports.create(), springBoneJoint.node.transformLocal), + boneAxis: vec3_exports.normalize(vec3_exports.create(), childSpringBoneJoint.node.getTranslationLocal(this._tempV3)), + /* Ensure bone length is at least 1cm to avoid jittery behaviour from zero-length bones */ + boneLength: Math.max(0.01, vec3_exports.length(boneDirection)), + /* Tail positions in center space, if needed */ + prevTailCenter: null, + currentTailCenter: null + }; + if (springChain.center) { + state2.prevTailCenter = springChain.center.transformPointInverseWorld(vec3_exports.create(), childSpringBonePosition); + state2.currentTailCenter = vec3_exports.copy(vec3_exports.create(), childSpringBonePosition); + } + springBoneJoint.state = state2; + } + } + } + update(dt) { + if (!this._initialized) { + return; + } + this._resolveLookAt(); + this._resolveConstraints(); + this._updateSpringBones(dt); + } + _rangeMap(rangeMap, input) { + const maxValue = rangeMap.inputMaxValue; + const outputScale = rangeMap.outputScale; + return Math.min(input, maxValue) / maxValue * outputScale; + } + _resolveLookAt() { + if (!this._lookAt || !this.lookAtTarget) { + return; + } + const lookAtSource = this.bones.head.transformPointWorld(this._tempV3A, this._lookAt.offsetFromHeadBone); + const lookAtTarget = this.lookAtTarget.getTranslationWorld(this._tempV3B); + const lookAtDirection = vec3_exports.sub(this._tempV3A, lookAtTarget, lookAtSource); + vec3_exports.normalize(lookAtDirection, lookAtDirection); + this.bones.head.parent.transformVectorInverseWorld(lookAtDirection); + const z = vec3_exports.dot(lookAtDirection, this._forwardVector); + const x = vec3_exports.dot(lookAtDirection, this._rightVector); + const yaw = Math.atan2(x, z) * this._rad2deg; + const xz = Math.sqrt(x * x + z * z); + const y = vec3_exports.dot(lookAtDirection, this._upVector); + let pitch = Math.atan2(-y, xz) * this._rad2deg; + if (pitch > 0) { + pitch = this._rangeMap(this._lookAt.verticalDown, pitch); + } else { + pitch = -this._rangeMap(this._lookAt.verticalUp, -pitch); + } + if (this.bones.leftEye) { + let yawLeft = yaw; + if (yawLeft > 0) { + yawLeft = this._rangeMap(this._lookAt.horizontalInner, yawLeft); + } else { + yawLeft = -this._rangeMap(this._lookAt.horizontalOuter, -yawLeft); + } + const eyeRotation = quat_exports.fromEuler(this._tempQuatA, pitch, yawLeft, 0); + this.bones.leftEye.rotationLocal = quat_exports.multiply(eyeRotation, this.restPose.leftEye, eyeRotation); + } + if (this.bones.rightEye) { + let yawRight = yaw; + if (yawRight > 0) { + yawRight = this._rangeMap(this._lookAt.horizontalOuter, yawRight); + } else { + yawRight = -this._rangeMap(this._lookAt.horizontalInner, -yawRight); + } + const eyeRotation = quat_exports.fromEuler(this._tempQuatA, pitch, yawRight, 0); + this.bones.rightEye.rotationLocal = quat_exports.multiply(eyeRotation, this.restPose.rightEye, eyeRotation); + } + } + _resolveConstraints() { + for (const nodeConstraint of this._nodeConstraints) { + this._resolveConstraint(nodeConstraint); + } + } + _resolveConstraint(nodeConstraint) { + const dstRestQuat = nodeConstraint.destinationRestLocalRotation; + const srcRestQuatInv = nodeConstraint.sourceRestLocalRotationInv; + const targetQuat = quat_exports.identity(this._tempQuatA); + switch (nodeConstraint.type) { + case "roll": + { + const deltaSrcQuat = quat_exports.multiply(this._tempQuatA, srcRestQuatInv, nodeConstraint.source.rotationLocal); + const deltaSrcQuatInParent = quat_exports.multiply(this._tempQuatA, nodeConstraint.sourceRestLocalRotation, deltaSrcQuat); + quat_exports.mul(deltaSrcQuatInParent, deltaSrcQuatInParent, srcRestQuatInv); + const dstRestQuatInv = quat_exports.invert(this._tempQuatB, dstRestQuat); + const deltaSrcQuatInDst = quat_exports.multiply(this._tempQuatB, dstRestQuatInv, deltaSrcQuatInParent); + quat_exports.multiply(deltaSrcQuatInDst, deltaSrcQuatInDst, dstRestQuat); + const toVec = vec3_exports.transformQuat(this._tempV3A, nodeConstraint.axis, deltaSrcQuatInDst); + const fromToQuat = quat_exports.rotationTo(this._tempQuatA, nodeConstraint.axis, toVec); + quat_exports.mul(targetQuat, dstRestQuat, quat_exports.invert(this._tempQuat, fromToQuat)); + quat_exports.mul(targetQuat, targetQuat, deltaSrcQuatInDst); + } + break; + case "aim": + { + const dstParentWorldQuat = nodeConstraint.destination.parent.rotationWorld; + const fromVec = vec3_exports.transformQuat(this._tempV3A, nodeConstraint.axis, dstRestQuat); + vec3_exports.transformQuat(fromVec, fromVec, dstParentWorldQuat); + const toVec = nodeConstraint.source.getTranslationWorld(this._tempV3B); + vec3_exports.sub(toVec, toVec, nodeConstraint.destination.getTranslationWorld(this._tempV3)); + vec3_exports.normalize(toVec, toVec); + const fromToQuat = quat_exports.rotationTo(this._tempQuatA, fromVec, toVec); + quat_exports.mul(targetQuat, quat_exports.invert(this._tempQuat, dstParentWorldQuat), fromToQuat); + quat_exports.mul(targetQuat, targetQuat, dstParentWorldQuat); + quat_exports.mul(targetQuat, targetQuat, dstRestQuat); + } + break; + case "rotation": + { + const srcDeltaQuat = quat_exports.mul(targetQuat, srcRestQuatInv, nodeConstraint.source.rotationLocal); + quat_exports.mul(targetQuat, dstRestQuat, srcDeltaQuat); + } + break; + } + quat_exports.slerp(targetQuat, dstRestQuat, targetQuat, nodeConstraint.weight); + nodeConstraint.destination.rotationLocal = targetQuat; + } + _updateSpringBones(dt) { + this._sphereColliders.forEach(({ object, shape, cache }) => { + const offset2 = vec3_exports.copy(cache.head, shape.offset); + object.transformVectorWorld(offset2); + vec3_exports.add(cache.head, object.getTranslationWorld(this._tempV3), offset2); + }); + this._capsuleColliders.forEach(({ object, shape, cache }) => { + const shapeCenter = object.getTranslationWorld(this._tempV3A); + const headOffset = vec3_exports.copy(cache.head, shape.offset); + object.transformVectorWorld(headOffset); + vec3_exports.add(cache.head, shapeCenter, headOffset); + const tailOffset = vec3_exports.copy(cache.tail, shape.tail); + object.transformVectorWorld(tailOffset); + vec3_exports.add(cache.tail, shapeCenter, tailOffset); + }); + this._springChains.forEach((springChain) => { + for (let i = 0; i < springChain.joints.length - 1; ++i) { + const joint = springChain.joints[i]; + const parentWorldRotation = joint.node.parent ? joint.node.parent.rotationWorld : this._identityQuat; + const inertia = this._inertia; + if (springChain.center) { + vec3_exports.sub(inertia, joint.state.currentTailCenter, joint.state.prevTailCenter); + springChain.center.transformVectorWorld(inertia); + } else { + vec3_exports.sub(inertia, joint.state.currentTail, joint.state.prevTail); + } + vec3_exports.scale(inertia, inertia, 1 - joint.dragForce); + const stiffness = vec3_exports.copy(this._stiffness, joint.state.boneAxis); + vec3_exports.transformQuat(stiffness, stiffness, joint.state.initialLocalRotation); + vec3_exports.transformQuat(stiffness, stiffness, parentWorldRotation); + vec3_exports.scale(stiffness, stiffness, dt * joint.stiffness); + const external = vec3_exports.scale(this._external, joint.gravityDir, dt * joint.gravityPower); + const nextTail = vec3_exports.copy(this._tempV3A, joint.state.currentTail); + vec3_exports.add(nextTail, nextTail, inertia); + vec3_exports.add(nextTail, nextTail, stiffness); + vec3_exports.add(nextTail, nextTail, external); + const worldPosition = joint.node.getTranslationWorld(this._tempV3B); + vec3_exports.sub(nextTail, nextTail, worldPosition); + vec3_exports.normalize(nextTail, nextTail); + vec3_exports.scaleAndAdd(nextTail, worldPosition, nextTail, joint.state.boneLength); + for (const { shape, cache } of springChain.sphereColliders) { + let tailToShape = this._tailToShape; + const sphereCenter = cache.head; + tailToShape = vec3_exports.sub(tailToShape, nextTail, sphereCenter); + const radius = shape.radius + joint.hitRadius; + const dist2 = vec3_exports.length(tailToShape) - radius; + if (dist2 < 0) { + vec3_exports.normalize(tailToShape, tailToShape); + vec3_exports.scaleAndAdd(nextTail, nextTail, tailToShape, -dist2); + vec3_exports.sub(nextTail, nextTail, worldPosition); + vec3_exports.normalize(nextTail, nextTail); + vec3_exports.scaleAndAdd(nextTail, worldPosition, nextTail, joint.state.boneLength); + } + } + for (const { shape, cache } of springChain.capsuleColliders) { + let tailToShape = this._tailToShape; + const head = cache.head; + const tail = cache.tail; + tailToShape = vec3_exports.sub(tailToShape, nextTail, head); + const headToTail = vec3_exports.sub(this._headToTail, tail, head); + const dot5 = vec3_exports.dot(headToTail, tailToShape); + if (vec3_exports.squaredLength(headToTail) <= dot5) { + vec3_exports.sub(tailToShape, nextTail, tail); + } else if (dot5 > 0) { + vec3_exports.scale(headToTail, headToTail, dot5 / vec3_exports.squaredLength(headToTail)); + vec3_exports.sub(tailToShape, tailToShape, headToTail); + } + const radius = shape.radius + joint.hitRadius; + const dist2 = vec3_exports.length(tailToShape) - radius; + if (dist2 < 0) { + vec3_exports.normalize(tailToShape, tailToShape); + vec3_exports.scaleAndAdd(nextTail, nextTail, tailToShape, -dist2); + vec3_exports.sub(nextTail, nextTail, worldPosition); + vec3_exports.normalize(nextTail, nextTail); + vec3_exports.scaleAndAdd(nextTail, worldPosition, nextTail, joint.state.boneLength); + } + } + vec3_exports.copy(joint.state.prevTail, joint.state.currentTail); + vec3_exports.copy(joint.state.currentTail, nextTail); + if (springChain.center) { + vec3_exports.copy(joint.state.prevTailCenter, joint.state.currentTailCenter); + vec3_exports.copy(joint.state.currentTailCenter, nextTail); + springChain.center.transformPointInverseWorld(joint.state.currentTailCenter); + } + joint.node.parent.transformPointInverseWorld(nextTail); + const nextTailDualQuat = quat2_exports.fromTranslation(this._tempQuat2, nextTail); + quat2_exports.multiply(nextTailDualQuat, joint.state.initialLocalTransformInvert, nextTailDualQuat); + quat2_exports.getTranslation(nextTail, nextTailDualQuat); + vec3_exports.normalize(nextTail, nextTail); + const jointRotation = quat_exports.rotationTo(this._tempQuatA, joint.state.boneAxis, nextTail); + joint.node.rotationLocal = quat_exports.mul(this._tempQuatA, joint.state.initialLocalRotation, jointRotation); + } + }); + } + /** + * @param {boolean} firstPerson Whether the model should render for first person or third person views + */ + set firstPerson(firstPerson) { + this._firstPersonAnnotations.forEach((annotation) => { + const visible = firstPerson == annotation.firstPerson || firstPerson != annotation.thirdPerson; + annotation.node.getComponents("mesh").forEach((mesh) => { + mesh.active = visible; + }); + }); + } +}; +__publicField(Vrm, "TypeName", "vrm"); +__publicField(Vrm, "Properties", { + /** URL to a VRM file to load */ + src: { type: Type.String }, + /** Object the VRM is looking at */ + lookAtTarget: { type: Type.Object } +}); + +// node_modules/@wonderlandengine/components/dist/wasd-controls.js +var WasdControlsComponent = class extends Component3 { + init() { + this.up = false; + this.right = false; + this.down = false; + this.left = false; + window.addEventListener("keydown", this.press.bind(this)); + window.addEventListener("keyup", this.release.bind(this)); + } + start() { + this.headObject = this.headObject || this.object; + } + update() { + let direction2 = [0, 0, 0]; + if (this.up) + direction2[2] -= 1; + if (this.down) + direction2[2] += 1; + if (this.left) + direction2[0] -= 1; + if (this.right) + direction2[0] += 1; + vec3_exports.normalize(direction2, direction2); + direction2[0] *= this.speed; + direction2[2] *= this.speed; + vec3_exports.transformQuat(direction2, direction2, this.headObject.transformWorld); + this.object.translate(direction2); + } + press(e) { + if (e.keyCode === 38 || e.keyCode === 87 || e.keyCode === 90) { + this.up = true; + } else if (e.keyCode === 39 || e.keyCode === 68) { + this.right = true; + } else if (e.keyCode === 40 || e.keyCode === 83) { + this.down = true; + } else if (e.keyCode === 37 || e.keyCode === 65 || e.keyCode === 81) { + this.left = true; + } + } + release(e) { + if (e.keyCode === 38 || e.keyCode === 87 || e.keyCode === 90) { + this.up = false; + } else if (e.keyCode === 39 || e.keyCode === 68) { + this.right = false; + } else if (e.keyCode === 40 || e.keyCode === 83) { + this.down = false; + } else if (e.keyCode === 37 || e.keyCode === 65 || e.keyCode === 81) { + this.left = false; + } + } +}; +__publicField(WasdControlsComponent, "TypeName", "wasd-controls"); +__publicField(WasdControlsComponent, "Properties", { + /** Movement speed in m/s. */ + speed: { type: Type.Float, default: 0.1 }, + /** Object of which the orientation is used to determine forward direction */ + headObject: { type: Type.Object } +}); + +// js/game.js +var state = { + shotCount: 0, + /* Global function used to update the score display */ + updateCounter: null, + updateScore: null, + gameOver: true, + score: 0, + resetButton: null, + bgMusic: null, + victoryMusic: null, + mouseSound: null, + mouseSpawner: null, + targetsSpawned: 0, + maxTargets: 20, + /* Overwritten by mouse-spawner */ + floorHeight: 0, + getPlayerLocation: null, + updateMoveDuration: null, + bulletSpawner: null, + hideLogo: null, + despawnTarget: null, + firstShot: false, + launch: null, + paused: false, + isFirstGameOver: false, + zombieCount: 0, + // Wave system properties + currentWave: 1, + zombiesKilledInWave: 0, + waveProperties: { + baseZombies: 10, + // Starting number of zombies in wave 1 + zombiesIncrease: 10, + // How many more zombies per wave + baseSpeed: 2.5, + // Starting speed in wave 1 (increased from 1.5) + speedIncreasePercent: 50 + // Speed increase percentage per wave (increased to 50%) + }, + getCurrentWave() { + return this.currentWave; + }, + getWaveZombieCount() { + return this.waveProperties.baseZombies + (this.currentWave - 1) * this.waveProperties.zombiesIncrease; + }, + getWaveSpeed() { + const speedMultiplier = 1 + (this.currentWave - 1) * this.waveProperties.speedIncreasePercent / 100; + const speed = this.waveProperties.baseSpeed * speedMultiplier; + console.log(`Wave ${this.currentWave} speed: ${speed.toFixed(2)} (multiplier: ${speedMultiplier.toFixed(2)})`); + return speed * (this.zombieCount * 1e-3 + 1); + }, + getRemainingZombies() { + const total = this.getWaveZombieCount(); + const killed = this.zombiesKilledInWave; + console.log(`Wave ${this.currentWave}: ${killed}/${total} zombies killed`); + return total - killed; + }, + updateWaveDisplay() { + const remaining = this.getRemainingZombies(); + const nextWaveZombies = this.waveProperties.baseZombies + this.currentWave * this.waveProperties.zombiesIncrease; + const speed = this.getWaveSpeed(); + this.updateScore( + `Wave ${this.currentWave} - Remaining: ${remaining} zombies - Speed: ${speed.toFixed(1)} - Next Wave: ${nextWaveZombies} zombies` + ); + }, + zombieKilled() { + this.zombiesKilledInWave++; + this.score++; + const remaining = this.getRemainingZombies(); + if (remaining <= 0) { + console.log(`Wave ${this.currentWave} complete! Starting next wave...`); + this.nextWave(); + } else { + this.updateWaveDisplay(); + } + }, + nextWave() { + this.currentWave++; + this.zombiesKilledInWave = 0; + const newZombieCount = this.getWaveZombieCount(); + if (this.mouseSpawner) { + console.log(`Setting new wave ${this.currentWave} with ${newZombieCount} zombies`); + this.mouseSpawner.maxTargets = newZombieCount; + this.mouseSpawner.reset(); + } + this.updateWaveDisplay(); + }, + loseGame() { + console.log("Game Over triggered"); + this.gameOver = true; + this.isFirstGameOver = true; + if (this.mouseSound) { + this.mouseSound.stop(); + } + if (this.mouseSpawner) { + this.mouseSpawner.reset(); + } + if (this.resetButton) { + console.log("Showing reset button"); + this.resetButton.unhide(); + this.resetButton.object.children[0].getComponent("mesh").active = true; + this.resetButton.object.children[1].getComponent("text").active = true; + this.resetButton.active = true; + } else { + console.log("Reset button not found!"); + } + this.updateScore(`Game Over! You survived ${this.currentWave} waves and eliminated ${this.score} zombies!`); + }, + restart() { + if (this.mouseSpawner) { + this.mouseSpawner.reset(); + } + if (this.isFirstGameOver) { + window.location.reload(); + } + this.zombieCount = 0; + this.victoryMusic.stop(); + this.gameOver = false; + this.shotCount = 0; + this.score = 0; + this.currentWave = 1; + this.zombiesKilledInWave = 0; + if (this.mouseSpawner) { + const initialZombies = this.getWaveZombieCount(); + console.log(`Restarting game with ${initialZombies} zombies in wave 1`); + this.mouseSpawner.maxTargets = initialZombies; + } + this.updateCounter(); + this.updateWaveDisplay(); + } +}; + +// js/bg-music.js +var BgMusic = class extends Component3 { + init() { + } +}; +__publicField(BgMusic, "TypeName", "bg-music"); +__publicField(BgMusic, "Properties", {}); + +// js/bullet-physics.js +var newDir = new Float32Array(3); +var BulletPhysics = class extends Component3 { + init() { + this.dir = new Float32Array(3); + this.position = [0, 0, 0]; + this.object.getPositionWorld(this.position); + this.correctedSpeed = this.speed / 6; + this.collision = this.object.getComponent("collision", 0); + if (!this.collision) { + console.warn( + "'bullet-physics' component on object", + this.object.name, + "requires a collision component" + ); + } + } + update(dt) { + if (isNaN(dt)) { + console.log("dt is NaN"); + return; + } + if (this.scored) + return; + this.object.getPositionWorld(this.position); + if (this.position[1] <= state.floorHeight + this.collision.extents[0]) { + this.destroyBullet(0); + return; + } + if (vec3_exports.length(this.position) > 175) { + this.destroyBullet(0); + return; + } + newDir.set(this.dir); + vec3_exports.scale(newDir, newDir, this.correctedSpeed); + vec3_exports.add(this.position, this.position, newDir); + this.object.setPositionLocal(this.position); + let overlaps = this.collision.queryOverlaps(); + for (let i = 0; i < overlaps.length; ++i) { + let t = overlaps[i].object.getComponent("score-trigger"); + if (t) { + t.onHit(() => this.destroyBullet(0)); + this.destroyBullet(0); + break; + } + } + } + destroyBullet(time) { + const meshComp = this.object.getComponent("mesh", 0); + if (meshComp) + meshComp.active = false; + if (this.collision) + this.collision.active = false; + this.object.active = false; + if (time === 0) { + this.engine.scene.removeObject(this.object); + } else { + setTimeout(() => { + this.engine.scene.removeObject(this.object); + }, time); + } + } +}; +__publicField(BulletPhysics, "TypeName", "bullet-physics"); +__publicField(BulletPhysics, "Properties", { + speed: { type: Type.Float, default: 1 } +}); + +// js/bullet-spawner.js +var tempQuat22 = new Float32Array(8); +var BulletSpawner = class extends Component3 { + static onRegister(engine2) { + engine2.registerComponent(HowlerAudioSource); + engine2.registerComponent(BulletPhysics); + } + init() { + state.launch = function(dir) { + let bullet = this.spawnBullet(); + bullet.object.setTransformLocal(this.object.getTransformWorld(tempQuat22)); + bullet.object.setDirty(); + bullet.physics.dir.set(dir); + bullet.physics.scored = false; + bullet.physics.active = true; + state.shotCount++; + state.updateCounter(); + if (!state.firstShot) { + state.hideLogo(); + state.updateMoveDuration(true); + state.firstShot = true; + } + }.bind(this); + } + start() { + this.engine.onXRSessionStart.add(this.xrSessionStart.bind(this)); + this.start = new Float32Array(2); + this.bullets = []; + this.nextIndex = 0; + this.lastShotTime = 0; + state.bulletSpawner = this.object; + this.soundClick = this.object.addComponent(HowlerAudioSource, { + src: "sfx/9mm-pistol-shoot-short-reverb-7152.mp3", + volume: 0.5 + }); + } + onTouchDown(e) { + if (e.inputSource.handedness == "right") { + let currentTime = Date.now(); + let lastShotTimeGap = Math.abs(currentTime - this.lastShotTime); + if (lastShotTimeGap > 500) { + const dir = [0, 0, 0]; + this.object.getComponent("cursor").cursorRayObject.getForward(dir); + this.pulse(e.inputSource.gamepad); + state.launch(dir); + this.lastShotTime = currentTime; + this.soundClick.play(); + } + } + } + spawnBullet() { + const obj = this.engine.scene.addObject(); + obj.scaleLocal([0.05, 0.05, 0.05]); + obj.addComponent("mesh", { + mesh: this.bulletMesh, + material: this.bulletMaterial + }); + obj.addComponent("collision", { + shape: WL.Collider.Sphere, + extents: [0.05, 0, 0], + group: 1 << 0 + }); + const physics = obj.addComponent(BulletPhysics, { + speed: this.bulletSpeed + }); + physics.active = true; + return { + object: obj, + physics + }; + } + //vibrates controller for haptic feedback + pulse(gamepad) { + let actuator; + if (!gamepad || !gamepad.hapticActuators) { + return; + } + actuator = gamepad.hapticActuators[0]; + if (!actuator) + return; + actuator.pulse(1, 100); + } + onActivate() { + if (!this.engine.xr) + return; + this.engine.xr.session.addEventListener( + "selectstart", + this.onTouchDown.bind(this) + ); + } + xrSessionStart(session) { + if (!this.active) + return; + session.addEventListener("selectstart", this.onTouchDown.bind(this)); + } +}; +__publicField(BulletSpawner, "TypeName", "bullet-spawner"); +__publicField(BulletSpawner, "Properties", { + bulletMesh: { type: Type.Mesh }, + bulletMaterial: { type: Type.Material }, + bulletSpeed: { type: Type.Float, default: 1 } +}); + +// js/confetti-particles.js +var ConfettiParticles = class extends Component3 { + init() { + this.time = 0; + this.count = 0; + } + start() { + this.objects = []; + this.velocities = []; + this.spins = []; + this.time = 2; + this.origin = new Float32Array(3); + this.activeCount = 0; + for (let i = 0; i < this.maxParticles; ++i) { + this.velocities[i] = [0, 0, 0]; + this.spins[i] = [0, 0, 0, 0]; + let particle = this.objects[i] = { + object: this.engine.scene.addObject(), + mesh: null + }; + particle.object.name = "particle" + this.count.toString(); + particle.mesh = particle.object.addComponent("mesh"); + particle.mesh.mesh = this.mesh; + particle.mesh.active = true; + } + } + burst() { + for (let i = 0; i < this.particlesPerBurst; ++i) + this.spawn(); + } + update(dt) { + this.time = (this.time || 0) + dt; + if (this.debug && this.time > 3) { + this.burst(); + this.time = 0; + } + let distance2 = [0, 0, 0]; + const tmp = [0, 0, 0, 0]; + for (let i = 0; i < this.activeCount; ++i) { + let o = this.objects[i]; + let obj = o.object; + obj.getTranslationWorld(this.origin); + const vel = this.velocities[i]; + const spins = this.spins[i]; + if (vel[0] != 0 || vel[1] != 0 || vel[2] != 0) { + vel[1] = Math.max(vel[1] - 9.81 * dt, -0.75); + } + if (this.origin[1] + vel[1] * dt <= state.floorHeight + this.particleScale && vel[1] <= 0) { + const frict = 1 / (1 - vel[1]); + vel[0] = frict * vel[0]; + vel[2] = frict * vel[2]; + vel[1] = -0.3 * vel[1]; + if (vel[1] > 0 && vel[1] < 0.1) { + vel[0] = vel[1] = vel[2] = 0; + spins[0] = spins[1] = spins[2] = 0; + spins[3] = 1; + obj.getUp(tmp); + quat_exports.rotationTo(tmp, tmp, [0, 0, 1]); + obj.rotateObject(tmp); + --this.activeCount; + this.swap(i, this.activeCount); + --i; + } + } + } + for (let i = 0; i < this.activeCount; ++i) { + let obj = this.objects[i].object; + vec3_exports.scale(distance2, this.velocities[i], dt); + obj.translate(distance2); + const spins = this.spins[i]; + if (spins[3] != 1) + obj.rotateObject(spins); + } + } + swap(a, b) { + if (b >= this.maxParticles) + return; + if (a >= this.maxParticles) + return; + const o = this.objects[a]; + const vel = this.velocities[a]; + const spins = this.spins[a]; + this.objects[a] = this.objects[b]; + this.velocities[a] = this.velocities[b]; + this.spins[a] = this.spins[b]; + this.objects[b] = o; + this.velocities[b] = vel; + this.spins[b] = spins; + } + /** Spawn a particle */ + spawn() { + let index = this.count % this.maxParticles; + let particle = this.objects[index]; + particle.object.resetTransform(); + particle.object.scale([0.25 * this.particleScale, this.particleScale, 0.02 * this.particleScale]); + quat2_exports.getTranslation(this.origin, this.object.transformWorld); + particle.object.translate(this.origin); + particle.mesh.material = [this.material0, this.material1, this.material2][Math.floor(Math.random() * 3)]; + this.velocities[index][0] = Math.random() - 0.5; + this.velocities[index][1] = Math.random(); + this.velocities[index][2] = Math.random() - 0.5; + vec3_exports.normalize(this.velocities[index], this.velocities[index]); + this.velocities[index][0] *= 0.2 * this.initialSpeed; + this.velocities[index][1] *= Math.random(0.2 * this.initialSpeed) + 0.8 * this.initialSpeed; + this.velocities[index][2] *= 0.2 * this.initialSpeed; + quat_exports.fromEuler( + this.spins[index], + Math.floor(Math.random() * 20), + Math.floor(Math.random() * 20), + Math.floor(Math.random() * 20) + ); + this.count += 1; + this.swap(index, this.activeCount); + this.activeCount = Math.min(this.activeCount + 1, this.maxParticles); + } +}; +__publicField(ConfettiParticles, "TypeName", "confetti-particles"); +__publicField(ConfettiParticles, "Properties", { + /* Mesh for spawned particles */ + mesh: { type: Type.Mesh, default: null }, + material0: { type: Type.Material, default: null }, + material1: { type: Type.Material, default: null }, + material2: { type: Type.Material, default: null }, + /* Maximum number of particles, once limit is reached, particles are recycled first-in-first-out. */ + maxParticles: { type: Type.Int, default: 64 }, + /* Initial speed of emitted particles. */ + initialSpeed: { type: Type.Float, default: 30 }, + /* Size of a particle */ + particleScale: { type: Type.Float, default: 0.1 }, + particlesPerBurst: { type: Type.Int, default: 32 }, + debug: { type: Type.Bool, default: false } +}); + +// js/hud-control.js +var HudControl = class extends Component3 { + init() { + this.currentPos = [0, 0, 0]; + this.object.getTranslationWorld(this.currentPos); + } + update() { + let newPos = []; + const dir = [0, 0, 0]; + this.object.getForward(dir); + let trans = []; + this.headObject.getTranslationWorld(trans); + this.object.resetTranslation(); + vec3_exports.add(newPos, trans, this.currentPos); + this.object.translate(trans); + this.currentPos = trans; + } + trim(num) { + let result = num.toFixed(2); + return result; + } +}; +__publicField(HudControl, "TypeName", "hud-control"); +__publicField(HudControl, "Properties", { + headObject: { type: Type.Object } +}); + +// js/joystick-movement.js +var JoystickMovement = class extends Component3 { + init() { + this.lastTime = Date.now(); + this.velocity = vec3_exports.fromValues(0, 0, 0); + this.gamepadLeft = null; + this.session = null; + this.lastBlowTime = 0; + this.continuousBlow = false; + this.isGrounded = true; + this.remainingAirJumps = this.maxAirJumps; + this.blowStartTime = 0; + this.currentBlowDuration = 0; + } + start() { + this.headObject = this.headObject || this.object; + this.engine.onXRSessionStart.push(this.setupVREvents.bind(this)); + } + update() { + const now = Date.now(); + const dt = (now - this.lastTime) / 1e3; + this.lastTime = now; + const worldPos = this.headObject.getTranslationWorld(); + const wasGrounded = this.isGrounded; + this.isGrounded = worldPos[1] <= 0.01; + if (!wasGrounded && this.isGrounded) { + this.remainingAirJumps = this.maxAirJumps; + } + if (this.gamepadLeft?.axes) { + const x = this.gamepadLeft.axes[2]; + const y = this.gamepadLeft.axes[3]; + let dir = vec3_exports.fromValues(0, 0, 0); + let angle2 = 0; + if (y < -0.1) + dir[2] = -1; + else if (y > 0.1) + dir[2] = 1; + if (x > 0.1) + angle2 = -this.turnAngle * this.turnSpeedMultiplier; + else if (x < -0.1) + angle2 = this.turnAngle * this.turnSpeedMultiplier; + if (Math.abs(y) > Math.abs(x)) { + vec3_exports.normalize(dir, dir); + vec3_exports.scale(dir, dir, this.moveSpeed * dt); + vec3_exports.transformQuat(dir, dir, this.headObject.transformWorld); + this.headObject.translate(dir); + } else if (Math.abs(x) > 0.1) { + this.headObject.rotateAxisAngleDegObject([0, 1, 0], angle2 * dt); + } + } + if (this.continuousBlow && this.gamepadLeft?.buttons[0].pressed) { + if (this.blowStartTime === 0) { + this.blowStartTime = now; + this.currentBlowDuration = 0; + } + this.currentBlowDuration = (now - this.blowStartTime) / 1e3; + if (this.currentBlowDuration <= this.maxBlowDuration && (this.isGrounded || this.remainingAirJumps > 0)) { + this.applyBlowForce(); + if (this.gamepadLeft.hapticActuators) { + this.gamepadLeft.hapticActuators[0].pulse(0.3, 50); + } + } + } else { + this.blowStartTime = 0; + this.currentBlowDuration = 0; + } + if (!this.isGrounded) { + this.velocity[1] -= this.gravity * dt; + } + const delta = vec3_exports.clone(this.velocity); + vec3_exports.scale(delta, delta, dt); + this.headObject.translate(delta); + const newPos = this.headObject.getTranslationWorld(); + if (newPos[1] < 0) { + newPos[1] = 0; + this.headObject.setTranslationWorld(newPos); + this.velocity[1] = 0; + this.isGrounded = true; + this.remainingAirJumps = this.maxAirJumps; + } + const friction = Math.pow(this.friction, dt * 60); + this.velocity[0] *= friction; + this.velocity[2] *= friction; + } + setupVREvents(session) { + this.session = session; + session.addEventListener("end", () => { + this.gamepadLeft = null; + this.session = null; + }); + for (const src of session.inputSources || []) { + if (src.handedness === "left") + this.gamepadLeft = src.gamepad; + } + session.addEventListener("inputsourceschange", (e) => { + for (const src of e.added || []) { + if (src.handedness === "left") + this.gamepadLeft = src.gamepad; + } + }); + session.addEventListener("selectstart", this.onTriggerDown.bind(this)); + session.addEventListener("selectend", this.onTriggerUp.bind(this)); + } + onTriggerDown(event) { + const src = event.inputSource; + if (src.handedness !== "left" || !this.gamepadLeft) + return; + this.continuousBlow = true; + this.blowStartTime = Date.now(); + this.currentBlowDuration = 0; + if (this.isGrounded || this.remainingAirJumps > 0) { + this.applyBlowForce(); + if (!this.isGrounded) { + this.remainingAirJumps--; + } + } + } + onTriggerUp(event) { + const src = event.inputSource; + if (src.handedness !== "left") + return; + this.continuousBlow = false; + this.blowStartTime = 0; + this.currentBlowDuration = 0; + } + applyBlowForce() { + if (!this.cursorObject) + return; + const cursor = this.cursorObject.getComponent("cursor"); + if (!cursor) + return; + const rayObj = cursor.cursorRayObject || cursor.object; + const dir = vec3_exports.create(); + rayObj.getForward(dir); + vec3_exports.normalize(dir, dir); + const horizontalDir = vec3_exports.fromValues(dir[0], 0, dir[2]); + vec3_exports.normalize(horizontalDir, horizontalDir); + const verticalDir = vec3_exports.fromValues(0, dir[1], 0); + const finalDir = vec3_exports.create(); + vec3_exports.scale(horizontalDir, horizontalDir, this.horizontalForceMultiplier); + vec3_exports.scale(verticalDir, verticalDir, 1); + vec3_exports.add(finalDir, horizontalDir, verticalDir); + vec3_exports.normalize(finalDir, finalDir); + vec3_exports.scale(finalDir, finalDir, -this.blowForce); + vec3_exports.add(this.velocity, this.velocity, finalDir); + } +}; +__publicField(JoystickMovement, "TypeName", "joystick-movement"); +__publicField(JoystickMovement, "Properties", { + moveSpeed: { type: Type.Float, default: 0.1 }, + // speed for joystick movement + turnAngle: { type: Type.Float, default: 30 }, + // turning speed deg/s + headObject: { type: Type.Object }, + // object to move/rotate + blowForce: { type: Type.Float, default: 1 }, + // impulse magnitude (m/s) + friction: { type: Type.Float, default: 0.95 }, + // horizontal damping [0-1] + gravity: { type: Type.Float, default: 9.81 }, + // gravity m/s² + cursorObject: { type: Type.Object }, + // object containing `cursor` component + blowDelay: { type: Type.Float, default: 0.5 }, + // delay between blows in seconds + turnSpeedMultiplier: { type: Type.Float, default: 1 }, + // multiplier for turn speed + maxAirJumps: { type: Type.Int, default: 3 }, + // maximum number of air jumps + horizontalForceMultiplier: { type: Type.Float, default: 10 }, + // multiplier for horizontal force + maxBlowDuration: { type: Type.Float, default: 3 } + // maximum continuous blow duration in seconds +}); + +// js/mouse-look-custom.js +var MouseLookCustom = class extends Component3 { + init() { + this.currentRotationY = 0; + this.currentRotationX = 0; + this.origin = new Float32Array(3); + this.parentOrigin = new Float32Array(3); + this.rotationX = 0; + this.rotationY = 0; + } + start() { + this.lastShotTime = 0; + state.bulletSpawner = this.object; + this.soundClick = this.object.addComponent("howler-audio-source", { + src: "sfx/9mm-pistol-shoot-short-reverb-7152.mp3", + volume: 0.5 + }); + document.addEventListener("mousemove", (e) => { + if (this.active && (this.mouseDown || !this.requireMouseDown)) { + this.rotationY = -this.sensitity * e.movementX / 100; + this.rotationX = -this.sensitity * e.movementY / 100; + this.currentRotationX += this.rotationX; + this.currentRotationY += this.rotationY; + this.currentRotationX = Math.min(1.507, this.currentRotationX); + this.currentRotationX = Math.max(-1.507, this.currentRotationX); + this.object.getTranslationWorld(this.origin); + const parent = this.object.parent; + if (parent !== null) { + parent.getTranslationWorld(this.parentOrigin); + vec3_exports.sub(this.origin, this.origin, this.parentOrigin); + } + this.object.resetTranslationRotation(); + this.object.rotateAxisAngleRad([1, 0, 0], this.currentRotationX); + this.object.rotateAxisAngleRad([0, 1, 0], this.currentRotationY); + this.object.translate(this.origin); + } + }); + const canvas2 = this.engine.canvas; + if (this.pointerLockOnClick) { + canvas2.addEventListener("mousedown", () => { + canvas2.requestPointerLock = canvas2.requestPointerLock || canvas2.mozRequestPointerLock || canvas2.webkitRequestPointerLock; + canvas2.requestPointerLock(); + }); + } + if (this.requireMouseDown) { + if (this.mouseButtonIndex == 2) { + canvas2.addEventListener("contextmenu", function(e) { + e.preventDefault(); + }, false); + } + canvas2.addEventListener("mousedown", function(e) { + if (e.button == this.mouseButtonIndex) { + this.mouseDown = true; + document.body.style.cursor = "grabbing"; + if (e.button == 1) { + e.preventDefault(); + return false; + } + } + }.bind(this)); + canvas2.addEventListener("mouseup", function(e) { + if (e.button == this.mouseButtonIndex) { + this.mouseDown = false; + document.body.style.cursor = "initial"; + } + }.bind(this)); + } else { + canvas2.addEventListener("mousedown", function(e) { + if (e.button == this.mouseButtonIndex) { + if (e.button == 0) { + let currentTime = Date.now(); + let lastShotTimeGap = Math.abs(currentTime - this.lastShotTime); + if (lastShotTimeGap > 500) { + try { + const dir = [0, 0, 0]; + this.object.getForward(dir); + state.launch(dir); + this.lastShotTime = currentTime; + this.soundClick.play(); + } catch (e2) { + console.log("mouse shoot >> ", e2); + } + } + } + } + }.bind(this)); + } + } +}; +__publicField(MouseLookCustom, "TypeName", "mouse-look-custom"); +__publicField(MouseLookCustom, "Properties", { + /** Mouse look sensitivity */ + sensitity: { type: Type.Float, default: 0.25 }, + /** Require a mouse button to be pressed to control view. + * Otherwise view will allways follow mouse movement */ + requireMouseDown: { type: Type.Bool, default: true }, + /** If "moveOnClick" is enabled, mouse button which should + * be held down to control view */ + mouseButtonIndex: { type: Type.Int, default: 0 }, + /** Enables pointer lock on "mousedown" event on this.enginecanvas */ + pointerLockOnClick: { type: Type.Bool, default: false }, + bulletMesh: { type: Type.Mesh }, + bulletMaterial: { type: Type.Material }, + bulletSpeed: { type: Type.Float, default: 1 } +}); + +// js/mouse-mover.js +var MouseMover = class extends Component3 { + init() { + } + update(dt) { + if (isNaN(dt)) + return; + if (state.gameOver || state.paused) + return; + const currentSpeed = state.getWaveSpeed(); + const currentPos = vec3_exports.create(); + this.object.getPositionLocal(currentPos); + const playerPos = state.getPlayerLocation(); + if (vec3_exports.dist(currentPos, playerPos) < 0.5) { + console.log("Zombie reached player, game over"); + state.loseGame(); + return; + } + const dir = vec3_exports.fromValues( + playerPos[0] - currentPos[0], + 0, + playerPos[2] - currentPos[2] + ); + if (vec3_exports.len(dir) < 0.01) + return; + vec3_exports.normalize(dir, dir); + const moveVec = vec3_exports.scale(vec3_exports.create(), dir, currentSpeed * dt); + vec3_exports.add(currentPos, currentPos, moveVec); + this.object.setPositionLocal(currentPos); + currentPos[1] = state.floorHeight; + this.object.setPositionLocal(currentPos); + this.object.setPositionLocal(currentPos); + this.object.getComponent("collision").setPositionLocal(currentPos); + this.object.getComponent("collision").setHeight(state.floorHeight); + const angleDeg = Math.atan2(dir[0], dir[2]) * (180 / Math.PI); + this.object.resetRotation(); + this.object.rotateAxisAngleDegLocal([0, 1, 0], angleDeg); + } +}; +__publicField(MouseMover, "TypeName", "mouse-mover"); +__publicField(MouseMover, "Properties", {}); + +// js/score-trigger.js +var tempQuat23 = new Float32Array(8); +var ScoreTrigger = class extends Component3 { + init() { + this.collision = this.object.getComponent("collision"); + this.soundHit = this.object.addComponent("howler-audio-source", { + src: "sfx/high-pitched-aha-103125.mp3", + volume: 1.9 + }); + this.soundPop = this.object.addComponent("howler-audio-source", { + src: "sfx/pop-94319.mp3", + volume: 1.9 + }); + state.victoryMusic = this.object.addComponent("howler-audio-source", { + src: "music/level-win-6416.mp3", + volume: 1.9 + }); + } + onHit(callback) { + if (typeof callback === "function") { + this.particles.setTransformWorld( + this.object.getTransformWorld(tempQuat23) + ); + this.particles.getComponent("confetti-particles").burst(); + state.zombiesKilledInWave++; + state.zombieCount++; + const elementPontos = document.getElementById("pontos-test"); + if (elementPontos) { + elementPontos.innerHTML = state.zombieCount; + } + state.despawnTarget(this.object.parent); + callback(); + } else { + console.log("Nenhum callback fornecido."); + } + this.particles.setTransformWorld(this.object.getTransformWorld(tempQuat23)); + this.particles.getComponent("confetti-particles").burst(); + state.despawnTarget(this.object.parent); + state.incrementScore(); + this.soundHit.play(); + this.soundPop.play(); + } +}; +__publicField(ScoreTrigger, "TypeName", "score-trigger"); +__publicField(ScoreTrigger, "Properties", { + particles: { type: Type.Object } +}); + +// js/mouse-spawner.js +var tempQuat24 = new Float32Array(8); +var MouseSpawner = class extends Component3 { + time = 0; + spawnInterval = 1.2; + targets = []; + // Para controle de waves por lado + sides = ["+X", "-X", "+Z", "-Z"]; + currentSide = null; + sideCount = 0; + waveNumber = 1; + static onRegister(engine2) { + engine2.registerComponent(ScoreTrigger); + engine2.registerComponent(HowlerAudioSource); + } + init() { + state.despawnTarget = function(obj) { + for (let i = 0; i < this.targets.length; i++) { + if (obj.objectId == this.targets[i].objectId) { + this.targets.splice(i, 1); + break; + } + } + obj.destroy(); + }.bind(this); + } + start() { + state.mouseSpawner = this; + this.spawnInterval = this.initialSpawnInterval; + this.pickNewSide(); + this.spawnTarget(); + } + update(dt) { + if (state.gameOver || state.paused) + return; + this.time += dt; + if (this.time >= this.spawnInterval) { + this.time = 0; + this.spawnTarget(); + if (this.spawnInterval > this.minSpawnInterval) { + this.spawnInterval *= this.spawnIntervalDecreaseRate; + } + } + } + reset() { + for (let obj of this.targets) { + obj.destroy(); + } + this.targets = []; + this.time = 0; + this.spawnInterval = this.initialSpawnInterval; + this.waveNumber = 1; + this.sideCount = 0; + this.pickNewSide(); + this.object.resetPosition(); + } + pickNewSide() { + const idx = Math.floor(Math.random() * this.sides.length); + this.currentSide = this.sides[idx]; + this.sideCount = 0; + state.updateScore(`Wave ${this.waveNumber} \u2013 Lado ${this.currentSide}`); + } + spawnTarget() { + if (this.sideCount >= 6) { + this.waveNumber++; + this.pickNewSide(); + } + const obj = this.engine.scene.addObject(); + obj.setTransformLocal(this.object.getTransformWorld(tempQuat24)); + const minR = this.spawnRadiusMin; + const maxR = this.spawnRadiusMax; + let dx = 0, dz = 0; + switch (this.currentSide) { + case "+X": + dx = minR + Math.random() * (maxR - minR); + dz = (Math.random() * 2 - 1) * maxR; + break; + case "-X": + dx = -(minR + Math.random() * (maxR - minR)); + dz = (Math.random() * 2 - 1) * maxR; + break; + case "+Z": + dz = minR + Math.random() * (maxR - minR); + dx = (Math.random() * 2 - 1) * maxR; + break; + case "-Z": + dz = -(minR + Math.random() * (maxR - minR)); + dx = (Math.random() * 2 - 1) * maxR; + break; + } + obj.translateLocal([dx, 0, dz]); + obj.scaleLocal([1, 1, 1]); + const mesh = obj.addComponent("mesh"); + mesh.mesh = this.targetMesh; + mesh.material = this.targetMaterial; + mesh.active = true; + obj.addComponent(MouseMover); + obj.rotateAxisAngleDegLocal([1, 0, 0], 180); + if (this.spawnAnimation) { + const anim = obj.addComponent("animation"); + anim.playCount = 1; + anim.animation = this.spawnAnimation; + anim.active = true; + anim.play(); + } + const trigger = this.engine.scene.addObject(obj); + trigger.addComponent("collision", { + collider: WL.Collider.Sphere, + extents: [1.5, 1.5, 1.5], + group: 1 << 0, + active: true + }); + trigger.translateLocal([0, 1, 0]); + trigger.addComponent(ScoreTrigger, { + particles: this.particles + }); + obj.setDirty(); + this.targets.push(obj); + this.sideCount++; + } +}; +__publicField(MouseSpawner, "TypeName", "mouse-spawner"); +__publicField(MouseSpawner, "Properties", { + targetMesh: { type: Type.Mesh }, + targetMaterial: { type: Type.Material }, + spawnAnimation: { type: Type.Animation }, + particles: { type: Type.Object }, + initialSpawnInterval: { type: Type.Float, default: 2 }, + minSpawnInterval: { type: Type.Float, default: 0.5 }, + spawnIntervalDecreaseRate: { type: Type.Float, default: 0.95 }, + // Raio mínimo/ máximo para spawn + spawnRadiusMin: { type: Type.Float, default: 15 }, + spawnRadiusMax: { type: Type.Float, default: 30 } +}); + +// js/play-again-button.js +var PlayAgainButton = class extends Component3 { + start() { + this.collision = this.object.getComponent("collision"); + this.soundPop = this.object.addComponent("howler-audio-source", { + src: "sfx/pop-94319.mp3", + volume: 1.9 + }); + state.resetButton = this; + this.unhide(); + } + restart() { + console.log("Restarting game"); + state.restart(); + this.hide(); + } + hide() { + console.log("Hiding button"); + if (this.object.children[0]) { + const mesh = this.object.children[0].getComponent("mesh"); + if (mesh) + mesh.active = false; + } + if (this.object.children[1]) { + const text = this.object.children[1].getComponent("text"); + if (text) + text.active = false; + } + this.active = false; + } + unhide() { + console.log("Unhiding button"); + if (this.object.children[0]) { + const mesh = this.object.children[0].getComponent("mesh"); + if (mesh) + mesh.active = true; + } + if (this.object.children[1]) { + const text = this.object.children[1].getComponent("text"); + if (text) + text.active = true; + } + this.active = true; + } + update(dt) { + if (!this.active) + return; + let overlaps = this.collision.queryOverlaps(); + for (let i = 0; i < overlaps.length; ++i) { + let p = overlaps[i].object.getComponent("bullet-physics"); + if (p) { + this.restart(); + this.soundPop.play(); + break; + } + } + } +}; +__publicField(PlayAgainButton, "TypeName", "play-again-button"); +__publicField(PlayAgainButton, "Properties", {}); + +// js/player-location.js +var PlayerLocation = class extends Component3 { + static onRegister(engine2) { + engine2.registerComponent(MouseMover); + } + init() { + this.playerLocation = [0, 0, 0]; + this.object.getPositionWorld(this.playerLocation); + state.getPlayerLocation = function() { + return this.playerLocation; + }.bind(this); + } + update() { + let currentLocation = [0, 0, 0]; + this.object.getPositionWorld(currentLocation); + let locationDistance = vec3_exports.dist(currentLocation, this.playerLocation); + if (locationDistance != 0) { + this.playerLocation = currentLocation; + } + } +}; +__publicField(PlayerLocation, "TypeName", "player-location"); +__publicField(PlayerLocation, "Properties", {}); + +// js/score-display.js +var ScoreDisplay = class extends Component3 { + init() { + this.text = this.object.getComponent("text"); + state.updateScore = function(text) { + this.text.text = text; + }.bind(this); + this.engine.onXRSessionStart.push(function() { + state.updateScore(`Eliminate all ${state.maxTargets} rats.`); + }); + } +}; +__publicField(ScoreDisplay, "TypeName", "score-display"); +__publicField(ScoreDisplay, "Properties", {}); + +// js/shot-counter.js +var ShotCounter = class extends Component3 { + init() { + this.text = this.object.getComponent("text"); + state.updateCounter = function() { + if (!state.gameOver) { + this.text.text = "Shots Fired: " + state.shotCount; + } + }.bind(this); + } +}; +__publicField(ShotCounter, "TypeName", "shot-counter"); +__publicField(ShotCounter, "Properties", {}); + +// js/teleport-custom.js +var TeleportCustom = class extends Component3 { + init() { + this._prevThumbstickAxis = new Float32Array(2); + this._tempVec = new Float32Array(3); + this._tempVec0 = new Float32Array(3); + this._currentIndicatorRotation = 0; + this.input = this.object.getComponent("input"); + if (!this.input) { + console.error(this.object.name, "generic-teleport-component.js: input component is required on the object"); + return; + } + if (!this.teleportIndicatorMeshObject) { + console.error(this.object.name, "generic-teleport-component.js: Teleport indicator mesh is missing"); + return; + } + if (!this.camRoot) { + console.error(this.object.name, "generic-teleport-component.js: camRoot not set"); + return; + } + this.isIndicating = false; + this.indicatorHidden = true; + this.hitSpot = new Float32Array(3); + this._hasHit = false; + this._extraRotation = 0; + this._currentStickAxes = new Float32Array(2); + } + start() { + if (this.handedness == 0) { + const inputComp = this.object.getComponent("input"); + if (!inputComp) { + console.warn( + "teleport component on object", + this.object.name, + 'was configured with handedness "input component", but object has no input component.' + ); + } else { + this.handedness = inputComp.handedness; + this.input = inputComp; + } + } else { + this.handedness = ["left", "right"][this.handedness - 1]; + } + this.engine.onXRSessionStart.push(this.setupVREvents.bind(this)); + this.teleportIndicatorMeshObject.active = false; + } + /* Get current camera Y rotation */ + _getCamRotation() { + this.eyeLeft.getForward(this._tempVec); + this._tempVec[1] = 0; + vec3_exports.normalize(this._tempVec, this._tempVec); + return Math.atan2(this._tempVec[0], this._tempVec[2]); + } + update() { + let inputLength = 0; + if (this.gamepad && this.gamepad.axes) { + this._currentStickAxes[0] = this.gamepad.axes[2]; + this._currentStickAxes[1] = this.gamepad.axes[3]; + inputLength = Math.abs(this._currentStickAxes[0]) + Math.abs(this._currentStickAxes[1]); + } + if (!this.isIndicating && this._prevThumbstickAxis[1] >= this.thumbstickActivationThreshhold && this._currentStickAxes[1] < this.thumbstickActivationThreshhold) { + this.isIndicating = true; + } else if (this.isIndicating && inputLength < this.thumbstickDeactivationThreshhold) { + this.isIndicating = false; + this.teleportIndicatorMeshObject.active = false; + if (this._hasHit) { + this._teleportPlayer(this.hitSpot, this._extraRotation); + } + } + if (this.isIndicating && this.teleportIndicatorMeshObject && this.input) { + const origin = this._tempVec0; + quat2_exports.getTranslation(origin, this.object.transformWorld); + const direction2 = this.object.getForward(this._tempVec); + let rayHit = this.rayHit = this.rayCastMode == 0 ? this.engine.scene.rayCast(origin, direction2, 1 << this.floorGroup) : this.engine.physics.rayCast(origin, direction2, 1 << this.floorGroup, this.maxDistance); + if (rayHit.hitCount > 0) { + this.indicatorHidden = false; + this._extraRotation = Math.PI + Math.atan2(this._currentStickAxes[0], this._currentStickAxes[1]); + this._currentIndicatorRotation = this._getCamRotation() + (this._extraRotation - Math.PI); + this.teleportIndicatorMeshObject.resetTranslationRotation(); + this.teleportIndicatorMeshObject.rotateAxisAngleRad([0, 1, 0], this._currentIndicatorRotation); + this.teleportIndicatorMeshObject.translate(rayHit.locations[0]); + this.teleportIndicatorMeshObject.translate([0, this.indicatorYOffset, 0]); + this.teleportIndicatorMeshObject.active = true; + this.hitSpot.set(rayHit.locations[0]); + this._hasHit = true; + } else { + if (!this.indicatorHidden) { + this.teleportIndicatorMeshObject.active = false; + this.indicatorHidden = true; + } + this._hasHit = false; + } + } + this._prevThumbstickAxis.set(this._currentStickAxes); + } + setupVREvents(s) { + this.session = s; + s.addEventListener("end", function() { + this.gamepad = null; + this.session = null; + }.bind(this)); + if (s.inputSources && s.inputSources.length) { + for (let i = 0; i < s.inputSources.length; i++) { + let inputSource = s.inputSources[i]; + if (inputSource.handedness == this.handedness) { + this.gamepad = inputSource.gamepad; + } + } + } + s.addEventListener("inputsourceschange", function(e) { + if (e.added && e.added.length) { + for (let i = 0; i < e.added.length; i++) { + let inputSource = e.added[i]; + if (inputSource.handedness == this.handedness) { + this.gamepad = inputSource.gamepad; + } + } + } + }.bind(this)); + } + _teleportPlayer(newPosition, rotationToAdd) { + this.camRoot.rotateAxisAngleRad([0, 1, 0], rotationToAdd); + const p = this._tempVec; + const p1 = this._tempVec0; + if (this.session) { + this.eyeLeft.getTranslationWorld(p); + this.eyeRight.getTranslationWorld(p1); + vec3_exports.add(p, p, p1); + vec3_exports.scale(p, p, 0.5); + } else { + this.cam.getTranslationWorld(p); + } + this.camRoot.getTranslationWorld(p1); + vec3_exports.sub(p, p1, p); + p[0] += newPosition[0]; + p[1] = newPosition[1]; + p[2] += newPosition[2]; + this.camRoot.setTranslationWorld(p); + } +}; +__publicField(TeleportCustom, "TypeName", "teleport-custom"); +__publicField(TeleportCustom, "Properties", { + /** Object that will be placed as indiciation forwhere the player will teleport to. */ + teleportIndicatorMeshObject: { type: Type.Object }, + /** Root of the player, the object that will be positioned on teleportation. */ + camRoot: { type: Type.Object }, + /** Left eye for use in VR*/ + eyeLeft: { type: Type.Object }, + /** Right eye for use in VR*/ + eyeRight: { type: Type.Object }, + /** Handedness for VR cursors to accept trigger events only from respective controller. */ + handedness: { type: Type.Enum, values: ["input component", "left", "right", "none"], default: "input component" }, + /** Collision group of valid "floor" objects that can be teleported on */ + floorGroup: { type: Type.Int, default: 1 }, + /** How far the thumbstick needs to be pushed to have the teleport target indicator show up */ + thumbstickActivationThreshhold: { type: Type.Float, default: -0.7 }, + /** How far the thumbstick needs to be released to execute the teleport */ + thumbstickDeactivationThreshhold: { type: Type.Float, default: 0.3 }, + /** Offset to apply to the indicator object, e.g. to avoid it from Z-fighting with the floor */ + indicatorYOffset: { type: Type.Float, default: 0.01 }, + /** Mode for raycasting, whether to use PhysX or simple collision components */ + rayCastMode: { type: Type.Enum, values: ["collision", "physx"], default: "collision" }, + /** Max distance for PhysX raycast */ + maxDistance: { type: Type.Float, default: 100 } +}); + +// js/wasd-controls-custom.js +var WasdControlsCustom = class extends Component3 { + init() { + this.up = false; + this.right = false; + this.down = false; + this.left = false; + this.verticalVelocity = 0; + this.isJumping = false; + this.jumpsRemaining = this.maxJumps + 1; + this.centerPos = vec3_exports.create(); + if (this.headObject) { + this.headObject.getPositionLocal(this.centerPos); + } + window.addEventListener("keydown", this.press.bind(this)); + window.addEventListener("keyup", this.release.bind(this)); + } + update(dt) { + if (!this.headObject) + return; + const direction2 = vec3_exports.create(); + if (this.up || this.right || this.down || this.left) { + const forward = vec3_exports.fromValues(0, 0, 0); + this.object.getForward(forward); + forward[1] = 0; + vec3_exports.normalize(forward, forward); + vec3_exports.scale(forward, forward, this.speed); + const rightV = vec3_exports.fromValues(0, 0, 0); + this.object.getRight(rightV); + rightV[1] = 0; + vec3_exports.normalize(rightV, rightV); + vec3_exports.scale(rightV, rightV, this.speed); + if (this.up) + vec3_exports.add(direction2, direction2, forward); + if (this.down) + vec3_exports.sub(direction2, direction2, forward); + if (this.right) + vec3_exports.add(direction2, direction2, rightV); + if (this.left) + vec3_exports.sub(direction2, direction2, rightV); + } + const newPos = vec3_exports.create(); + this.headObject.getPositionLocal(newPos); + vec3_exports.add(newPos, newPos, direction2); + this.verticalVelocity -= this.gravity * dt; + newPos[1] += this.verticalVelocity * dt; + if (newPos[1] <= this.groundLevel) { + newPos[1] = this.groundLevel; + this.verticalVelocity = 0; + this.jumpsRemaining = this.maxJumps + 1; + this.isJumping = false; + } else { + this.isJumping = true; + } + const minX = this.centerPos[0] - this.boundaryLimit; + const maxX = this.centerPos[0] + this.boundaryLimit; + const minZ = this.centerPos[2] - this.boundaryLimit; + const maxZ = this.centerPos[2] + this.boundaryLimit; + newPos[0] = Math.min(Math.max(newPos[0], minX), maxX); + newPos[2] = Math.min(Math.max(newPos[2], minZ), maxZ); + this.headObject.setPositionLocal(newPos); + } + press(e) { + if (e.keyCode === 38 || e.keyCode === 87 || e.keyCode === 90) + this.up = true; + else if (e.keyCode === 39 || e.keyCode === 68) + this.right = true; + else if (e.keyCode === 40 || e.keyCode === 83) + this.down = true; + else if (e.keyCode === 37 || e.keyCode === 65 || e.keyCode === 81) + this.left = true; + else if (e.keyCode === 32 && this.jumpsRemaining > 0) { + this.verticalVelocity = this.jumpForce; + this.jumpsRemaining--; + } + } + release(e) { + if (e.keyCode === 38 || e.keyCode === 87 || e.keyCode === 90) + this.up = false; + else if (e.keyCode === 39 || e.keyCode === 68) + this.right = false; + else if (e.keyCode === 40 || e.keyCode === 83) + this.down = false; + else if (e.keyCode === 37 || e.keyCode === 65 || e.keyCode === 81) + this.left = false; + } +}; +__publicField(WasdControlsCustom, "TypeName", "wasd-controls-custom"); +__publicField(WasdControlsCustom, "Properties", { + speed: { type: Type.Float, default: 0.1 }, + headObject: { type: Type.Object }, + boundaryLimit: { type: Type.Float, default: 12 }, + gravity: { type: Type.Float, default: 9.8 }, + jumpForce: { type: Type.Float, default: 8 }, + groundLevel: { type: Type.Float, default: 0 }, + maxJumps: { type: Type.Int, default: 2 } + // número de pulos extras permitidos no ar +}); + +// js/index.js +var Constants = { + ProjectName: "MyWonderland", + RuntimeBaseName: "WonderlandRuntime", + WebXRRequiredFeatures: ["local"], + WebXROptionalFeatures: ["local", "local-floor", "hand-tracking", "hit-test"] +}; +var RuntimeOptions = { + physx: false, + loader: false, + xrFramebufferScaleFactor: 1, + xrOfferSession: { + mode: "auto", + features: Constants.WebXRRequiredFeatures, + optionalFeatures: Constants.WebXROptionalFeatures + }, + canvas: "canvas" +}; +var engine = await loadRuntime(Constants.RuntimeBaseName, RuntimeOptions); +Object.assign(engine, dist_exports); +window.WL = engine; +engine.onSceneLoaded.once(() => { + const el = document.getElementById("version"); + if (el) + setTimeout(() => el.remove(), 2e3); +}); +function requestSession(mode) { + engine.requestXRSession( + mode, + Constants.WebXRRequiredFeatures, + Constants.WebXROptionalFeatures + ).catch((e) => console.error(e)); +} +function setupButtonsXR() { + const arButton = document.getElementById("ar-button"); + if (arButton) { + arButton.dataset.supported = engine.arSupported; + arButton.addEventListener("click", () => requestSession("immersive-ar")); + } + const vrButton = document.getElementById("vr-button"); + if (vrButton) { + vrButton.dataset.supported = engine.vrSupported; + vrButton.addEventListener("click", () => requestSession("immersive-vr")); + } +} +if (document.readyState === "loading") { + window.addEventListener("load", setupButtonsXR); +} else { + setupButtonsXR(); +} +engine.registerComponent(Cursor); +engine.registerComponent(CursorTarget); +engine.registerComponent(FingerCursor); +engine.registerComponent(FixedFoveation); +engine.registerComponent(HandTracking); +engine.registerComponent(HowlerAudioListener); +engine.registerComponent(PlayerHeight); +engine.registerComponent(VrModeActiveSwitch); +engine.registerComponent(BgMusic); +engine.registerComponent(BulletSpawner); +engine.registerComponent(ConfettiParticles); +engine.registerComponent(HudControl); +engine.registerComponent(JoystickMovement); +engine.registerComponent(MouseLookCustom); +engine.registerComponent(MouseSpawner); +engine.registerComponent(PlayAgainButton); +engine.registerComponent(PlayerLocation); +engine.registerComponent(ScoreDisplay); +engine.registerComponent(ShotCounter); +engine.registerComponent(TeleportCustom); +engine.registerComponent(WasdControlsCustom); +engine.scene.load(`${Constants.ProjectName}.bin`); +/*! Bundled license information: + +howler/dist/howler.js: + (*! + * howler.js v2.2.3 + * howlerjs.com + * + * (c) 2013-2020, James Simpson of GoldFire Studios + * goldfirestudios.com + * + * MIT License + *) + (*! + * Spatial Plugin - Adds support for stereo and 3D audio where Web Audio is supported. + * + * howler.js v2.2.3 + * howlerjs.com + * + * (c) 2013-2020, James Simpson of GoldFire Studios + * goldfirestudios.com + * + * MIT License + *) +*/ +//# sourceMappingURL=MyWonderland-bundle.js.map diff --git a/deploy/MyWonderland-bundle.js.map b/deploy/MyWonderland-bundle.js.map new file mode 100644 index 0000000..63c6f80 --- /dev/null +++ b/deploy/MyWonderland-bundle.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../node_modules/howler/dist/howler.js", "../node_modules/earcut/src/earcut.js", "../node_modules/node_modules/wasm-feature-detect/dist/esm/index.js", "../node_modules/@wonderlandengine/api/src/property.ts", "../node_modules/@wonderlandengine/api/src/decorators.ts", "../node_modules/@wonderlandengine/api/src/utils/object.ts", "../node_modules/@wonderlandengine/api/src/utils/event.ts", "../node_modules/@wonderlandengine/api/src/resources/resource.ts", "../node_modules/@wonderlandengine/api/src/utils/cbor.ts", "../node_modules/@wonderlandengine/api/src/component.ts", "../node_modules/@wonderlandengine/api/src/utils/fetch.ts", "../node_modules/@wonderlandengine/api/src/prefab.ts", "../node_modules/@wonderlandengine/api/src/utils/misc.ts", "../node_modules/@wonderlandengine/api/src/wonderland.ts", "../node_modules/@wonderlandengine/api/src/version.ts", "../node_modules/@wonderlandengine/api/src/resources/material-manager.ts", "../node_modules/@wonderlandengine/api/src/resources/mesh-manager.ts", "../node_modules/@wonderlandengine/api/src/resources/texture-manager.ts", "../node_modules/@wonderlandengine/api/src/webxr.ts", "../node_modules/@wonderlandengine/api/src/scene-gltf.ts", "../node_modules/@wonderlandengine/api/src/scene.ts", "../node_modules/@wonderlandengine/api/src/engine.ts", "../node_modules/@wonderlandengine/api/src/utils/bitset.ts", "../node_modules/@wonderlandengine/api/src/utils/logger.ts", "../node_modules/@wonderlandengine/api/src/wasm.ts", "../node_modules/@wonderlandengine/api/src/index.ts", "../node_modules/@wonderlandengine/components/dist/8thwall-camera.js", "../node_modules/@wonderlandengine/components/dist/utils/webxr.js", "../node_modules/@wonderlandengine/components/dist/anchor.js", "../node_modules/@wonderlandengine/components/dist/cursor-target.js", "../node_modules/gl-matrix/esm/common.js", "../node_modules/gl-matrix/esm/mat3.js", "../node_modules/gl-matrix/esm/mat4.js", "../node_modules/gl-matrix/esm/quat.js", "../node_modules/gl-matrix/esm/vec3.js", "../node_modules/gl-matrix/esm/vec4.js", "../node_modules/gl-matrix/esm/quat2.js", "../node_modules/@wonderlandengine/components/dist/hit-test-location.js", "../node_modules/@wonderlandengine/components/dist/cursor.js", "../node_modules/@wonderlandengine/components/dist/debug-object.js", "../node_modules/@wonderlandengine/components/dist/finger-cursor.js", "../node_modules/@wonderlandengine/components/dist/fixed-foveation.js", "../node_modules/@wonderlandengine/components/dist/hand-tracking.js", "../node_modules/@wonderlandengine/components/dist/howler-audio-listener.js", "../node_modules/@wonderlandengine/components/dist/howler-audio-source.js", "../node_modules/@wonderlandengine/components/dist/utils/utils.js", "../node_modules/@wonderlandengine/components/dist/image-texture.js", "../node_modules/@wonderlandengine/components/dist/mouse-look.js", "../node_modules/@wonderlandengine/components/dist/player-height.js", "../node_modules/@wonderlandengine/components/dist/target-framerate.js", "../node_modules/@wonderlandengine/components/dist/teleport.js", "../node_modules/@wonderlandengine/components/dist/trail.js", "../node_modules/@wonderlandengine/components/dist/two-joint-ik-solver.js", "../node_modules/@wonderlandengine/components/dist/video-texture.js", "../node_modules/@wonderlandengine/components/dist/vr-mode-active-switch.js", "../node_modules/@wonderlandengine/components/dist/plane-detection.js", "../node_modules/@wonderlandengine/components/dist/vrm.js", "../node_modules/@wonderlandengine/components/dist/wasd-controls.js", "../js/game.js", "../js/bg-music.js", "../js/bullet-physics.js", "../js/bullet-spawner.js", "../js/confetti-particles.js", "../js/hud-control.js", "../js/joystick-movement.js", "../js/mouse-look-custom.js", "../js/mouse-mover.js", "../js/score-trigger.js", "../js/mouse-spawner.js", "../js/play-again-button.js", "../js/player-location.js", "../js/score-display.js", "../js/shot-counter.js", "../js/teleport-custom.js", "../js/wasd-controls-custom.js", "../js/index.js"], + "sourcesContent": ["/*!\n * howler.js v2.2.3\n * howlerjs.com\n *\n * (c) 2013-2020, James Simpson of GoldFire Studios\n * goldfirestudios.com\n *\n * MIT License\n */\n\n(function() {\n\n 'use strict';\n\n /** Global Methods **/\n /***************************************************************************/\n\n /**\n * Create the global controller. All contained methods and properties apply\n * to all sounds that are currently playing or will be in the future.\n */\n var HowlerGlobal = function() {\n this.init();\n };\n HowlerGlobal.prototype = {\n /**\n * Initialize the global Howler object.\n * @return {Howler}\n */\n init: function() {\n var self = this || Howler;\n\n // Create a global ID counter.\n self._counter = 1000;\n\n // Pool of unlocked HTML5 Audio objects.\n self._html5AudioPool = [];\n self.html5PoolSize = 10;\n\n // Internal properties.\n self._codecs = {};\n self._howls = [];\n self._muted = false;\n self._volume = 1;\n self._canPlayEvent = 'canplaythrough';\n self._navigator = (typeof window !== 'undefined' && window.navigator) ? window.navigator : null;\n\n // Public properties.\n self.masterGain = null;\n self.noAudio = false;\n self.usingWebAudio = true;\n self.autoSuspend = true;\n self.ctx = null;\n\n // Set to false to disable the auto audio unlocker.\n self.autoUnlock = true;\n\n // Setup the various state values for global tracking.\n self._setup();\n\n return self;\n },\n\n /**\n * Get/set the global volume for all sounds.\n * @param {Float} vol Volume from 0.0 to 1.0.\n * @return {Howler/Float} Returns self or current volume.\n */\n volume: function(vol) {\n var self = this || Howler;\n vol = parseFloat(vol);\n\n // If we don't have an AudioContext created yet, run the setup.\n if (!self.ctx) {\n setupAudioContext();\n }\n\n if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {\n self._volume = vol;\n\n // Don't update any of the nodes if we are muted.\n if (self._muted) {\n return self;\n }\n\n // When using Web Audio, we just need to adjust the master gain.\n if (self.usingWebAudio) {\n self.masterGain.gain.setValueAtTime(vol, Howler.ctx.currentTime);\n }\n\n // Loop through and change volume for all HTML5 audio nodes.\n for (var i=0; i=0; i--) {\n self._howls[i].unload();\n }\n\n // Create a new AudioContext to make sure it is fully reset.\n if (self.usingWebAudio && self.ctx && typeof self.ctx.close !== 'undefined') {\n self.ctx.close();\n self.ctx = null;\n setupAudioContext();\n }\n\n return self;\n },\n\n /**\n * Check for codec support of specific extension.\n * @param {String} ext Audio file extention.\n * @return {Boolean}\n */\n codecs: function(ext) {\n return (this || Howler)._codecs[ext.replace(/^x-/, '')];\n },\n\n /**\n * Setup various state values for global tracking.\n * @return {Howler}\n */\n _setup: function() {\n var self = this || Howler;\n\n // Keeps track of the suspend/resume state of the AudioContext.\n self.state = self.ctx ? self.ctx.state || 'suspended' : 'suspended';\n\n // Automatically begin the 30-second suspend process\n self._autoSuspend();\n\n // Check if audio is available.\n if (!self.usingWebAudio) {\n // No audio is available on this system if noAudio is set to true.\n if (typeof Audio !== 'undefined') {\n try {\n var test = new Audio();\n\n // Check if the canplaythrough event is available.\n if (typeof test.oncanplaythrough === 'undefined') {\n self._canPlayEvent = 'canplay';\n }\n } catch(e) {\n self.noAudio = true;\n }\n } else {\n self.noAudio = true;\n }\n }\n\n // Test to make sure audio isn't disabled in Internet Explorer.\n try {\n var test = new Audio();\n if (test.muted) {\n self.noAudio = true;\n }\n } catch (e) {}\n\n // Check for supported codecs.\n if (!self.noAudio) {\n self._setupCodecs();\n }\n\n return self;\n },\n\n /**\n * Check for browser support for various codecs and cache the results.\n * @return {Howler}\n */\n _setupCodecs: function() {\n var self = this || Howler;\n var audioTest = null;\n\n // Must wrap in a try/catch because IE11 in server mode throws an error.\n try {\n audioTest = (typeof Audio !== 'undefined') ? new Audio() : null;\n } catch (err) {\n return self;\n }\n\n if (!audioTest || typeof audioTest.canPlayType !== 'function') {\n return self;\n }\n\n var mpegTest = audioTest.canPlayType('audio/mpeg;').replace(/^no$/, '');\n\n // Opera version <33 has mixed MP3 support, so we need to check for and block it.\n var ua = self._navigator ? self._navigator.userAgent : '';\n var checkOpera = ua.match(/OPR\\/([0-6].)/g);\n var isOldOpera = (checkOpera && parseInt(checkOpera[0].split('/')[1], 10) < 33);\n var checkSafari = ua.indexOf('Safari') !== -1 && ua.indexOf('Chrome') === -1;\n var safariVersion = ua.match(/Version\\/(.*?) /);\n var isOldSafari = (checkSafari && safariVersion && parseInt(safariVersion[1], 10) < 15);\n\n self._codecs = {\n mp3: !!(!isOldOpera && (mpegTest || audioTest.canPlayType('audio/mp3;').replace(/^no$/, ''))),\n mpeg: !!mpegTest,\n opus: !!audioTest.canPlayType('audio/ogg; codecs=\"opus\"').replace(/^no$/, ''),\n ogg: !!audioTest.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/, ''),\n oga: !!audioTest.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/, ''),\n wav: !!(audioTest.canPlayType('audio/wav; codecs=\"1\"') || audioTest.canPlayType('audio/wav')).replace(/^no$/, ''),\n aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),\n caf: !!audioTest.canPlayType('audio/x-caf;').replace(/^no$/, ''),\n m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\n m4b: !!(audioTest.canPlayType('audio/x-m4b;') || audioTest.canPlayType('audio/m4b;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\n mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\n weba: !!(!isOldSafari && audioTest.canPlayType('audio/webm; codecs=\"vorbis\"').replace(/^no$/, '')),\n webm: !!(!isOldSafari && audioTest.canPlayType('audio/webm; codecs=\"vorbis\"').replace(/^no$/, '')),\n dolby: !!audioTest.canPlayType('audio/mp4; codecs=\"ec-3\"').replace(/^no$/, ''),\n flac: !!(audioTest.canPlayType('audio/x-flac;') || audioTest.canPlayType('audio/flac;')).replace(/^no$/, '')\n };\n\n return self;\n },\n\n /**\n * Some browsers/devices will only allow audio to be played after a user interaction.\n * Attempt to automatically unlock audio on the first user interaction.\n * Concept from: http://paulbakaus.com/tutorials/html5/web-audio-on-ios/\n * @return {Howler}\n */\n _unlockAudio: function() {\n var self = this || Howler;\n\n // Only run this if Web Audio is supported and it hasn't already been unlocked.\n if (self._audioUnlocked || !self.ctx) {\n return;\n }\n\n self._audioUnlocked = false;\n self.autoUnlock = false;\n\n // Some mobile devices/platforms have distortion issues when opening/closing tabs and/or web views.\n // Bugs in the browser (especially Mobile Safari) can cause the sampleRate to change from 44100 to 48000.\n // By calling Howler.unload(), we create a new AudioContext with the correct sampleRate.\n if (!self._mobileUnloaded && self.ctx.sampleRate !== 44100) {\n self._mobileUnloaded = true;\n self.unload();\n }\n\n // Scratch buffer for enabling iOS to dispose of web audio buffers correctly, as per:\n // http://stackoverflow.com/questions/24119684\n self._scratchBuffer = self.ctx.createBuffer(1, 1, 22050);\n\n // Call this method on touch start to create and play a buffer,\n // then check if the audio actually played to determine if\n // audio has now been unlocked on iOS, Android, etc.\n var unlock = function(e) {\n // Create a pool of unlocked HTML5 Audio objects that can\n // be used for playing sounds without user interaction. HTML5\n // Audio objects must be individually unlocked, as opposed\n // to the WebAudio API which only needs a single activation.\n // This must occur before WebAudio setup or the source.onended\n // event will not fire.\n while (self._html5AudioPool.length < self.html5PoolSize) {\n try {\n var audioNode = new Audio();\n\n // Mark this Audio object as unlocked to ensure it can get returned\n // to the unlocked pool when released.\n audioNode._unlocked = true;\n\n // Add the audio node to the pool.\n self._releaseHtml5Audio(audioNode);\n } catch (e) {\n self.noAudio = true;\n break;\n }\n }\n\n // Loop through any assigned audio nodes and unlock them.\n for (var i=0; i= 55.\n if (typeof self.ctx.resume === 'function') {\n self.ctx.resume();\n }\n\n // Setup a timeout to check that we are unlocked on the next event loop.\n source.onended = function() {\n source.disconnect(0);\n\n // Update the unlocked state and prevent this check from happening again.\n self._audioUnlocked = true;\n\n // Remove the touch start listener.\n document.removeEventListener('touchstart', unlock, true);\n document.removeEventListener('touchend', unlock, true);\n document.removeEventListener('click', unlock, true);\n document.removeEventListener('keydown', unlock, true);\n\n // Let all sounds know that audio has been unlocked.\n for (var i=0; i 0 ? sound._seek : self._sprite[sprite][0] / 1000);\n var duration = Math.max(0, ((self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000) - seek);\n var timeout = (duration * 1000) / Math.abs(sound._rate);\n var start = self._sprite[sprite][0] / 1000;\n var stop = (self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000;\n sound._sprite = sprite;\n\n // Mark the sound as ended instantly so that this async playback\n // doesn't get grabbed by another call to play while this one waits to start.\n sound._ended = false;\n\n // Update the parameters of the sound.\n var setParams = function() {\n sound._paused = false;\n sound._seek = seek;\n sound._start = start;\n sound._stop = stop;\n sound._loop = !!(sound._loop || self._sprite[sprite][2]);\n };\n\n // End the sound instantly if seek is at the end.\n if (seek >= stop) {\n self._ended(sound);\n return;\n }\n\n // Begin the actual playback.\n var node = sound._node;\n if (self._webAudio) {\n // Fire this when the sound is ready to play to begin Web Audio playback.\n var playWebAudio = function() {\n self._playLock = false;\n setParams();\n self._refreshBuffer(sound);\n\n // Setup the playback params.\n var vol = (sound._muted || self._muted) ? 0 : sound._volume;\n node.gain.setValueAtTime(vol, Howler.ctx.currentTime);\n sound._playStart = Howler.ctx.currentTime;\n\n // Play the sound using the supported method.\n if (typeof node.bufferSource.start === 'undefined') {\n sound._loop ? node.bufferSource.noteGrainOn(0, seek, 86400) : node.bufferSource.noteGrainOn(0, seek, duration);\n } else {\n sound._loop ? node.bufferSource.start(0, seek, 86400) : node.bufferSource.start(0, seek, duration);\n }\n\n // Start a new timer if none is present.\n if (timeout !== Infinity) {\n self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);\n }\n\n if (!internal) {\n setTimeout(function() {\n self._emit('play', sound._id);\n self._loadQueue();\n }, 0);\n }\n };\n\n if (Howler.state === 'running' && Howler.ctx.state !== 'interrupted') {\n playWebAudio();\n } else {\n self._playLock = true;\n\n // Wait for the audio context to resume before playing.\n self.once('resume', playWebAudio);\n\n // Cancel the end timer.\n self._clearTimer(sound._id);\n }\n } else {\n // Fire this when the sound is ready to play to begin HTML5 Audio playback.\n var playHtml5 = function() {\n node.currentTime = seek;\n node.muted = sound._muted || self._muted || Howler._muted || node.muted;\n node.volume = sound._volume * Howler.volume();\n node.playbackRate = sound._rate;\n\n // Some browsers will throw an error if this is called without user interaction.\n try {\n var play = node.play();\n\n // Support older browsers that don't support promises, and thus don't have this issue.\n if (play && typeof Promise !== 'undefined' && (play instanceof Promise || typeof play.then === 'function')) {\n // Implements a lock to prevent DOMException: The play() request was interrupted by a call to pause().\n self._playLock = true;\n\n // Set param values immediately.\n setParams();\n\n // Releases the lock and executes queued actions.\n play\n .then(function() {\n self._playLock = false;\n node._unlocked = true;\n if (!internal) {\n self._emit('play', sound._id);\n } else {\n self._loadQueue();\n }\n })\n .catch(function() {\n self._playLock = false;\n self._emit('playerror', sound._id, 'Playback was unable to start. This is most commonly an issue ' +\n 'on mobile devices and Chrome where playback was not within a user interaction.');\n\n // Reset the ended and paused values.\n sound._ended = true;\n sound._paused = true;\n });\n } else if (!internal) {\n self._playLock = false;\n setParams();\n self._emit('play', sound._id);\n }\n\n // Setting rate before playing won't work in IE, so we set it again here.\n node.playbackRate = sound._rate;\n\n // If the node is still paused, then we can assume there was a playback issue.\n if (node.paused) {\n self._emit('playerror', sound._id, 'Playback was unable to start. This is most commonly an issue ' +\n 'on mobile devices and Chrome where playback was not within a user interaction.');\n return;\n }\n\n // Setup the end timer on sprites or listen for the ended event.\n if (sprite !== '__default' || sound._loop) {\n self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);\n } else {\n self._endTimers[sound._id] = function() {\n // Fire ended on this audio node.\n self._ended(sound);\n\n // Clear this listener.\n node.removeEventListener('ended', self._endTimers[sound._id], false);\n };\n node.addEventListener('ended', self._endTimers[sound._id], false);\n }\n } catch (err) {\n self._emit('playerror', sound._id, err);\n }\n };\n\n // If this is streaming audio, make sure the src is set and load again.\n if (node.src === 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA') {\n node.src = self._src;\n node.load();\n }\n\n // Play immediately if ready, or wait for the 'canplaythrough'e vent.\n var loadedNoReadyState = (window && window.ejecta) || (!node.readyState && Howler._navigator.isCocoonJS);\n if (node.readyState >= 3 || loadedNoReadyState) {\n playHtml5();\n } else {\n self._playLock = true;\n self._state = 'loading';\n\n var listener = function() {\n self._state = 'loaded';\n \n // Begin playback.\n playHtml5();\n\n // Clear this listener.\n node.removeEventListener(Howler._canPlayEvent, listener, false);\n };\n node.addEventListener(Howler._canPlayEvent, listener, false);\n\n // Cancel the end timer.\n self._clearTimer(sound._id);\n }\n }\n\n return sound._id;\n },\n\n /**\n * Pause playback and save current position.\n * @param {Number} id The sound ID (empty to pause all in group).\n * @return {Howl}\n */\n pause: function(id) {\n var self = this;\n\n // If the sound hasn't loaded or a play() promise is pending, add it to the load queue to pause when capable.\n if (self._state !== 'loaded' || self._playLock) {\n self._queue.push({\n event: 'pause',\n action: function() {\n self.pause(id);\n }\n });\n\n return self;\n }\n\n // If no id is passed, get all ID's to be paused.\n var ids = self._getSoundIds(id);\n\n for (var i=0; i Returns the group's volume value.\n * volume(id) -> Returns the sound id's current volume.\n * volume(vol) -> Sets the volume of all sounds in this Howl group.\n * volume(vol, id) -> Sets the volume of passed sound id.\n * @return {Howl/Number} Returns self or current volume.\n */\n volume: function() {\n var self = this;\n var args = arguments;\n var vol, id;\n\n // Determine the values based on arguments.\n if (args.length === 0) {\n // Return the value of the groups' volume.\n return self._volume;\n } else if (args.length === 1 || args.length === 2 && typeof args[1] === 'undefined') {\n // First check if this is an ID, and if not, assume it is a new volume.\n var ids = self._getSoundIds();\n var index = ids.indexOf(args[0]);\n if (index >= 0) {\n id = parseInt(args[0], 10);\n } else {\n vol = parseFloat(args[0]);\n }\n } else if (args.length >= 2) {\n vol = parseFloat(args[0]);\n id = parseInt(args[1], 10);\n }\n\n // Update the volume or return the current volume.\n var sound;\n if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) {\n // If the sound hasn't loaded, add it to the load queue to change volume when capable.\n if (self._state !== 'loaded'|| self._playLock) {\n self._queue.push({\n event: 'volume',\n action: function() {\n self.volume.apply(self, args);\n }\n });\n\n return self;\n }\n\n // Set the group volume.\n if (typeof id === 'undefined') {\n self._volume = vol;\n }\n\n // Update one or all volumes.\n id = self._getSoundIds(id);\n for (var i=0; i 0) ? len / steps : len);\n var lastTick = Date.now();\n\n // Store the value being faded to.\n sound._fadeTo = to;\n\n // Update the volume value on each interval tick.\n sound._interval = setInterval(function() {\n // Update the volume based on the time since the last tick.\n var tick = (Date.now() - lastTick) / len;\n lastTick = Date.now();\n vol += diff * tick;\n\n // Round to within 2 decimal points.\n vol = Math.round(vol * 100) / 100;\n\n // Make sure the volume is in the right bounds.\n if (diff < 0) {\n vol = Math.max(to, vol);\n } else {\n vol = Math.min(to, vol);\n }\n\n // Change the volume.\n if (self._webAudio) {\n sound._volume = vol;\n } else {\n self.volume(vol, sound._id, true);\n }\n\n // Set the group's volume.\n if (isGroup) {\n self._volume = vol;\n }\n\n // When the fade is complete, stop it and fire event.\n if ((to < from && vol <= to) || (to > from && vol >= to)) {\n clearInterval(sound._interval);\n sound._interval = null;\n sound._fadeTo = null;\n self.volume(to, sound._id);\n self._emit('fade', sound._id);\n }\n }, stepLen);\n },\n\n /**\n * Internal method that stops the currently playing fade when\n * a new fade starts, volume is changed or the sound is stopped.\n * @param {Number} id The sound id.\n * @return {Howl}\n */\n _stopFade: function(id) {\n var self = this;\n var sound = self._soundById(id);\n\n if (sound && sound._interval) {\n if (self._webAudio) {\n sound._node.gain.cancelScheduledValues(Howler.ctx.currentTime);\n }\n\n clearInterval(sound._interval);\n sound._interval = null;\n self.volume(sound._fadeTo, id);\n sound._fadeTo = null;\n self._emit('fade', id);\n }\n\n return self;\n },\n\n /**\n * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments.\n * loop() -> Returns the group's loop value.\n * loop(id) -> Returns the sound id's loop value.\n * loop(loop) -> Sets the loop value for all sounds in this Howl group.\n * loop(loop, id) -> Sets the loop value of passed sound id.\n * @return {Howl/Boolean} Returns self or current loop value.\n */\n loop: function() {\n var self = this;\n var args = arguments;\n var loop, id, sound;\n\n // Determine the values for loop and id.\n if (args.length === 0) {\n // Return the grou's loop value.\n return self._loop;\n } else if (args.length === 1) {\n if (typeof args[0] === 'boolean') {\n loop = args[0];\n self._loop = loop;\n } else {\n // Return this sound's loop value.\n sound = self._soundById(parseInt(args[0], 10));\n return sound ? sound._loop : false;\n }\n } else if (args.length === 2) {\n loop = args[0];\n id = parseInt(args[1], 10);\n }\n\n // If no id is passed, get all ID's to be looped.\n var ids = self._getSoundIds(id);\n for (var i=0; i Returns the first sound node's current playback rate.\n * rate(id) -> Returns the sound id's current playback rate.\n * rate(rate) -> Sets the playback rate of all sounds in this Howl group.\n * rate(rate, id) -> Sets the playback rate of passed sound id.\n * @return {Howl/Number} Returns self or the current playback rate.\n */\n rate: function() {\n var self = this;\n var args = arguments;\n var rate, id;\n\n // Determine the values based on arguments.\n if (args.length === 0) {\n // We will simply return the current rate of the first node.\n id = self._sounds[0]._id;\n } else if (args.length === 1) {\n // First check if this is an ID, and if not, assume it is a new rate value.\n var ids = self._getSoundIds();\n var index = ids.indexOf(args[0]);\n if (index >= 0) {\n id = parseInt(args[0], 10);\n } else {\n rate = parseFloat(args[0]);\n }\n } else if (args.length === 2) {\n rate = parseFloat(args[0]);\n id = parseInt(args[1], 10);\n }\n\n // Update the playback rate or return the current value.\n var sound;\n if (typeof rate === 'number') {\n // If the sound hasn't loaded, add it to the load queue to change playback rate when capable.\n if (self._state !== 'loaded' || self._playLock) {\n self._queue.push({\n event: 'rate',\n action: function() {\n self.rate.apply(self, args);\n }\n });\n\n return self;\n }\n\n // Set the group rate.\n if (typeof id === 'undefined') {\n self._rate = rate;\n }\n\n // Update one or all volumes.\n id = self._getSoundIds(id);\n for (var i=0; i Returns the first sound node's current seek position.\n * seek(id) -> Returns the sound id's current seek position.\n * seek(seek) -> Sets the seek position of the first sound node.\n * seek(seek, id) -> Sets the seek position of passed sound id.\n * @return {Howl/Number} Returns self or the current seek position.\n */\n seek: function() {\n var self = this;\n var args = arguments;\n var seek, id;\n\n // Determine the values based on arguments.\n if (args.length === 0) {\n // We will simply return the current position of the first node.\n if (self._sounds.length) {\n id = self._sounds[0]._id;\n }\n } else if (args.length === 1) {\n // First check if this is an ID, and if not, assume it is a new seek position.\n var ids = self._getSoundIds();\n var index = ids.indexOf(args[0]);\n if (index >= 0) {\n id = parseInt(args[0], 10);\n } else if (self._sounds.length) {\n id = self._sounds[0]._id;\n seek = parseFloat(args[0]);\n }\n } else if (args.length === 2) {\n seek = parseFloat(args[0]);\n id = parseInt(args[1], 10);\n }\n\n // If there is no ID, bail out.\n if (typeof id === 'undefined') {\n return 0;\n }\n\n // If the sound hasn't loaded, add it to the load queue to seek when capable.\n if (typeof seek === 'number' && (self._state !== 'loaded' || self._playLock)) {\n self._queue.push({\n event: 'seek',\n action: function() {\n self.seek.apply(self, args);\n }\n });\n\n return self;\n }\n\n // Get the sound.\n var sound = self._soundById(id);\n\n if (sound) {\n if (typeof seek === 'number' && seek >= 0) {\n // Pause the sound and update position for restarting playback.\n var playing = self.playing(id);\n if (playing) {\n self.pause(id, true);\n }\n\n // Move the position of the track and cancel timer.\n sound._seek = seek;\n sound._ended = false;\n self._clearTimer(id);\n\n // Update the seek position for HTML5 Audio.\n if (!self._webAudio && sound._node && !isNaN(sound._node.duration)) {\n sound._node.currentTime = seek;\n }\n\n // Seek and emit when ready.\n var seekAndEmit = function() {\n // Restart the playback if the sound was playing.\n if (playing) {\n self.play(id, true);\n }\n\n self._emit('seek', id);\n };\n\n // Wait for the play lock to be unset before emitting (HTML5 Audio).\n if (playing && !self._webAudio) {\n var emitSeek = function() {\n if (!self._playLock) {\n seekAndEmit();\n } else {\n setTimeout(emitSeek, 0);\n }\n };\n setTimeout(emitSeek, 0);\n } else {\n seekAndEmit();\n }\n } else {\n if (self._webAudio) {\n var realTime = self.playing(id) ? Howler.ctx.currentTime - sound._playStart : 0;\n var rateSeek = sound._rateSeek ? sound._rateSeek - sound._seek : 0;\n return sound._seek + (rateSeek + realTime * Math.abs(sound._rate));\n } else {\n return sound._node.currentTime;\n }\n }\n }\n\n return self;\n },\n\n /**\n * Check if a specific sound is currently playing or not (if id is provided), or check if at least one of the sounds in the group is playing or not.\n * @param {Number} id The sound id to check. If none is passed, the whole sound group is checked.\n * @return {Boolean} True if playing and false if not.\n */\n playing: function(id) {\n var self = this;\n\n // Check the passed sound ID (if any).\n if (typeof id === 'number') {\n var sound = self._soundById(id);\n return sound ? !sound._paused : false;\n }\n\n // Otherwise, loop through all sounds and check if any are playing.\n for (var i=0; i= 0) {\n Howler._howls.splice(index, 1);\n }\n\n // Delete this sound from the cache (if no other Howl is using it).\n var remCache = true;\n for (i=0; i= 0) {\n remCache = false;\n break;\n }\n }\n\n if (cache && remCache) {\n delete cache[self._src];\n }\n\n // Clear global errors.\n Howler.noAudio = false;\n\n // Clear out `self`.\n self._state = 'unloaded';\n self._sounds = [];\n self = null;\n\n return null;\n },\n\n /**\n * Listen to a custom event.\n * @param {String} event Event name.\n * @param {Function} fn Listener to call.\n * @param {Number} id (optional) Only listen to events for this sound.\n * @param {Number} once (INTERNAL) Marks event to fire only once.\n * @return {Howl}\n */\n on: function(event, fn, id, once) {\n var self = this;\n var events = self['_on' + event];\n\n if (typeof fn === 'function') {\n events.push(once ? {id: id, fn: fn, once: once} : {id: id, fn: fn});\n }\n\n return self;\n },\n\n /**\n * Remove a custom event. Call without parameters to remove all events.\n * @param {String} event Event name.\n * @param {Function} fn Listener to remove. Leave empty to remove all.\n * @param {Number} id (optional) Only remove events for this sound.\n * @return {Howl}\n */\n off: function(event, fn, id) {\n var self = this;\n var events = self['_on' + event];\n var i = 0;\n\n // Allow passing just an event and ID.\n if (typeof fn === 'number') {\n id = fn;\n fn = null;\n }\n\n if (fn || id) {\n // Loop through event store and remove the passed function.\n for (i=0; i=0; i--) {\n // Only fire the listener if the correct ID is used.\n if (!events[i].id || events[i].id === id || event === 'load') {\n setTimeout(function(fn) {\n fn.call(this, id, msg);\n }.bind(self, events[i].fn), 0);\n\n // If this event was setup with `once`, remove it.\n if (events[i].once) {\n self.off(event, events[i].fn, events[i].id);\n }\n }\n }\n\n // Pass the event type into load queue so that it can continue stepping.\n self._loadQueue(event);\n\n return self;\n },\n\n /**\n * Queue of actions initiated before the sound has loaded.\n * These will be called in sequence, with the next only firing\n * after the previous has finished executing (even if async like play).\n * @return {Howl}\n */\n _loadQueue: function(event) {\n var self = this;\n\n if (self._queue.length > 0) {\n var task = self._queue[0];\n\n // Remove this task if a matching event was passed.\n if (task.event === event) {\n self._queue.shift();\n self._loadQueue();\n }\n\n // Run the task if no event type is passed.\n if (!event) {\n task.action();\n }\n }\n\n return self;\n },\n\n /**\n * Fired when playback ends at the end of the duration.\n * @param {Sound} sound The sound object to work with.\n * @return {Howl}\n */\n _ended: function(sound) {\n var self = this;\n var sprite = sound._sprite;\n\n // If we are using IE and there was network latency we may be clipping\n // audio before it completes playing. Lets check the node to make sure it\n // believes it has completed, before ending the playback.\n if (!self._webAudio && sound._node && !sound._node.paused && !sound._node.ended && sound._node.currentTime < sound._stop) {\n setTimeout(self._ended.bind(self, sound), 100);\n return self;\n }\n\n // Should this sound loop?\n var loop = !!(sound._loop || self._sprite[sprite][2]);\n\n // Fire the ended event.\n self._emit('end', sound._id);\n\n // Restart the playback for HTML5 Audio loop.\n if (!self._webAudio && loop) {\n self.stop(sound._id, true).play(sound._id);\n }\n\n // Restart this timer if on a Web Audio loop.\n if (self._webAudio && loop) {\n self._emit('play', sound._id);\n sound._seek = sound._start || 0;\n sound._rateSeek = 0;\n sound._playStart = Howler.ctx.currentTime;\n\n var timeout = ((sound._stop - sound._start) * 1000) / Math.abs(sound._rate);\n self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);\n }\n\n // Mark the node as paused.\n if (self._webAudio && !loop) {\n sound._paused = true;\n sound._ended = true;\n sound._seek = sound._start || 0;\n sound._rateSeek = 0;\n self._clearTimer(sound._id);\n\n // Clean up the buffer source.\n self._cleanBuffer(sound._node);\n\n // Attempt to auto-suspend AudioContext if no sounds are still playing.\n Howler._autoSuspend();\n }\n\n // When using a sprite, end the track.\n if (!self._webAudio && !loop) {\n self.stop(sound._id, true);\n }\n\n return self;\n },\n\n /**\n * Clear the end timer for a sound playback.\n * @param {Number} id The sound ID.\n * @return {Howl}\n */\n _clearTimer: function(id) {\n var self = this;\n\n if (self._endTimers[id]) {\n // Clear the timeout or remove the ended listener.\n if (typeof self._endTimers[id] !== 'function') {\n clearTimeout(self._endTimers[id]);\n } else {\n var sound = self._soundById(id);\n if (sound && sound._node) {\n sound._node.removeEventListener('ended', self._endTimers[id], false);\n }\n }\n\n delete self._endTimers[id];\n }\n\n return self;\n },\n\n /**\n * Return the sound identified by this ID, or return null.\n * @param {Number} id Sound ID\n * @return {Object} Sound object or null.\n */\n _soundById: function(id) {\n var self = this;\n\n // Loop through all sounds and find the one with this ID.\n for (var i=0; i=0; i--) {\n if (cnt <= limit) {\n return;\n }\n\n if (self._sounds[i]._ended) {\n // Disconnect the audio source when using Web Audio.\n if (self._webAudio && self._sounds[i]._node) {\n self._sounds[i]._node.disconnect(0);\n }\n\n // Remove sounds until we have the pool size.\n self._sounds.splice(i, 1);\n cnt--;\n }\n }\n },\n\n /**\n * Get all ID's from the sounds pool.\n * @param {Number} id Only return one ID if one is passed.\n * @return {Array} Array of IDs.\n */\n _getSoundIds: function(id) {\n var self = this;\n\n if (typeof id === 'undefined') {\n var ids = [];\n for (var i=0; i= 0;\n\n if (Howler._scratchBuffer && node.bufferSource) {\n node.bufferSource.onended = null;\n node.bufferSource.disconnect(0);\n if (isIOS) {\n try { node.bufferSource.buffer = Howler._scratchBuffer; } catch(e) {}\n }\n }\n node.bufferSource = null;\n\n return self;\n },\n\n /**\n * Set the source to a 0-second silence to stop any downloading (except in IE).\n * @param {Object} node Audio node to clear.\n */\n _clearSound: function(node) {\n var checkIE = /MSIE |Trident\\//.test(Howler._navigator && Howler._navigator.userAgent);\n if (!checkIE) {\n node.src = 'data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA';\n }\n }\n };\n\n /** Single Sound Methods **/\n /***************************************************************************/\n\n /**\n * Setup the sound object, which each node attached to a Howl group is contained in.\n * @param {Object} howl The Howl parent group.\n */\n var Sound = function(howl) {\n this._parent = howl;\n this.init();\n };\n Sound.prototype = {\n /**\n * Initialize a new Sound object.\n * @return {Sound}\n */\n init: function() {\n var self = this;\n var parent = self._parent;\n\n // Setup the default parameters.\n self._muted = parent._muted;\n self._loop = parent._loop;\n self._volume = parent._volume;\n self._rate = parent._rate;\n self._seek = 0;\n self._paused = true;\n self._ended = true;\n self._sprite = '__default';\n\n // Generate a unique ID for this sound.\n self._id = ++Howler._counter;\n\n // Add itself to the parent's pool.\n parent._sounds.push(self);\n\n // Create the new node.\n self.create();\n\n return self;\n },\n\n /**\n * Create and setup a new sound object, whether HTML5 Audio or Web Audio.\n * @return {Sound}\n */\n create: function() {\n var self = this;\n var parent = self._parent;\n var volume = (Howler._muted || self._muted || self._parent._muted) ? 0 : self._volume;\n\n if (parent._webAudio) {\n // Create the gain node for controlling volume (the source will connect to this).\n self._node = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain();\n self._node.gain.setValueAtTime(volume, Howler.ctx.currentTime);\n self._node.paused = true;\n self._node.connect(Howler.masterGain);\n } else if (!Howler.noAudio) {\n // Get an unlocked Audio object from the pool.\n self._node = Howler._obtainHtml5Audio();\n\n // Listen for errors (http://dev.w3.org/html5/spec-author-view/spec.html#mediaerror).\n self._errorFn = self._errorListener.bind(self);\n self._node.addEventListener('error', self._errorFn, false);\n\n // Listen for 'canplaythrough' event to let us know the sound is ready.\n self._loadFn = self._loadListener.bind(self);\n self._node.addEventListener(Howler._canPlayEvent, self._loadFn, false);\n\n // Listen for the 'ended' event on the sound to account for edge-case where\n // a finite sound has a duration of Infinity.\n self._endFn = self._endListener.bind(self);\n self._node.addEventListener('ended', self._endFn, false);\n\n // Setup the new audio node.\n self._node.src = parent._src;\n self._node.preload = parent._preload === true ? 'auto' : parent._preload;\n self._node.volume = volume * Howler.volume();\n\n // Begin loading the source.\n self._node.load();\n }\n\n return self;\n },\n\n /**\n * Reset the parameters of this sound to the original state (for recycle).\n * @return {Sound}\n */\n reset: function() {\n var self = this;\n var parent = self._parent;\n\n // Reset all of the parameters of this sound.\n self._muted = parent._muted;\n self._loop = parent._loop;\n self._volume = parent._volume;\n self._rate = parent._rate;\n self._seek = 0;\n self._rateSeek = 0;\n self._paused = true;\n self._ended = true;\n self._sprite = '__default';\n\n // Generate a new ID so that it isn't confused with the previous sound.\n self._id = ++Howler._counter;\n\n return self;\n },\n\n /**\n * HTML5 Audio error listener callback.\n */\n _errorListener: function() {\n var self = this;\n\n // Fire an error event and pass back the code.\n self._parent._emit('loaderror', self._id, self._node.error ? self._node.error.code : 0);\n\n // Clear the event listener.\n self._node.removeEventListener('error', self._errorFn, false);\n },\n\n /**\n * HTML5 Audio canplaythrough listener callback.\n */\n _loadListener: function() {\n var self = this;\n var parent = self._parent;\n\n // Round up the duration to account for the lower precision in HTML5 Audio.\n parent._duration = Math.ceil(self._node.duration * 10) / 10;\n\n // Setup a sprite if none is defined.\n if (Object.keys(parent._sprite).length === 0) {\n parent._sprite = {__default: [0, parent._duration * 1000]};\n }\n\n if (parent._state !== 'loaded') {\n parent._state = 'loaded';\n parent._emit('load');\n parent._loadQueue();\n }\n\n // Clear the event listener.\n self._node.removeEventListener(Howler._canPlayEvent, self._loadFn, false);\n },\n\n /**\n * HTML5 Audio ended listener callback.\n */\n _endListener: function() {\n var self = this;\n var parent = self._parent;\n\n // Only handle the `ended`` event if the duration is Infinity.\n if (parent._duration === Infinity) {\n // Update the parent duration to match the real audio duration.\n // Round up the duration to account for the lower precision in HTML5 Audio.\n parent._duration = Math.ceil(self._node.duration * 10) / 10;\n\n // Update the sprite that corresponds to the real duration.\n if (parent._sprite.__default[1] === Infinity) {\n parent._sprite.__default[1] = parent._duration * 1000;\n }\n\n // Run the regular ended method.\n parent._ended(self);\n }\n\n // Clear the event listener since the duration is now correct.\n self._node.removeEventListener('ended', self._endFn, false);\n }\n };\n\n /** Helper Methods **/\n /***************************************************************************/\n\n var cache = {};\n\n /**\n * Buffer a sound from URL, Data URI or cache and decode to audio source (Web Audio API).\n * @param {Howl} self\n */\n var loadBuffer = function(self) {\n var url = self._src;\n\n // Check if the buffer has already been cached and use it instead.\n if (cache[url]) {\n // Set the duration from the cache.\n self._duration = cache[url].duration;\n\n // Load the sound into this Howl.\n loadSound(self);\n\n return;\n }\n\n if (/^data:[^;]+;base64,/.test(url)) {\n // Decode the base64 data URI without XHR, since some browsers don't support it.\n var data = atob(url.split(',')[1]);\n var dataView = new Uint8Array(data.length);\n for (var i=0; i 0) {\n cache[self._src] = buffer;\n loadSound(self, buffer);\n } else {\n error();\n }\n };\n\n // Decode the buffer into an audio source.\n if (typeof Promise !== 'undefined' && Howler.ctx.decodeAudioData.length === 1) {\n Howler.ctx.decodeAudioData(arraybuffer).then(success).catch(error);\n } else {\n Howler.ctx.decodeAudioData(arraybuffer, success, error);\n }\n }\n\n /**\n * Sound is now loaded, so finish setting everything up and fire the loaded event.\n * @param {Howl} self\n * @param {Object} buffer The decoded buffer sound source.\n */\n var loadSound = function(self, buffer) {\n // Set the duration.\n if (buffer && !self._duration) {\n self._duration = buffer.duration;\n }\n\n // Setup a sprite if none is defined.\n if (Object.keys(self._sprite).length === 0) {\n self._sprite = {__default: [0, self._duration * 1000]};\n }\n\n // Fire the loaded event.\n if (self._state !== 'loaded') {\n self._state = 'loaded';\n self._emit('load');\n self._loadQueue();\n }\n };\n\n /**\n * Setup the audio context when available, or switch to HTML5 Audio mode.\n */\n var setupAudioContext = function() {\n // If we have already detected that Web Audio isn't supported, don't run this step again.\n if (!Howler.usingWebAudio) {\n return;\n }\n\n // Check if we are using Web Audio and setup the AudioContext if we are.\n try {\n if (typeof AudioContext !== 'undefined') {\n Howler.ctx = new AudioContext();\n } else if (typeof webkitAudioContext !== 'undefined') {\n Howler.ctx = new webkitAudioContext();\n } else {\n Howler.usingWebAudio = false;\n }\n } catch(e) {\n Howler.usingWebAudio = false;\n }\n\n // If the audio context creation still failed, set using web audio to false.\n if (!Howler.ctx) {\n Howler.usingWebAudio = false;\n }\n\n // Check if a webview is being used on iOS8 or earlier (rather than the browser).\n // If it is, disable Web Audio as it causes crashing.\n var iOS = (/iP(hone|od|ad)/.test(Howler._navigator && Howler._navigator.platform));\n var appVersion = Howler._navigator && Howler._navigator.appVersion.match(/OS (\\d+)_(\\d+)_?(\\d+)?/);\n var version = appVersion ? parseInt(appVersion[1], 10) : null;\n if (iOS && version && version < 9) {\n var safari = /safari/.test(Howler._navigator && Howler._navigator.userAgent.toLowerCase());\n if (Howler._navigator && !safari) {\n Howler.usingWebAudio = false;\n }\n }\n\n // Create and expose the master GainNode when using Web Audio (useful for plugins or advanced usage).\n if (Howler.usingWebAudio) {\n Howler.masterGain = (typeof Howler.ctx.createGain === 'undefined') ? Howler.ctx.createGainNode() : Howler.ctx.createGain();\n Howler.masterGain.gain.setValueAtTime(Howler._muted ? 0 : Howler._volume, Howler.ctx.currentTime);\n Howler.masterGain.connect(Howler.ctx.destination);\n }\n\n // Re-run the setup on Howler.\n Howler._setup();\n };\n\n // Add support for AMD (Asynchronous Module Definition) libraries such as require.js.\n if (typeof define === 'function' && define.amd) {\n define([], function() {\n return {\n Howler: Howler,\n Howl: Howl\n };\n });\n }\n\n // Add support for CommonJS libraries such as browserify.\n if (typeof exports !== 'undefined') {\n exports.Howler = Howler;\n exports.Howl = Howl;\n }\n\n // Add to global in Node.js (for testing, etc).\n if (typeof global !== 'undefined') {\n global.HowlerGlobal = HowlerGlobal;\n global.Howler = Howler;\n global.Howl = Howl;\n global.Sound = Sound;\n } else if (typeof window !== 'undefined') { // Define globally in case AMD is not available or unused.\n window.HowlerGlobal = HowlerGlobal;\n window.Howler = Howler;\n window.Howl = Howl;\n window.Sound = Sound;\n }\n})();\n\n\n/*!\n * Spatial Plugin - Adds support for stereo and 3D audio where Web Audio is supported.\n * \n * howler.js v2.2.3\n * howlerjs.com\n *\n * (c) 2013-2020, James Simpson of GoldFire Studios\n * goldfirestudios.com\n *\n * MIT License\n */\n\n(function() {\n\n 'use strict';\n\n // Setup default properties.\n HowlerGlobal.prototype._pos = [0, 0, 0];\n HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0];\n\n /** Global Methods **/\n /***************************************************************************/\n\n /**\n * Helper method to update the stereo panning position of all current Howls.\n * Future Howls will not use this value unless explicitly set.\n * @param {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.\n * @return {Howler/Number} Self or current stereo panning value.\n */\n HowlerGlobal.prototype.stereo = function(pan) {\n var self = this;\n\n // Stop right here if not using Web Audio.\n if (!self.ctx || !self.ctx.listener) {\n return self;\n }\n\n // Loop through all Howls and update their stereo panning.\n for (var i=self._howls.length-1; i>=0; i--) {\n self._howls[i].stereo(pan);\n }\n\n return self;\n };\n\n /**\n * Get/set the position of the listener in 3D cartesian space. Sounds using\n * 3D position will be relative to the listener's position.\n * @param {Number} x The x-position of the listener.\n * @param {Number} y The y-position of the listener.\n * @param {Number} z The z-position of the listener.\n * @return {Howler/Array} Self or current listener position.\n */\n HowlerGlobal.prototype.pos = function(x, y, z) {\n var self = this;\n\n // Stop right here if not using Web Audio.\n if (!self.ctx || !self.ctx.listener) {\n return self;\n }\n\n // Set the defaults for optional 'y' & 'z'.\n y = (typeof y !== 'number') ? self._pos[1] : y;\n z = (typeof z !== 'number') ? self._pos[2] : z;\n\n if (typeof x === 'number') {\n self._pos = [x, y, z];\n\n if (typeof self.ctx.listener.positionX !== 'undefined') {\n self.ctx.listener.positionX.setTargetAtTime(self._pos[0], Howler.ctx.currentTime, 0.1);\n self.ctx.listener.positionY.setTargetAtTime(self._pos[1], Howler.ctx.currentTime, 0.1);\n self.ctx.listener.positionZ.setTargetAtTime(self._pos[2], Howler.ctx.currentTime, 0.1);\n } else {\n self.ctx.listener.setPosition(self._pos[0], self._pos[1], self._pos[2]);\n }\n } else {\n return self._pos;\n }\n\n return self;\n };\n\n /**\n * Get/set the direction the listener is pointing in the 3D cartesian space.\n * A front and up vector must be provided. The front is the direction the\n * face of the listener is pointing, and up is the direction the top of the\n * listener is pointing. Thus, these values are expected to be at right angles\n * from each other.\n * @param {Number} x The x-orientation of the listener.\n * @param {Number} y The y-orientation of the listener.\n * @param {Number} z The z-orientation of the listener.\n * @param {Number} xUp The x-orientation of the top of the listener.\n * @param {Number} yUp The y-orientation of the top of the listener.\n * @param {Number} zUp The z-orientation of the top of the listener.\n * @return {Howler/Array} Returns self or the current orientation vectors.\n */\n HowlerGlobal.prototype.orientation = function(x, y, z, xUp, yUp, zUp) {\n var self = this;\n\n // Stop right here if not using Web Audio.\n if (!self.ctx || !self.ctx.listener) {\n return self;\n }\n\n // Set the defaults for optional 'y' & 'z'.\n var or = self._orientation;\n y = (typeof y !== 'number') ? or[1] : y;\n z = (typeof z !== 'number') ? or[2] : z;\n xUp = (typeof xUp !== 'number') ? or[3] : xUp;\n yUp = (typeof yUp !== 'number') ? or[4] : yUp;\n zUp = (typeof zUp !== 'number') ? or[5] : zUp;\n\n if (typeof x === 'number') {\n self._orientation = [x, y, z, xUp, yUp, zUp];\n\n if (typeof self.ctx.listener.forwardX !== 'undefined') {\n self.ctx.listener.forwardX.setTargetAtTime(x, Howler.ctx.currentTime, 0.1);\n self.ctx.listener.forwardY.setTargetAtTime(y, Howler.ctx.currentTime, 0.1);\n self.ctx.listener.forwardZ.setTargetAtTime(z, Howler.ctx.currentTime, 0.1);\n self.ctx.listener.upX.setTargetAtTime(xUp, Howler.ctx.currentTime, 0.1);\n self.ctx.listener.upY.setTargetAtTime(yUp, Howler.ctx.currentTime, 0.1);\n self.ctx.listener.upZ.setTargetAtTime(zUp, Howler.ctx.currentTime, 0.1);\n } else {\n self.ctx.listener.setOrientation(x, y, z, xUp, yUp, zUp);\n }\n } else {\n return or;\n }\n\n return self;\n };\n\n /** Group Methods **/\n /***************************************************************************/\n\n /**\n * Add new properties to the core init.\n * @param {Function} _super Core init method.\n * @return {Howl}\n */\n Howl.prototype.init = (function(_super) {\n return function(o) {\n var self = this;\n\n // Setup user-defined default properties.\n self._orientation = o.orientation || [1, 0, 0];\n self._stereo = o.stereo || null;\n self._pos = o.pos || null;\n self._pannerAttr = {\n coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : 360,\n coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : 360,\n coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : 0,\n distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : 'inverse',\n maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : 10000,\n panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : 'HRTF',\n refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : 1,\n rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : 1\n };\n\n // Setup event listeners.\n self._onstereo = o.onstereo ? [{fn: o.onstereo}] : [];\n self._onpos = o.onpos ? [{fn: o.onpos}] : [];\n self._onorientation = o.onorientation ? [{fn: o.onorientation}] : [];\n\n // Complete initilization with howler.js core's init function.\n return _super.call(this, o);\n };\n })(Howl.prototype.init);\n\n /**\n * Get/set the stereo panning of the audio source for this sound or all in the group.\n * @param {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.\n * @param {Number} id (optional) The sound ID. If none is passed, all in group will be updated.\n * @return {Howl/Number} Returns self or the current stereo panning value.\n */\n Howl.prototype.stereo = function(pan, id) {\n var self = this;\n\n // Stop right here if not using Web Audio.\n if (!self._webAudio) {\n return self;\n }\n\n // If the sound hasn't loaded, add it to the load queue to change stereo pan when capable.\n if (self._state !== 'loaded') {\n self._queue.push({\n event: 'stereo',\n action: function() {\n self.stereo(pan, id);\n }\n });\n\n return self;\n }\n\n // Check for PannerStereoNode support and fallback to PannerNode if it doesn't exist.\n var pannerType = (typeof Howler.ctx.createStereoPanner === 'undefined') ? 'spatial' : 'stereo';\n\n // Setup the group's stereo panning if no ID is passed.\n if (typeof id === 'undefined') {\n // Return the group's stereo panning if no parameters are passed.\n if (typeof pan === 'number') {\n self._stereo = pan;\n self._pos = [pan, 0, 0];\n } else {\n return self._stereo;\n }\n }\n\n // Change the streo panning of one or all sounds in group.\n var ids = self._getSoundIds(id);\n for (var i=0; i Returns the group's values.\n * pannerAttr(id) -> Returns the sound id's values.\n * pannerAttr(o) -> Set's the values of all sounds in this Howl group.\n * pannerAttr(o, id) -> Set's the values of passed sound id.\n *\n * Attributes:\n * coneInnerAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,\n * inside of which there will be no volume reduction.\n * coneOuterAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,\n * outside of which the volume will be reduced to a constant value of `coneOuterGain`.\n * coneOuterGain - (0 by default) A parameter for directional audio sources, this is the gain outside of the\n * `coneOuterAngle`. It is a linear value in the range `[0, 1]`.\n * distanceModel - ('inverse' by default) Determines algorithm used to reduce volume as audio moves away from\n * listener. Can be `linear`, `inverse` or `exponential.\n * maxDistance - (10000 by default) The maximum distance between source and listener, after which the volume\n * will not be reduced any further.\n * refDistance - (1 by default) A reference distance for reducing volume as source moves further from the listener.\n * This is simply a variable of the distance model and has a different effect depending on which model\n * is used and the scale of your coordinates. Generally, volume will be equal to 1 at this distance.\n * rolloffFactor - (1 by default) How quickly the volume reduces as source moves from listener. This is simply a\n * variable of the distance model and can be in the range of `[0, 1]` with `linear` and `[0, \u221E]`\n * with `inverse` and `exponential`.\n * panningModel - ('HRTF' by default) Determines which spatialization algorithm is used to position audio.\n * Can be `HRTF` or `equalpower`.\n *\n * @return {Howl/Object} Returns self or current panner attributes.\n */\n Howl.prototype.pannerAttr = function() {\n var self = this;\n var args = arguments;\n var o, id, sound;\n\n // Stop right here if not using Web Audio.\n if (!self._webAudio) {\n return self;\n }\n\n // Determine the values based on arguments.\n if (args.length === 0) {\n // Return the group's panner attribute values.\n return self._pannerAttr;\n } else if (args.length === 1) {\n if (typeof args[0] === 'object') {\n o = args[0];\n\n // Set the grou's panner attribute values.\n if (typeof id === 'undefined') {\n if (!o.pannerAttr) {\n o.pannerAttr = {\n coneInnerAngle: o.coneInnerAngle,\n coneOuterAngle: o.coneOuterAngle,\n coneOuterGain: o.coneOuterGain,\n distanceModel: o.distanceModel,\n maxDistance: o.maxDistance,\n refDistance: o.refDistance,\n rolloffFactor: o.rolloffFactor,\n panningModel: o.panningModel\n };\n }\n\n self._pannerAttr = {\n coneInnerAngle: typeof o.pannerAttr.coneInnerAngle !== 'undefined' ? o.pannerAttr.coneInnerAngle : self._coneInnerAngle,\n coneOuterAngle: typeof o.pannerAttr.coneOuterAngle !== 'undefined' ? o.pannerAttr.coneOuterAngle : self._coneOuterAngle,\n coneOuterGain: typeof o.pannerAttr.coneOuterGain !== 'undefined' ? o.pannerAttr.coneOuterGain : self._coneOuterGain,\n distanceModel: typeof o.pannerAttr.distanceModel !== 'undefined' ? o.pannerAttr.distanceModel : self._distanceModel,\n maxDistance: typeof o.pannerAttr.maxDistance !== 'undefined' ? o.pannerAttr.maxDistance : self._maxDistance,\n refDistance: typeof o.pannerAttr.refDistance !== 'undefined' ? o.pannerAttr.refDistance : self._refDistance,\n rolloffFactor: typeof o.pannerAttr.rolloffFactor !== 'undefined' ? o.pannerAttr.rolloffFactor : self._rolloffFactor,\n panningModel: typeof o.pannerAttr.panningModel !== 'undefined' ? o.pannerAttr.panningModel : self._panningModel\n };\n }\n } else {\n // Return this sound's panner attribute values.\n sound = self._soundById(parseInt(args[0], 10));\n return sound ? sound._pannerAttr : self._pannerAttr;\n }\n } else if (args.length === 2) {\n o = args[0];\n id = parseInt(args[1], 10);\n }\n\n // Update the values of the specified sounds.\n var ids = self._getSoundIds(id);\n for (var i=0; i 80 * dim) {\n minX = maxX = data[0];\n minY = maxY = data[1];\n\n for (var i = dim; i < outerLen; i += dim) {\n x = data[i];\n y = data[i + 1];\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n }\n\n // minX, minY and invSize are later used to transform coords into integers for z-order calculation\n invSize = Math.max(maxX - minX, maxY - minY);\n invSize = invSize !== 0 ? 32767 / invSize : 0;\n }\n\n earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);\n\n return triangles;\n}\n\n// create a circular doubly linked list from polygon points in the specified winding order\nfunction linkedList(data, start, end, dim, clockwise) {\n var i, last;\n\n if (clockwise === (signedArea(data, start, end, dim) > 0)) {\n for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);\n } else {\n for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);\n }\n\n if (last && equals(last, last.next)) {\n removeNode(last);\n last = last.next;\n }\n\n return last;\n}\n\n// eliminate colinear or duplicate points\nfunction filterPoints(start, end) {\n if (!start) return start;\n if (!end) end = start;\n\n var p = start,\n again;\n do {\n again = false;\n\n if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {\n removeNode(p);\n p = end = p.prev;\n if (p === p.next) break;\n again = true;\n\n } else {\n p = p.next;\n }\n } while (again || p !== end);\n\n return end;\n}\n\n// main ear slicing loop which triangulates a polygon (given as a linked list)\nfunction earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {\n if (!ear) return;\n\n // interlink polygon nodes in z-order\n if (!pass && invSize) indexCurve(ear, minX, minY, invSize);\n\n var stop = ear,\n prev, next;\n\n // iterate through ears, slicing them one by one\n while (ear.prev !== ear.next) {\n prev = ear.prev;\n next = ear.next;\n\n if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {\n // cut off the triangle\n triangles.push(prev.i / dim | 0);\n triangles.push(ear.i / dim | 0);\n triangles.push(next.i / dim | 0);\n\n removeNode(ear);\n\n // skipping the next vertex leads to less sliver triangles\n ear = next.next;\n stop = next.next;\n\n continue;\n }\n\n ear = next;\n\n // if we looped through the whole remaining polygon and can't find any more ears\n if (ear === stop) {\n // try filtering points and slicing again\n if (!pass) {\n earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);\n\n // if this didn't work, try curing all small self-intersections locally\n } else if (pass === 1) {\n ear = cureLocalIntersections(filterPoints(ear), triangles, dim);\n earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);\n\n // as a last resort, try splitting the remaining polygon into two\n } else if (pass === 2) {\n splitEarcut(ear, triangles, dim, minX, minY, invSize);\n }\n\n break;\n }\n }\n}\n\n// check whether a polygon node forms a valid ear with adjacent nodes\nfunction isEar(ear) {\n var a = ear.prev,\n b = ear,\n c = ear.next;\n\n if (area(a, b, c) >= 0) return false; // reflex, can't be an ear\n\n // now make sure we don't have other points inside the potential ear\n var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;\n\n // triangle bbox; min & max are calculated like this for speed\n var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),\n y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),\n x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),\n y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy);\n\n var p = c.next;\n while (p !== a) {\n if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 &&\n pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) &&\n area(p.prev, p, p.next) >= 0) return false;\n p = p.next;\n }\n\n return true;\n}\n\nfunction isEarHashed(ear, minX, minY, invSize) {\n var a = ear.prev,\n b = ear,\n c = ear.next;\n\n if (area(a, b, c) >= 0) return false; // reflex, can't be an ear\n\n var ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;\n\n // triangle bbox; min & max are calculated like this for speed\n var x0 = ax < bx ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx),\n y0 = ay < by ? (ay < cy ? ay : cy) : (by < cy ? by : cy),\n x1 = ax > bx ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx),\n y1 = ay > by ? (ay > cy ? ay : cy) : (by > cy ? by : cy);\n\n // z-order range for the current triangle bbox;\n var minZ = zOrder(x0, y0, minX, minY, invSize),\n maxZ = zOrder(x1, y1, minX, minY, invSize);\n\n var p = ear.prevZ,\n n = ear.nextZ;\n\n // look for points inside the triangle in both directions\n while (p && p.z >= minZ && n && n.z <= maxZ) {\n if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&\n pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;\n p = p.prevZ;\n\n if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&\n pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;\n n = n.nextZ;\n }\n\n // look for remaining points in decreasing z-order\n while (p && p.z >= minZ) {\n if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c &&\n pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;\n p = p.prevZ;\n }\n\n // look for remaining points in increasing z-order\n while (n && n.z <= maxZ) {\n if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c &&\n pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;\n n = n.nextZ;\n }\n\n return true;\n}\n\n// go through all polygon nodes and cure small local self-intersections\nfunction cureLocalIntersections(start, triangles, dim) {\n var p = start;\n do {\n var a = p.prev,\n b = p.next.next;\n\n if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {\n\n triangles.push(a.i / dim | 0);\n triangles.push(p.i / dim | 0);\n triangles.push(b.i / dim | 0);\n\n // remove two nodes involved\n removeNode(p);\n removeNode(p.next);\n\n p = start = b;\n }\n p = p.next;\n } while (p !== start);\n\n return filterPoints(p);\n}\n\n// try splitting polygon into two and triangulate them independently\nfunction splitEarcut(start, triangles, dim, minX, minY, invSize) {\n // look for a valid diagonal that divides the polygon into two\n var a = start;\n do {\n var b = a.next.next;\n while (b !== a.prev) {\n if (a.i !== b.i && isValidDiagonal(a, b)) {\n // split the polygon in two by the diagonal\n var c = splitPolygon(a, b);\n\n // filter colinear points around the cuts\n a = filterPoints(a, a.next);\n c = filterPoints(c, c.next);\n\n // run earcut on each half\n earcutLinked(a, triangles, dim, minX, minY, invSize, 0);\n earcutLinked(c, triangles, dim, minX, minY, invSize, 0);\n return;\n }\n b = b.next;\n }\n a = a.next;\n } while (a !== start);\n}\n\n// link every hole into the outer loop, producing a single-ring polygon without holes\nfunction eliminateHoles(data, holeIndices, outerNode, dim) {\n var queue = [],\n i, len, start, end, list;\n\n for (i = 0, len = holeIndices.length; i < len; i++) {\n start = holeIndices[i] * dim;\n end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;\n list = linkedList(data, start, end, dim, false);\n if (list === list.next) list.steiner = true;\n queue.push(getLeftmost(list));\n }\n\n queue.sort(compareX);\n\n // process holes from left to right\n for (i = 0; i < queue.length; i++) {\n outerNode = eliminateHole(queue[i], outerNode);\n }\n\n return outerNode;\n}\n\nfunction compareX(a, b) {\n return a.x - b.x;\n}\n\n// find a bridge between vertices that connects hole with an outer ring and and link it\nfunction eliminateHole(hole, outerNode) {\n var bridge = findHoleBridge(hole, outerNode);\n if (!bridge) {\n return outerNode;\n }\n\n var bridgeReverse = splitPolygon(bridge, hole);\n\n // filter collinear points around the cuts\n filterPoints(bridgeReverse, bridgeReverse.next);\n return filterPoints(bridge, bridge.next);\n}\n\n// David Eberly's algorithm for finding a bridge between hole and outer polygon\nfunction findHoleBridge(hole, outerNode) {\n var p = outerNode,\n hx = hole.x,\n hy = hole.y,\n qx = -Infinity,\n m;\n\n // find a segment intersected by a ray from the hole's leftmost point to the left;\n // segment's endpoint with lesser x will be potential connection point\n do {\n if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {\n var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);\n if (x <= hx && x > qx) {\n qx = x;\n m = p.x < p.next.x ? p : p.next;\n if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint\n }\n }\n p = p.next;\n } while (p !== outerNode);\n\n if (!m) return null;\n\n // look for points inside the triangle of hole point, segment intersection and endpoint;\n // if there are no points found, we have a valid connection;\n // otherwise choose the point of the minimum angle with the ray as connection point\n\n var stop = m,\n mx = m.x,\n my = m.y,\n tanMin = Infinity,\n tan;\n\n p = m;\n\n do {\n if (hx >= p.x && p.x >= mx && hx !== p.x &&\n pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {\n\n tan = Math.abs(hy - p.y) / (hx - p.x); // tangential\n\n if (locallyInside(p, hole) &&\n (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {\n m = p;\n tanMin = tan;\n }\n }\n\n p = p.next;\n } while (p !== stop);\n\n return m;\n}\n\n// whether sector in vertex m contains sector in vertex p in the same coordinates\nfunction sectorContainsSector(m, p) {\n return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;\n}\n\n// interlink polygon nodes in z-order\nfunction indexCurve(start, minX, minY, invSize) {\n var p = start;\n do {\n if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);\n p.prevZ = p.prev;\n p.nextZ = p.next;\n p = p.next;\n } while (p !== start);\n\n p.prevZ.nextZ = null;\n p.prevZ = null;\n\n sortLinked(p);\n}\n\n// Simon Tatham's linked list merge sort algorithm\n// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html\nfunction sortLinked(list) {\n var i, p, q, e, tail, numMerges, pSize, qSize,\n inSize = 1;\n\n do {\n p = list;\n list = null;\n tail = null;\n numMerges = 0;\n\n while (p) {\n numMerges++;\n q = p;\n pSize = 0;\n for (i = 0; i < inSize; i++) {\n pSize++;\n q = q.nextZ;\n if (!q) break;\n }\n qSize = inSize;\n\n while (pSize > 0 || (qSize > 0 && q)) {\n\n if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {\n e = p;\n p = p.nextZ;\n pSize--;\n } else {\n e = q;\n q = q.nextZ;\n qSize--;\n }\n\n if (tail) tail.nextZ = e;\n else list = e;\n\n e.prevZ = tail;\n tail = e;\n }\n\n p = q;\n }\n\n tail.nextZ = null;\n inSize *= 2;\n\n } while (numMerges > 1);\n\n return list;\n}\n\n// z-order of a point given coords and inverse of the longer side of data bbox\nfunction zOrder(x, y, minX, minY, invSize) {\n // coords are transformed into non-negative 15-bit integer range\n x = (x - minX) * invSize | 0;\n y = (y - minY) * invSize | 0;\n\n x = (x | (x << 8)) & 0x00FF00FF;\n x = (x | (x << 4)) & 0x0F0F0F0F;\n x = (x | (x << 2)) & 0x33333333;\n x = (x | (x << 1)) & 0x55555555;\n\n y = (y | (y << 8)) & 0x00FF00FF;\n y = (y | (y << 4)) & 0x0F0F0F0F;\n y = (y | (y << 2)) & 0x33333333;\n y = (y | (y << 1)) & 0x55555555;\n\n return x | (y << 1);\n}\n\n// find the leftmost node of a polygon ring\nfunction getLeftmost(start) {\n var p = start,\n leftmost = start;\n do {\n if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p;\n p = p.next;\n } while (p !== start);\n\n return leftmost;\n}\n\n// check if a point lies within a convex triangle\nfunction pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {\n return (cx - px) * (ay - py) >= (ax - px) * (cy - py) &&\n (ax - px) * (by - py) >= (bx - px) * (ay - py) &&\n (bx - px) * (cy - py) >= (cx - px) * (by - py);\n}\n\n// check if a diagonal between two polygon nodes is valid (lies in polygon interior)\nfunction isValidDiagonal(a, b) {\n return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges\n (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible\n (area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors\n equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case\n}\n\n// signed area of a triangle\nfunction area(p, q, r) {\n return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);\n}\n\n// check if two points are equal\nfunction equals(p1, p2) {\n return p1.x === p2.x && p1.y === p2.y;\n}\n\n// check if two segments intersect\nfunction intersects(p1, q1, p2, q2) {\n var o1 = sign(area(p1, q1, p2));\n var o2 = sign(area(p1, q1, q2));\n var o3 = sign(area(p2, q2, p1));\n var o4 = sign(area(p2, q2, q1));\n\n if (o1 !== o2 && o3 !== o4) return true; // general case\n\n if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1\n if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1\n if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2\n if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2\n\n return false;\n}\n\n// for collinear points p, q, r, check if point q lies on segment pr\nfunction onSegment(p, q, r) {\n return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);\n}\n\nfunction sign(num) {\n return num > 0 ? 1 : num < 0 ? -1 : 0;\n}\n\n// check if a polygon diagonal intersects any polygon segments\nfunction intersectsPolygon(a, b) {\n var p = a;\n do {\n if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&\n intersects(p, p.next, a, b)) return true;\n p = p.next;\n } while (p !== a);\n\n return false;\n}\n\n// check if a polygon diagonal is locally inside the polygon\nfunction locallyInside(a, b) {\n return area(a.prev, a, a.next) < 0 ?\n area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :\n area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;\n}\n\n// check if the middle point of a polygon diagonal is inside the polygon\nfunction middleInside(a, b) {\n var p = a,\n inside = false,\n px = (a.x + b.x) / 2,\n py = (a.y + b.y) / 2;\n do {\n if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&\n (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))\n inside = !inside;\n p = p.next;\n } while (p !== a);\n\n return inside;\n}\n\n// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;\n// if one belongs to the outer ring and another to a hole, it merges it into a single ring\nfunction splitPolygon(a, b) {\n var a2 = new Node(a.i, a.x, a.y),\n b2 = new Node(b.i, b.x, b.y),\n an = a.next,\n bp = b.prev;\n\n a.next = b;\n b.prev = a;\n\n a2.next = an;\n an.prev = a2;\n\n b2.next = a2;\n a2.prev = b2;\n\n bp.next = b2;\n b2.prev = bp;\n\n return b2;\n}\n\n// create a node and optionally link it with previous one (in a circular doubly linked list)\nfunction insertNode(i, x, y, last) {\n var p = new Node(i, x, y);\n\n if (!last) {\n p.prev = p;\n p.next = p;\n\n } else {\n p.next = last.next;\n p.prev = last;\n last.next.prev = p;\n last.next = p;\n }\n return p;\n}\n\nfunction removeNode(p) {\n p.next.prev = p.prev;\n p.prev.next = p.next;\n\n if (p.prevZ) p.prevZ.nextZ = p.nextZ;\n if (p.nextZ) p.nextZ.prevZ = p.prevZ;\n}\n\nfunction Node(i, x, y) {\n // vertex index in coordinates array\n this.i = i;\n\n // vertex coordinates\n this.x = x;\n this.y = y;\n\n // previous and next vertex nodes in a polygon ring\n this.prev = null;\n this.next = null;\n\n // z-order curve value\n this.z = 0;\n\n // previous and next nodes in z-order\n this.prevZ = null;\n this.nextZ = null;\n\n // indicates whether this is a steiner point\n this.steiner = false;\n}\n\n// return a percentage difference between the polygon area and its triangulation area;\n// used to verify correctness of triangulation\nearcut.deviation = function (data, holeIndices, dim, triangles) {\n var hasHoles = holeIndices && holeIndices.length;\n var outerLen = hasHoles ? holeIndices[0] * dim : data.length;\n\n var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));\n if (hasHoles) {\n for (var i = 0, len = holeIndices.length; i < len; i++) {\n var start = holeIndices[i] * dim;\n var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;\n polygonArea -= Math.abs(signedArea(data, start, end, dim));\n }\n }\n\n var trianglesArea = 0;\n for (i = 0; i < triangles.length; i += 3) {\n var a = triangles[i] * dim;\n var b = triangles[i + 1] * dim;\n var c = triangles[i + 2] * dim;\n trianglesArea += Math.abs(\n (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -\n (data[a] - data[b]) * (data[c + 1] - data[a + 1]));\n }\n\n return polygonArea === 0 && trianglesArea === 0 ? 0 :\n Math.abs((trianglesArea - polygonArea) / polygonArea);\n};\n\nfunction signedArea(data, start, end, dim) {\n var sum = 0;\n for (var i = start, j = end - dim; i < end; i += dim) {\n sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);\n j = i;\n }\n return sum;\n}\n\n// turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts\nearcut.flatten = function (data) {\n var dim = data[0][0].length,\n result = {vertices: [], holes: [], dimensions: dim},\n holeIndex = 0;\n\n for (var i = 0; i < data.length; i++) {\n for (var j = 0; j < data[i].length; j++) {\n for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]);\n }\n if (i > 0) {\n holeIndex += data[i - 1].length;\n result.holes.push(holeIndex);\n }\n }\n return result;\n};\n", "export const bigInt=()=>(async e=>{try{return(await WebAssembly.instantiate(e)).instance.exports.b(BigInt(0))===BigInt(0)}catch(e){return!1}})(new Uint8Array([0,97,115,109,1,0,0,0,1,6,1,96,1,126,1,126,3,2,1,0,7,5,1,1,98,0,0,10,6,1,4,0,32,0,11])),bulkMemory=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,5,3,1,0,1,10,14,1,12,0,65,0,65,0,65,0,252,10,0,0,11])),exceptions=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,8,1,6,0,6,64,25,11,11])),extendedConst=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,5,3,1,0,1,11,9,1,0,65,1,65,2,106,11,0])),gc=()=>(async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,95,1,120,0])))(),jspi=()=>(async()=>\"Suspending\"in WebAssembly)(),memory64=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,5,3,1,4,1])),multiMemory=()=>(async()=>{try{return new WebAssembly.Module(new Uint8Array([0,97,115,109,1,0,0,0,5,5,2,0,0,0,0])),!0}catch(e){return!1}})(),multiValue=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,6,1,96,0,2,127,127,3,2,1,0,10,8,1,6,0,65,0,65,0,11])),mutableGlobals=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,2,8,1,1,97,1,98,3,127,1,6,6,1,127,1,65,0,11,7,5,1,1,97,3,1])),referenceTypes=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,7,1,5,0,208,112,26,11])),relaxedSimd=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,15,1,13,0,65,1,253,15,65,2,253,15,253,128,2,11])),saturatedFloatToInt=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,12,1,10,0,67,0,0,0,0,252,0,26,11])),signExtensions=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,8,1,6,0,65,0,192,26,11])),simd=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11])),streamingCompilation=()=>(async()=>\"compileStreaming\"in WebAssembly)(),tailCall=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,10,6,1,4,0,18,0,11])),threads=()=>(async e=>{try{return\"undefined\"!=typeof MessageChannel&&(new MessageChannel).port1.postMessage(new SharedArrayBuffer(1)),WebAssembly.validate(e)}catch(e){return!1}})(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,5,4,1,3,1,1,10,11,1,9,0,65,0,254,16,2,0,26,11])),typeReflection=()=>(async()=>\"Function\"in WebAssembly)();\n", "/**\n * Component property type.\n */\nexport enum Type {\n /**\n * **Native**\n *\n * Property of a native component. Must not be used in custom components.\n *\n * @hidden\n */\n Native = 0,\n\n /**\n * **Bool**:\n *\n * Appears in the editor as a checkbox.\n *\n * Initial value is `false`, unless overridden by the `default` property.\n */\n Bool = 1,\n\n /**\n * **Int**:\n *\n * Appears in the editor as an integer input field.\n *\n * Initial value is `0`, unless overridden by the `default` property.\n */\n Int = 2,\n\n /**\n * **Float**:\n *\n * Appears in the editor as a floating point input field.\n *\n * Initial value is `0.0`, unless overridden by the `default` property.\n */\n Float = 3,\n\n /**\n * **String / Text**:\n *\n * Appears in the editor as a single-line text input field.\n *\n * Initial value is an empty string, unless overridden by the `default`\n * property.\n */\n String = 4,\n\n /**\n * **Enumeration**:\n *\n * Appears in the editor as a dropdown with given values. The additional\n * `values` parameter with selection options is mandatory.\n *\n * The property value is resolved to an **index** into the `values` array.\n *\n * Initial value is the first element in `values`, unless overridden by\n * the `default` property. The `default` value can be a string or an index\n * into `values`.\n *\n * @example\n *\n * ```js\n * camera: {type: Type.Enum, values: ['auto', 'back', 'front'], default: 'auto'},\n * ```\n */\n Enum = 5,\n\n /**\n * **Object reference**:\n *\n * Appears in the editor as an object resource selection dropdown\n * with object picker.\n *\n * Initial value is `null`.\n */\n Object = 6,\n\n /**\n * **Mesh reference**:\n *\n * Appears in the editor as a mesh resource selection dropdown.\n *\n * Initial value is `null`.\n */\n Mesh = 7,\n\n /**\n * **Texture reference**:\n *\n * Appears in the editor as a texture resource selection dropdown.\n *\n * Initial value is `null`.\n */\n Texture = 8,\n\n /**\n * **Material reference**:\n *\n * Appears in the editor as a material resource selection dropdown.\n *\n * Initial value is `null`.\n */\n Material = 9,\n\n /**\n * **Animation reference**:\n *\n * Appears in the editor as an animation resource selection dropdown.\n *\n * Initial value is `null`.\n */\n Animation = 10,\n\n /**\n * **Skin reference**:\n *\n * Appears in the editor as a skin resource selection dropdown.\n *\n * Initial value is `null`.\n */\n Skin = 11,\n\n /**\n * **Color**:\n *\n * Appears in the editor as a color widget.\n *\n * Initial value is `[0.0, 0.0, 0.0, 1.0]`, unless overridden by the\n * `default` property.\n */\n Color = 12,\n\n /**\n * **Vector of two floats**:\n *\n * Appears in the editor as a two-element floating point input field.\n *\n * Initial value is `[0.0, 0.0]`, unless overridden by the\n * `default` property.\n */\n Vector2 = 13,\n\n /**\n * **Vector of three floats**:\n *\n * Appears in the editor as a three-element floating point input field.\n *\n * Initial value is `[0.0, 0.0, 0.0]`, unless overridden by the\n * `default` property.\n */\n Vector3 = 14,\n\n /**\n * **Vector of four floats**:\n *\n * Appears in the editor as a four-element floating point input field.\n *\n * Initial value is `[0.0, 0.0, 0.0, 0.0]`, unless overridden by the\n * `default` property.\n */\n Vector4 = 15,\n\n /**\n * **Array of property**:\n *\n * Appears in the editor as a list of **N** elements.\n *\n * Initial value is `[]`, unless overridden by the `default` property.\n */\n Array = 16,\n\n /**\n * **Class with sub-properties**:\n *\n * Appears in the editor as a nested properties structure.\n *\n * Initial value is `{}`, unless overridden by the `default` property.\n */\n Record = 17,\n\n /**\n * **Particle effect reference**:\n *\n * Appears in the editor as a particle effect resource selection dropdown.\n *\n * Initial value is `null`.\n */\n ParticleEffect = 18,\n\n /** @hidden */\n Count = 19,\n}\n\n/**\n * Cloning interface for component properties.\n *\n * Used for component initialization and cloning.\n */\nexport interface PropertyCloner {\n /**\n * Clone a property value.\n * @param type Type of the property.\n * @param value Value of the property.\n * @returns The cloned value.\n */\n clone(type: Type, value: any): any;\n}\n\n/**\n * Interface describing a record class used in properties.\n *\n * ```ts\n * import {property, PropertyRecord} from '@wonderlandengine/api';\n * class Record implements PropertyRecord {\n * @property.string()\n * string: string;\n * @property.record(SubRecord)\n * subrecord: SubRecord;\n * }\n * ```\n *\n * @note Implementing `PropertyRecord` isn't required.\n */\nexport interface PropertyRecord {\n new (): unknown;\n /** Static properties definition, similar to {@link Component.Properties}. */\n Properties?: Record;\n /** @hidden */\n _propertyOrder?: string[];\n}\n\n/**\n * Default cloner implementation.\n *\n * Clones array-like properties and leaves all other types unchanged.\n */\nexport class DefaultPropertyCloner implements PropertyCloner {\n clone(type: Type, value: any): any {\n switch (type) {\n case Type.Color:\n case Type.Vector2:\n case Type.Vector3:\n case Type.Vector4:\n case Type.Array:\n /** @todo: Array should be deep cloned. */\n return value.slice();\n case Type.Record: {\n if (!value || !value.constructor) return value;\n\n const RecordClass = value.constructor as PropertyRecord;\n const result = new RecordClass() as Record;\n for (const key in RecordClass.Properties) {\n const prop = RecordClass.Properties[key];\n result[key] = this.clone(prop.type, value[key]);\n }\n return result;\n }\n default:\n return value;\n }\n }\n}\n\n/** Default cloner for property values. */\nexport const defaultPropertyCloner = new DefaultPropertyCloner();\n\n/**\n * Custom component property.\n *\n * For more information about component properties, have a look\n * at the {@link Component.Properties} attribute.\n */\nexport interface ComponentProperty {\n /** Property type. */\n type: Type;\n /** Default value, depending on type. */\n default?: any;\n /** Values for {@link Type.Enum} */\n values?: string[];\n /** Record definition, used if the property type is {@link Type.Record}. */\n record?: PropertyRecord;\n element?: ComponentProperty;\n required?: boolean;\n /**\n * Cloner for the property.\n *\n * If not defined, falls back to {@link defaultPropertyCloner}. To prevent\n * any cloning, set a custom cloner that passes the original value back\n * from {@link PropertyCloner.clone}. */\n cloner?: PropertyCloner;\n}\n\n/**\n * Component property namespace.\n *\n * Usage:\n *\n * ```js\n * import {Component, Property} from '@wonderlandengine/api';\n *\n * class MyComponent extends Component {\n * static Properties = {\n * myBool: Property.bool(true),\n * myInt: Property.int(42),\n * myString: Property.string('Hello World!'),\n * myMesh: Property.mesh()\n * }\n * }\n * ```\n *\n * It's possible to nest properties using record, and array:\n *\n * ```js\n * import {Component, Property} from '@wonderlandengine/api';\n *\n * class Weapon {\n * static Properties = {\n * damage: Property.float(100.0),\n * fireRate: Property.int(10)\n * }\n * }\n * class Zombie {\n * static Properties = {\n * names: Property.string('Mad Zombie'),\n * animation: Property.animation()\n * }\n * }\n *\n * class Player extends Component {\n * static Properties = {\n * weapon: Property.record(Weapon),\n * zombieTargets: Property.array(Zombie),\n * }\n * }\n * ```\n *\n * For TypeScript users, you can use the decorators instead.\n */\nexport const Property = {\n /**\n * Create an boolean property.\n *\n * @param defaultValue The default value. If not provided, defaults to `false`.\n */\n bool(defaultValue: boolean = false): ComponentProperty {\n return {type: Type.Bool, default: defaultValue};\n },\n\n /**\n * Create an integer property.\n *\n * @param defaultValue The default value. If not provided, defaults to `0`.\n */\n int(defaultValue: number = 0): ComponentProperty {\n return {type: Type.Int, default: defaultValue};\n },\n\n /**\n * Create an float property.\n *\n * @param defaultValue The default value. If not provided, defaults to `0.0`.\n */\n float(defaultValue: number = 0.0): ComponentProperty {\n return {type: Type.Float, default: defaultValue};\n },\n\n /**\n * Create an string property.\n *\n * @param defaultValue The default value. If not provided, defaults to `''`.\n */\n string(defaultValue = ''): ComponentProperty {\n return {type: Type.String, default: defaultValue};\n },\n\n /**\n * Create an enumeration property.\n *\n * @param values The list of values.\n * @param defaultValue The default value. Can be a string or an index into\n * `values`. If not provided, defaults to the first element.\n */\n enum(values: string[], defaultValue?: string | number): ComponentProperty {\n return {type: Type.Enum, values, default: defaultValue};\n },\n\n /** Create an {@link Object3D} reference property. */\n object(opts?: PropertyReferenceOptions): ComponentProperty {\n return {type: Type.Object, default: null, required: opts?.required ?? false};\n },\n\n /** Create a {@link Mesh} reference property. */\n mesh(opts?: PropertyReferenceOptions): ComponentProperty {\n return {type: Type.Mesh, default: null, required: opts?.required ?? false};\n },\n\n /** Create a {@link Texture} reference property. */\n texture(opts?: PropertyReferenceOptions): ComponentProperty {\n return {type: Type.Texture, default: null, required: opts?.required ?? false};\n },\n\n /** Create a {@link Material} reference property. */\n material(opts?: PropertyReferenceOptions): ComponentProperty {\n return {type: Type.Material, default: null, required: opts?.required ?? false};\n },\n\n /** Create an {@link Animation} reference property. */\n animation(opts?: PropertyReferenceOptions): ComponentProperty {\n return {type: Type.Animation, default: null, required: opts?.required ?? false};\n },\n\n /** Create a {@link Skin} reference property. */\n skin(opts?: PropertyReferenceOptions): ComponentProperty {\n return {type: Type.Skin, default: null, required: opts?.required ?? false};\n },\n\n /** Create a {@link ParticleEffect} reference property. */\n particleEffect(opts?: PropertyReferenceOptions): ComponentProperty {\n return {\n type: Type.ParticleEffect,\n default: null,\n required: opts?.required ?? false,\n };\n },\n\n /**\n * Create a color property.\n *\n * @param r The red component, in the range [0; 1].\n * @param g The green component, in the range [0; 1].\n * @param b The blue component, in the range [0; 1].\n * @param a The alpha component, in the range [0; 1].\n */\n color(r = 0.0, g = 0.0, b = 0.0, a = 1.0): ComponentProperty {\n return {type: Type.Color, default: [r, g, b, a]};\n },\n\n /**\n * Create a two-element vector property.\n *\n * @param x The x component.\n * @param y The y component.\n */\n vector2(x = 0.0, y = 0.0): ComponentProperty {\n return {type: Type.Vector2, default: [x, y]};\n },\n\n /**\n * Create a three-element vector property.\n *\n * @param x The x component.\n * @param y The y component.\n * @param z The z component.\n */\n vector3(x = 0.0, y = 0.0, z = 0.0): ComponentProperty {\n return {type: Type.Vector3, default: [x, y, z]};\n },\n\n /**\n * Create a four-element vector property.\n *\n * @param x The x component.\n * @param y The y component.\n * @param z The z component.\n * @param w The w component.\n */\n vector4(x = 0.0, y = 0.0, z = 0.0, w = 0.0): ComponentProperty {\n return {type: Type.Vector4, default: [x, y, z, w]};\n },\n\n /**\n * Create a class property.\n *\n * @param definition The template class, containing sub properties.\n */\n record(definition: PropertyRecord): ComponentProperty {\n return {type: Type.Record, record: definition, default: new definition()};\n },\n\n /**\n * Create an unsized array of properties.\n *\n * @note Array properties can only contain a single element type.\n *\n * @param element The element property.\n */\n array(element: ComponentProperty): ComponentProperty {\n /** @todo: Implement ArrayBuffer, which are much faster to decode */\n return {type: Type.Array, element, default: undefined};\n },\n};\n\n/**\n * Options to create a reference property, i.e.,\n * object, mesh, animation, skin, etc...\n */\nexport interface PropertyReferenceOptions {\n /** If `true`, the component will throw if the property isn't initialized. */\n required?: boolean;\n}\n\n/** All the keys that exists on the {@link Property} object. */\nexport type PropertyKeys = keyof typeof Property;\n\n/** Retrieve all the argument types of a {@link Property} function. */\nexport type PropertyArgs = Parameters<(typeof Property)[key]>;\n", "import {Component, ComponentConstructor} from './wonderland.js';\nimport {Property, PropertyArgs, PropertyKeys, ComponentProperty, Type} from './property.js';\n\n/**\n * Decorator for JS component properties.\n *\n * @param data The property description as an object literal\n * @returns A decorator function modifying the `Properties` static\n * attribute\n */\nfunction propertyDecorator(data: ComponentProperty) {\n return function (target: any, propertyKey: string): void {\n const ctor = target.constructor as ComponentConstructor;\n ctor.Properties = ctor.hasOwnProperty('Properties') ? ctor.Properties : {};\n ctor.Properties[propertyKey] = data;\n };\n}\n\n/**\n * Decorator for making a getter enumerable.\n *\n * Usage:\n *\n * ```ts\n * class MyClass {\n * @enumerable()\n * get projectionMatrix(): Float32Array { ... }\n * }\n * ```\n */\nexport function enumerable() {\n return function (_: any, __: string, descriptor: PropertyDescriptor) {\n descriptor.enumerable = true;\n };\n}\n\n/**\n * Decorator for native properties.\n *\n * Usage:\n *\n * ```ts\n * class MyClass {\n * @nativeProperty()\n * get projectionMatrix(): Float32Array { ... }\n * }\n * ```\n */\nexport function nativeProperty() {\n return function (\n target: Component,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n enumerable()(target, propertyKey, descriptor);\n propertyDecorator({type: Type.Native})(target, propertyKey);\n };\n}\n\n/**\n * Property decorators namespace.\n *\n * You can use the decorators to mark a class attribute as\n * a Wonderland Engine property.\n *\n * Usage:\n *\n * ```ts\n * import {Mesh} from '@wonderlandengine/api';\n * import {property} from '@wonderlandengine/api/decorators.js';\n *\n * class MyComponent extends Component {\n * @property.bool(true)\n * myBool!: boolean;\n *\n * @property.int(42)\n * myInt!: number;\n *\n * @property.string('Hello World!')\n * myString!: string;\n *\n * @property.mesh()\n * myMesh!: Mesh;\n * }\n * ```\n *\n * For JavaScript users, please declare the properties statically.\n */\nexport const property = {} as {\n [key in PropertyKeys]: (\n ...args: PropertyArgs\n ) => ReturnType;\n};\n\nfor (const name in Property) {\n /* Assign each property functor to a TypeScript decorator.\n * This code extracts parameters and return type to provide proper\n * typings to the user. */\n property[name as PropertyKeys] = (...args: PropertyArgs) => {\n const functor = Property[name as PropertyKeys] as (\n ...args: unknown[]\n ) => ComponentProperty;\n return propertyDecorator(functor(...args));\n };\n}\n", "import {ImageLike} from '../types.js';\n\n/**\n * Check if a given value is a native string or a `String` instance.\n *\n * @param value The value to check.\n * @returns `true` if the `value` has type string literal or `String`, `false` otherwise.\n */\nexport function isString(value: any): value is string {\n if (value === '') return true;\n return value && (typeof value === 'string' || value.constructor === String);\n}\n\n/**\n * Check if a given value is a native number or a `Number` instance.\n *\n * @param value The value to check.\n * @returns `true` if the `value` has type number literal or `Number`, `false` otherwise.\n */\nexport function isNumber(value: any): value is number {\n if (value === null || value === undefined) return false;\n return typeof value === 'number' || value.constructor === Number;\n}\n\n/**\n * Check whether a given value is a visual media.\n *\n * @param value The value to check\n * @returns `true` if the `value` is an image, video, or canvas.\n */\nexport function isImageLike(value: any): value is ImageLike {\n return (\n value instanceof HTMLImageElement ||\n value instanceof HTMLVideoElement ||\n value instanceof HTMLCanvasElement\n );\n}\n", "/**\n * Listener callback type, used in {@link Emitter.add}.\n */\nexport type ListenerCallback = (...data: T) => void;\n\n/**\n * Registration options for a listener in an {@link Emitter}.\n */\nexport interface ListenerOptions {\n /**\n * Listener identifier. This is used to find and remove the listener\n * without needing the callback reference.\n *\n * The identifier can be any type. However, remember that the comparison will be\n * by-value for primitive types (string, number), but by reference for objects.\n *\n * For more information, please look at the {@link Emitter.remove} method.\n */\n id: any | undefined;\n /**\n * If `true`, the listener is automatically removed after it's invoked.\n * Defaults to `false`.\n */\n once: boolean;\n}\n\n/** Internal listener type. */\ntype Listener = ListenerOptions & {\n callback: ListenerCallback;\n};\n\n/** Internal type for transaction. */\nenum TransactionType {\n Addition = 1,\n Removal = 2,\n}\n\n/**\n * Event emitter.\n *\n * This class allows to register listeners that will get notified by the emitter.\n *\n * Usage example:\n *\n * ```js\n * // `onPreRender` is an `Emitter` instance.\n * scene.onPreRender.add(() => console.log('before rendering'));\n * // `onPostRender` is an `Emitter` instance.\n * scene.onPostRender.add(() => console.log('after rendering'));\n * ```\n *\n * You can create your own emitters:\n *\n * ```js\n * import {Emitter} from '@wonderlandengine/api';\n *\n * const emitter = new Emitter();\n * ```\n *\n * You can notify listeners in to your emitter using {@link Emitter.notify}:\n *\n * ```js\n * // Notifies all the listeners.\n * emitter.notify();\n * // Notifies all the listeners with some data.\n * emitter.notify({ myInt: 42, myStr: 'Hello World!' });\n * ```\n *\n * @category event\n */\nexport class Emitter {\n /**\n * List of listeners to trigger when `notify` is called.\n *\n * @hidden\n */\n protected readonly _listeners: Listener[] = [];\n\n /**\n * `true` if the emitter is currently notifying listeners. This\n * is used to defer addition and removal.\n *\n * @hidden\n */\n private _notifying = false;\n\n /**\n * Pending additions / removals, performed during a notification.\n *\n * @hidden\n */\n private readonly _transactions: {\n type: TransactionType;\n data: Listener | ListenerCallback;\n }[] = [];\n\n /**\n * Register a new listener to be triggered on {@link Emitter.notify}.\n *\n * Basic usage:\n *\n * ```js\n * emitter.add((data) => {\n * console.log('event received!');\n * console.log(data);\n * });\n * ```\n *\n * Automatically remove the listener when an event is received:\n *\n * ```js\n * emitter.add((data) => {\n * console.log('event received!');\n * console.log(data);\n * }, {once: true});\n * ```\n *\n * @param listener The callback to register.\n * @param opts The listener options. For more information, please have a look\n * at the {@link ListenerOptions} interface.\n *\n * @returns Reference to self (for method chaining)\n */\n add(listener: ListenerCallback, opts: Partial = {}): this {\n const {once = false, id = undefined} = opts;\n const data = {id, once, callback: listener};\n if (this._notifying) {\n this._transactions.push({type: TransactionType.Addition, data});\n return this;\n }\n this._listeners.push(data);\n return this;\n }\n\n /**\n * Equivalent to {@link Emitter.add}.\n *\n * @param listeners The callback(s) to register.\n * @returns Reference to self (for method chaining).\n *\n * @deprecated Please use {@link Emitter.add} instead.\n */\n push(...listeners: ListenerCallback[]): this {\n for (const cb of listeners) this.add(cb);\n return this;\n }\n\n /**\n * Register a new listener to be triggered on {@link Emitter.notify}.\n *\n * Once notified, the listener will be automatically removed.\n *\n * The method is equivalent to calling {@link Emitter.add} with:\n *\n * ```js\n * emitter.add(listener, {once: true});\n * ```\n *\n * @param listener The callback to register.\n *\n * @returns Reference to self (for method chaining).\n */\n once(listener: ListenerCallback) {\n return this.add(listener, {once: true});\n }\n\n /**\n * Remove a registered listener.\n *\n * Usage with a callback:\n *\n * ```js\n * const listener = (data) => console.log(data);\n * emitter.add(listener);\n *\n * // Remove using the callback reference:\n * emitter.remove(listener);\n * ```\n *\n * Usage with an id:\n *\n * ```js\n * emitter.add((data) => console.log(data), {id: 'my-callback'});\n *\n * // Remove using the id:\n * emitter.remove('my-callback');\n * ```\n *\n * Using identifiers, you will need to ensure your value is unique to avoid\n * removing listeners from other libraries, e.g.,:\n *\n * ```js\n * emitter.add((data) => console.log(data), {id: 'non-unique'});\n * // This second listener could be added by a third-party library.\n * emitter.add((data) => console.log('Hello From Library!'), {id: 'non-unique'});\n *\n * // Ho Snap! This also removed the library listener!\n * emitter.remove('non-unique');\n * ```\n *\n * The identifier can be any type. However, remember that the comparison will be\n * by-value for primitive types (string, number), but by reference for objects.\n *\n * Example:\n *\n * ```js\n * emitter.add(() => console.log('Hello'), {id: {value: 42}});\n * emitter.add(() => console.log('World!'), {id: {value: 42}});\n * emitter.remove({value: 42}); // None of the above listeners match!\n * emitter.notify(); // Prints 'Hello' and 'World!'.\n * ```\n *\n * Here, both emitters have id `{value: 42}`, but the comparison is made by reference. Thus,\n * the `remove()` call has no effect. We can make it work by doing:\n *\n * ```js\n * const id = {value: 42};\n * emitter.add(() => console.log('Hello'), {id});\n * emitter.add(() => console.log('World!'), {id});\n * emitter.remove(id); // Same reference, it works!\n * emitter.notify(); // Doesn't print.\n * ```\n *\n * @param listener The registered callback or a value representing the `id`.\n *\n * @returns Reference to self (for method chaining)\n */\n remove(listener: ListenerCallback | any): this {\n if (this._notifying) {\n this._transactions.push({type: TransactionType.Removal, data: listener});\n return this;\n }\n\n const listeners = this._listeners;\n for (let i = 0; i < listeners.length; ++i) {\n const target = listeners[i];\n if (target.callback === listener || target.id === listener) {\n listeners.splice(i--, 1);\n }\n }\n return this;\n }\n\n /**\n * Check whether the listener is registered.\n *\n * @note This method performs a linear search.\n *\n * * @note Doesn't account for pending listeners, i.e.,\n * listeners added / removed during a notification.\n *\n * @param listener The registered callback or a value representing the `id`.\n * @returns `true` if the handle is found, `false` otherwise.\n */\n has(listener: ListenerCallback | any): boolean {\n const listeners = this._listeners;\n for (let i = 0; i < listeners.length; ++i) {\n const target = listeners[i];\n if (target.callback === listener || target.id === listener) return true;\n }\n return false;\n }\n\n /**\n * Notify listeners with the given data object.\n *\n * @note This method ensures all listeners are called even if\n * an exception is thrown. For (possibly) faster notification,\n * please use {@link Emitter.notifyUnsafe}.\n *\n * @param data The data to pass to listener when invoked.\n */\n notify(...data: T): void {\n const listeners = this._listeners;\n\n this._notifying = true;\n for (let i = 0; i < listeners.length; ++i) {\n const listener = listeners[i];\n if (listener.once) listeners.splice(i--, 1);\n try {\n listener.callback(...data);\n } catch (e) {\n console.error(e);\n }\n }\n this._notifying = false;\n\n /* Process deferred additions & removals */\n this._flushTransactions();\n }\n\n /**\n * Notify listeners with the given data object.\n *\n * @note Because this method doesn't catch exceptions, some listeners\n * will be skipped on a throw. Please use {@link Emitter.notify} for safe\n * notification.\n *\n * @param data The data to pass to listener when invoked.\n */\n notifyUnsafe(...data: T): void {\n const listeners = this._listeners;\n\n for (let i = 0; i < listeners.length; ++i) {\n const listener = listeners[i];\n if (listener.once) listeners.splice(i--, 1);\n listener.callback(...data);\n }\n\n /* Process deferred additions & removals */\n this._flushTransactions();\n }\n\n /**\n * Return a promise that will resolve on the next event.\n *\n * @note The promise might never resolve if no event is sent.\n *\n * @returns A promise that resolves with the data passed to\n * {@link Emitter.notify}.\n */\n promise(): Promise {\n return new Promise((res, _) => {\n this.once((...args) => {\n if (args.length > 1) {\n res(args);\n } else {\n res(args[0] as T);\n }\n });\n });\n }\n\n /**\n * Number of listeners.\n *\n * @note Doesn't account for pending listeners, i.e.,\n * listeners added / removed during a notification.\n */\n get listenerCount() {\n return this._listeners.length;\n }\n\n /** `true` if it has no listeners, `false` otherwise. */\n get isEmpty() {\n return this.listenerCount === 0;\n }\n\n /**\n * Flush all pending transactions.\n *\n * @hidden\n */\n private _flushTransactions() {\n const listeners = this._listeners;\n for (const transaction of this._transactions) {\n if (transaction.type === TransactionType.Addition) {\n listeners.push(transaction.data as Listener);\n } else {\n this.remove(transaction.data as ListenerCallback);\n }\n }\n this._transactions.length = 0;\n }\n}\n\n/**\n * Registration options for a listener in an {@link RetainEmitter}.\n *\n * Those options extend {@link ListenerOptions}.\n */\nexport interface RetainListenerOptions extends ListenerOptions {\n /**\n * If `true`, directly resolves if the emitter retains a value. If `false`,\n * the listener isn't invoked until the next {@link Emitter.notify}.\n *\n * Defaults to `true`.\n */\n immediate: boolean;\n}\n\n/* Dummy value used with RetainEmitter. */\nconst RetainEmitterUndefined: Record = {};\n\n/**\n * Event emitter that retains event data when notified.\n *\n * After a notification happens, subsequent calls to {@link RetainEmitter.add} will get\n * automatically notified.\n *\n * You can use another emitter in order to cancel the last retained event:\n *\n * ```js\n * import {Emitter, RetainedEmitter} from '@wonderlandengine/api';\n *\n * const onStart = new RetainedEmitter();\n *\n * onStart.notify(42);\n * onStart.add((data) => console.log(data)) // Prints '42'.\n * ```\n *\n * You can reset the state of the emitter, i.e., making it forget about the\n * last event using:\n *\n * ```js\n * import {Emitter, RetainedEmitter} from '@wonderlandengine/api';\n *\n * const onStart = new RetainedEmitter();\n * onStart.notify(42);\n * onStart.add((data) => console.log(data)) // Prints '42'.\n *\n * // Reset the state of the emitter.\n * onStart.reset();\n * onStart.add((data) => console.log(data)) // Doesn't print anything.\n * ```\n *\n * For more information about emitters, please have a look at the base {@link Emitter} class.\n *\n * @category event\n */\nexport class RetainEmitter extends Emitter {\n /** Pre-resolved data. @hidden */\n protected _event: T | typeof RetainEmitterUndefined = RetainEmitterUndefined;\n\n /**\n * Emitter target used to reset the state of this emitter.\n *\n * @hidden\n */\n protected readonly _reset: Emitter | undefined;\n\n /** @override */\n add(listener: ListenerCallback, opts?: Partial): this {\n const immediate = opts?.immediate ?? true;\n if (this._event !== RetainEmitterUndefined && immediate) {\n listener(...(this._event as T));\n }\n super.add(listener, opts);\n return this;\n }\n\n /**\n * @override\n *\n * @param listener The callback to register.\n * @param immediate If `true`, directly resolves if the emitter retains a value.\n *\n * @returns Reference to self (for method chaining).\n */\n once(listener: ListenerCallback, immediate?: boolean) {\n return this.add(listener, {once: true, immediate});\n }\n\n /** @override */\n notify(...data: T): void {\n this._event = data;\n super.notify(...data);\n }\n\n /** @override */\n notifyUnsafe(...data: T): void {\n this._event = data;\n super.notifyUnsafe(...data);\n }\n\n /**\n * Reset the state of the emitter.\n *\n * Further call to {@link Emitter.add} will not automatically resolve,\n * until a new call to {@link Emitter.notify} is performed.\n *\n * @returns Reference to self (for method chaining)\n */\n reset(): this {\n this._event = RetainEmitterUndefined;\n return this;\n }\n\n /** Returns the retained data, or `undefined` if no data was retained. */\n get data(): T | undefined {\n return this.isDataRetained ? this._event : undefined;\n }\n\n /** `true` if data is retained from the last event, `false` otherwise. */\n get isDataRetained(): boolean {\n return this._event !== RetainEmitterUndefined;\n }\n}\n", "import {WonderlandEngine} from '../engine.js';\nimport {Prefab} from '../prefab.js';\nimport {Scene} from '../scene.js';\nimport {FirstConstructorParam} from '../types.js';\n\n/** Interface for a resource class */\ntype ResourceConstructor = {\n new (host: FirstConstructorParam, index: number): T;\n};\n\n/**\n * Create a proxy throwing destroyed errors upon access.\n *\n * @param type The type to display upon error\n * @returns The proxy instance\n */\nfunction createDestroyedProxy(\n host: FirstConstructorParam,\n type: ResourceConstructor\n) {\n return new Proxy(\n {},\n {\n get(_, param: string) {\n if (param === 'isDestroyed') return true;\n throw new Error(\n `Cannot read '${param}' of destroyed '${type.name}' resource from ${host}`\n );\n },\n set(_, param: string) {\n throw new Error(\n `Cannot write '${param}' of destroyed '${type.name}' resource from ${host}`\n );\n },\n }\n );\n}\n\n/**\n * Base class for engine resources, such as:\n * - {@link Texture}\n * - {@link Mesh}\n * - {@link Material}\n *\n * @since 1.2.0\n */\nexport abstract class Resource {\n /** Relative index in the host. @hidden */\n readonly _index: number = -1;\n /** For compatibility with SceneResource. @hidden */\n readonly _id: number = -1;\n /** @hidden */\n private readonly _engine: WonderlandEngine;\n\n constructor(engine: WonderlandEngine, index: number) {\n this._engine = engine;\n this._index = index;\n this._id = index;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._engine;\n }\n\n /** Index of this resource in the {@link Scene}'s manager. */\n get index() {\n return this._index;\n }\n\n /**\n * Checks equality by comparing ids and **not** the JavaScript reference.\n *\n * @deprecated Use JavaScript reference comparison instead:\n *\n * ```js\n * const meshA = engine.meshes.create({vertexCount: 1});\n * const meshB = engine.meshes.create({vertexCount: 1});\n * const meshC = meshB;\n * console.log(meshA === meshB); // false\n * console.log(meshA === meshA); // true\n * console.log(meshB === meshC); // true\n * ```\n */\n equals(other: this | undefined | null): boolean {\n if (!other) return false;\n return this._index === other._index;\n }\n\n /**\n * `true` if the object is destroyed, `false` otherwise.\n *\n * If {@link WonderlandEngine.erasePrototypeOnDestroy} is `true`,\n * reading a class attribute / method will throw.\n */\n get isDestroyed() {\n return this._index <= 0;\n }\n}\n\n/**\n * Base class for scene resources, such as:\n * * - {@link Texture}\n * - {@link Mesh}\n * - {@link Material}\n * - {@link Skin}\n * - {@link Animation}\n *\n * @since 1.2.0\n */\nexport abstract class SceneResource {\n /** @hidden */\n static _pack(scene: number, index: number) {\n return (scene << 22) | index;\n }\n\n /** Relative index in the host. @hidden */\n readonly _index: number = -1;\n /** For compatibility with SceneResource. @hidden */\n readonly _id: number = -1;\n /** @hidden */\n protected readonly _scene: Prefab;\n\n constructor(scene: Prefab, index: number) {\n this._scene = scene;\n this._index = index;\n this._id = SceneResource._pack(scene._index, index);\n }\n\n /**\n * Checks equality by comparing ids and **not** the JavaScript reference.\n *\n * @deprecated Use JavaScript reference comparison instead:\n *\n * ```js\n * const meshA = engine.meshes.create({vertexCount: 1});\n * const meshB = engine.meshes.create({vertexCount: 1});\n * const meshC = meshB;\n * console.log(meshA === meshB); // false\n * console.log(meshA === meshA); // true\n * console.log(meshB === meshC); // true\n * ```\n */\n equals(other: this | undefined | null): boolean {\n if (!other) return false;\n return this._id === other._id;\n }\n\n /** Hosting instance. */\n get scene() {\n return this._scene;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._scene.engine;\n }\n\n /** Index of this resource in the {@link Scene}'s manager. */\n get index() {\n return this._index;\n }\n\n /**\n * `true` if the object is destroyed, `false` otherwise.\n *\n * If {@link WonderlandEngine.erasePrototypeOnDestroy} is `true`,\n * reading a class attribute / method will throw.\n */\n get isDestroyed() {\n return this._id <= 0;\n }\n}\n\n/**\n * Manager for resources.\n *\n * Resources are accessed via the engine they belong to.\n *\n * @see {@link WonderlandEngine.textures}, {@link WonderlandEngine.meshes},\n * and {@link WonderlandEngine.materials}.\n *\n * @since 1.2.0\n */\nexport class ResourceManager {\n /** @hidden */\n protected readonly _host: FirstConstructorParam;\n /** Cache. @hidden */\n protected readonly _cache: (T | null)[] = [];\n\n /** Resource class. @hidden */\n private readonly _template: ResourceConstructor;\n\n /** Destructor proxy, used if {@link WonderlandEngine.erasePrototypeOnDestroy} is `true`. @hidden */\n private _destructor: {} | null = null;\n\n private readonly _engine: WonderlandEngine;\n\n /**\n * Create a new manager\n *\n * @param host The host containing the managed resources.\n * @param Class The class to instantiate when wrapping an index.\n *\n * @hidden\n */\n constructor(host: FirstConstructorParam, Class: ResourceConstructor) {\n this._host = host;\n this._template = Class;\n this._engine = (host as Prefab).engine ?? host;\n }\n\n /**\n * Wrap the index into a resource instance.\n *\n * @note The index is relative to the host, i.e., doesn't pack the host index (if any).\n *\n * @param index The resource index.\n * @returns\n */\n wrap(index: number) {\n if (index <= 0) return null;\n const texture =\n this._cache[index] ??\n (this._cache[index] = new this._template(this._host, index));\n return texture;\n }\n\n /**\n * Retrieve the resource at the given index.\n *\n * @note The index is relative to the host, i.e., doesn't pack the host index.\n */\n get(index: number): T | null {\n return this._cache[index] ?? null;\n }\n\n /** Number of textures allocated in the manager. */\n get allocatedCount() {\n return this._cache.length;\n }\n\n /**\n * Number of textures in the manager.\n *\n * @note For performance reasons, avoid calling this method when possible.\n */\n get count() {\n let count = 0;\n for (const res of this._cache) {\n if (res && res.index >= 0) ++count;\n }\n return count;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._engine;\n }\n\n /**\n * Destroy the instance.\n *\n * @note This method takes care of the prototype destruction.\n *\n * @hidden\n */\n _destroy(instance: T) {\n const index = instance.index;\n (instance._index as number) = -1;\n (instance._id as number) = -1;\n this._cache[index] = null;\n\n if (!this.engine.erasePrototypeOnDestroy) return;\n\n if (!this._destructor)\n this._destructor = createDestroyedProxy(this._host, this._template);\n Object.setPrototypeOf(instance, this._destructor);\n }\n\n /**\n * Mark all instances as destroyed.\n *\n * @hidden\n */\n _clear() {\n if (!this.engine.erasePrototypeOnDestroy) return;\n for (let i = 0; i < this._cache.length; ++i) {\n const instance = this._cache[i];\n if (instance) this._destroy(instance);\n }\n this._cache.length = 0;\n }\n}\n", "/*\n * The MIT License (MIT)\n *\n * Copyright (c) 2014-2016 Patrick Gansterer \n * Copyright (c) 2020-2023 Aaron Huggins \n * Copyright (c) 2024 Wonderland GmbH \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy of\n * this software and associated documentation files (the \"Software\"), to deal in\n * the Software without restriction, including without limitation the rights to\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n * the Software, and to permit persons to whom the Software is furnished to do so,\n * subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nconst kCborTagBignum = 2;\nconst kCborTagNegativeBignum = 3;\n/* RFC 8746 Tag values for typed little-endian arrays */\nconst kCborTagUint8 = 64;\nconst kCborTagUint16 = 69;\nconst kCborTagUint32 = 70;\nconst kCborTagUint64 = 71;\nconst kCborTagInt8 = 72;\nconst kCborTagInt16 = 77;\nconst kCborTagInt32 = 78;\nconst kCborTagInt64 = 79;\nconst kCborTagFloat32 = 85;\nconst kCborTagFloat64 = 86;\n\n/**\n * Whether key/value dictionaries should be decoded as objects or as\n * `Map`. The latter is useful if non-string keys or iterating in\n * insertion order are required.\n */\nexport type DictionaryOption = 'object' | 'map';\n\n/** Options for the decoder. */\nexport interface Options {\n /** The dictionary type to use. Defaults to `object`. */\n dictionary?: DictionaryOption;\n}\n\n/** A function to modify tagged values which are encountered during decoding. */\nexport type Tagger = (tag: number | bigint, value: any) => any;\n\n/**\n * Converts a Concise Binary Object Representation (CBOR) buffer into an object.\n *\n * ```js\n * const buffer = new Uint8Array([0xa2, 0x01, 0x02, 0x03, 0x04]).buffer;\n * const decoded = decode(buffer);\n * console.log(decoded); // { \"1\": 2, \"3\": 4 }\n * ```\n *\n * CBOR values can be wrapped in a numeric tag. To handle and possibly\n * transform tagged values, pass a tagger function:\n *\n * ```js\n * const buffer = new Uint8Array([\n * 0xa1, 0x63, 0x75, 0x72, 0x6c, 0xd8, 0x20, 0x70,\n * 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,\n * 0x69, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f\n * ]);\n * const decoded = decode(buffer, (tag, value) => {\n * if (tag === 32) return new URL(value);\n * return value;\n * });\n * console.log(decoded); // { url: URL { href: \"http://site.com/\" } }\n * ```\n *\n * Decoded basic types generally match the equivalent JavaScript types. Byte\n * strings are decoded to Uint8Array.\n *\n * Tagged values are left as-is, with the following exceptions:\n * - Bignum values (byte strings with tag 2 or 3) are decoded to BigInt\n * - Little-endian versions of typed arrays as defined in RFC 8746 are decoded\n * to JavaScript typed arrays\n *\n * @param data A valid CBOR buffer.\n * @param tagger Optional callback for transformation of tagged values.\n * @param options Options for decoding behavior.\n * @returns The CBOR buffer converted to a JavaScript value.\n */\nexport function decode(\n data: Uint8Array,\n tagger: Tagger = (_, value) => value,\n options: Options = {}\n): T {\n const reader = new CBORReader(data);\n reader.tagger = tagger;\n if (options.dictionary) {\n reader.dictionary = options.dictionary;\n }\n\n const ret = reader.decodeItem();\n if (reader.offset !== data.byteLength) {\n throw new Error('CBORError: Remaining bytes');\n }\n return ret;\n}\n\nexport enum CBORType {\n Array = 0,\n Record = 1,\n Constant = 2,\n Native = 3,\n}\n\n/**\n * Check whether the type is an array, a record,\n * or a native type (decodable without recursion).\n *\n * @param typeInfo Type information obtained using `readTypeInfo`.\n * @returns The type category.\n */\nexport function getType(typeInfo: number): CBORType {\n const majorType = typeInfo >> 5;\n const additionalInformation = typeInfo & 0x1f;\n switch (majorType) {\n case 4:\n return CBORType.Array;\n case 5:\n return CBORType.Record;\n case 7:\n return CBORType.Constant;\n }\n return CBORType.Native;\n}\n\nexport function isUndefined(type: CBORType, length: number) {\n return type === CBORType.Constant && length === 23;\n}\n\n/**\n * CBOR reader.\n */\nexport class CBORReader {\n dataView: DataView;\n data: Uint8Array;\n offset: number;\n tagger: Tagger = (_, value) => value;\n dictionary: 'object' | 'map' = 'object';\n\n constructor(data: Uint8Array) {\n this.dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n this.data = data;\n this.offset = 0;\n }\n\n /* Wonderland extra methods. */\n\n /**\n * Read type information and move the cursor.\n *\n * @note Must be called before reading the value.\n */\n readTypeInfo() {\n return this.readUint8();\n }\n\n /**\n * Read the array length.\n *\n * @note Must not be called if the type is a constant.\n *\n * @param typeInfo Type information obtained using `readTypeInfo`.\n * @returns The array length.\n */\n readArrayLength(typeInfo: number) {\n const majorType = typeInfo >> 5;\n const additionalInformation = typeInfo & 0x1f;\n const length = this.readLength(additionalInformation);\n if (length < 0 && (majorType < 2 || 6 < majorType)) {\n throw new Error('CBORError: Invalid length');\n }\n return length;\n }\n\n /* CBOR original decoding methods. */\n\n /**\n * Recursively read the item.\n *\n * @note This method will recursively allocate arrays and records\n * based the item type.\n *\n * @param typeInfo Type information obtained using `readTypeInfo`.\n * @returns The decoded value.\n */\n readItem(typeInfo: number, inputLen: number | null = null): any {\n const majorType = typeInfo >> 5;\n const additionalInformation = typeInfo & 0x1f;\n let i;\n\n if (majorType === 7) {\n switch (additionalInformation) {\n case 25:\n return this.readFloat16();\n case 26:\n return this.readFloat32();\n case 27:\n return this.readFloat64();\n }\n }\n\n let length = inputLen === null ? this.readLength(additionalInformation) : inputLen;\n if (length < 0 && (majorType < 2 || 6 < majorType)) {\n throw new Error('CBORError: Invalid length');\n }\n\n switch (majorType) {\n case 0:\n return length;\n case 1:\n if (typeof length === 'number') {\n return -1 - length;\n }\n return -1n - length;\n case 2: {\n if (length < 0) {\n const elements = [];\n let fullArrayLength = 0;\n while ((length = this.readIndefiniteStringLength(majorType)) >= 0) {\n fullArrayLength += length;\n elements.push(this.readArrayBuffer(length));\n }\n const fullArray = new Uint8Array(fullArrayLength);\n let fullArrayOffset = 0;\n for (i = 0; i < elements.length; ++i) {\n fullArray.set(elements[i], fullArrayOffset);\n fullArrayOffset += elements[i].length;\n }\n return fullArray;\n }\n return this.readArrayBuffer(length as number).slice();\n }\n case 3: {\n /* Can't use TextDecoder.decode() because not all browsers\n * support calling it with an underlying SharedArrayBuffer yet.\n * See https://github.com/whatwg/encoding/issues/172 */\n /** @todo Re-evaluate browser support */\n const utf16data: number[] = [];\n if (length < 0) {\n while ((length = this.readIndefiniteStringLength(majorType)) >= 0) {\n this.appendUtf16Data(utf16data, length);\n }\n } else {\n this.appendUtf16Data(utf16data, length as number);\n }\n let string = '';\n const DECODE_CHUNK_SIZE = 8192;\n for (i = 0; i < utf16data.length; i += DECODE_CHUNK_SIZE) {\n string += String.fromCharCode.apply(\n null,\n utf16data.slice(i, i + DECODE_CHUNK_SIZE)\n );\n }\n return string;\n }\n case 4: {\n let retArray;\n if (length < 0) {\n retArray = [];\n while (!this.readBreak()) {\n retArray.push(this.decodeItem());\n }\n } else {\n retArray = new Array(length);\n for (i = 0; i < length; ++i) {\n retArray[i] = this.decodeItem();\n }\n }\n return retArray;\n }\n case 5: {\n if (this.dictionary === 'map') {\n const retMap = new Map();\n for (i = 0; i < length || (length < 0 && !this.readBreak()); ++i) {\n const key = this.decodeItem();\n if (retMap.has(key)) {\n throw new Error('CBORError: Duplicate key encountered');\n }\n retMap.set(key, this.decodeItem());\n }\n return retMap;\n }\n const retObject: any = {};\n for (i = 0; i < length || (length < 0 && !this.readBreak()); ++i) {\n const key = this.decodeItem();\n if (Object.prototype.hasOwnProperty.call(retObject, key)) {\n throw new Error('CBORError: Duplicate key encountered');\n }\n retObject[key] = this.decodeItem();\n }\n return retObject;\n }\n case 6: {\n const value = this.decodeItem();\n const tag = length;\n if (value instanceof Uint8Array) {\n switch (tag) {\n case kCborTagBignum:\n case kCborTagNegativeBignum:\n let num = value.reduce((acc, n) => (acc << 8n) + BigInt(n), 0n);\n if (tag == kCborTagNegativeBignum) {\n num = -1n - num;\n }\n return num;\n /* Little-endian typed arrays (RFC 8746) */\n case kCborTagUint8:\n return value;\n case kCborTagInt8:\n return new Int8Array(value.buffer);\n case kCborTagUint16:\n return new Uint16Array(value.buffer);\n case kCborTagInt16:\n return new Int16Array(value.buffer);\n case kCborTagUint32:\n return new Uint32Array(value.buffer);\n case kCborTagInt32:\n return new Int32Array(value.buffer);\n case kCborTagUint64:\n return new BigUint64Array(value.buffer);\n case kCborTagInt64:\n return new BigInt64Array(value.buffer);\n case kCborTagFloat32:\n return new Float32Array(value.buffer);\n case kCborTagFloat64:\n return new Float64Array(value.buffer);\n }\n }\n return this.tagger(tag, value);\n }\n case 7:\n switch (length) {\n case 20:\n return false;\n case 21:\n return true;\n case 22:\n return null;\n case 23:\n return undefined;\n default:\n return length as number;\n }\n }\n }\n\n decodeItem(): any {\n const initialByte = this.readUint8();\n return this.readItem(initialByte);\n }\n\n readArrayBuffer(length: number) {\n return this.commitRead(\n length,\n this.data.subarray(this.offset, this.offset + length)\n );\n }\n readFloat16() {\n const POW_2_24 = 5.960464477539063e-8;\n\n const tempArrayBuffer = new ArrayBuffer(4);\n const tempDataView = new DataView(tempArrayBuffer);\n const value = this.readUint16();\n\n const sign = value & 0x8000;\n let exponent = value & 0x7c00;\n const fraction = value & 0x03ff;\n\n if (exponent === 0x7c00) exponent = 0xff << 10;\n else if (exponent !== 0) exponent += (127 - 15) << 10;\n else if (fraction !== 0) return (sign ? -1 : 1) * fraction * POW_2_24;\n\n tempDataView.setUint32(0, (sign << 16) | (exponent << 13) | (fraction << 13));\n return tempDataView.getFloat32(0);\n }\n readFloat32(): number {\n return this.commitRead(4, this.dataView.getFloat32(this.offset));\n }\n readFloat64(): number {\n return this.commitRead(8, this.dataView.getFloat64(this.offset));\n }\n readUint8(): number {\n return this.commitRead(1, this.data[this.offset]);\n }\n readUint16(): number {\n return this.commitRead(2, this.dataView.getUint16(this.offset));\n }\n readUint32(): number {\n return this.commitRead(4, this.dataView.getUint32(this.offset));\n }\n readUint64(): bigint {\n return this.commitRead(8, this.dataView.getBigUint64(this.offset));\n }\n readBreak(): boolean {\n if (this.data[this.offset] !== 0xff) return false;\n this.offset += 1;\n return true;\n }\n readLength(additionalInformation: number): number | bigint {\n if (additionalInformation < 24) return additionalInformation;\n if (additionalInformation === 24) return this.readUint8();\n if (additionalInformation === 25) return this.readUint16();\n if (additionalInformation === 26) return this.readUint32();\n if (additionalInformation === 27) {\n const integer = this.readUint64();\n if (integer <= Number.MAX_SAFE_INTEGER) return Number(integer);\n return integer;\n }\n if (additionalInformation === 31) return -1;\n throw new Error('CBORError: Invalid length encoding');\n }\n\n readIndefiniteStringLength(majorType: number): number {\n const initialByte = this.readUint8();\n if (initialByte === 0xff) return -1;\n const length = this.readLength(initialByte & 0x1f);\n if (length < 0 || initialByte >> 5 !== majorType) {\n throw new Error('CBORError: Invalid indefinite length element');\n }\n return Number(length);\n }\n appendUtf16Data(utf16data: number[], length: number) {\n for (let i = 0; i < length; ++i) {\n let value = this.readUint8();\n if (value & 0x80) {\n if (value < 0xe0) {\n value = ((value & 0x1f) << 6) | (this.readUint8() & 0x3f);\n length -= 1;\n } else if (value < 0xf0) {\n value =\n ((value & 0x0f) << 12) |\n ((this.readUint8() & 0x3f) << 6) |\n (this.readUint8() & 0x3f);\n length -= 2;\n } else {\n value =\n ((value & 0x07) << 18) |\n ((this.readUint8() & 0x3f) << 12) |\n ((this.readUint8() & 0x3f) << 6) |\n (this.readUint8() & 0x3f);\n length -= 3;\n }\n }\n\n if (value < 0x10000) {\n utf16data.push(value);\n } else {\n value -= 0x10000;\n utf16data.push(0xd800 | (value >> 10));\n utf16data.push(0xdc00 | (value & 0x3ff));\n }\n }\n }\n\n private commitRead(length: number, value: T): T {\n this.offset += length;\n return value;\n }\n}\n", "import {Prefab} from './prefab.js';\nimport {\n defaultPropertyCloner,\n ComponentProperty,\n Type,\n PropertyRecord,\n} from './property.js';\nimport {\n AnimationComponent,\n BrokenComponent,\n CollisionComponent,\n Component,\n ComponentConstructor,\n DestroyedComponentInstance,\n InputComponent,\n LightComponent,\n MeshComponent,\n Object3D,\n PhysXComponent,\n TextComponent,\n ViewComponent,\n ParticleEffectComponent,\n LogTag,\n inheritProperties,\n} from './wonderland.js';\n\nimport {CBORReader, CBORType, getType, isUndefined} from './utils/cbor.js';\n\n/** Native components list */\nexport interface NativeComponents {\n 'collision': CollisionComponent;\n 'text': TextComponent;\n 'view': ViewComponent;\n 'mesh': MeshComponent;\n 'input': InputComponent;\n 'light': LightComponent;\n 'animation': AnimationComponent;\n 'physx': PhysXComponent;\n 'particle-effect': ParticleEffectComponent;\n}\n\n/**\n * Manage all component managers in a scene.\n *\n * @hidden\n */\nexport class ComponentManagers {\n /** Animation manager index. */\n readonly animation: number = -1;\n /** Collision manager index. */\n readonly collision: number = -1;\n /** JavaScript manager index. */\n readonly js: number = -1;\n /** Physx manager index. */\n readonly physx: number = -1;\n /** View manager index. */\n readonly view: number = -1;\n\n /**\n * Component class instances per type to avoid GC.\n *\n * @note Maps the manager index to the list of components.\n *\n * @todo: Refactor ResourceManager and re-use for components.\n */\n private readonly _cache: (Component | null)[][] = [];\n /** Manager index to component class. */\n private readonly _constructors: ComponentConstructor[];\n /* Manager name to the manager index. */\n private readonly _nativeManagers: Map = new Map();\n\n /** Host instance. */\n private readonly _scene: Prefab;\n\n constructor(scene: Prefab) {\n this._scene = scene;\n const wasm = this._scene.engine.wasm;\n\n const native = [\n AnimationComponent,\n CollisionComponent,\n InputComponent,\n LightComponent,\n MeshComponent,\n PhysXComponent,\n TextComponent,\n ViewComponent,\n ParticleEffectComponent,\n ];\n this._cache = new Array(native.length);\n this._constructors = new Array(native.length);\n\n for (const Class of native) {\n const ptr = wasm.tempUTF8(Class.TypeName);\n const manager = wasm._wl_scene_get_component_manager_index(scene._index, ptr);\n this._constructors[manager] = Class;\n this._cache[manager] = [] as Component[];\n this._nativeManagers.set(Class.TypeName, manager);\n }\n\n this.animation = this._nativeManagers.get(AnimationComponent.TypeName)!;\n this.collision = this._nativeManagers.get(CollisionComponent.TypeName)!;\n this.physx = this._nativeManagers.get(PhysXComponent.TypeName)!;\n this.view = this._nativeManagers.get(ViewComponent.TypeName)!;\n\n const ptr = wasm.tempUTF8('js');\n this.js = wasm._wl_scene_get_component_manager_index(scene._index, ptr);\n this._cache[this.js] = [] as Component[];\n }\n\n createJs(index: number, id: number, type: number, object: number) {\n const wasm = this._scene.engine.wasm;\n const ctor = wasm._componentTypes[type];\n if (!ctor) {\n throw new Error(`Type index ${type} isn't registered`);\n }\n\n const log = this._scene.engine.log;\n\n let component = null;\n try {\n component = new ctor(this._scene, this.js, id);\n } catch (e) {\n log.error(\n LogTag.Component,\n `Exception during instantiation of component ${ctor.TypeName}`\n );\n log.error(LogTag.Component, e);\n component = new BrokenComponent(this._scene);\n }\n component._object = this._scene.wrap(object);\n\n try {\n component.resetProperties();\n } catch (e) {\n log.error(\n LogTag.Component,\n `Exception during ${component.type} resetProperties() on object ${component.object.name}`\n );\n log.error(LogTag.Component, e);\n }\n\n this._scene._jsComponents[index] = component;\n\n /* Add to cache. This is required because destruction is\n * ID-based and not index-based. */\n this._cache[this.js][id] = component;\n\n return component;\n }\n\n /**\n * Get components of type T.\n *\n * @param type Component class.\n * @returns An array containing all components in this scene.\n */\n components(type: ComponentConstructor, active: boolean): T[] {\n return this.componentsFromTypename(type.TypeName, active) as T[];\n }\n\n /**\n * Similar to {@link getComponents}, but directly from string.\n *\n * @param typename The component `TypeName` attribute.\n */\n componentsFromTypename(typename: string, active: boolean): Component[] {\n const wasm = this._scene.engine.wasm;\n\n const manager = this.getNativeManager(typename) ?? this.js;\n const subTypeIndex =\n manager === this.js ? wasm._componentTypeIndices[typename] ?? 0 : 0;\n\n const maxRead = wasm._tempMemSize / 4;\n const components: Component[] = [];\n let read = 0;\n let offset = 0;\n while (\n (read = wasm._wl_scene_get_components(\n this._scene._index,\n manager,\n subTypeIndex,\n active,\n offset,\n maxRead,\n wasm._tempMem\n ))\n ) {\n for (let i = 0; i < read; ++i) {\n const id = wasm._tempMemUint32[i];\n components.push(this.wrapAny(manager, id) as Component);\n }\n /* Offset by `maxRead` because js components of different types are contiguous.\n * `read` could be less than `maxRead`, and we want to skip all\n * the components already processed. */\n offset += maxRead;\n }\n\n return components;\n }\n\n /**\n * Get component of type T.\n *\n * @note Currently only works for native components, but we will extend this later (and expose it in the API)\n */\n componentAt(type: ComponentConstructor, index: number): T {\n const wasm = this._scene.engine.wasm;\n const manager = this.getNativeManager(type.TypeName)!;\n const id = wasm._wl_scene_get_component(this._scene._index, manager, index);\n return this.wrapAny(manager, id) as T;\n }\n\n /**\n * Retrieve a cached component.\n *\n * @param manager The manager index.\n * @param id The component id.\n * @returns The component if cached, `null` otherwise.\n */\n get(manager: number, id: number) {\n return this._cache[manager][id] ?? null;\n }\n\n /**\n * Wrap the animation.\n *\n * @param id Id to wrap.\n * @returns The previous instance if it was cached, or a new one.\n */\n wrapAnimation(id: number): AnimationComponent {\n return this.wrapNative(this.animation, id) as AnimationComponent;\n }\n\n /**\n * Wrap the collision.\n *\n * @param id Id to wrap.\n * @returns The previous instance if it was cached, or a new one.\n */\n wrapCollision(id: number): CollisionComponent {\n return this.wrapNative(this.collision, id) as CollisionComponent;\n }\n\n /**\n * Wrap the view.\n *\n * @param id Id to wrap.\n * @returns The previous instance if it was cached, or a new one.\n */\n wrapView(id: number): ViewComponent {\n return this.wrapNative(this.view, id) as ViewComponent;\n }\n\n /**\n * Wrap the physx.\n *\n * @param id Id to wrap.\n * @returns The previous instance if it was cached, or a new one.\n */\n wrapPhysx(id: number): PhysXComponent {\n return this.wrapNative(this.physx, id) as PhysXComponent;\n }\n\n /**\n * Retrieves a component instance if it exists, or create and cache\n * a new one.\n *\n * @note This api is meant to be used internally. Please have a look at\n * {@link Object3D.addComponent} instead.\n *\n * @param componentType Component manager index\n * @param componentId Component id in the manager\n *\n * @returns JavaScript instance wrapping the native component\n */\n wrapNative(manager: number, id: number) {\n if (id < 0) return null;\n\n const cache = this._cache[manager];\n if (cache[id]) return cache[id];\n\n const scene = this._scene;\n const Class = this._constructors[manager];\n const component = new Class(scene, manager, id);\n cache[id] = component;\n return component;\n }\n\n /**\n * Wrap a native or js component.\n *\n * @throws For JavaScript components that weren't previously cached,\n * since that would be a bug in the runtime / api.\n *\n * @param manager The manager index.\n * @param id The id to wrap.\n * @returns The previous instance if it was cached, or a new one.\n */\n wrapAny(manager: number, id: number) {\n if (id < 0) return null;\n\n if (manager === this.js) {\n const found = this._cache[this.js][id];\n if (!found) {\n throw new Error('JS components must always be cached');\n }\n return found.constructor !== BrokenComponent ? found : null;\n }\n\n return this.wrapNative(manager, id);\n }\n\n getNativeManager(name: string): number | null {\n const manager = this._nativeManagers.get(name);\n return manager !== undefined ? manager : null;\n }\n\n /**\n * Perform cleanup upon component destruction.\n *\n * @param instance The instance to destroy.\n *\n * @hidden\n */\n destroy(instance: Component) {\n const localId = instance._localId;\n const manager = instance._manager;\n (instance._id as number) = -1;\n (instance._localId as number) = -1;\n (instance._manager as number) = -1;\n\n const erasePrototypeOnDestroy = this._scene.engine.erasePrototypeOnDestroy;\n /* Destroy the prototype of this instance to avoid using a dangling component */\n if (erasePrototypeOnDestroy && instance) {\n Object.setPrototypeOf(instance, DestroyedComponentInstance);\n }\n\n /* Remove from the cache to avoid side-effects when\n * re-creating a component with the same id. */\n this._cache[manager][localId] = null;\n }\n\n /** Number of managers **registered** in the scene, i.e., `js`, `names`, etc... */\n get managersCount() {\n return this._scene.engine.wasm._wl_scene_get_component_manager_count(\n this._scene._index\n );\n }\n}\n\nexport function resetComponentProperties(record: Component) {\n const ctor = record.constructor as ComponentConstructor;\n const properties = ctor.Properties;\n if (!properties) return;\n for (const name in properties) {\n const property = properties[name];\n const cloner = property.cloner ?? defaultPropertyCloner;\n (record as Record)[name] = cloner.clone(\n property.type,\n property.default\n );\n }\n}\n\nexport function setupComponentClass(ctor: ComponentConstructor | PropertyRecord) {\n inheritProperties(ctor);\n /** @todo: Merge the _setupDefaults and _setPropertyOrder calls with this function. */\n for (const name in ctor.Properties) {\n const prop = ctor.Properties[name];\n let record = null;\n switch (prop.type) {\n case Type.Record:\n record = prop.record;\n break;\n case Type.Array:\n record = prop.element?.record;\n break;\n default:\n break;\n }\n if (record && !record._propertyOrder) setupComponentClass(record);\n }\n _setupDefaults(ctor);\n _setPropertyOrder(ctor);\n}\n\n/**\n * Decode component properties from CBOR encoded data.\n *\n * ## Failure\n *\n * When decoding properties, failures will not be treated as errors. Failures include:\n * - Invalid parameters count\n * - Wrongly defined property\n *\n * When a failure occurs, the cbor reader will be consumed to prevent\n * errors to cascade in the following components.\n */\nexport class ComponentPropertyDecoder {\n scene: Prefab;\n offsets: Uint32Array;\n\n constructor(scene: Prefab, offsets: Uint32Array) {\n this.scene = scene;\n this.offsets = offsets;\n }\n\n decode(cbor: CBORReader, component: Component) {\n const ctor = component.constructor as ComponentConstructor;\n if (!ctor._propertyOrder) setupComponentClass(ctor);\n\n const typeInfo = cbor.readTypeInfo();\n if (ctor === BrokenComponent) {\n return cbor.readItem(typeInfo); /* Consumme the cbor state */\n }\n\n const cborType = getType(typeInfo);\n if (cborType !== CBORType.Array) {\n this._error(`${component} parameters not encoded as an array.`);\n return cbor.readItem(typeInfo);\n }\n\n const paramNames = ctor._propertyOrder;\n const count = cbor.readArrayLength(typeInfo) as number;\n if (count !== paramNames.length) {\n this._error(\n `${component} has ${count} parameters encoded, but expected ${paramNames.length}`\n );\n return cbor.readItem(typeInfo, count);\n }\n\n for (let j = 0; j < count; ++j) {\n const name = paramNames[j];\n const property = ctor.Properties[name];\n (component as any)[name] = this.decodeProperty(cbor, name, property);\n }\n }\n\n decodeProperty(cbor: CBORReader, name: string, property: ComponentProperty) {\n const typeInfo = cbor.readTypeInfo();\n\n if (property.type === Type.Record) {\n return this.decodeRecordProperty(cbor, name, property, typeInfo);\n } else if (property.type === Type.Array) {\n return this.decodeArrayProperty(cbor, name, property, typeInfo);\n }\n\n let value = cbor.readItem(typeInfo);\n\n /* Default values are sent as undefined to avoid wasting space */\n if (value === undefined) {\n const cloner = property.cloner ?? defaultPropertyCloner;\n value = cloner.clone(property.type, property.default);\n return value;\n }\n /* This skips unset resource parameters as those are sent\n * directly with type null. Offsets for Int and Float types\n * should always be 0. */\n /** @todo CBOR tag to mark resources? Wastes a byte though. */\n if (typeof value === 'number') {\n value += this.offsets[property.type];\n }\n\n const engine = this.scene.engine;\n switch (property.type) {\n case Type.Bool:\n case Type.Int:\n case Type.Float:\n case Type.String:\n case Type.Enum:\n case Type.Vector2:\n case Type.Vector3:\n case Type.Vector4:\n /* Nothing to do */\n return value;\n case Type.Object: {\n const wasm = engine.wasm;\n return value\n ? this.scene.wrap(wasm._wl_object_id(this.scene._index, value))\n : null;\n }\n case Type.Mesh:\n return engine.meshes.wrap(value);\n case Type.Texture:\n return engine.textures.wrap(value);\n case Type.Material:\n return engine.materials.wrap(value);\n case Type.ParticleEffect:\n return engine.particleEffects.wrap(value);\n case Type.Animation:\n return this.scene.animations.wrap(value);\n case Type.Skin:\n return this.scene.skins.wrap(value);\n case Type.Color: {\n /* Colors are sent as Uint8Array. Normalize positive\n * integer values to 0-1. Don't assume any bit size\n * here, could become Uint16Array as well. */\n const max = (1 << (value.BYTES_PER_ELEMENT * 8)) - 1;\n return Float32Array.from(value, (f: number, _) => f / max);\n }\n }\n }\n\n decodeRecordProperty(\n cbor: CBORReader,\n name: string,\n property: ComponentProperty,\n typeInfo: number\n ) {\n const cborType = getType(typeInfo);\n if (cborType !== CBORType.Array && cborType !== CBORType.Constant) {\n /* For memory/performance reason, records are serialized as arrays,\n * since keys aren't needed during the decoding phase. */\n this._error(`Record parameter '${name}' not serialized as an array`);\n cbor.readItem(typeInfo);\n return null;\n }\n const record = property.record;\n if (!record) {\n this._error(`Record parameter '${name}' .record is undefined`);\n cbor.readItem(typeInfo);\n return null;\n }\n\n if (!record._propertyOrder) setupComponentClass(record);\n const propertyOrder = record._propertyOrder as string[];\n\n const count = cbor.readArrayLength(typeInfo) as number;\n if (isUndefined(cborType, count)) {\n const cloner = property.cloner ?? defaultPropertyCloner;\n return cloner.clone(property.type, property.default);\n }\n\n const result = new (record as any)();\n if (count !== propertyOrder.length) {\n const propsCount = propertyOrder.length;\n this._error(\n `Record parameter ${name} has ${count} sub-parameters encoded, but expected ${propsCount}`\n );\n cbor.readItem(typeInfo, count);\n return result;\n }\n const order = record._propertyOrder!;\n for (let i = 0; i < order.length; ++i) {\n const key = order[i];\n const prop = record.Properties![key];\n (result as any)[key] = this.decodeProperty(cbor, key, prop);\n }\n return result;\n }\n\n decodeArrayProperty(\n cbor: CBORReader,\n name: string,\n property: ComponentProperty,\n typeInfo: number\n ) {\n const log = this.scene.engine.log;\n const cborType = getType(typeInfo);\n if (cborType !== CBORType.Array && cborType !== CBORType.Constant) {\n /* For memory/performance reason, records are serialized as arrays,\n * since keys aren't needed during the decoding phase. */\n this._error(`Array parameter '${name}' not serialized as an array`);\n cbor.readItem(typeInfo);\n return [];\n }\n\n if (!property.element) {\n this._error(`Array parameter '${name}' .element property is undefined`);\n cbor.readItem(typeInfo);\n return [];\n }\n\n const count = cbor.readArrayLength(typeInfo) as number;\n if (isUndefined(cborType, count)) return [];\n\n const result = new Array(count);\n for (let i = 0; i < count; ++i) {\n result[i] = this.decodeProperty(cbor, name, property.element);\n }\n return result;\n }\n\n private _error(msg: string) {\n const log = this.scene.engine.log;\n log.error(LogTag.Engine, msg);\n }\n}\n\n/**\n * Default component parameter value per type.\n */\nconst _componentDefaults = new Map([\n [Type.Bool, false],\n [Type.Int, 0],\n [Type.Float, 0.0],\n [Type.String, ''],\n [Type.Enum, undefined],\n [Type.Object, null],\n [Type.Mesh, null],\n [Type.Texture, null],\n [Type.Material, null],\n [Type.Animation, null],\n [Type.Skin, null],\n [Type.ParticleEffect, null],\n [Type.Color, Float32Array.from([0.0, 0.0, 0.0, 1.0])],\n [Type.Vector2, Float32Array.from([0.0, 0.0])],\n [Type.Vector3, Float32Array.from([0.0, 0.0, 0.0])],\n [Type.Vector4, Float32Array.from([0.0, 0.0, 0.0, 0.0])],\n [Type.Record, undefined],\n [Type.Array, []],\n]);\n\n/**\n * Setup the defaults value of the properties on a given\n * component class.\n *\n * @param ctor The component class\n */\nfunction _setupDefaults(ctor: ComponentConstructor | PropertyRecord) {\n for (const name in ctor.Properties) {\n const p = ctor.Properties[name];\n\n if (p.type === Type.Enum) {\n /* Enum default can be a string or an index. Convert to and/or\n * sanity-check the index. */\n if (p.values?.length) {\n /* Don't try to look up the default if the user specified a\n * number or we already converted to one. */\n if (typeof p.default !== 'number') {\n /* If undefined, missing element or wrong type this returns\n * -1 which becomes 0 below. This matches editor behavior. */\n p.default = p.values.indexOf(p.default);\n }\n if (p.default < 0 || p.default >= p.values.length) {\n p.default = 0;\n }\n } else {\n /* There's no index value that makes sense */\n p.default = undefined;\n }\n } else if (\n (p.type === Type.Color ||\n p.type === Type.Vector2 ||\n p.type === Type.Vector3 ||\n p.type === Type.Vector4) &&\n Array.isArray(p.default)\n ) {\n /* Defaults provided by user code are currently always a standard\n * array, but become a typed array on the property */\n p.default = Float32Array.from(p.default);\n } else if (p.type === Type.Record && p.record) {\n p.default = new p.record();\n resetComponentProperties(p.default);\n } else if (p.default === undefined) {\n const cloner = p.cloner ?? defaultPropertyCloner;\n p.default = cloner.clone(p.type, _componentDefaults.get(p.type));\n }\n ctor.prototype[name] = p.default;\n }\n}\n\n/**\n * Determines a fixed order for property attributes used for deserialization.\n *\n * @param ctor The component class\n */\nfunction _setPropertyOrder(ctor: ComponentConstructor | PropertyRecord) {\n ctor._propertyOrder = ctor.hasOwnProperty('Properties')\n ? Object.keys(ctor.Properties!).sort()\n : [];\n}\n", "import {ImageLike, ProgressCallback} from '../types.js';\n\n/**\n * Transformer for {@link TransformStream} that passes read progress to a\n * callback.\n *\n * Invokes the callback for each streamed chunk, and one final time when the\n * stream closes.\n *\n * @hidden\n */\nclass FetchProgressTransformer implements Transformer {\n #progress = 0;\n #callback: ProgressCallback;\n #totalSize: number;\n\n /**\n * Constructor.\n * @param callback Callback that receives the progress.\n * @param totalSize Total size of the data. Pass 0 to indicate that the\n * size is unknown, then the callback will only be called once after\n * all data was transferred.\n */\n constructor(callback: ProgressCallback, totalSize = 0) {\n this.#callback = callback;\n this.#totalSize = totalSize;\n }\n\n transform(chunk: Uint8Array, controller: TransformStreamDefaultController) {\n controller.enqueue(chunk);\n this.#progress += chunk.length;\n if (this.#totalSize > 0) {\n this.#callback(this.#progress, this.#totalSize);\n }\n }\n\n flush() {\n this.#callback(this.#progress, this.#progress);\n }\n}\n\n/**\n * Sink for `WritableStream` that writes data to an `ArrayBuffer`.\n *\n * @hidden\n */\nexport class ArrayBufferSink implements UnderlyingSink {\n #buffer: Uint8Array;\n #offset = 0;\n\n /**\n * Constructor.\n * @param size Initial size of the buffer. If less than the received data,\n * the buffer is dynamically reallocated.\n */\n constructor(size = 0) {\n this.#buffer = new Uint8Array(size);\n }\n\n /** Get the received data as an `ArrayBuffer`. */\n get arrayBuffer() {\n const arrayBuffer = this.#buffer.buffer;\n if (this.#offset < arrayBuffer.byteLength) {\n return arrayBuffer.slice(0, this.#offset);\n }\n return arrayBuffer;\n }\n\n write(chunk: Uint8Array) {\n const newLength = this.#offset + chunk.length;\n if (newLength > this.#buffer.length) {\n const newBuffer = new Uint8Array(\n Math.max(this.#buffer.length * 1.5, newLength)\n );\n newBuffer.set(this.#buffer);\n this.#buffer = newBuffer;\n }\n this.#buffer.set(chunk, this.#offset);\n this.#offset = newLength;\n }\n}\n\n/**\n * Source for `ReadableStream` that reads data from an`ArrayBuffer`.\n *\n * @hidden\n */\nexport class ArrayBufferSource implements UnderlyingSource {\n #buffer: ArrayBuffer;\n\n /**\n * Constructor.\n * @param buffer Buffer to read from.\n */\n constructor(buffer: ArrayBuffer) {\n this.#buffer = buffer;\n }\n\n start(controller: ReadableStreamController) {\n if (this.#buffer.byteLength > 0) {\n controller.enqueue(new Uint8Array(this.#buffer));\n }\n controller.close();\n }\n}\n\n/**\n * Fetch a file as an `ArrayBuffer`, with fetch progress passed to a callback.\n *\n * @param path Path of the file to fetch.\n * @param onProgress Callback receiving the current fetch progress and total\n * size, in bytes. Also called a final time on completion.\n * @param signal Abort signal passed to `fetch()`.\n * @returns Promise that resolves when the fetch successfully completes.\n */\nexport async function fetchWithProgress(\n path: string,\n onProgress?: ProgressCallback,\n signal?: AbortSignal\n): Promise {\n const res = await fetch(path, {signal});\n if (!res.ok) throw res.statusText;\n if (!onProgress || !res.body) return res.arrayBuffer();\n let size = Number(res.headers.get('Content-Length') ?? 0);\n if (Number.isNaN(size)) size = 0;\n const sink = new ArrayBufferSink(size);\n await res.body\n .pipeThrough(new TransformStream(new FetchProgressTransformer(onProgress, size)))\n .pipeTo(new WritableStream(sink));\n return sink.arrayBuffer;\n}\n\n/**\n * Fetch a file as a `ReadableStream`, with fetch progress passed to a\n * callback.\n *\n * @param path Path of the file to fetch.\n * @param onProgress Callback receiving the current fetch progress and total\n * size, in bytes. Also called a final time on completion.\n * @param signal Abort signal passed to `fetch()`.\n * @returns Promise that resolves when the fetch successfully completes.\n */\nexport async function fetchStreamWithProgress(\n path: string,\n onProgress?: ProgressCallback,\n signal?: AbortSignal\n): Promise> {\n const res = await fetch(path, {signal});\n if (!res.ok) throw res.statusText;\n const body = res.body ?? new ReadableStream();\n let size = Number(res.headers.get('Content-Length') ?? 0);\n if (Number.isNaN(size)) size = 0;\n if (!onProgress) return body;\n const stream = body.pipeThrough(\n new TransformStream(new FetchProgressTransformer(onProgress, size))\n );\n return stream;\n}\n\n/**\n * Get parent path from a URL.\n *\n * @param url URL to get the parent from.\n * @returns Parent URL without trailing slash.\n */\nexport function getBaseUrl(url: string): string {\n return url.substring(0, url.lastIndexOf('/'));\n}\n\n/**\n * Get the filename of a url.\n *\n * @param url The url to extract the name from.\n * @returns A string containing the filename. If no filename is found,\n * returns the input string.\n */\nexport function getFilename(url: string): string {\n if (url.endsWith('/')) {\n /* Remove trailing slash. */\n url = url.substring(0, url.lastIndexOf('/'));\n }\n const lastSlash = url.lastIndexOf('/');\n if (lastSlash < 0) return url;\n return url.substring(lastSlash + 1);\n}\n\n/**\n * Promise resolved once the image is ready to be used\n *\n * @param image The image, video, or canvas to wait for.\n * @returns A promise with the image, once it's ready to be used.\n */\nexport function onImageReady(image: T): Promise {\n return new Promise((res, rej) => {\n if (image instanceof HTMLVideoElement) {\n if (image.readyState >= 2) {\n res(image as T);\n return;\n }\n image.addEventListener(\n 'loadeddata',\n () => {\n if (image.readyState >= 2) res(image);\n },\n {once: true}\n );\n return;\n } else if (\n !(image instanceof HTMLImageElement) ||\n (image as HTMLImageElement).complete\n ) {\n res(image);\n return;\n }\n image.addEventListener('load', () => res(image), {once: true});\n image.addEventListener('error', rej, {once: true});\n });\n}\n", "import {ComponentManagers, NativeComponents} from './component.js';\nimport {WonderlandEngine} from './engine.js';\nimport {ResourceManager, SceneResource} from './resources/resource.js';\nimport {ProgressCallback} from './types.js';\nimport {\n fetchStreamWithProgress,\n fetchWithProgress,\n getBaseUrl,\n getFilename,\n onImageReady,\n} from './utils/fetch.js';\nimport {isString} from './utils/object.js';\nimport {\n Animation,\n CollisionCallback,\n Component,\n ComponentConstructor,\n DestroyedObjectInstance,\n Object3D,\n Skin,\n} from './wonderland.js';\n\n/** Scene loading options. */\nexport interface SceneLoadOptions {\n /** An in-memory buffer, containing the bytes of a `.bin` file. */\n buffer: ArrayBuffer;\n\n /** Path from which resources are resolved (images, languages, etc...). */\n baseURL: string;\n\n /** If `true`, dispatches a ready event in the document. */\n dispatchReadyEvent?: boolean;\n}\n\n/** Loading options for in-memory data. */\nexport interface InMemoryLoadOptions {\n /** An in-memory buffer, containing the bytes of a `.bin` file. */\n buffer: ArrayBuffer;\n /** Path from which resources are resolved (images, languages, etc...). */\n baseURL: string;\n /** Name of the file. This is the same that will be retrieved via {@link Scene#filename} */\n filename?: string;\n /**\n * Force to re-download the components bundle, and not use the version from cache.\n *\n * If not set, defaults to `false`.\n *\n * @note For now, this setting only affects the main scene loading.\n */\n nocache?: boolean;\n}\n\n/**\n * Loading options for streamed data.\n *\n * @hidden\n */\nexport interface StreamLoadOptions {\n /** Stream that transfers the bytes of a `.bin` file. */\n stream: ReadableStream;\n /** Path from which resources are resolved (images, languages, etc...). */\n baseURL: string;\n /** Name of the file. This is the same that will be retrieved via {@link Scene#filename} */\n filename?: string;\n}\n\n/** Options for loading files from a URL. */\nexport interface UrlLoadOptions {\n /** URL to load. */\n url: string;\n /** Signal to abort the file fetch request. */\n signal?: AbortSignal;\n /**\n * Whether to load the file as parts of it are fetched. If `false`,\n * fetches the entire file first before loading. Streamed loading reduces\n * memory usage. If not set, defaults to `true`.\n */\n streamed?: boolean;\n /**\n * Force to re-download the components bundle, and not use the version from cache.\n *\n * If not set, defaults to `false`.\n *\n * @note For now, this setting only affects the main scene loading.\n */\n nocache?: boolean;\n}\n\n/** Options used during loading. */\nexport type LoadOptions = string | (UrlLoadOptions & Partial);\n\n/**\n * Base class for prefabs, scenes, and glTF.\n *\n * For more information have a look at the derived types:\n * - {@link Scene} for Wonderland Engine activatable scenes (.bin)\n * - {@link PrefabGLTF} for glTF scenes\n *\n * #### Resources\n *\n * While **meshes**, **textures**, and **materials** are shared\n * on the {@link WonderlandEngine} instance, a scene comes with:\n * - Animations: Managed using {@link Prefab.animations}\n * - Skins: Managed using {@link Prefab.skins}\n *\n * Those resources are bound to the object hierarchy and are thus required to be\n * per-scene.\n *\n * #### Destruction\n *\n * For now, destroying a scene doesn't automatically remove the resources it\n * references in the engine. For more information, have a look at the\n * {@link Scene.destroy} method.\n *\n * #### Isolation\n *\n * It's forbidden to mix objects and components from different scenes, e.g.,\n *\n * ```js\n * const objA = sceneA.addChild();\n * const objB = sceneB.addChild();\n * objA.parent = objB; // Throws\n * ```\n *\n * @category scene\n *\n * @since 1.2.0\n */\nexport class Prefab {\n /**\n * Create an object with shape `{url: string}`.\n *\n * - If the parameter is a string, a new object is returned\n * - If the parameter is an object, the same reference is returned\n *\n * @param options The url or options object.\n * @returns An object of the form `{url: string, ...}`.\n *\n * @hidden\n */\n static makeUrlLoadOptions(options: LoadOptions): UrlLoadOptions & Partial {\n return isString(options)\n ? ({url: options} as UrlLoadOptions & Partial)\n : options;\n }\n\n /**\n * Load an `ArrayBuffer` using fetch.\n *\n * @param opts The url or options.\n * @param progress Progress callback\n * @returns An {@link InMemoryLoadOptions} object.\n *\n * @hidden\n */\n static async loadBuffer(\n options: LoadOptions,\n progress?: ProgressCallback\n ): Promise {\n const opts = Prefab.makeUrlLoadOptions(options);\n const buffer = await fetchWithProgress(opts.url, progress, opts.signal);\n const baseURL = getBaseUrl(opts.url);\n const filename = getFilename(opts.url);\n return {...opts, buffer, baseURL, filename};\n }\n\n /**\n * Load a `ReadableStream` using fetch.\n *\n * @param opts The url or options.\n * @param progress Progress callback\n * @returns A {@link StreamLoadOptions} object.\n *\n * @hidden\n */\n static async loadStream(\n options: LoadOptions,\n progress?: ProgressCallback\n ): Promise {\n const opts = Prefab.makeUrlLoadOptions(options);\n const stream = await fetchStreamWithProgress(opts.url, progress, opts.signal);\n const baseURL = getBaseUrl(opts.url);\n const filename = getFilename(opts.url);\n return {...opts, stream, baseURL, filename};\n }\n\n /**\n * Validate the in-memory load options.\n *\n * @param options Options to validate.\n * @returns Validated options object.\n *\n * @hidden\n */\n static validateBufferOptions(options: InMemoryLoadOptions): {\n buffer: ArrayBuffer;\n baseURL: string;\n url: string;\n } {\n const {buffer, baseURL, filename = 'scene.bin'} = options;\n if (!buffer) {\n throw new Error(\"missing 'buffer' in options\");\n }\n if (!isString(baseURL)) {\n throw new Error(\"missing 'baseURL' in options\");\n }\n const url = baseURL ? `${baseURL}/${filename}` : filename;\n return {buffer, baseURL, url};\n }\n\n /**\n * Validate the stream load options.\n *\n * @param options Options to validate.\n * @returns Validated options object.\n *\n * @hidden\n */\n static validateStreamOptions(options: StreamLoadOptions): {\n stream: ReadableStream;\n baseURL: string;\n url: string;\n } {\n const {stream, baseURL, filename = 'scene.bin'} = options;\n if (!stream) {\n throw new Error(\"missing 'stream' in options\");\n }\n if (!isString(baseURL)) {\n throw new Error(\"missing 'baseURL' in options\");\n }\n const url = baseURL ? `${baseURL}/${filename}` : filename;\n return {stream, baseURL, url};\n }\n\n /** Index in the scene manager. @hidden */\n readonly _index;\n /** @hidden */\n protected _engine: WonderlandEngine;\n\n /**\n * Component manager caching to avoid GC.\n *\n * @hidden\n */\n readonly _components: ComponentManagers;\n /**\n * JavaScript components for this scene.\n *\n * This array is moved into the WASM instance upon activation.\n *\n * @hidden\n */\n readonly _jsComponents: Component[] = [];\n\n /**\n * The map is indexed using the physx component id.\n *\n * @hidden\n */\n readonly _pxCallbacks: Map = new Map();\n\n /** @hidden */\n private readonly _animations;\n /** @hidden */\n private readonly _skins;\n\n /**\n * Object class instances to avoid GC.\n *\n * @hidden\n */\n private readonly _objectCache: (Object3D | null)[] = [];\n\n /**\n * `onDestroy()` depth.\n *\n * Multiple components can stack `onDestroy()` calls.\n */\n private _pendingDestroy: number = 0;\n\n /**\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n constructor(engine: WonderlandEngine, index: number) {\n this._engine = engine;\n this._index = index;\n\n this._components = new ComponentManagers(this);\n this._animations = new ResourceManager(this as Prefab, Animation);\n this._skins = new ResourceManager(this as Prefab, Skin);\n }\n\n /**\n * Add a new {@link Object3D} to the root object.\n *\n * See {@link Object3D.addChild} for more information.\n *\n * @returns A new {@link Object3D}.\n */\n addChild() {\n return this.wrap(0).addChild();\n }\n\n /**\n * Add an object to the scene.\n *\n * Alternatively, you can use {@link Prefab.addChild} to add the object\n * to the root, or {@link Object3D.addChild} to add the object to a given parent:\n *\n * ```js\n * const parent = scene.addChild();\n * const child = parent.addChild();\n * // Equivalent using scene.addObject\n * const sibling = scene.addObject(parent);\n * ```\n *\n * @param parent Parent object or `null`.\n * @returns A newly created object.\n */\n addObject(parent: Object3D | null = null): Object3D {\n if (parent?.markedDestroyed) {\n throw new Error(`Failed to add object. ${parent} is marked as destroyed.`);\n }\n this.assertOrigin(parent);\n return (parent ?? this.wrap(0)).addChild();\n }\n\n /**\n * Batch-add objects to the scene.\n *\n * Will provide better performance for adding multiple objects (e.g. > 16)\n * than calling {@link Scene#addObject} repeatedly in a loop.\n *\n * By providing upfront information of how many objects will be required,\n * the engine is able to batch-allocate the required memory rather than\n * convervatively grow the memory in small steps.\n *\n * @experimental This API might change in upcoming versions.\n *\n * @param count Number of objects to add.\n * @param parent Parent object or `null`, default `null`.\n * @param componentCountHint Hint for how many components in total will\n * be added to the created objects afterwards, default `0`.\n * @returns Newly created objects\n */\n addObjects(\n count: number,\n parent: Object3D | null = null,\n componentCountHint: number = 0\n ): Object3D[] {\n const parentId = parent ? parent._id : 0;\n this.engine.wasm.requireTempMem(count * 2);\n const actualCount = this.engine.wasm._wl_scene_add_objects(\n this._index,\n parentId,\n count,\n componentCountHint || 0,\n this.engine.wasm._tempMem,\n this.engine.wasm._tempMemSize >> 1\n );\n const ids = this.engine.wasm._tempMemUint16.subarray(0, actualCount);\n const wrapper = this.wrap.bind(this);\n const objects = Array.from(ids, wrapper);\n return objects;\n }\n\n /**\n * Pre-allocate memory for a given amount of objects and components.\n *\n * Will provide better performance for adding objects later with {@link Scene#addObject}\n * and {@link Scene#addObjects}.\n *\n * By providing upfront information of how many objects will be required,\n * the engine is able to batch-allocate the required memory rather than\n * conservatively grow the memory in small steps.\n *\n * **Experimental:** This API might change in upcoming versions.\n *\n * @param objectCount Number of objects to add.\n * @param componentCountPerType Amount of components to\n * allocate for {@link Object3D.addComponent}, e.g. `{mesh: 100, collision: 200, \"my-comp\": 100}`.\n * @since 0.8.10\n */\n reserveObjects(objectCount: number, componentCountPerType: Record) {\n const wasm = this.engine.wasm;\n if (!componentCountPerType) return;\n\n const countsPerTypeIndex = wasm._tempMemInt;\n const managerCount = this._components.managersCount;\n for (let i = 0; i < managerCount; ++i) countsPerTypeIndex[i] = 0;\n\n const names = Object.keys(componentCountPerType);\n for (const name of names) {\n const count = componentCountPerType[name];\n const nativeIndex = this._components.getNativeManager(name);\n countsPerTypeIndex[nativeIndex !== null ? nativeIndex : this._components.js] +=\n count;\n }\n wasm._wl_scene_reserve_objects(this._index, objectCount, wasm._tempMem);\n }\n\n /**\n * Root object's children.\n *\n * See {@link Object3D.getChildren} for more information.\n *\n * @param out Destination array, expected to have at least `this.childrenCount` elements.\n * @returns The `out` parameter.\n */\n getChildren(out: Object3D[] = new Array(this.childrenCount)): Object3D[] {\n const root = this.wrap(0);\n return root.getChildren(out);\n }\n\n /** @overload */\n getComponents(typeOrClass: K): NativeComponents[K][];\n /** @overload */\n getComponents(typeOrClass: string): Component[];\n /**\n * Get all the components of a given type.\n *\n * #### Usage\n *\n * ```js\n * const meshes = scene.getComponents('mesh');\n * const zombies = scene.getComponents(ZombieComponent);\n * ```\n *\n * @param typeOrClass Typename to create a component of. Can be native or\n * custom JavaScript component type. It's also possible to give a class definition.\n * In this case, the method will use the `class.TypeName` field.\n * @returns An array containing all components in this scene.\n * Returns an empty array if the component isn't registered.\n */\n getComponents(typeOrClass: ComponentConstructor): T[];\n getComponents(typeOrClass: ComponentConstructor | string): Component[] {\n const typename = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName;\n return this._components.componentsFromTypename(typename, false);\n }\n\n /** @overload */\n getActiveComponents(\n typeOrClass: K\n ): NativeComponents[K][];\n /** @overload */\n getActiveComponents(typeOrClass: string): Component[];\n /**\n * Get all the **active** components of a given type.\n *\n * ```js\n * const zombies = scene.getActiveComponents(ZombieComponent);\n * for(const zombie of zombies) zombie.active = false;\n *\n * const list = scene.getActiveComponents(ZombieComponent);\n * console.log(list.length); // Prints `0`\n * ```\n *\n * @param typeOrClass Typename to create a component of. Can be native or\n * custom JavaScript component type. It's also possible to give a class definition.\n * In this case, the method will use the `class.TypeName` field.\n * @returns An array containing all components in this scene.\n * Returns an empty array if the component isn't registered.\n */\n getActiveComponents(typeOrClass: ComponentConstructor): T[];\n getActiveComponents(typeOrClass: ComponentConstructor | string): Component[] {\n const typename = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName;\n return this._components.componentsFromTypename(typename, true);\n }\n\n /**\n * Top-level objects of this scene.\n *\n * See {@link Object3D.children} for more information.\n *\n * @since 1.2.0\n */\n get children(): Object3D[] {\n const root = this.wrap(0);\n return root.children;\n }\n\n /** The number of children of the root object. */\n get childrenCount(): number {\n const root = this.wrap(0);\n return this.engine.wasm._wl_object_get_children_count(root._id);\n }\n\n /**\n * Search for objects matching the name.\n *\n * See {@link Object3D.findByName} for more information.\n *\n * @param name The name to search for.\n * @param recursive If `true`, the method will look at all the objects of\n * this scene. If `false`, this method will only perform the search in\n * root objects.\n * @returns An array of {@link Object3D} matching the name.\n *\n * @since 1.2.0\n */\n findByName(name: string, recursive = false): Object3D[] {\n const root = this.wrap(0);\n return root.findByName(name, recursive);\n }\n\n /**\n * Search for all **top-level** objects matching the name.\n *\n * See {@link Object3D.findByNameDirect} for more information.\n *\n * @param name The name to search for.\n * @returns An array of {@link Object3D} matching the name.\n *\n * @since 1.2.0\n */\n findByNameDirect(name: string): Object3D[] {\n const root = this.wrap(0);\n return root.findByNameDirect(name);\n }\n\n /**\n * Search for **all objects** matching the name.\n *\n * See {@link Object3D.findByNameRecursive} for more information.\n *\n * @param name The name to search for.\n * @returns An array of {@link Object3D} matching the name.\n *\n * @since 1.2.0\n */\n findByNameRecursive(name: string): Object3D[] {\n const root = this.wrap(0);\n return root.findByNameRecursive(name);\n }\n\n /**\n * Wrap an object ID using {@link Object}.\n *\n * @note This method performs caching and will return the same\n * instance on subsequent calls.\n *\n * @param objectId ID of the object to create.\n *\n * @returns The object\n */\n wrap(objectId: number): Object3D {\n const cache = this._objectCache;\n const o = cache[objectId] || (cache[objectId] = new Object3D(this, objectId));\n return o;\n }\n\n /**\n * Destroy the scene.\n *\n * For now, destroying a scene doesn't remove the resources it references. Thus,\n * you will need to reload a main scene to free the memory.\n *\n * For more information about destruction, have a look at the {@link Scene.destroy} method.\n */\n destroy() {\n if (this._pendingDestroy > 0) {\n throw new Error(\"It's forbidden to destroy a scene from onDestroy().\");\n }\n this._pxCallbacks.clear();\n this.engine._destroyScene(this);\n }\n\n /* Public Getters & Setters */\n\n /**\n * `true` if the scene is active, `false` otherwise.\n *\n * Always false for {@link Prefab} and {@link PrefabGLTF}.\n */\n get isActive() {\n return !!this.engine.wasm._wl_scene_active(this._index);\n }\n\n /**\n * Relative directory of the scene that was loaded.\n *\n * This is used for loading any files relative to the scene.\n */\n get baseURL(): string {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_scene_get_baseURL(this._index);\n if (!ptr) return '';\n\n return wasm.UTF8ToString(ptr);\n }\n\n /**\n * Filename used when loading the file.\n *\n * If the scenes was loaded from memory and no filename was provided,\n * this accessor will return an empty string.\n */\n get filename(): string {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_scene_get_filename(this._index);\n if (!ptr) return '';\n return wasm.UTF8ToString(ptr);\n }\n\n /** Animation resources */\n get animations() {\n return this._animations;\n }\n\n /** Skin resources */\n get skins() {\n return this._skins;\n }\n\n /** Hosting engine instance. */\n get engine(): WonderlandEngine {\n return this._engine;\n }\n\n /**\n * `true` if the object is destroyed, `false` otherwise.\n *\n * If {@link WonderlandEngine.erasePrototypeOnDestroy} is `true`,\n * reading a class attribute / method will throw.\n */\n get isDestroyed(): boolean {\n return this._index < 0;\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Scene(destroyed)';\n }\n return `Scene('${this.filename}', ${this._index})`;\n }\n\n /**\n * Checks that the input's scene is the same as this instance.\n *\n * It is forbidden to mix objects and components from different scenes, e.g.,\n *\n * ```js\n * const objA = sceneA.addObject();\n * const objB = sceneA.addObject();\n * objA.parent = objB; // Throws\n * ```\n *\n * @param other Object / component to check.\n *\n * @throws If other's scene isn't the same reference as this.\n */\n assertOrigin(other: Object3D | Component | SceneResource | undefined | null) {\n if (other && (other.scene as Prefab) !== this) {\n throw new Error(`Attempt to use ${other} from ${other.scene} in ${this}`);\n }\n }\n\n /**\n * Download components and initialize the scene.\n *\n * @hidden\n */\n _initialize() {\n const wasm = this.engine.wasm;\n /* Initialize component parameters */\n wasm._wl_scene_initialize(this._index);\n }\n\n /**\n * Perform cleanup upon object destruction.\n *\n * @param localId The id to destroy.\n *\n * @hidden\n */\n _destroyObject(localId: number) {\n const instance = this._objectCache[localId];\n if (!instance) return;\n\n (instance._id as number) = -1;\n (instance._localId as number) = -1;\n\n /* Destroy the prototype of this instance to avoid using a dangling object */\n if (this.engine.erasePrototypeOnDestroy && instance) {\n Object.setPrototypeOf(instance, DestroyedObjectInstance);\n }\n\n /* Remove from the cache to avoid side-effects when\n * re-creating an object with the same id. */\n this._objectCache[localId] = null;\n }\n\n /**\n * Performs JavaScript only destrution of a component.\n *\n * @note Prefer to use this method to manually calling the destruction code,\n * to protect the user from illegal method calls in {@link Component.onDestroy}.\n *\n * @param manager The component manager index.\n * @param id The component id.\n *\n * @hidden\n */\n _destroyComponent(manager: number, id: number) {\n const component = this._components.get(manager, id);\n ++this._pendingDestroy;\n component?._triggerOnDestroy();\n --this._pendingDestroy;\n }\n}\n", "/**\n * Schedule a timeout, resolving in `time` milliseconds.\n *\n * @note `setTimeout` being a macro-task, this method can\n * be use as a debounce call.\n *\n * @param time The time until it resolves, in milliseconds.\n * @returns A promise resolving in `time` ms.\n */\nexport function timeout(time: number): Promise {\n return new Promise((res) => setTimeout(res, time));\n}\n\n/**\n * Clamp the value in the range [min; max].\n *\n * @param val The value to clamp.\n * @param min The minimum value (inclusive).\n * @param max The maximum value (inclusive).\n * @returns The clamped value.\n */\nexport function clamp(val: number, min: number, max: number): number {\n return Math.max(Math.min(max, val), min);\n}\n\n/**\n * Capitalize the first letter in a string.\n *\n * @note The string must be UTF-8.\n *\n * @param str The string to format.\n * @returns The string with the first letter capitalized.\n */\nexport function capitalizeFirstUTF8(str: string) {\n return `${str[0].toUpperCase()}${str.substring(1)}`;\n}\n\n/**\n * Create a proxy throwing destroyed errors upon access.\n *\n * @param type The type to display upon error\n * @returns The proxy instance\n */\nexport function createDestroyedProxy(type: string) {\n return new Proxy(\n {},\n {\n get(_, param: string) {\n if (param === 'isDestroyed') return true;\n throw new Error(`Cannot read '${param}' of destroyed ${type}`);\n },\n set(_, param: string) {\n throw new Error(`Cannot write '${param}' of destroyed ${type}`);\n },\n }\n );\n}\n", "/**\n * Types\n */\n\nimport {nativeProperty, enumerable} from './decorators.js';\nimport {WonderlandEngine} from './engine.js';\nimport {isImageLike, isNumber, isString} from './utils/object.js';\nimport {Emitter} from './utils/event.js';\nimport {Material} from './resources/material-manager.js';\nimport {\n ComponentProperty,\n PropertyRecord,\n Type,\n defaultPropertyCloner,\n} from './property.js';\nimport {Constructor, ImageLike, NumberArray, TypedArray, TypedArrayCtor} from './types.js';\nimport {Resource, SceneResource} from './resources/resource.js';\nimport {Prefab} from './prefab.js';\nimport {Scene} from './scene.js';\nimport {createDestroyedProxy} from './utils/misc.js';\nimport {NativeComponents, resetComponentProperties} from './component.js';\n\n/**\n * Component constructor type.\n *\n * For more information, please have a look at the {@link Component} class.\n */\nexport type ComponentConstructor = {\n new (scene: Prefab, manager: number, id: number): T;\n} & {\n _isBaseComponent: boolean;\n _propertyOrder: string[];\n TypeName: string;\n Properties: Record;\n InheritProperties?: boolean;\n onRegister?: (engine: WonderlandEngine) => void;\n};\n\n/**\n * Component prototype interface.\n *\n * User component's should have the same structure.\n */\nexport interface ComponentProto {\n /**\n * Triggered after the component instantiation.\n * For more information, please have a look at {@link Component.init}.\n */\n init?: () => void;\n /**\n * Triggered after the component is activated for the first time.\n * For more information, please have a look at {@link Component.start}.\n */\n start?: () => void;\n /**\n * Triggered once per frame.\n * For more information, please have a look at {@link Component.update}.\n *\n * @param dt Delta time, time since last update.\n */\n update?: (dt: number) => void;\n /**\n * Triggered when the component goes from deactivated to activated.\n * For more information, please have a look at {@link Component.onActivate}.\n */\n onActivate?: () => void;\n /**\n * Triggered when the component goes from activated to deactivated.\n * For more information, please have a look at {@link Component.onDeactivate}.\n */\n onDeactivate?: () => void;\n /**\n * Triggered when the component is removed from its object.\n * For more information, please have a look at {@link Component.onDestroy}.\n *\n * @since 0.9.0\n */\n onDestroy?: () => void;\n}\n\n/**\n * Callback triggered on collision event.\n *\n * @param type Type of the event.\n * @param other Other component that was (un)collided with\n */\nexport type CollisionCallback = (type: CollisionEventType, other: PhysXComponent) => void;\n\n/** @todo Remove at 1.0.0 */\ndeclare const WL: WonderlandEngine;\n\n/**\n * Wonderland Engine API\n * @namespace WL\n */\n\n/**\n * Default set of logging tags used by the API.\n */\nexport enum LogTag {\n /** Initialization, component registration, etc... */\n Engine = 0,\n /** Scene loading */\n Scene = 1,\n /** Component init, update, etc... */\n Component = 2,\n}\n\n/**\n * Collider type enum for {@link CollisionComponent}.\n */\nexport enum Collider {\n /**\n * **Sphere Collider**:\n *\n * Simplest and most performant collision shape. If this type is set on a\n * {@link CollisionComponent}, only the first component of\n * {@link CollisionComponent#extents} will be used to determine the radius.\n */\n Sphere = 0,\n\n /**\n * **Axis Aligned Bounding Box Collider**:\n *\n * Box that is always aligned to XYZ axis. It cannot be rotated but is more\n * efficient than {@link Collider.Box}.\n */\n AxisAlignedBox = 1,\n\n /**\n * **Aligned Bounding Box Collider**:\n *\n * Box that matches the object's rotation and translation correctly. This\n * is the least efficient collider and should only be chosen over\n * {@link Collider.Sphere} and {@link Collider.AxisAlignedBox} if really\n * necessary.\n */\n Box = 2,\n}\n\n/**\n * Alignment type enum for {@link TextComponent}.\n */\nexport enum Alignment {\n /** Text start is at object origin */\n Left = 0,\n\n /** Text center is at object origin */\n Center = 1,\n\n /** Text end is at object origin */\n Right = 2,\n}\n\n/**\n * Vertical alignment type enum for {@link TextComponent}.\n */\nexport enum VerticalAlignment {\n /** Text line is at object origin */\n Line = 0,\n\n /** Text middle is at object origin */\n Middle = 1,\n\n /** Text top is at object origin */\n Top = 2,\n\n /** Text bottom is at object origin */\n Bottom = 3,\n}\n\n/**\n * Justification type enum for {@link TextComponent}.\n *\n * @deprecated Please use {@link VerticalAlignment} instead.\n */\nexport const Justification = VerticalAlignment;\n\n/**\n * Effect type enum for {@link TextComponent}.\n */\nexport enum TextEffect {\n /** Text is rendered normally */\n None = 0,\n\n /** Text is rendered with an outline */\n Outline = 1,\n\n /** Text is rendered with a drop shadow */\n Shadow = 2,\n}\n\n/**\n * Wrap mode enum for {@link TextComponent}.\n *\n * @since 1.2.1\n */\nexport enum TextWrapMode {\n /** Text doesn't wrap automatically, only with explicit newline */\n None = 0,\n\n /** Text wraps at word boundaries */\n Soft = 1,\n\n /** Text wraps anywhere */\n Hard = 2,\n\n /** Text is cut off */\n Clip = 3,\n}\n\n/**\n * Input type enum for {@link InputComponent}.\n */\nexport enum InputType {\n /** Head input */\n Head = 0,\n\n /** Left eye input */\n EyeLeft = 1,\n\n /** Right eye input */\n EyeRight = 2,\n\n /** Left controller input */\n ControllerLeft = 3,\n\n /** Right controller input */\n ControllerRight = 4,\n\n /** Left ray input */\n RayLeft = 5,\n\n /** Right ray input */\n RayRight = 6,\n}\n\n/**\n * Projection type enum for {@link ViewComponent}.\n */\nexport enum ProjectionType {\n /** Perspective projection */\n Perspective = 0,\n\n /** Orthographic projection */\n Orthographic = 1,\n}\n\n/**\n * Light type enum for {@link LightComponent}.\n */\nexport enum LightType {\n /** Point light */\n Point = 0,\n\n /** Spot light */\n Spot = 1,\n\n /** Sun light / Directional light */\n Sun = 2,\n}\n\n/**\n * Animation state of {@link AnimationComponent}.\n */\nexport enum AnimationState {\n /** Animation is currently playing */\n Playing = 0,\n\n /** Animation is paused and will continue at current playback\n * time on {@link AnimationComponent#play} */\n Paused = 1,\n\n /** Animation is stopped */\n Stopped = 2,\n}\n\n/**\n * Root motion mode of {@link AnimationComponent}.\n */\nexport enum RootMotionMode {\n /** Do nothing */\n None = 0,\n /** Move and rotate root with the delta of its motion */\n ApplyToOwner = 1,\n /** Store the motion to be retrieved by a JS script */\n Script = 2,\n}\n\n/**\n * Rigid body force mode for {@link PhysXComponent#addForce} and {@link PhysXComponent#addTorque}.\n *\n * [PhysX API Reference](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxapi/files/structPxForceMode.html)\n */\nexport enum ForceMode {\n /** Apply as force */\n Force = 0,\n\n /** Apply as impulse */\n Impulse = 1,\n\n /** Apply as velocity change, mass dependent */\n VelocityChange = 2,\n\n /** Apply as mass dependent force */\n Acceleration = 3,\n}\n\n/**\n * Collision callback event type.\n */\nexport enum CollisionEventType {\n /** Touch/contact detected, collision */\n Touch = 0,\n\n /** Touch/contact lost, uncollide */\n TouchLost = 1,\n\n /** Touch/contact with trigger detected */\n TriggerTouch = 2,\n\n /** Touch/contact with trigger lost */\n TriggerTouchLost = 3,\n}\n\n/**\n * Rigid body {@link PhysXComponent#shape}.\n *\n * [PhysX SDK Guide](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/Geometry.html#geometry-types).\n */\nexport enum Shape {\n /** No shape. */\n None = 0,\n\n /** Sphere shape. */\n Sphere = 1,\n\n /** Capsule shape. */\n Capsule = 2,\n\n /** Box shape. */\n Box = 3,\n\n /** Plane shape. */\n Plane = 4,\n\n /** Convex mesh shape. */\n ConvexMesh = 5,\n\n /** Triangle mesh shape. */\n TriangleMesh = 6,\n}\n\n/**\n * Mesh attribute enum.\n * @since 0.9.0\n */\nexport enum MeshAttribute {\n /** Position attribute, 3 floats */\n Position = 0,\n\n /** Tangent attribute, 4 floats */\n Tangent = 1,\n\n /** Normal attribute, 3 floats */\n Normal = 2,\n\n /** Texture coordinate attribute, 2 floats */\n TextureCoordinate = 3,\n\n /** Color attribute, 4 floats, RGBA, range `0` to `1` */\n Color = 4,\n\n /** Joint id attribute, 8 unsigned ints */\n JointId = 5,\n\n /** Joint weights attribute, 8 floats */\n JointWeight = 6,\n\n /** Secondary texture coordinate attribute, 2 floats */\n SecondaryTextureCoordinate = 7,\n}\n\n/** Proxy used to override prototypes of destroyed objects. */\nexport const DestroyedObjectInstance = createDestroyedProxy('object');\n/** Proxy used to override prototypes of destroyed components. */\nexport const DestroyedComponentInstance = createDestroyedProxy('component');\n/** Proxy used to override prototypes of destroyed prefabs. */\nexport const DestroyedPrefabInstance = createDestroyedProxy('prefab/scene');\n\n/**\n * Check whether a given shape is a mesh or not.\n *\n * @param shape The shape to check.\n * @returns `true` if the shape is a mesh, `false` if it's a primitive.\n */\nfunction isMeshShape(shape: Shape): boolean {\n return shape === Shape.ConvexMesh || shape === Shape.TriangleMesh;\n}\n\n/**\n * Check whether an object is the {@link Component} class or not,\n *\n * @note This method if foolproof to developers inadvertently using\n * multiple Wonderland Engine API in a bundle.\n *\n * @param value The object to check.\n * @returns `true` if the object is a {@link Component} class, `false` otherwise.\n */\nfunction isBaseComponentClass(\n value: Constructor | null\n): value is ComponentConstructor {\n return (\n !!value &&\n value.hasOwnProperty('_isBaseComponent') &&\n (value as ComponentConstructor)._isBaseComponent\n );\n}\n\n/**\n * Constants.\n */\n\n/**\n * Default world up vector.\n */\nconst UP_VECTOR = [0, 1, 0];\n\nconst SQRT_3 = Math.sqrt(3);\n\n/**\n * Provides access to a component instance of a specified component type.\n *\n * @example\n *\n * This is how you extend this class to create your own custom\n * component:\n *\n * ```js\n * import { Component, Type } from '@wonderlandengine/api';\n *\n * export class MyComponent extends Component {\n * static TypeName = 'my-component';\n * static Properties = {\n * myBoolean: { type: Type.Boolean, default: false },\n * };\n * start() {}\n * onActivate() {}\n * onDeactivate() {}\n * update(dt) {}\n * }\n * ```\n *\n * In a component, the scene can be accessed using `this.scene`:\n *\n * ```js\n * import { Component, Type } from '@wonderlandengine/api';\n *\n * export class MyComponent extends Component {\n * static TypeName = 'my-component';\n * start() {\n * const obj = this.scene.addObject();\n * }\n * }\n * ```\n */\nexport class Component {\n /**\n * Pack scene index and component id.\n *\n * @param scene Scene index.\n * @param id Component id.\n * @returns The packed id.\n *\n * @hidden\n */\n static _pack(scene: number, id: number) {\n return (scene << 22) | id;\n }\n\n /**\n * `true` for every class inheriting from this class.\n *\n * @note This is a workaround for `instanceof` to prevent issues\n * that could arise when an application ends up using multiple API versions.\n *\n * @hidden\n */\n static readonly _isBaseComponent = true;\n\n /**\n * Fixed order of attributes in the `Properties` array.\n *\n * @note This is used for parameter deserialization and is filled during\n * component registration.\n *\n * @hidden\n */\n static _propertyOrder: string[] = [];\n\n /**\n * Unique identifier for this component class.\n *\n * This is used to register, add, and retrieve components of a given type.\n */\n static TypeName: string;\n\n /**\n * Properties of this component class.\n *\n * Properties are public attributes that can be configured via the\n * Wonderland Editor.\n *\n * Example:\n *\n * ```js\n * import { Component, Type } from '@wonderlandengine/api';\n * class MyComponent extends Component {\n * static TypeName = 'my-component';\n * static Properties = {\n * myBoolean: { type: Type.Boolean, default: false },\n * myFloat: { type: Type.Float, default: false },\n * myTexture: { type: Type.Texture, default: null },\n * };\n * }\n * ```\n *\n * Properties are automatically added to each component instance, and are\n * accessible like any JS attribute:\n *\n * ```js\n * // Creates a new component and set each properties value:\n * const myComponent = object.addComponent(MyComponent, {\n * myBoolean: true,\n * myFloat: 42.0,\n * myTexture: null\n * });\n *\n * // You can also override the properties on the instance:\n * myComponent.myBoolean = false;\n * myComponent.myFloat = -42.0;\n * ```\n *\n * #### References\n *\n * Reference types (i.e., mesh, object, etc...) can also be listed as **required**:\n *\n * ```js\n * import {Component, Property} from '@wonderlandengine/api';\n *\n * class MyComponent extends Component {\n * static Properties = {\n * myObject: Property.object({required: true}),\n * myAnimation: Property.animation({required: true}),\n * myTexture: Property.texture({required: true}),\n * myMesh: Property.mesh({required: true}),\n * }\n * }\n * ```\n *\n * Please note that references are validated **once** before the call to {@link Component.start} only,\n * via the {@link Component.validateProperties} method.\n */\n static Properties: Record;\n\n /**\n * When set to `true`, the child class inherits from the parent\n * properties, as shown in the following example:\n *\n * ```js\n * import {Component, Property} from '@wonderlandengine/api';\n *\n * class Parent extends Component {\n * static TypeName = 'parent';\n * static Properties = {parentName: Property.string('parent')}\n * }\n *\n * class Child extends Parent {\n * static TypeName = 'child';\n * static Properties = {name: Property.string('child')}\n * static InheritProperties = true;\n *\n * start() {\n * // Works because `InheritProperties` is `true`.\n * console.log(`${this.name} inherits from ${this.parentName}`);\n * }\n * }\n * ```\n *\n * @note Properties defined in descendant classes will override properties\n * with the same name defined in ancestor classes.\n *\n * Defaults to `true`.\n */\n static InheritProperties?: boolean;\n\n /**\n * Called when this component class is registered.\n *\n * @example\n *\n * This callback can be used to register dependencies of a component,\n * e.g., component classes that need to be registered in order to add\n * them at runtime with {@link Object3D.addComponent}, independent of whether\n * they are used in the editor.\n *\n * ```js\n * class Spawner extends Component {\n * static TypeName = 'spawner';\n *\n * static onRegister(engine) {\n * engine.registerComponent(SpawnedComponent);\n * }\n *\n * // You can now use addComponent with SpawnedComponent\n * }\n * ```\n *\n * @example\n *\n * This callback can be used to register different implementations of a\n * component depending on client features or API versions.\n *\n * ```js\n * // Properties need to be the same for all implementations!\n * const SharedProperties = {};\n *\n * class Anchor extends Component {\n * static TypeName = 'spawner';\n * static Properties = SharedProperties;\n *\n * static onRegister(engine) {\n * if(navigator.xr === undefined) {\n * /* WebXR unsupported, keep this dummy component *\\/\n * return;\n * }\n * /* WebXR supported! Override already registered dummy implementation\n * * with one depending on hit-test API support *\\/\n * engine.registerComponent(window.HitTestSource === undefined ?\n * AnchorWithoutHitTest : AnchorWithHitTest);\n * }\n *\n * // This one implements no functions\n * }\n * ```\n */\n static onRegister?: (engine: WonderlandEngine) => void;\n\n /**\n * Allows to inherit properties directly inside the editor.\n *\n * @note Do not use directly, prefer using {@link inheritProperties}.\n *\n * @hidden\n */\n static _inheritProperties() {\n inheritProperties(this);\n }\n\n /**\n * Triggered when the component is initialized by the runtime. This method\n * will only be triggered **once** after instantiation.\n *\n * @note During the initialization phase, `this.scene` will not match\n * `engine.scene`, since `engine.scene` references the **active** scene:\n *\n * ```js\n * import {Component} from '@wonderlandengine/api';\n *\n * class MyComponent extends Component{\n * init() {\n * const activeScene = this.engine.scene;\n * console.log(this.scene === activeScene); // Prints `false`\n * }\n * start() {\n * const activeScene = this.engine.scene;\n * console.log(this.scene === activeScene); // Prints `true`\n * }\n * }\n * ```\n */\n init?(): void;\n\n /**\n * Triggered when the component is started by the runtime, or activated.\n *\n * You can use that to re-initialize the state of the component.\n */\n start?(): void;\n\n /**\n * Triggered **every frame** by the runtime.\n *\n * You should perform your business logic in this method. Example:\n *\n * ```js\n * import { Component, Type } from '@wonderlandengine/api';\n *\n * class TranslateForwardComponent extends Component {\n * static TypeName = 'translate-forward-component';\n * static Properties = {\n * speed: { type: Type.Float, default: 1.0 }\n * };\n * constructor() {\n * this._forward = new Float32Array([0, 0, 0]);\n * }\n * update(dt) {\n * this.object.getForward(this._forward);\n * this._forward[0] *= this.speed;\n * this._forward[1] *= this.speed;\n * this._forward[2] *= this.speed;\n * this.object.translate(this._forward);\n * }\n * }\n * ```\n *\n * @param delta Elapsed time between this frame and the previous one, in **seconds**.\n */\n update?(delta: number): void;\n\n /**\n * Triggered when the component goes from an inactive state to an active state.\n *\n * @note When using ({@link WonderlandEngine.switchTo}), all the components\n * that were previously active will trigger this method.\n *\n * @note You can manually activate or deactivate a component using: {@link Component.active:setter}.\n */\n onActivate?(): void;\n\n /**\n * Triggered when the component goes from an activated state to an inactive state.\n *\n * @note When using ({@link WonderlandEngine.switchTo}), the components of\n * the scene getting deactivated will trigger this method.\n *\n * @note You can manually activate or deactivate a component using: {@link Component.active:setter}.\n */\n onDeactivate?(): void;\n\n /**\n * Triggered when the component is removed from its object.\n * For more information, please have a look at {@link Component.onDestroy}.\n *\n * @note This method will not be triggered for inactive scene being destroyed.\n *\n * @since 0.9.0\n */\n onDestroy?(): void;\n\n /** Manager index. @hidden */\n readonly _manager: number;\n /** Packed id, containing the scene and the local id. @hidden */\n readonly _id: number;\n /** Id relative to the scene component's manager. @hidden */\n readonly _localId: number;\n\n /**\n * Object containing this object.\n *\n * **Note**: This is cached for faster retrieval.\n *\n * @hidden\n */\n _object: Object3D | null;\n\n /** Scene instance. @hidden */\n protected readonly _scene: Prefab;\n\n /**\n * Create a new instance\n *\n * @param engine The engine instance.\n * @param manager Index of the manager.\n * @param id WASM component instance index.\n *\n * @hidden\n */\n constructor(scene: Prefab, manager: number = -1, id: number = -1) {\n this._scene = scene;\n this._manager = manager;\n this._localId = id;\n this._id = Component._pack(scene._index, id);\n this._object = null;\n }\n\n /** Scene this component is part of. */\n get scene() {\n return this._scene;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._scene.engine;\n }\n\n /** The name of this component's type */\n get type(): string {\n const ctor = this.constructor as ComponentConstructor;\n return ctor.TypeName;\n }\n\n /** The object this component is attached to. */\n get object(): Object3D {\n if (!this._object) {\n const objectId = this.engine.wasm._wl_component_get_object(\n this._manager,\n this._id\n );\n this._object = this._scene.wrap(objectId);\n }\n return this._object;\n }\n\n /**\n * Set whether this component is active.\n *\n * Activating/deactivating a component comes at a small cost of reordering\n * components in the respective component manager. This function therefore\n * is not a trivial assignment.\n *\n * Does nothing if the component is already activated/deactivated.\n *\n * @param active New active state.\n */\n set active(active: boolean) {\n this.engine.wasm._wl_component_setActive(this._manager, this._id, active);\n }\n\n /** `true` if the component is marked as active and its scene is active. */\n get active(): boolean {\n return this.markedActive && this._scene.isActive;\n }\n\n /**\n * `true` if the component is marked as active in the scene, `false` otherwise.\n *\n * @note At the opposite of {@link Component.active}, this accessor doesn't\n * take into account whether the scene is active or not.\n */\n get markedActive(): boolean {\n return this.engine.wasm._wl_component_isActive(this._manager, this._id) != 0;\n }\n\n /**\n * Copy all the properties from `src` into this instance.\n *\n * @note Only properties are copied. If a component needs to\n * copy extra data, it needs to override this method.\n *\n * #### Example\n *\n * ```js\n * class MyComponent extends Component {\n * nonPropertyData = 'Hello World';\n *\n * copy(src) {\n * super.copy(src);\n * this.nonPropertyData = src.nonPropertyData;\n * return this;\n * }\n * }\n * ```\n *\n * @note This method is called by {@link Object3D.clone}. Do not attempt to:\n * - Create new component\n * - Read references to other objects\n *\n * When cloning via {@link Object3D.clone}, this method will be called before\n * {@link Component.start}.\n *\n * @note JavaScript component properties aren't retargeted. Thus, references\n * inside the source object will not be retargeted to the destination object,\n * at the exception of the skin data on {@link MeshComponent} and {@link AnimationComponent}.\n *\n * @param src The source component to copy from.\n *\n * @returns Reference to self (for method chaining).\n */\n copy(src: Record): this {\n const ctor = this.constructor as ComponentConstructor;\n const properties = ctor.Properties;\n if (!properties) return this;\n for (const name in properties) {\n const property = properties[name];\n const value = src[name];\n if (value === undefined) continue;\n const cloner = property.cloner ?? defaultPropertyCloner;\n (this as Record)[name] = cloner.clone(property.type, value);\n }\n return this;\n }\n\n /**\n * Remove this component from its objects and destroy it.\n *\n * It is best practice to set the component to `null` after,\n * to ensure it does not get used later.\n *\n * ```js\n * c.destroy();\n * c = null;\n * ```\n * @since 0.9.0\n */\n destroy(): void {\n const manager = this._manager;\n if (manager < 0 || this._id < 0) return;\n\n /* This call will mark the component destroyed,\n * automatically calling `_triggerOnDestroy`. */\n this.engine.wasm._wl_component_remove(manager, this._id);\n }\n\n /**\n * Checks equality by comparing ids and **not** the JavaScript reference.\n *\n * @deprecate Use JavaScript reference comparison instead:\n *\n * ```js\n * const componentA = obj.addComponent('mesh');\n * const componentB = obj.addComponent('mesh');\n * const componentC = componentB;\n * console.log(componentA === componentB); // false\n * console.log(componentA === componentA); // true\n * console.log(componentB === componentC); // true\n * ```\n */\n equals(otherComponent: Component | undefined | null): boolean {\n /** @todo(2.0.0): Remove this method. */\n if (!otherComponent) return false;\n return this._manager === otherComponent._manager && this._id === otherComponent._id;\n }\n\n /**\n * Reset the component properties to default.\n *\n * @note This is automatically called during the component instantiation.\n *\n * @returns Reference to self (for method chaining).\n */\n resetProperties(): this {\n resetComponentProperties(this);\n return this;\n }\n\n /** @deprecated Use {@link Component.resetProperties} instead. */\n reset(): this {\n return this.resetProperties();\n }\n\n /**\n * Validate the properties on this instance.\n *\n * @throws If any of the required properties isn't initialized\n * on this instance.\n */\n validateProperties(): void {\n const ctor = this.constructor as ComponentConstructor;\n if (!ctor.Properties) return;\n\n for (const name in ctor.Properties) {\n if (!ctor.Properties[name].required) continue;\n if (!(this as Record)[name]) {\n throw new Error(`Property '${name}' is required but was not initialized`);\n }\n }\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Component(destroyed)';\n }\n return `Component('${this.type}', ${this._localId})`;\n }\n\n /**\n * `true` if the component is destroyed, `false` otherwise.\n *\n * If {@link WonderlandEngine.erasePrototypeOnDestroy} is `true`,\n * reading a custom property will not work:\n *\n * ```js\n * engine.erasePrototypeOnDestroy = true;\n *\n * const comp = obj.addComponent('mesh');\n * comp.customParam = 'Hello World!';\n *\n * console.log(comp.isDestroyed); // Prints `false`\n * comp.destroy();\n * console.log(comp.isDestroyed); // Prints `true`\n * console.log(comp.customParam); // Throws an error\n * ```\n *\n * @since 1.1.1\n */\n get isDestroyed(): boolean {\n return this._id < 0;\n }\n\n /** @hidden */\n _copy(src: this, offsetsPtr: number, copyInfoPtr: number) {\n const wasm = this.engine.wasm;\n\n /** @todo: Support retargeting for `Object3D.clone` as well. */\n const offsets = wasm.HEAPU32;\n const offsetsStart = offsetsPtr >>> 2;\n\n const copyInfoStart = copyInfoPtr >>> 1;\n const srcRootIndex = wasm.HEAPU16[copyInfoStart]; /* Defaults to max ushort */\n const srcRootSize = wasm.HEAPU16[copyInfoStart + 1]; /* Defaults to 0 */\n const dstRootIndex = wasm.HEAPU16[copyInfoStart + 2]; /* Defaults to 0 */\n\n const destScene = this._scene;\n\n const ctor = this.constructor as ComponentConstructor;\n for (const name in ctor.Properties) {\n const value = (src as Record)[name];\n if (value === null) {\n (this as Record)[name] = null;\n continue;\n }\n\n const prop = ctor.Properties[name];\n const offset = offsets[offsetsStart + prop.type];\n\n let retargeted = null;\n switch (prop.type) {\n case Type.Object: {\n let index = wasm._wl_object_index((value as Object3D)._id) + offset;\n const dist = index - srcRootIndex;\n if (dist >= 0 && dist <= srcRootSize) {\n /* Only retarget if a reference points into the source hierarchy. */\n index = dstRootIndex + dist;\n }\n const id = wasm._wl_object_id(destScene._index, index);\n retargeted = destScene.wrap(id);\n break;\n }\n case Type.Animation:\n retargeted = destScene.animations.wrap(\n offset + (value as Animation)._index\n );\n break;\n case Type.Skin:\n retargeted = destScene.skins.wrap(offset + (value as Skin)._index);\n break;\n default:\n const cloner = prop.cloner ?? defaultPropertyCloner;\n retargeted = cloner.clone(prop.type, value);\n break;\n }\n (this as Record)[name] = retargeted;\n }\n return this;\n }\n\n /**\n * Trigger the component {@link Component.init} method.\n *\n * @note Use this method instead of directly calling {@link Component.init},\n * because this method creates an handler for the {@link Component.start}.\n *\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n _triggerInit() {\n if (this.init) {\n try {\n this.init();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} init() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n }\n }\n\n /* Arm onActivate() with the initial start() call */\n const oldActivate = this.onActivate;\n this.onActivate = function () {\n this.onActivate = oldActivate;\n let failed = false;\n try {\n this.validateProperties();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} validateProperties() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n failed = true;\n }\n\n try {\n this.start?.();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} start() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n failed = true;\n }\n\n if (failed) {\n this.active = false;\n return;\n }\n\n if (!this.onActivate) return;\n\n try {\n this.onActivate();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} onActivate() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n }\n };\n }\n\n /**\n * Trigger the component {@link Component.update} method.\n *\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n _triggerUpdate(dt: number) {\n if (!this.update) return;\n try {\n this.update(dt);\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} update() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n if (this.engine.wasm._deactivate_component_on_error) {\n this.active = false;\n }\n }\n }\n\n /**\n * Trigger the component {@link Component.onActivate} method.\n *\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n _triggerOnActivate() {\n if (!this.onActivate) return;\n try {\n this.onActivate();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} onActivate() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n }\n }\n\n /**\n * Trigger the component {@link Component.onDeactivate} method.\n *\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n _triggerOnDeactivate() {\n if (!this.onDeactivate) return;\n try {\n this.onDeactivate();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} onDeactivate() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n }\n }\n\n /**\n * Trigger the component {@link Component.onDestroy} method.\n *\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n _triggerOnDestroy() {\n try {\n if (this.onDestroy) this.onDestroy();\n } catch (e) {\n this.engine.log.error(\n LogTag.Component,\n `Exception during ${this.type} onDestroy() on object ${this.object.name}`\n );\n this.engine.log.error(LogTag.Component, e);\n }\n this._scene._components.destroy(this);\n }\n}\n\n/**\n * Components must be registered before loading / appending a scene.\n *\n * It's possible to end up with a broken component in the following cases:\n *\n * - Component wasn't registered when the scene was loaded\n * - Component instantiation failed\n *\n * This dummy component is thus used as a placeholder by the engine.\n */\nexport class BrokenComponent extends Component {\n static TypeName = '__broken-component__';\n}\n\n/**\n * Merge the ascendant properties of class\n *\n * This method walks the prototype chain, and merges\n * all the properties found in parent components.\n *\n * Example:\n *\n * ```js\n * import {Property, inheritProperties} from '@wonderlandengine/api';\n *\n * class Parent {\n * static Properties = { parentProp: Property.string('parent') };\n * }\n *\n * class Child extends Parent {\n * static Properties = { childProp: Property.string('child') };\n * }\n * inheritProperties(Child);\n * ```\n *\n * @param target The class in which properties should be merged\n *\n * @hidden\n */\nexport function inheritProperties(target: ComponentConstructor | PropertyRecord) {\n const chain: ComponentConstructor[] = [];\n let curr: Constructor | null = target;\n while (curr && !isBaseComponentClass(curr)) {\n const comp = curr as ComponentConstructor;\n\n /* Stop at the first class that doesn't require properties merging */\n const needsMerge = comp.hasOwnProperty('InheritProperties')\n ? comp.InheritProperties\n : true;\n if (!needsMerge) break;\n\n if (comp.hasOwnProperty('Properties')) {\n /* We push properties even if the object isn't deriving from a Component class.\n * This could theoretically lead to issue, when inheriting from another type\n * of component (lit-like elements) but extremely unlikely. */\n chain.push(comp);\n }\n curr = Object.getPrototypeOf(curr);\n }\n\n /* No prototype merge is needed. */\n if (!chain.length || (chain.length === 1 && chain[0] === target)) {\n return;\n }\n\n const merged: Record = {};\n for (let i = chain.length - 1; i >= 0; --i) {\n Object.assign(merged, chain[i].Properties);\n }\n target.Properties = merged;\n}\n\n/**\n * Native collision component.\n *\n * Provides access to a native collision component instance.\n */\nexport class CollisionComponent extends Component {\n /** @override */\n static TypeName = 'collision';\n\n /** @overload */\n getExtents(): Float32Array;\n /**\n * Collision component extents.\n *\n * If {@link collider} returns {@link Collider.Sphere}, only the first\n * component of the returned vector is used.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getExtents(out: T): T;\n getExtents(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_collision_component_get_extents(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n return out;\n }\n\n /** Collision component collider */\n @nativeProperty()\n get collider(): Collider {\n return this.engine.wasm._wl_collision_component_get_collider(this._id);\n }\n\n /**\n * Set collision component collider.\n *\n * @param collider Collider of the collision component.\n */\n set collider(collider: Collider) {\n this.engine.wasm._wl_collision_component_set_collider(this._id, collider);\n }\n\n /**\n * Equivalent to {@link CollisionComponent.getExtents}.\n *\n * @note Prefer to use {@link CollisionComponent.getExtents} for performance.\n */\n @nativeProperty()\n get extents(): Float32Array {\n /** @todo: Break at 2.0.0. Do not allow modifying memory in-place. */\n const wasm = this.engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_collision_component_get_extents(this._id),\n 3\n );\n }\n\n /**\n * Set collision component extents.\n *\n * If {@link collider} returns {@link Collider.Sphere}, only the first\n * component of the passed vector is used.\n *\n * Example:\n *\n * ```js\n * // Spans 1 unit on the x-axis, 2 on the y-axis, 3 on the z-axis.\n * collision.extent = [1, 2, 3];\n * ```\n *\n * @param extents Extents of the collision component, expects a\n * 3 component array.\n */\n set extents(extents: Readonly) {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_collision_component_get_extents(this._id) / 4; /* Align F32 */\n wasm.HEAPF32[ptr] = extents[0];\n wasm.HEAPF32[ptr + 1] = extents[1];\n wasm.HEAPF32[ptr + 2] = extents[2];\n }\n\n /**\n * Get collision component radius.\n *\n * @note If {@link collider} is not {@link Collider.Sphere}, the returned value\n * corresponds to the radius of a sphere enclosing the shape.\n *\n * Example:\n *\n * ```js\n * sphere.radius = 3.0;\n * console.log(sphere.radius); // 3.0\n *\n * box.extents = [2.0, 2.0, 2.0];\n * console.log(box.radius); // 1.732...\n * ```\n *\n */\n get radius(): number {\n const wasm = this.engine.wasm;\n if (this.collider === Collider.Sphere)\n return wasm.HEAPF32[wasm._wl_collision_component_get_extents(this._id) >> 2];\n const extents = new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_collision_component_get_extents(this._id),\n 3\n );\n const x2 = extents[0] * extents[0];\n const y2 = extents[1] * extents[1];\n const z2 = extents[2] * extents[2];\n return Math.sqrt(x2 + y2 + z2) / 2;\n }\n\n /**\n * Set collision component radius.\n *\n * @param radius Radius of the collision component\n *\n * @note If {@link collider} is not {@link Collider.Sphere},\n * the extents are set to form a square that fits a sphere with the provided radius.\n *\n * Example:\n *\n * ```js\n * aabbCollision.radius = 2.0; // AABB fits a sphere of radius 2.0\n * boxCollision.radius = 3.0; // Box now fits a sphere of radius 3.0, keeping orientation\n * ```\n *\n */\n set radius(radius: number) {\n const length = this.collider === Collider.Sphere ? radius : (2 * radius) / SQRT_3;\n this.extents.set([length, length, length]);\n }\n\n /**\n * Collision component group.\n *\n * The groups is a bitmask that is compared to other components in {@link CollisionComponent#queryOverlaps}\n * or the group in {@link Scene#rayCast}.\n *\n * Colliders that have no common groups will not overlap with each other. If a collider\n * has none of the groups set for {@link Scene#rayCast}, the ray will not hit it.\n *\n * Each bit represents belonging to a group, see example.\n *\n * ```js\n * // c belongs to group 2\n * c.group = (1 << 2);\n *\n * // c belongs to group 0\n * c.group = (1 << 0);\n *\n * // c belongs to group 0 *and* 2\n * c.group = (1 << 0) | (1 << 2);\n *\n * (c.group & (1 << 2)) != 0; // true\n * (c.group & (1 << 7)) != 0; // false\n * ```\n */\n @nativeProperty()\n get group(): number {\n return this.engine.wasm._wl_collision_component_get_group(this._id);\n }\n\n /**\n * Set collision component group.\n *\n * @param group Group mask of the collision component.\n */\n set group(group: number) {\n this.engine.wasm._wl_collision_component_set_group(this._id, group);\n }\n\n /**\n * Query overlapping objects.\n *\n * Usage:\n *\n * ```js\n * const collision = object.getComponent('collision');\n * const overlaps = collision.queryOverlaps();\n * for(const otherCollision of overlaps) {\n * const otherObject = otherCollision.object;\n * console.log(`Collision with object ${otherObject.objectId}`);\n * }\n * ```\n *\n * @returns Collision components overlapping this collider.\n */\n queryOverlaps(): CollisionComponent[] {\n const count = this.engine.wasm._wl_collision_component_query_overlaps(\n this._id,\n this.engine.wasm._tempMem,\n this.engine.wasm._tempMemSize >> 1\n );\n const overlaps: CollisionComponent[] = new Array(count);\n for (let i = 0; i < count; ++i) {\n const id = this.engine.wasm._tempMemUint16[i];\n overlaps[i] = this._scene._components.wrapCollision(id);\n }\n return overlaps;\n }\n}\n\n/**\n * Native text component\n *\n * Provides access to a native text component instance\n */\nexport class TextComponent extends Component {\n /** @override */\n static TypeName = 'text';\n\n /** Text component alignment. */\n @nativeProperty()\n get alignment(): Alignment {\n return this.engine.wasm._wl_text_component_get_horizontal_alignment(this._id);\n }\n\n /**\n * Set text component alignment.\n *\n * @param alignment Alignment for the text component.\n */\n set alignment(alignment: Alignment) {\n this.engine.wasm._wl_text_component_set_horizontal_alignment(this._id, alignment);\n }\n\n /**\n * Text component vertical alignment.\n * @since 1.2.0\n */\n @nativeProperty()\n get verticalAlignment(): VerticalAlignment {\n return this.engine.wasm._wl_text_component_get_vertical_alignment(this._id);\n }\n\n /**\n * Set text component vertical alignment.\n *\n * @param verticalAlignment Vertical for the text component.\n * @since 1.2.0\n */\n set verticalAlignment(verticalAlignment: VerticalAlignment) {\n this.engine.wasm._wl_text_component_set_vertical_alignment(\n this._id,\n verticalAlignment\n );\n }\n\n /**\n * Text component justification.\n *\n * @deprecated Please use {@link TextComponent.verticalAlignment} instead.\n */\n @nativeProperty()\n get justification() {\n return this.verticalAlignment;\n }\n\n /**\n * Set text component justification.\n *\n * @param justification Justification for the text component.\n *\n * @deprecated Please use {@link TextComponent.verticalAlignment} instead.\n */\n set justification(justification: VerticalAlignment) {\n this.verticalAlignment = justification;\n }\n\n /**\n * Whether text is justified horizontally.\n *\n * Aligns text horizontally to both margins by adding space between words.\n *\n * Requires {@link wrapMode} to be {@link TextWrapMode.Soft} or\n * {@link TextWrapMode.Hard} and non-0 {@link wrapWidth}.\n *\n * The last line in a paragraph is never justified and respects\n * {@link alignment}.\n *\n * @since 1.3.0\n */\n @nativeProperty()\n get justified(): boolean {\n return !!this.engine.wasm._wl_text_component_get_justified(this._id);\n }\n\n /**\n * Set whether text is justified horizontally.\n *\n * @param justified New justified state for the text component.\n * @since 1.3.0\n */\n set justified(justified: boolean) {\n this.engine.wasm._wl_text_component_set_justified(this._id, justified);\n }\n\n /** Text component character spacing. */\n @nativeProperty()\n get characterSpacing(): number {\n return this.engine.wasm._wl_text_component_get_character_spacing(this._id);\n }\n\n /**\n * Set text component character spacing.\n *\n * @param spacing Character spacing for the text component.\n */\n set characterSpacing(spacing: number) {\n this.engine.wasm._wl_text_component_set_character_spacing(this._id, spacing);\n }\n\n /** Text component line spacing. */\n @nativeProperty()\n get lineSpacing(): number {\n return this.engine.wasm._wl_text_component_get_line_spacing(this._id);\n }\n\n /**\n * Set text component line spacing.\n *\n * @param spacing Line spacing for the text component\n */\n set lineSpacing(spacing: number) {\n this.engine.wasm._wl_text_component_set_line_spacing(this._id, spacing);\n }\n\n /** Text component effect. */\n @nativeProperty()\n get effect(): TextEffect {\n return this.engine.wasm._wl_text_component_get_effect(this._id);\n }\n\n /**\n * Set text component effect.\n *\n * @param effect Effect for the text component\n */\n set effect(effect: TextEffect) {\n this.engine.wasm._wl_text_component_set_effect(this._id, effect);\n }\n\n /**\n * Equivalent to {@link getEffectOffset}.\n *\n * @note Prefer to use {@link getEffectOffset} for performance.\n *\n * @since 1.3.0\n */\n @nativeProperty()\n get effectOffset(): Float32Array {\n return this.getEffectOffset();\n }\n\n /**\n * Equivalent to {@link setEffectOffset}.\n *\n * @since 1.3.0\n */\n set effectOffset(offset: Readonly) {\n this.setEffectOffset(offset);\n }\n\n /** @overload */\n getEffectOffset(): Float32Array;\n /**\n * Get text component effect offset.\n *\n * @param out Destination array, expected to have at least 2 elements.\n * @returns The `out` parameter.\n *\n * @since 1.3.0\n */\n getEffectOffset(out: T): T;\n getEffectOffset(out: NumberArray = new Float32Array(2)): NumberArray {\n const wasm = this.engine.wasm;\n wasm._wl_text_component_get_effectOffset(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n return out;\n }\n\n /**\n * Set text component effect offset.\n *\n * The offset is given as X and Y factors for {@link Font.emHeight}. E.g. a\n * value of 2 in one axis offsets the effect by two font line heights. A\n * positive Y value moves the effect **upwards**.\n *\n * @param offset Array with new offset, expected to have at least 2 elements.\n *\n * @since 1.3.0\n */\n setEffectOffset(offset: Readonly) {\n const wasm = this.engine.wasm;\n wasm._tempMemFloat.set(offset);\n wasm._wl_text_component_set_effectOffset(this._id, wasm._tempMem);\n }\n\n /**\n * Text component line wrap mode.\n * @since 1.2.1\n */\n @nativeProperty()\n get wrapMode(): TextWrapMode {\n return this.engine.wasm._wl_text_component_get_wrapMode(this._id);\n }\n\n /**\n * Set text component line wrap mode.\n *\n * @param wrapMode Line wrap mode for the text component.\n * @since 1.2.1\n */\n set wrapMode(wrapMode: TextWrapMode) {\n this.engine.wasm._wl_text_component_set_wrapMode(this._id, wrapMode);\n }\n\n /**\n * Text component line wrap width, in object space.\n * @since 1.2.1\n */\n @nativeProperty()\n get wrapWidth(): number {\n return this.engine.wasm._wl_text_component_get_wrapWidth(this._id);\n }\n\n /**\n * Set text component line wrap width.\n *\n * Only takes effect when {@link wrapMode} is something other than\n * {@link TextWrapMode.None}.\n *\n * @param width Line wrap width for the text component, in object space.\n * @since 1.2.1\n */\n set wrapWidth(width: number) {\n this.engine.wasm._wl_text_component_set_wrapWidth(this._id, width);\n }\n\n /** Text component text. */\n @nativeProperty()\n get text(): string {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_text_component_get_text(this._id);\n return wasm.UTF8ToString(ptr);\n }\n\n /**\n * Set text component text.\n *\n * @param text Text of the text component.\n */\n set text(text: any) {\n const wasm = this.engine.wasm;\n wasm._wl_text_component_set_text(this._id, wasm.tempUTF8(text.toString()));\n }\n\n /**\n * Set material to render the text with.\n *\n * @param material New material.\n */\n set material(material: Material | null | undefined) {\n const matIndex = material ? material._id : 0;\n this.engine.wasm._wl_text_component_set_material(this._id, matIndex);\n }\n\n /** Material used to render the text. */\n @nativeProperty()\n get material(): Material | null {\n const index = this.engine.wasm._wl_text_component_get_material(this._id);\n return this.engine.materials.wrap(index);\n }\n\n /** @overload */\n getBoundingBoxForText(text: string): Float32Array;\n /**\n * Axis-aligned bounding box for a given text, in object space.\n *\n * To calculate the size for the currently set text, use\n * {@link getBoundingBox}.\n *\n * Useful for calculating the text size before an update and potentially\n * adjusting the text:\n *\n * ```js\n * let updatedName = 'some very long name';\n * const box = new Float32Array(4);\n * text.getBoundingBoxForText(updatedName, box);\n * const width = box[2] - box[0];\n * if(width > 2.0) {\n * updatedName = updatedName.slice(0, 5) + '...';\n * }\n * text.text = updatedName;\n * ```\n *\n * @param text Text string to calculate the bounding box for.\n * @param out Preallocated array to write into, to avoid garbage,\n * otherwise will allocate a new Float32Array.\n *\n * @returns Bounding box - left, bottom, right, top.\n */\n getBoundingBoxForText(text: string, out: T): T;\n /** @overload */\n getBoundingBoxForText(\n text: string,\n out: T | Float32Array = new Float32Array(4)\n ): T | Float32Array {\n const wasm = this.engine.wasm;\n /* Offset by 4 floats, output is written to _tempMem */\n const textPtr = wasm.tempUTF8(text, 4 * 4);\n this.engine.wasm._wl_text_component_get_boundingBox(\n this._id,\n textPtr,\n wasm._tempMem\n );\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n out[3] = wasm._tempMemFloat[3];\n return out as T;\n }\n\n /** @overload */\n getBoundingBox(): Float32Array;\n /**\n * Axis-aligned bounding box, in object space.\n *\n * The bounding box is computed using the current component properties\n * that influence the position and size of the text. The bounding box is\n * affected by alignment, spacing, effect type and the font set in the\n * material.\n *\n * To calculate the size for a different text, use\n * {@link getBoundingBoxForText}.\n *\n * Useful for adjusting text position or scaling:\n *\n * ```js\n * const box = new Float32Array(4);\n * text.getBoundingBox(box);\n * const width = box[2] - box[0];\n * // Make text 1m wide\n * text.object.setScalingLocal([1/width, 1, 1]);\n * ```\n *\n * @param text Text string to calculate the bounding box for.\n * @param out Preallocated array to write into, to avoid garbage,\n * otherwise will allocate a new Float32Array.\n *\n * @returns Bounding box - left, bottom, right, top.\n */\n getBoundingBox(out: T): T;\n /** @overload */\n getBoundingBox(\n out: T | Float32Array = new Float32Array(4)\n ): T | Float32Array {\n const wasm = this.engine.wasm;\n this.engine.wasm._wl_text_component_get_boundingBox(this._id, 0, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n out[3] = wasm._tempMemFloat[3];\n return out as T;\n }\n}\n\n/**\n * Native view component.\n *\n * Provides access to a native view component instance.\n */\nexport class ViewComponent extends Component {\n /** @override */\n static TypeName = 'view';\n\n /**\n * Projection type of the view.\n *\n * @since 1.2.2\n */\n @nativeProperty()\n get projectionType(): ProjectionType {\n return this.engine.wasm._wl_view_component_get_projectionType(this._id);\n }\n\n /**\n * Set the projection type of the view.\n *\n * @param type New projection type.\n * @since 1.2.2\n */\n set projectionType(type) {\n this.engine.wasm._wl_view_component_set_projectionType(this._id, type);\n }\n\n /** @overload */\n getProjectionMatrix(): Float32Array;\n /**\n * Projection matrix.\n *\n * A 4x4 matrix that transforms from view space to a WebGL-compatible clip\n * space (-1 to 1 on all axes, near plane at -1, far plane at 1).\n *\n * If an XR session is active and this is the left or right eye view, this\n * returns the projection matrix reported by the device.\n *\n * @note This is not necessarily the final projection matrix used for\n * rendering. You can use it for unprojecting from screen space coordinates\n * to view space.\n *\n * @param out Destination array/vector, expected to have at least 16 elements.\n * @returns The `out` parameter.\n */\n getProjectionMatrix(out: T): T;\n getProjectionMatrix(out: NumberArray = new Float32Array(16)): NumberArray {\n const wasm = this.engine.wasm;\n wasm.requireTempMem(16 * 4);\n wasm._wl_view_component_get_projectionMatrix(this._id, wasm._tempMem);\n for (let i = 0; i < 16; ++i) {\n out[i] = wasm._tempMemFloat[i];\n }\n return out;\n }\n\n /**\n * Equivalent to {@link ViewComponent.getProjectionMatrix}.\n *\n * @note Prefer to use {@link ViewComponent.getProjectionMatrix} for performance.\n */\n @enumerable()\n get projectionMatrix(): Float32Array {\n return this.getProjectionMatrix();\n }\n\n /**\n * Override projection matrix for this view.\n *\n * Bypasses the generation of the projection matrix from viewport, fov, near, far.\n *\n * @hidden\n */\n _setProjectionMatrix(v: Readonly) {\n const wasm = this._scene.engine.wasm;\n wasm.requireTempMem(16 * 4);\n wasm._tempMemFloat.set(v);\n wasm._wl_view_component_set_projectionMatrix(this._id, wasm._tempMem);\n }\n\n /**\n * Generate projection matrix from viewport, fov, near, far.\n *\n * Overwrites any projection matrix set manually.\n *\n * @hidden\n */\n _generateProjectionMatrix() {\n const wasm = this._scene.engine.wasm;\n wasm._wl_view_component_generate_projectionMatrix(this._id);\n }\n\n /** ViewComponent near clipping plane value. */\n @nativeProperty()\n get near(): number {\n return this.engine.wasm._wl_view_component_get_near(this._id);\n }\n\n /**\n * Set near clipping plane distance for the view.\n *\n * If an XR session is active, the change will apply in the\n * following frame, otherwise the change is immediate.\n *\n * @param near Near depth value.\n */\n set near(near: number) {\n this.engine.wasm._wl_view_component_set_near(this._id, near);\n /* Projection parameters must be updated for WebXR as well */\n if (this.engine.xr && this.scene.isActive && this.active) {\n this.engine.webxr.updateProjectionParams(near, this.far);\n }\n }\n\n /** Far clipping plane value. */\n @nativeProperty()\n get far(): number {\n return this.engine.wasm._wl_view_component_get_far(this._id);\n }\n\n /**\n * Set far clipping plane distance for the view.\n *\n * If an XR session is active, the change will apply in the\n * following frame, otherwise the change is immediate.\n *\n * @param far Near depth value.\n */\n set far(far: number) {\n this.engine.wasm._wl_view_component_set_far(this._id, far);\n /* Projection parameters must be updated for WebXR as well */\n if (this.engine.xr && this.scene.isActive && this.active) {\n this.engine.webxr.updateProjectionParams(this.near, far);\n }\n }\n\n /**\n * Get the horizontal field of view for the view, **in degrees**.\n *\n * If an XR session is active and this is the left or right eye view, this\n * returns the field of view reported by the device, regardless of the fov\n * that was set.\n */\n @nativeProperty()\n get fov(): number {\n return this.engine.wasm._wl_view_component_get_fov(this._id);\n }\n\n /**\n * Set the horizontal field of view for the view, **in degrees**.\n *\n * Only has an effect if {@link projectionType} is\n * {@link ProjectionType.Perspective}.\n *\n * If an XR session is active and this is the left or right eye view, the\n * field of view reported by the device is used and this value is ignored.\n * After the XR session ends, the new value is applied.\n *\n * @param fov Horizontal field of view, **in degrees**.\n */\n set fov(fov) {\n this.engine.wasm._wl_view_component_set_fov(this._id, fov);\n }\n\n /** @overload */\n getViewport(): Int32Array;\n /**\n * Get viewport of the view of the form: [x, y, width, height].\n *\n * @param out Destination array/vector, expected to have at least 4 elements.\n * @returns The `out` parameter.\n */\n getViewport(out: T): T;\n getViewport(out: NumberArray = new Int32Array(4)): NumberArray {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_view_component_get_viewport(this._id);\n for (let i = 0; i < 4; ++i) {\n out[i] = wasm.HEAPF32[ptr + i];\n }\n return out;\n }\n\n /**\n * Get viewport of the view of the form: [x, y, width, height].\n */\n @enumerable()\n get viewport(): Int32Array {\n return this.getViewport();\n }\n\n /**\n * Set viewport.\n *\n * @hidden\n */\n _setViewport(x: number, y: number, width: number, height: number) {\n const wasm = this._scene.engine.wasm;\n wasm._wl_view_component_set_viewport(this._id, x, y, width, height);\n }\n\n /**\n * Get the width of the orthographic viewing volume.\n *\n * @since 1.2.2\n */\n @nativeProperty()\n get extent(): number {\n return this.engine.wasm._wl_view_component_get_extent(this._id);\n }\n\n /**\n * Set the width of the orthographic viewing volume.\n *\n * Only has an effect if {@link projectionType} is\n * {@link ProjectionType.Orthographic}.\n *\n * @param extent New extent.\n * @since 1.2.2\n */\n set extent(extent) {\n this.engine.wasm._wl_view_component_set_extent(this._id, extent);\n }\n}\n\n/**\n * Native input component.\n *\n * Provides access to a native input component instance.\n */\nexport class InputComponent extends Component {\n /** @override */\n static TypeName = 'input';\n\n /** Input component type */\n @nativeProperty()\n get inputType(): InputType {\n return this.engine.wasm._wl_input_component_get_type(this._id);\n }\n\n /**\n * Set input component type.\n *\n * @params New input component type.\n */\n set inputType(type: InputType) {\n this.engine.wasm._wl_input_component_set_type(this._id, type);\n }\n\n /**\n * WebXR Device API input source associated with this input component,\n * if type {@link InputType.ControllerLeft} or {@link InputType.ControllerRight}.\n */\n @enumerable()\n get xrInputSource(): XRInputSource | null {\n const xr = this.engine.xr;\n if (!xr) return null;\n\n for (let inputSource of xr.session.inputSources) {\n if (inputSource.handedness == this.handedness) {\n return inputSource;\n }\n }\n\n return null;\n }\n\n /**\n * 'left', 'right' or `null` depending on the {@link InputComponent#inputType}.\n */\n @enumerable()\n get handedness(): 'left' | 'right' | null {\n const inputType = this.inputType;\n if (\n inputType == InputType.ControllerRight ||\n inputType == InputType.RayRight ||\n inputType == InputType.EyeRight\n )\n return 'right';\n if (\n inputType == InputType.ControllerLeft ||\n inputType == InputType.RayLeft ||\n inputType == InputType.EyeLeft\n )\n return 'left';\n\n return null;\n }\n}\n\n/**\n * Native light component.\n *\n * Provides access to a native light component instance.\n */\nexport class LightComponent extends Component {\n /** @override */\n static TypeName = 'light';\n\n /** @overload */\n getColor(): Float32Array;\n /**\n * Get light color.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n * @since 1.0.0\n */\n getColor(out: T): T;\n getColor(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_light_component_get_color(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n return out;\n }\n\n /**\n * Set light color.\n *\n * @param c New color array/vector, expected to have at least 3 elements.\n * @since 1.0.0\n */\n setColor(c: Readonly): void {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_light_component_get_color(this._id) / 4; /* Align F32 */\n wasm.HEAPF32[ptr] = c[0];\n wasm.HEAPF32[ptr + 1] = c[1];\n wasm.HEAPF32[ptr + 2] = c[2];\n }\n\n /**\n * View on the light color.\n *\n * @note Prefer to use {@link getColor} in performance-critical code.\n */\n @nativeProperty()\n get color(): Float32Array {\n const wasm = this.engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_light_component_get_color(this._id),\n 3\n );\n }\n\n /**\n * Set light color.\n *\n * @param c Color of the light component.\n *\n * @note Prefer to use {@link setColor} in performance-critical code.\n */\n set color(c: Readonly) {\n this.color.set(c);\n }\n\n /** Light type. */\n @nativeProperty()\n get lightType(): LightType {\n return this.engine.wasm._wl_light_component_get_type(this._id);\n }\n\n /**\n * Set light type.\n *\n * @param lightType Type of the light component.\n */\n set lightType(t: LightType) {\n this.engine.wasm._wl_light_component_set_type(this._id, t);\n }\n\n /**\n * Light intensity.\n * @since 1.0.0\n */\n @nativeProperty()\n get intensity(): number {\n return this.engine.wasm._wl_light_component_get_intensity(this._id);\n }\n\n /**\n * Set light intensity.\n *\n * @param intensity Intensity of the light component.\n * @since 1.0.0\n */\n set intensity(intensity: number) {\n this.engine.wasm._wl_light_component_set_intensity(this._id, intensity);\n }\n\n /**\n * Outer angle for spot lights, in degrees.\n * @since 1.0.0\n */\n @nativeProperty()\n get outerAngle(): number {\n return this.engine.wasm._wl_light_component_get_outerAngle(this._id);\n }\n\n /**\n * Set outer angle for spot lights.\n *\n * @param angle Outer angle, in degrees.\n * @since 1.0.0\n */\n set outerAngle(angle: number) {\n this.engine.wasm._wl_light_component_set_outerAngle(this._id, angle);\n }\n\n /**\n * Inner angle for spot lights, in degrees.\n * @since 1.0.0\n */\n @nativeProperty()\n get innerAngle(): number {\n return this.engine.wasm._wl_light_component_get_innerAngle(this._id);\n }\n\n /**\n * Set inner angle for spot lights.\n *\n * @param angle Inner angle, in degrees.\n * @since 1.0.0\n */\n set innerAngle(angle: number) {\n this.engine.wasm._wl_light_component_set_innerAngle(this._id, angle);\n }\n\n /**\n * Whether the light casts shadows.\n * @since 1.0.0\n */\n @nativeProperty()\n get shadows(): boolean {\n return !!this.engine.wasm._wl_light_component_get_shadows(this._id);\n }\n\n /**\n * Set whether the light casts shadows.\n *\n * @param b Whether the light casts shadows.\n * @since 1.0.0\n */\n set shadows(b: boolean) {\n this.engine.wasm._wl_light_component_set_shadows(this._id, b);\n }\n\n /**\n * Range for shadows.\n * @since 1.0.0\n */\n @nativeProperty()\n get shadowRange(): number {\n return this.engine.wasm._wl_light_component_get_shadowRange(this._id);\n }\n\n /**\n * Set range for shadows.\n *\n * @param range Range for shadows.\n * @since 1.0.0\n */\n set shadowRange(range: number) {\n this.engine.wasm._wl_light_component_set_shadowRange(this._id, range);\n }\n\n /**\n * Bias value for shadows.\n * @since 1.0.0\n */\n @nativeProperty()\n get shadowBias(): number {\n return this.engine.wasm._wl_light_component_get_shadowBias(this._id);\n }\n\n /**\n * Set bias value for shadows.\n *\n * @param bias Bias for shadows.\n * @since 1.0.0\n */\n set shadowBias(bias: number) {\n this.engine.wasm._wl_light_component_set_shadowBias(this._id, bias);\n }\n\n /**\n * Normal bias value for shadows.\n * @since 1.0.0\n */\n @nativeProperty()\n get shadowNormalBias(): number {\n return this.engine.wasm._wl_light_component_get_shadowNormalBias(this._id);\n }\n\n /**\n * Set normal bias value for shadows.\n *\n * @param bias Normal bias for shadows.\n * @since 1.0.0\n */\n set shadowNormalBias(bias: number) {\n this.engine.wasm._wl_light_component_set_shadowNormalBias(this._id, bias);\n }\n\n /**\n * Texel size for shadows.\n * @since 1.0.0\n */\n @nativeProperty()\n get shadowTexelSize(): number {\n return this.engine.wasm._wl_light_component_get_shadowTexelSize(this._id);\n }\n\n /**\n * Set texel size for shadows.\n *\n * @param size Texel size for shadows.\n * @since 1.0.0\n */\n set shadowTexelSize(size: number) {\n this.engine.wasm._wl_light_component_set_shadowTexelSize(this._id, size);\n }\n\n /**\n * Cascade count for {@link LightType.Sun} shadows.\n * @since 1.0.0\n */\n @nativeProperty()\n get cascadeCount(): number {\n return this.engine.wasm._wl_light_component_get_cascadeCount(this._id);\n }\n\n /**\n * Set cascade count for {@link LightType.Sun} shadows.\n *\n * @param count Cascade count.\n * @since 1.0.0\n */\n set cascadeCount(count: number) {\n this.engine.wasm._wl_light_component_set_cascadeCount(this._id, count);\n }\n}\n\n/**\n * Native animation component.\n *\n * Provides access to a native animation component instance.\n */\nexport class AnimationComponent extends Component {\n /** @override */\n static TypeName = 'animation';\n\n /**\n * Emitter for animation events triggered on this component.\n *\n * The first argument is the name of the event.\n */\n readonly onEvent = new Emitter<[string]>();\n\n /**\n * Set animation to play.\n *\n * Make sure to {@link Animation#retarget} the animation to affect the\n * right objects.\n *\n * @param anim Animation to play.\n */\n set animation(anim: Animation | null | undefined) {\n this.scene.assertOrigin(anim);\n this.engine.wasm._wl_animation_component_set_animation(\n this._id,\n anim ? anim._id : 0\n );\n }\n\n /** Animation set for this component */\n @nativeProperty()\n get animation(): Animation | null {\n const index = this.engine.wasm._wl_animation_component_get_animation(this._id);\n return this._scene.animations.wrap(index);\n }\n\n /**\n * Set play count. Set to `0` to loop indefinitely.\n *\n * @param playCount Number of times to repeat the animation.\n */\n set playCount(playCount: number) {\n this.engine.wasm._wl_animation_component_set_playCount(this._id, playCount);\n }\n\n /** Number of times the animation is played. */\n @nativeProperty()\n get playCount(): number {\n return this.engine.wasm._wl_animation_component_get_playCount(this._id);\n }\n\n /**\n * Set speed. Set to negative values to run the animation backwards.\n *\n * Setting speed has an immediate effect for the current frame's update\n * and will continue with the speed from the current point in the animation.\n *\n * @param speed New speed at which to play the animation.\n * @since 0.8.10\n */\n set speed(speed: number) {\n this.engine.wasm._wl_animation_component_set_speed(this._id, speed);\n }\n\n /**\n * Speed factor at which the animation is played.\n *\n * @since 0.8.10\n */\n @nativeProperty()\n get speed(): number {\n return this.engine.wasm._wl_animation_component_get_speed(this._id);\n }\n\n /** Current playing state of the animation */\n @enumerable()\n get state(): AnimationState {\n return this.engine.wasm._wl_animation_component_state(this._id);\n }\n\n /**\n * How to handle root motion on this component.\n *\n * @since 1.2.2\n */\n @nativeProperty()\n @enumerable()\n get rootMotionMode(): RootMotionMode {\n return this.engine.wasm._wl_animation_component_get_rootMotionMode(this._id);\n }\n\n /**\n * Set how to handle root motion.\n *\n * @param mode Mode to handle root motion, see {@link RootMotionMode}.\n * @since 1.2.2\n */\n set rootMotionMode(mode: RootMotionMode) {\n this.engine.wasm._wl_animation_component_set_rootMotionMode(this._id, mode);\n }\n\n /**\n * Current iteration of the animation.\n *\n * If {@link playCount} is not unlimited, the value is in the range from\n * `0` to `playCount`.\n *\n * @since 1.2.3\n */\n get iteration(): number {\n return this.engine.wasm._wl_animation_component_get_iteration(this._id);\n }\n\n /**\n * Current playing position of the animation within the current iteration,\n * in seconds.\n *\n * The value is in the range from `0.0` to {@link AnimationComponent#duration},\n * if playing in reverse, this range is reversed as well.\n *\n * @since 1.2.3\n */\n get position(): number {\n return this.engine.wasm._wl_animation_component_get_position(this._id);\n }\n\n /**\n * Current duration to loop one iteration in seconds, offers a more accurate duration\n * than {@link Animation#duration} when blending multiple animations.\n *\n * @since 1.2.3\n */\n get duration(): number {\n return this.engine.wasm._wl_animation_component_get_duration(this._id);\n }\n\n /**\n * Play animation.\n *\n * If the animation is currently paused, resumes from that position. If the\n * animation is already playing, does nothing.\n *\n * To restart the animation, {@link AnimationComponent#stop} it first.\n */\n play(): void {\n this.engine.wasm._wl_animation_component_play(this._id);\n }\n\n /** Stop animation. */\n stop(): void {\n this.engine.wasm._wl_animation_component_stop(this._id);\n }\n\n /** Pause animation. */\n pause(): void {\n this.engine.wasm._wl_animation_component_pause(this._id);\n }\n\n /**\n * Get the value of a float parameter in the attached graph.\n * Throws if the parameter is missing.\n *\n * @param name Name of the parameter.\n * @since 1.2.0\n */\n getFloatParameter(name: string): number | null {\n const wasm = this.engine.wasm;\n const index = wasm._wl_animation_component_getGraphParamIndex(\n this._id,\n wasm.tempUTF8(name)\n );\n if (index === -1) {\n throw Error(`Missing parameter '${name}'`);\n }\n wasm._wl_animation_component_getGraphParamValue(this._id, index, wasm._tempMem);\n return wasm._tempMemFloat[0];\n }\n\n /**\n * Set the value of a float parameter in the attached graph\n * Throws if the parameter is missing.\n *\n * @param name Name of the parameter.\n * @param value Float value to set.\n * @returns 1 if the parameter was successfully set, 0 on fail.\n * @since 1.2.0\n */\n setFloatParameter(name: string, value: number): void {\n const wasm = this.engine.wasm;\n const index = wasm._wl_animation_component_getGraphParamIndex(\n this._id,\n wasm.tempUTF8(name)\n );\n if (index === -1) {\n throw Error(`Missing parameter '${name}'`);\n }\n wasm._tempMemFloat[0] = value;\n wasm._wl_animation_component_setGraphParamValue(this._id, index, wasm._tempMem);\n }\n\n /** @overload */\n getRootMotionTranslation(): Float32Array;\n /**\n * Get the root motion translation in **local space** calculated for the current frame.\n *\n * @note If {@link AnimationComponent.rootMotionMode} is not\n * set to {@link RootMotionMode.Script} this will always return an identity translation.\n *\n * @since 1.2.2\n */\n getRootMotionTranslation(out: T): T;\n getRootMotionTranslation(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n wasm._wl_animation_component_get_rootMotion_translation(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n\n /** @overload */\n getRootMotionRotation(): Float32Array;\n /**\n * Get the root motion rotation in **local space** calculated for the current frame.\n *\n * @note If {@link AnimationComponent.rootMotionMode} is not\n * set to {@link RootMotionMode.Script} this will always return an identity rotation.\n *\n * @since 1.2.2\n */\n getRootMotionRotation(out: T): T;\n getRootMotionRotation(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n wasm._wl_animation_component_get_rootMotion_rotation(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n}\n\n/**\n * Native mesh component.\n *\n * Provides access to a native mesh component instance.\n */\nexport class MeshComponent extends Component {\n /** @override */\n static TypeName = 'mesh';\n\n /**\n * Set material to render the mesh with.\n *\n * @param material Material to render the mesh with.\n */\n set material(material: Material | null | undefined) {\n this.engine.wasm._wl_mesh_component_set_material(\n this._id,\n material ? material._id : 0\n );\n }\n\n /** Material used to render the mesh. */\n @nativeProperty()\n get material(): Material | null {\n const index = this.engine.wasm._wl_mesh_component_get_material(this._id);\n return this.engine.materials.wrap(index);\n }\n\n /** Mesh rendered by this component. */\n @nativeProperty()\n get mesh(): Mesh | null {\n const index = this.engine.wasm._wl_mesh_component_get_mesh(this._id);\n return this.engine.meshes.wrap(index);\n }\n\n /**\n * Set mesh to rendered with this component.\n *\n * @param mesh Mesh rendered by this component.\n */\n set mesh(mesh: Mesh | null | undefined) {\n this.engine.wasm._wl_mesh_component_set_mesh(this._id, mesh?._id ?? 0);\n }\n\n /** Skin for this mesh component. */\n @nativeProperty()\n get skin(): Skin | null {\n const index = this.engine.wasm._wl_mesh_component_get_skin(this._id);\n return this._scene.skins.wrap(index);\n }\n\n /**\n * Set skin to transform this mesh component.\n *\n * @param skin Skin to use for rendering skinned meshes.\n */\n set skin(skin: Skin | null | undefined) {\n this.scene.assertOrigin(skin);\n this.engine.wasm._wl_mesh_component_set_skin(this._id, skin ? skin._id : 0);\n }\n\n /**\n * Morph targets for this mesh component.\n *\n * @since 1.2.0\n */\n @nativeProperty()\n get morphTargets(): MorphTargets | null {\n const index = this.engine.wasm._wl_mesh_component_get_morph_targets(this._id);\n return this.engine.morphTargets.wrap(index);\n }\n\n /**\n * Set morph targets to transform this mesh component.\n *\n * @param morphTargets Morph targets to use for rendering.\n *\n * @since 1.2.0\n */\n set morphTargets(morphTargets: MorphTargets | null | undefined) {\n this.engine.wasm._wl_mesh_component_set_morph_targets(\n this._id,\n morphTargets?._id ?? 0\n );\n }\n\n /**\n * Equivalent to {@link getMorphTargetWeights}.\n *\n * @note Prefer to use {@link getMorphTargetWeights} for performance.\n *\n * @since 1.2.0\n */\n @nativeProperty()\n get morphTargetWeights(): Float32Array {\n return this.getMorphTargetWeights();\n }\n\n /**\n * Set the morph target weights to transform this mesh component.\n *\n * @param weights New weights.\n *\n * @since 1.2.0\n */\n set morphTargetWeights(weights: Readonly) {\n this.setMorphTargetWeights(weights);\n }\n\n /** @overload */\n getMorphTargetWeights(): Float32Array;\n /**\n * Get morph target weights for this mesh component.\n *\n * @param out Destination array, expected to have at least as many elements\n * as {@link MorphTargets.count}.\n * @returns The `out` parameter.\n *\n * @since 1.2.0\n */\n getMorphTargetWeights(out: T): T;\n getMorphTargetWeights(out?: NumberArray): NumberArray {\n const wasm = this.engine.wasm;\n const count = wasm._wl_mesh_component_get_morph_target_weights(\n this._id,\n wasm._tempMem\n );\n if (!out) {\n out = new Float32Array(count);\n }\n for (let i = 0; i < count; ++i) {\n out[i] = wasm._tempMemFloat[i];\n }\n return out;\n }\n\n /**\n * Get the weight of a single morph target.\n *\n * @param target Index of the morph target.\n * @returns The weight.\n *\n * @since 1.2.0\n */\n getMorphTargetWeight(target: number) {\n const count = this.morphTargets?.count ?? 0;\n if (target >= count) {\n throw new Error(`Index ${target} is out of bounds for ${count} targets`);\n }\n return this.engine.wasm._wl_mesh_component_get_morph_target_weight(\n this._id,\n target\n );\n }\n\n /**\n * Set morph target weights for this mesh component.\n *\n * @param weights Array of new weights, expected to have at least as many\n * elements as {@link MorphTargets.count}.\n *\n * @since 1.2.0\n */\n setMorphTargetWeights(weights: Readonly) {\n const count = this.morphTargets?.count ?? 0;\n if (weights.length !== count) {\n throw new Error(`Expected ${count} weights but got ${weights.length}`);\n }\n const wasm = this.engine.wasm;\n wasm._tempMemFloat.set(weights);\n wasm._wl_mesh_component_set_morph_target_weights(\n this._id,\n wasm._tempMem,\n weights.length\n );\n }\n\n /**\n * Set the weight of a single morph target.\n *\n * @param target Index of the morph target.\n * @param weight The new weight.\n *\n * ## Usage\n *\n * ```js\n * const mesh = object.getComponent('mesh');\n * const mouthTarget = mesh.morphTargets.getTargetIndex('mouth');\n * mesh.setMorphTargetWeight(mouthTarget, 0.5);\n * ```\n *\n * @since 1.2.0\n */\n setMorphTargetWeight(target: number, weight: number) {\n const count = this.morphTargets?.count ?? 0;\n if (target >= count) {\n throw new Error(`Index ${target} is out of bounds for ${count} targets`);\n }\n this.engine.wasm._wl_mesh_component_set_morph_target_weight(\n this._id,\n target,\n weight\n );\n }\n}\n\n/**\n * Native particle effect component.\n *\n * @since 1.4.0\n */\nexport class ParticleEffectComponent extends Component {\n /** @override */\n static TypeName = 'particle-effect';\n\n /** Particle effect used by this component. */\n @nativeProperty()\n get particleEffect(): ParticleEffect | null {\n const index = this.engine.wasm._wl_particleEffect_component_get_particleEffect(\n this._id\n );\n return this.engine.particleEffects.wrap(index);\n }\n\n /**\n * Set particle effect to use with this component.\n *\n * @param particleEffect Particle effect used by this component.\n */\n set particleEffect(particleEffect: ParticleEffect | null | undefined) {\n this.engine.wasm._wl_particleEffect_component_set_particleEffect(\n this._id,\n particleEffect?._id ?? 0\n );\n }\n}\n\n/**\n * Enum for Physics axes locking\n *\n * See {@link PhysXComponent.angularLockAxis} and {@link PhysXComponent.linearLockAxis}.\n */\nexport enum LockAxis {\n /**\n * No axis selected.\n */\n None = 0,\n\n /**\n * **X axis**:\n */\n X = 1 << 0,\n\n /**\n * **Y axis**:\n */\n Y = 1 << 1,\n\n /**\n * **Z axis**:\n */\n Z = 1 << 2,\n}\n\n/**\n * Native physx rigid body component.\n *\n * Provides access to a native mesh component instance.\n * Only available when using physx enabled runtime, see \"Project Settings > Runtime\".\n */\nexport class PhysXComponent extends Component {\n /** @override */\n static TypeName = 'physx';\n\n /** @overload */\n getTranslationOffset(): Float32Array;\n /**\n * Local translation offset.\n *\n * Allows to move a physx component without creating a new object in the hierarchy.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n *\n * @since 1.1.1\n */\n getTranslationOffset(out: T): T;\n getTranslationOffset(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n wasm._wl_physx_component_get_offsetTranslation(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n\n /** @overload */\n getRotationOffset(): Float32Array;\n /**\n * Local rotation offset represented as a quaternion.\n *\n * Allows to rotate a physx component without creating a new object in the hierarchy.\n *\n * @param out Destination array/vector, expected to have at least 4 elements.\n * @returns The `out` parameter.\n *\n * @since 1.1.1\n */\n getRotationOffset(out: T): T;\n getRotationOffset(out: NumberArray = new Float32Array(4)): NumberArray {\n const wasm = this.engine.wasm;\n const ptr =\n wasm._wl_physx_component_get_offsetTransform(this._id) >> 2; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n out[3] = wasm.HEAPF32[ptr + 3];\n return out;\n }\n\n /** @overload */\n getExtents(): Float32Array;\n /**\n * The shape extents for collision detection.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getExtents(out: T): T;\n getExtents(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_physx_component_get_extents(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n return out;\n }\n\n /** @overload */\n getLinearVelocity(): Float32Array;\n /**\n * Linear velocity or `[0, 0, 0]` if the component is not active.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getLinearVelocity(out: T): T;\n getLinearVelocity(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n const tempMemFloat = wasm._tempMemFloat;\n wasm._wl_physx_component_get_linearVelocity(\n this._id,\n wasm._tempMem\n ); /* Align F32 */\n out[0] = tempMemFloat[0];\n out[1] = tempMemFloat[1];\n out[2] = tempMemFloat[2];\n return out;\n }\n\n /** @overload */\n getAngularVelocity(): Float32Array;\n /**\n * Angular velocity or `[0, 0, 0]` if the component is not active.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getAngularVelocity(out: T): T;\n getAngularVelocity(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this.engine.wasm;\n const tempMemFloat = wasm._tempMemFloat;\n wasm._wl_physx_component_get_angularVelocity(\n this._id,\n wasm._tempMem\n ); /* Align F32 */\n out[0] = tempMemFloat[0];\n out[1] = tempMemFloat[1];\n out[2] = tempMemFloat[2];\n return out;\n }\n\n /**\n * Set whether this rigid body is static.\n *\n * Setting this property only takes effect once the component\n * switches from inactive to active.\n *\n * @param b Whether the rigid body should be static.\n */\n set static(b: boolean) {\n this.engine.wasm._wl_physx_component_set_static(this._id, b);\n }\n\n /**\n * Whether this rigid body is static.\n *\n * This property returns whether the rigid body is *effectively*\n * static. If static property was set while the rigid body was\n * active, it will not take effect until the rigid body is set\n * inactive and active again. Until the component is set inactive,\n * this getter will return whether the rigid body is actually\n * static.\n */\n @nativeProperty()\n get static(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_static(this._id);\n }\n\n /**\n * Equivalent to {@link PhysXComponent.getTranslationOffset}.\n *\n * Gives a quick view of the offset in a debugger.\n *\n * @note Prefer to use {@link PhysXComponent.getTranslationOffset} for performance.\n *\n * @since 1.1.1\n */\n @nativeProperty()\n get translationOffset(): Float32Array {\n return this.getTranslationOffset();\n }\n\n /**\n * Set the offset translation.\n *\n * The array must be a vector of at least **3** elements.\n *\n * @note The component must be re-activated to apply the change.\n *\n * @since 1.1.1\n */\n set translationOffset(offset: ArrayLike) {\n const wasm = this.engine.wasm;\n wasm._wl_physx_component_set_offsetTranslation(\n this._id,\n offset[0],\n offset[1],\n offset[2]\n );\n }\n\n /**\n * Equivalent to {@link PhysXComponent.getRotationOffset}.\n *\n * Gives a quick view of the offset in a debugger.\n *\n * @note Prefer to use {@link PhysXComponent.getRotationOffset} for performance.\n *\n * @since 1.1.1\n */\n @nativeProperty()\n get rotationOffset(): Float32Array {\n return this.getRotationOffset();\n }\n\n /**\n * Set the offset rotation.\n *\n * The array must be a quaternion of at least **4** elements.\n *\n * @note The component must be re-activated to apply the change.\n *\n * @since 1.1.1\n */\n set rotationOffset(offset: ArrayLike) {\n const wasm = this.engine.wasm;\n wasm._wl_physx_component_set_offsetRotation(\n this._id,\n offset[0],\n offset[1],\n offset[2],\n offset[3]\n );\n }\n\n /**\n * Set whether this rigid body is kinematic.\n *\n * @param b Whether the rigid body should be kinematic.\n */\n set kinematic(b: boolean) {\n this.engine.wasm._wl_physx_component_set_kinematic(this._id, b);\n }\n\n /**\n * Whether this rigid body is kinematic.\n */\n @nativeProperty()\n get kinematic(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_kinematic(this._id);\n }\n\n /**\n * Set whether this rigid body's gravity is enabled.\n *\n * @param b Whether the rigid body's gravity should be enabled.\n */\n set gravity(b: boolean) {\n this.engine.wasm._wl_physx_component_set_gravity(this._id, b);\n }\n\n /**\n * Whether this rigid body's gravity flag is enabled.\n */\n @nativeProperty()\n get gravity(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_gravity(this._id);\n }\n\n /**\n * Set whether this rigid body's simulate flag is enabled.\n *\n * @param b Whether the rigid body's simulate flag should be enabled.\n */\n set simulate(b: boolean) {\n this.engine.wasm._wl_physx_component_set_simulate(this._id, b);\n }\n\n /**\n * Whether this rigid body's simulate flag is enabled.\n */\n @nativeProperty()\n get simulate(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_simulate(this._id);\n }\n\n /**\n * Set whether to allow simulation of this rigid body.\n *\n * {@link allowSimulation} and {@link trigger} can not be enabled at the\n * same time. Enabling {@link allowSimulation} while {@link trigger} is enabled\n * will disable {@link trigger}.\n *\n * @param b Whether to allow simulation of this rigid body.\n */\n set allowSimulation(b: boolean) {\n this.engine.wasm._wl_physx_component_set_allowSimulation(this._id, b);\n }\n\n /**\n * Whether to allow simulation of this rigid body.\n */\n @nativeProperty()\n get allowSimulation(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_allowSimulation(this._id);\n }\n\n /**\n * Set whether this rigid body may be queried in ray casts.\n *\n * @param b Whether this rigid body may be queried in ray casts.\n */\n set allowQuery(b: boolean) {\n this.engine.wasm._wl_physx_component_set_allowQuery(this._id, b);\n }\n\n /**\n * Whether this rigid body may be queried in ray casts.\n */\n @nativeProperty()\n get allowQuery(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_allowQuery(this._id);\n }\n\n /**\n * Set whether this physics body is a trigger.\n *\n * {@link allowSimulation} and {@link trigger} can not be enabled at the\n * same time. Enabling trigger while {@link allowSimulation} is enabled,\n * will disable {@link allowSimulation}.\n *\n * @param b Whether this physics body is a trigger.\n */\n set trigger(b: boolean) {\n this.engine.wasm._wl_physx_component_set_trigger(this._id, b);\n }\n\n /**\n * Whether this physics body is a trigger.\n */\n @nativeProperty()\n get trigger(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_trigger(this._id);\n }\n\n /**\n * Set the shape for collision detection.\n *\n * @param s New shape.\n * @since 0.8.5\n */\n set shape(s: Shape) {\n this.engine.wasm._wl_physx_component_set_shape(this._id, s);\n }\n\n /** The shape for collision detection. */\n @nativeProperty()\n get shape(): Shape {\n return this.engine.wasm._wl_physx_component_get_shape(this._id);\n }\n\n /**\n * Set additional data for the shape.\n *\n * Retrieved only from {@link PhysXComponent#shapeData}.\n * @since 0.8.10\n */\n set shapeData(d) {\n if (d == null || !isMeshShape(this.shape)) return;\n this.engine.wasm._wl_physx_component_set_shape_data(this._id, d.index);\n }\n\n /**\n * Additional data for the shape.\n *\n * `null` for {@link Shape} values: `None`, `Sphere`, `Capsule`, `Box`, `Plane`.\n * `{index: n}` for `TriangleMesh` and `ConvexHull`.\n *\n * This data is currently only for passing onto or creating other {@link PhysXComponent}.\n * @since 0.8.10\n */\n @nativeProperty()\n get shapeData(): {\n index: number;\n } | null {\n if (!isMeshShape(this.shape)) return null;\n return {\n index: this.engine.wasm._wl_physx_component_get_shape_data(this._id),\n };\n }\n\n /**\n * Set the shape extents for collision detection.\n *\n * @param e New extents for the shape.\n * @since 0.8.5\n */\n set extents(e: Readonly) {\n this.extents.set(e);\n }\n\n /**\n * Equivalent to {@link PhysXComponent.getExtents}.\n *\n * @note Prefer to use {@link PhysXComponent.getExtents} for performance.\n */\n @nativeProperty()\n get extents(): Float32Array {\n const wasm = this.engine.wasm;\n /** @todo: Break at 2.0.0. Do not allow modifying memory in-place. */\n const ptr = wasm._wl_physx_component_get_extents(this._id);\n return new Float32Array(wasm.HEAPF32.buffer, ptr, 3);\n }\n\n /**\n * Get staticFriction.\n */\n @nativeProperty()\n get staticFriction(): number {\n return this.engine.wasm._wl_physx_component_get_staticFriction(this._id);\n }\n\n /**\n * Set staticFriction.\n * @param v New staticFriction.\n */\n set staticFriction(v: number) {\n this.engine.wasm._wl_physx_component_set_staticFriction(this._id, v);\n }\n\n /**\n * Get dynamicFriction.\n */\n @nativeProperty()\n get dynamicFriction(): number {\n return this.engine.wasm._wl_physx_component_get_dynamicFriction(this._id);\n }\n\n /**\n * Set dynamicFriction\n * @param v New dynamicDamping.\n */\n set dynamicFriction(v: number) {\n this.engine.wasm._wl_physx_component_set_dynamicFriction(this._id, v);\n }\n\n /**\n * Get bounciness.\n * @since 0.9.0\n */\n @nativeProperty()\n get bounciness(): number {\n return this.engine.wasm._wl_physx_component_get_bounciness(this._id);\n }\n\n /**\n * Set bounciness.\n * @param v New bounciness.\n * @since 0.9.0\n */\n set bounciness(v: number) {\n this.engine.wasm._wl_physx_component_set_bounciness(this._id, v);\n }\n\n /**\n * Get linearDamping/\n */\n @nativeProperty()\n get linearDamping(): number {\n return this.engine.wasm._wl_physx_component_get_linearDamping(this._id);\n }\n\n /**\n * Set linearDamping.\n * @param v New linearDamping.\n */\n set linearDamping(v: number) {\n this.engine.wasm._wl_physx_component_set_linearDamping(this._id, v);\n }\n\n /** Get angularDamping. */\n @nativeProperty()\n get angularDamping(): number {\n return this.engine.wasm._wl_physx_component_get_angularDamping(this._id);\n }\n\n /**\n * Set angularDamping.\n * @param v New angularDamping.\n */\n set angularDamping(v: number) {\n this.engine.wasm._wl_physx_component_set_angularDamping(this._id, v);\n }\n\n /**\n * Set linear velocity.\n *\n * [PhysX Manual - \"Velocity\"](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyDynamics.html#velocity)\n *\n * Has no effect, if the component is not active.\n *\n * @param v New linear velocity.\n */\n set linearVelocity(v: Readonly) {\n this.engine.wasm._wl_physx_component_set_linearVelocity(this._id, v[0], v[1], v[2]);\n }\n\n /**\n * Equivalent to {@link PhysXComponent.getLinearVelocity}.\n *\n * @note Prefer to use {@link PhysXComponent.getLinearVelocity} for performance.\n */\n @nativeProperty()\n get linearVelocity(): Float32Array {\n /** @todo: Break at 2.0.0. Do not allow modifying memory in-place. */\n const wasm = this.engine.wasm;\n wasm._wl_physx_component_get_linearVelocity(this._id, wasm._tempMem);\n return new Float32Array(wasm.HEAPF32.buffer, wasm._tempMem, 3);\n }\n\n /**\n * Set angular velocity\n *\n * [PhysX Manual - \"Velocity\"](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyDynamics.html#velocity)\n *\n * Has no effect, if the component is not active.\n *\n * @param v New angular velocity\n */\n set angularVelocity(v: Readonly) {\n this.engine.wasm._wl_physx_component_set_angularVelocity(\n this._id,\n v[0],\n v[1],\n v[2]\n );\n }\n\n /**\n * Equivalent to {@link PhysXComponent.getAngularVelocity}.\n *\n * @note Prefer to use {@link PhysXComponent.getAngularVelocity} for performance.\n */\n @nativeProperty()\n get angularVelocity(): Float32Array {\n /** @todo: Break at 2.0.0. Do not allow modifying memory in-place. */\n const wasm = this.engine.wasm;\n wasm._wl_physx_component_get_angularVelocity(this._id, wasm._tempMem);\n return new Float32Array(wasm.HEAPF32.buffer, wasm._tempMem, 3);\n }\n\n /**\n * Set the components groups mask.\n *\n * @param flags New flags that need to be set.\n */\n set groupsMask(flags: number) {\n this.engine.wasm._wl_physx_component_set_groupsMask(this._id, flags);\n }\n\n /**\n * Get the components groups mask flags.\n *\n * Each bit represents membership to group, see example.\n *\n * ```js\n * // Assign c to group 2\n * c.groupsMask = (1 << 2);\n *\n * // Assign c to group 0\n * c.groupsMask = (1 << 0);\n *\n * // Assign c to group 0 and 2\n * c.groupsMask = (1 << 0) | (1 << 2);\n *\n * (c.groupsMask & (1 << 2)) != 0; // true\n * (c.groupsMask & (1 << 7)) != 0; // false\n * ```\n */\n @nativeProperty()\n get groupsMask(): number {\n return this.engine.wasm._wl_physx_component_get_groupsMask(this._id);\n }\n\n /**\n * Set the components blocks mask.\n *\n * @param flags New flags that need to be set.\n */\n set blocksMask(flags: number) {\n this.engine.wasm._wl_physx_component_set_blocksMask(this._id, flags);\n }\n\n /**\n * Get the components blocks mask flags.\n *\n * Each bit represents membership to the block, see example.\n *\n * ```js\n * // Block overlap with any objects in group 2\n * c.blocksMask = (1 << 2);\n *\n * // Block overlap with any objects in group 0\n * c.blocksMask = (1 << 0)\n *\n * // Block overlap with any objects in group 0 and 2\n * c.blocksMask = (1 << 0) | (1 << 2);\n *\n * (c.blocksMask & (1 << 2)) != 0; // true\n * (c.blocksMask & (1 << 7)) != 0; // false\n * ```\n */\n @nativeProperty()\n get blocksMask(): number {\n return this.engine.wasm._wl_physx_component_get_blocksMask(this._id);\n }\n\n /**\n * Set axes to lock for linear velocity.\n *\n * @param lock The Axis that needs to be set.\n *\n * Combine flags with Bitwise OR:\n *\n * ```js\n * body.linearLockAxis = LockAxis.X | LockAxis.Y; // x and y set\n * body.linearLockAxis = LockAxis.X; // y unset\n * ```\n *\n * @note This has no effect if the component is static.\n */\n set linearLockAxis(lock: LockAxis) {\n this.engine.wasm._wl_physx_component_set_linearLockAxis(this._id, lock);\n }\n\n /**\n * Get the linear lock axes flags.\n *\n * To get the state of a specific flag, Bitwise AND with the LockAxis needed.\n *\n * ```js\n * if(body.linearLockAxis & LockAxis.Y) {\n * console.log(\"The Y flag was set!\");\n * }\n * ```\n *\n * @return axes that are currently locked for linear movement.\n */\n @nativeProperty()\n get linearLockAxis(): LockAxis {\n return this.engine.wasm._wl_physx_component_get_linearLockAxis(this._id);\n }\n\n /**\n * Set axes to lock for angular velocity.\n *\n * @param lock The Axis that needs to be set.\n *\n * ```js\n * body.angularLockAxis = LockAxis.X | LockAxis.Y; // x and y set\n * body.angularLockAxis = LockAxis.X; // y unset\n * ```\n *\n * @note This has no effect if the component is static.\n */\n set angularLockAxis(lock: LockAxis) {\n this.engine.wasm._wl_physx_component_set_angularLockAxis(this._id, lock);\n }\n\n /**\n * Get the angular lock axes flags.\n *\n * To get the state of a specific flag, Bitwise AND with the LockAxis needed:\n *\n * ```js\n * if(body.angularLockAxis & LockAxis.Y) {\n * console.log(\"The Y flag was set!\");\n * }\n * ```\n *\n * @return axes that are currently locked for angular movement.\n */\n @nativeProperty()\n get angularLockAxis(): LockAxis {\n return this.engine.wasm._wl_physx_component_get_angularLockAxis(this._id);\n }\n\n /**\n * Set mass.\n *\n * [PhysX Manual - \"Mass Properties\"](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyDynamics.html#mass-properties)\n *\n * @param m New mass.\n */\n set mass(m: number) {\n this.engine.wasm._wl_physx_component_set_mass(this._id, m);\n }\n\n /** Mass */\n @nativeProperty()\n get mass(): number {\n return this.engine.wasm._wl_physx_component_get_mass(this._id);\n }\n\n /**\n * Set mass space interia tensor.\n *\n * [PhysX Manual - \"Mass Properties\"](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyDynamics.html#mass-properties)\n *\n * Has no effect, if the component is not active.\n *\n * @param v New mass space interatia tensor.\n */\n set massSpaceInteriaTensor(v: Readonly) {\n this.engine.wasm._wl_physx_component_set_massSpaceInertiaTensor(\n this._id,\n v[0],\n v[1],\n v[2]\n );\n }\n\n /**\n * Set the rigid body to sleep upon activation.\n *\n * When asleep, the rigid body will not be simulated until the next contact.\n *\n * @param flag `true` to sleep upon activation.\n *\n * @since 1.1.5\n */\n set sleepOnActivate(flag: boolean) {\n this.engine.wasm._wl_physx_component_set_sleepOnActivate(this._id, flag);\n }\n\n /**\n * `true` if the rigid body is set to sleep upon activation, `false` otherwise.\n *\n * @since 1.1.5\n */\n @nativeProperty()\n get sleepOnActivate(): boolean {\n return !!this.engine.wasm._wl_physx_component_get_sleepOnActivate(this._id);\n }\n\n /**\n * Apply a force.\n *\n * [PhysX Manual - \"Applying Forces and Torques\"](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyDynamics.html#applying-forces-and-torques)\n *\n * Has no effect, if the component is not active.\n *\n * @param f Force vector.\n * @param m Force mode, see {@link ForceMode}, default `Force`.\n * @param localForce Whether the force vector is in local space, default `false`.\n * @param p Position to apply force at, default is center of mass.\n * @param local Whether position is in local space, default `false`.\n */\n addForce(\n f: Readonly,\n m: ForceMode = ForceMode.Force,\n localForce: boolean = false,\n p?: Readonly,\n local: boolean = false\n ) {\n const wasm = this.engine.wasm;\n if (!p) {\n wasm._wl_physx_component_addForce(this._id, f[0], f[1], f[2], m, localForce);\n return;\n }\n wasm._wl_physx_component_addForceAt(\n this._id,\n f[0],\n f[1],\n f[2],\n m,\n localForce,\n p[0],\n p[1],\n p[2],\n local\n );\n }\n\n /**\n * Apply torque.\n *\n * [PhysX Manual - \"Applying Forces and Torques\"](https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/RigidBodyDynamics.html#applying-forces-and-torques)\n *\n * Has no effect, if the component is not active.\n *\n * @param f Force vector.\n * @param m Force mode, see {@link ForceMode}, default `Force`.\n */\n addTorque(f: Readonly, m: ForceMode = ForceMode.Force) {\n this.engine.wasm._wl_physx_component_addTorque(this._id, f[0], f[1], f[2], m);\n }\n\n /**\n * Add on collision callback.\n *\n * @param callback Function to call when this rigid body (un)collides with any other.\n *\n * ```js\n * let rigidBody = this.object.getComponent('physx');\n * rigidBody.onCollision(function(type, other) {\n * // Ignore uncollides\n * if(type == CollisionEventType.TouchLost) return;\n *\n * // Take damage on collision with enemies\n * if(other.object.name.startsWith(\"enemy-\")) {\n * this.applyDamage(10);\n * }\n * }.bind(this));\n * ```\n *\n * @returns Id of the new callback for use with {@link PhysXComponent#removeCollisionCallback}.\n */\n onCollision(callback: CollisionCallback): number {\n return this.onCollisionWith(this, callback);\n }\n\n /**\n * Add filtered on collision callback.\n *\n * @param otherComp Component for which callbacks will\n * be triggered. If you pass this component, the method is equivalent to.\n * {@link PhysXComponent#onCollision}.\n * @param callback Function to call when this rigid body\n * (un)collides with `otherComp`.\n * @returns Id of the new callback for use with {@link PhysXComponent#removeCollisionCallback}.\n */\n onCollisionWith(otherComp: this, callback: CollisionCallback): number {\n const callbacks = this.scene._pxCallbacks;\n if (!callbacks.has(this._id)) {\n callbacks.set(this._id, []);\n }\n callbacks.get(this._id)!.push(callback);\n return this.engine.wasm._wl_physx_component_addCallback(this._id, otherComp._id);\n }\n\n /**\n * Remove a collision callback added with {@link PhysXComponent#onCollision} or {@link PhysXComponent#onCollisionWith}.\n *\n * @param callbackId Callback id as returned by {@link PhysXComponent#onCollision} or {@link PhysXComponent#onCollisionWith}.\n * @throws When the callback does not belong to the component.\n * @throws When the callback does not exist.\n */\n removeCollisionCallback(callbackId: number): void {\n const r = this.engine.wasm._wl_physx_component_removeCallback(this._id, callbackId);\n const callbacks = this.scene._pxCallbacks;\n /* r is the amount of object to remove from the end of the\n * callbacks array for this object */\n if (r) callbacks.get(this._id)!.splice(-r);\n }\n}\n\n/**\n * Access to the physics scene\n */\nexport class Physics {\n /**\n * Hit.\n * @hidden\n */\n _hit: RayHit;\n\n /**\n * Wonderland Engine instance\n * @hidden\n */\n protected readonly _engine: WonderlandEngine;\n\n /**\n * Ray Hit\n * @hidden\n */\n private _rayHit: number;\n\n constructor(engine: WonderlandEngine) {\n this._engine = engine;\n const wasm = engine.wasm;\n this._rayHit = wasm._malloc(4 * (3 * 4 + 3 * 4 + 4 + 2) + 4);\n this._hit = new RayHit(engine.scene, this._rayHit);\n\n /* Setup the physics callback. */\n wasm._wl_physx_set_collision_callback(\n wasm.addFunction((a: number, index: number, type: number, b: number) => {\n const physxA = this._engine.scene._components.wrapPhysx(a)!;\n const physxB = this._engine.scene._components.wrapPhysx(b)!;\n /* We assume that the map returns an array. If not,\n * this is an internal engine error. */\n const callbacks = this._engine.scene._pxCallbacks.get(physxA._id)!;\n const callback = callbacks[index];\n callback(type, physxB as PhysXComponent);\n }, 'viiii')\n );\n }\n\n /**\n * Cast a ray through the scene and find intersecting physics components.\n *\n * The resulting ray hit will contain **up to 4** closest ray hits,\n * sorted by increasing distance.\n *\n * Example:\n *\n * ```js\n * const hit = engine.physics.rayCast(\n * [0, 0, 0],\n * [0, 0, 1],\n * 1 << 0 | 1 << 4, // Only check against physics components in groups 0 and 4\n * 25\n * );\n * if (hit.hitCount > 0) {\n * const locations = hit.getLocations();\n * console.log(`Object hit at: ${locations[0][0]}, ${locations[0][1]}, ${locations[0][2]}`);\n * }\n * ```\n *\n * @param o Ray origin.\n * @param d Ray direction.\n * @param groupMask Bitmask of physics groups to filter by: only objects\n * that are part of given groups are considered for the raycast.\n * @param maxDistance Maximum **inclusive** hit distance. Defaults to `100`.\n *\n * @returns The {@link RayHit} instance, cached by this class.\n *\n * @note The returned {@link RayHit} object is owned by the {@link Physics}\n * instance and will be reused with the next {@link Physics#rayCast} call.\n */\n rayCast(\n o: Readonly,\n d: Readonly,\n groupMask: number,\n maxDistance: number = 100.0\n ): RayHit {\n const scene = this._engine.scene._index;\n this._engine.wasm._wl_physx_ray_cast(\n scene,\n o[0],\n o[1],\n o[2],\n d[0],\n d[1],\n d[2],\n groupMask,\n maxDistance,\n this._rayHit\n );\n return this._hit;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._engine;\n }\n}\n\n/**\n * Mesh index type.\n */\nexport enum MeshIndexType {\n /** Single byte mesh index, range 0-255 */\n UnsignedByte = 1,\n\n /** Two byte mesh index, range 0-65535 */\n UnsignedShort = 2,\n\n /** Four byte mesh index, range 0-4294967295 */\n UnsignedInt = 4,\n}\n\n/**\n * Mesh skinning type.\n */\nexport enum MeshSkinningType {\n /** Not skinned */\n None = 0,\n\n /** Skinned, 4 joints/weight per vertex */\n FourJoints = 1,\n\n /** Skinned, 8 joints/weight per vertex */\n EightJoints = 2,\n}\n\n/**\n * Mesh constructor parameters object.\n *\n * Usage:\n *\n * ```js\n * const mesh = Mesh({vertexCount: 3, indexData: [0, 1, 2]});\n * ```\n */\nexport interface MeshParameters {\n /** Number of vertices to allocate. */\n vertexCount: number;\n /** Index data values. */\n indexData: Readonly;\n /** Index type, `null` if not indexed. */\n indexType: MeshIndexType;\n /** Whether the mesh should be skinned. Defaults to not skinned. */\n skinningType: MeshSkinningType;\n}\n\n/**\n * Wrapper around a native mesh data.\n *\n * For more information about how to create meshes, have a look at the\n * {@link MeshManager} class.\n *\n * #### Update\n *\n * To modify a mesh, you get access to a {@link MeshAttributeAccessor} that\n * allows you to modify the content of the buffers:\n *\n * Usage:\n *\n * ```js\n * const mesh = engine.es.create({vertexCount: 3, indexData: [0, 1, 2]});\n * const positions = mesh.attribute(MeshAttribute.Position);\n * ...\n * ```\n *\n * @note Meshes are **per-engine**, they can thus be shared by multiple scenes.\n */\nexport class Mesh extends Resource {\n /**\n * @deprecated Use {@link MeshManager.create} instead, accessible via {@link WonderlandEngine.meshes}:\n *\n * ```js\n * const mesh = engine.meshes.create({vertexCount: 3, indexData: [0, 1, 2]});\n * ...\n * mesh.update();\n * ```\n */\n constructor(engine: WonderlandEngine, params: Partial | number) {\n if (!isNumber(params)) {\n const mesh = engine.meshes.create(params);\n /* `super()` must be called as stated in the specification */\n super(engine, mesh._index);\n return mesh;\n }\n super(engine, params);\n }\n\n /** Number of vertices in this mesh. */\n get vertexCount(): number {\n return this.engine.wasm._wl_mesh_get_vertexCount(this._id);\n }\n\n /** Index data (read-only) or `null` if the mesh is not indexed. */\n get indexData(): Uint8Array | Uint16Array | Uint32Array | null {\n const wasm = this.engine.wasm;\n const tempMem = wasm._tempMem;\n const ptr = wasm._wl_mesh_get_indexData(this._id, tempMem, tempMem + 4);\n if (ptr === null) return null;\n\n const indexCount = wasm.HEAPU32[tempMem / 4];\n const indexSize = wasm.HEAPU32[tempMem / 4 + 1];\n switch (indexSize) {\n case MeshIndexType.UnsignedByte:\n return new Uint8Array(wasm.HEAPU8.buffer, ptr, indexCount);\n case MeshIndexType.UnsignedShort:\n return new Uint16Array(wasm.HEAPU16.buffer, ptr, indexCount);\n case MeshIndexType.UnsignedInt:\n return new Uint32Array(wasm.HEAPU32.buffer, ptr, indexCount);\n }\n return null;\n }\n\n /**\n * Apply changes to {@link attribute | vertex attributes}.\n *\n * Uploads the updated vertex attributes to the GPU and updates the bounding\n * sphere to match the new vertex positions.\n *\n * Since this is an expensive operation, call it only once you have performed\n * all modifications on a mesh and avoid calling if you did not perform any\n * modifications at all.\n */\n update() {\n this.engine.wasm._wl_mesh_update(this._id);\n }\n\n /** @overload */\n getBoundingSphere(): Float32Array;\n /** @overload */\n getBoundingSphere(out: T): T;\n /**\n * Mesh bounding sphere.\n *\n * @param out Preallocated array to write into, to avoid garbage,\n * otherwise will allocate a new Float32Array.\n *\n * ```js\n * const sphere = new Float32Array(4);\n * for(...) {\n * mesh.getBoundingSphere(sphere);\n * ...\n * }\n * ```\n *\n * If the position data is changed, call {@link Mesh#update} to update the\n * bounding sphere.\n *\n * @returns Bounding sphere, 0-2 sphere origin, 3 radius.\n */\n getBoundingSphere(out: T | Float32Array): T | Float32Array;\n getBoundingSphere(\n out: T | Float32Array = new Float32Array(4)\n ): T | Float32Array {\n const wasm = this.engine.wasm;\n this.engine.wasm._wl_mesh_get_boundingSphere(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n out[3] = wasm._tempMemFloat[3];\n return out as T;\n }\n\n /** @overload */\n attribute(\n attr: MeshAttribute.Position\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.Tangent\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.Normal\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.TextureCoordinate\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.Color\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.JointId\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.JointWeight\n ): MeshAttributeAccessor | null;\n /** @overload */\n attribute(\n attr: MeshAttribute.SecondaryTextureCoordinate\n ): MeshAttributeAccessor | null;\n /**\n * Get an attribute accessor to retrieve or modify data of give attribute.\n *\n * @param attr Attribute to get access to\n * @returns Attribute to get access to or `null`, if mesh does not have this attribute.\n *\n * Call {@link update} for changes to vertex attributes to take effect.\n *\n * If there are no shaders in the scene that use `TextureCoordinate` for example,\n * no meshes will have the `TextureCoordinate` attribute.\n *\n * For flexible reusable components, take this into account that only `Position`\n * is guaranteed to be present at all time.\n */\n attribute(attr: MeshAttribute): MeshAttributeAccessor | null;\n attribute(attr: MeshAttribute): MeshAttributeAccessor | null {\n if (typeof attr != 'number')\n throw new TypeError('Expected number, but got ' + typeof attr);\n\n const wasm = this.engine.wasm;\n const tempMemUint32 = wasm._tempMemUint32;\n wasm._wl_mesh_get_attribute(this._id, attr, wasm._tempMem);\n if (tempMemUint32[0] == 255) return null;\n\n const arraySize = tempMemUint32[5];\n return new MeshAttributeAccessor(this.engine, {\n attribute: tempMemUint32[0],\n offset: tempMemUint32[1],\n stride: tempMemUint32[2],\n formatSize: tempMemUint32[3],\n componentCount: tempMemUint32[4],\n /* The WASM API returns `0` for a scalar value. We clamp it to 1 as we strictly use it as a multiplier for get/set operations */\n arraySize: arraySize ? arraySize : 1,\n length: this.vertexCount,\n bufferType: (attr !== MeshAttribute.JointId\n ? Float32Array\n : Uint16Array) as TypedArrayCtor,\n });\n }\n\n /**\n * Destroy and free the meshes memory.\n *\n * It is best practice to set the mesh variable to `null` after calling\n * destroy to prevent accidental use:\n *\n * ```js\n * mesh.destroy();\n * mesh = null;\n * ```\n *\n * Accessing the mesh after destruction behaves like accessing an empty\n * mesh.\n *\n * @since 0.9.0\n */\n destroy(): void {\n this.engine.wasm._wl_mesh_destroy(this._id);\n this.engine.meshes._destroy(this);\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Mesh(destroyed)';\n }\n return `Mesh(${this._index})`;\n }\n}\n\n/**\n * Options to create a new {@link MeshAttributeAccessor} instance.\n */\ninterface MeshAttributeAccessorOptions {\n attribute: number;\n offset: number;\n stride: number;\n formatSize: number;\n componentCount: number;\n arraySize: number;\n length: number;\n bufferType: T;\n}\n\n/**\n * An iterator over a mesh vertex attribute.\n *\n * Usage:\n *\n * ```js\n * const mesh = this.object.getComponent('mesh').mesh;\n * const positions = mesh.attribute(MeshAttribute.Position);\n *\n * // Equivalent to `new Float32Array(3)`.\n * const temp = positions.createArray();\n *\n * for(let i = 0; i < positions.length; ++i) {\n * // `pos` will reference `temp` and thereby not allocate additional\n * // memory, which would cause a perf spike when collected.\n * const pos = positions.get(i, temp);\n * // Scale position by 2 on x-axis only.\n * pos[0] *= 2.0;\n * positions.set(i, pos);\n * }\n * // We're done modifying, tell the engine to move vertex data to the GPU.\n * mesh.update();\n * ```\n */\nexport class MeshAttributeAccessor {\n /** Max number of elements. */\n readonly length: number = 0;\n\n /** Wonderland Engine instance. @hidden */\n protected readonly _engine: WonderlandEngine;\n\n /** Attribute index. @hidden */\n private _attribute: number = -1;\n /** Attribute offset. @hidden */\n private _offset: number = 0;\n /** Attribute stride. @hidden */\n private _stride: number = 0;\n /** Format size native enum. @hidden */\n private _formatSize: number = 0;\n /** Number of components per vertex. @hidden */\n private _componentCount: number = 0;\n /** Number of values per vertex. @hidden */\n private _arraySize: number = 1;\n\n /**\n * Class to instantiate an ArrayBuffer to get/set values.\n */\n private _bufferType: T;\n /**\n * Function to allocate temporary WASM memory. It is cached in the accessor to avoid\n * conditionals during get/set.\n */\n private _tempBufferGetter: (bytes: number) => TypedArray;\n\n /**\n * Create a new instance.\n *\n * @note Please use {@link Mesh.attribute} to create a new instance.\n *\n * @param options Contains information about how to read the data.\n * @note Do not use this constructor. Instead, please use the {@link Mesh.attribute} method.\n *\n * @hidden\n */\n constructor(engine: WonderlandEngine, options: MeshAttributeAccessorOptions) {\n this._engine = engine;\n\n this._attribute = options.attribute;\n this._offset = options.offset;\n this._stride = options.stride;\n this._formatSize = options.formatSize;\n this._componentCount = options.componentCount;\n this._arraySize = options.arraySize;\n this._bufferType = options.bufferType;\n this.length = options.length;\n\n const wasm = this._engine.wasm;\n this._tempBufferGetter = (\n this._bufferType === Float32Array\n ? wasm.getTempBufferF32.bind(wasm)\n : wasm.getTempBufferU16.bind(wasm)\n ) as () => TypedArray;\n }\n\n /**\n * Create a new TypedArray to hold this attribute's values.\n *\n * This method is useful to create a view to hold the data to\n * pass to {@link get} and {@link set}\n *\n * Example:\n *\n * ```js\n * const vertexCount = 4;\n * const positionAttribute = mesh.attribute(MeshAttribute.Position);\n *\n * // A position has 3 floats per vertex. Thus, positions has length 3 * 4.\n * const positions = positionAttribute.createArray(vertexCount);\n * ```\n *\n * @param count The number of **vertices** expected.\n * @returns A TypedArray with the appropriate format to access the data\n */\n createArray(count = 1): TypedArray {\n count = count > this.length ? this.length : count;\n return new this._bufferType(\n count * this._componentCount * this._arraySize\n ) as TypedArray;\n }\n\n /** @overload */\n get(index: number): TypedArray;\n /**\n * Get attribute element.\n *\n * @param index Index\n * @param out Preallocated array to write into,\n * to avoid garbage, otherwise will allocate a new TypedArray.\n *\n * `out.length` needs to be a multiple of the attributes component count, see\n * {@link MeshAttribute}. If `out.length` is more than one multiple, it will be\n * filled with the next n attribute elements, which can reduce overhead\n * of this call.\n *\n * @returns The `out` parameter\n */\n get(index: number, out: T): T;\n get(index: number, out: NumberArray = this.createArray()): NumberArray {\n if (out.length % this._componentCount !== 0) {\n throw new Error(\n `out.length, ${out.length} is not a multiple of the attribute vector components, ${this._componentCount}`\n );\n }\n\n /* Maximum number of values supported by the accessor, to avoid an out-of-bound access */\n const componentCount = this._componentCount * this._arraySize;\n const len = Math.min(out.length, componentCount * this.length);\n\n const dest = this._tempBufferGetter(len);\n const elementSize = this._bufferType.BYTES_PER_ELEMENT;\n const destSize = elementSize * len;\n const srcFormatSize = this._formatSize * this._arraySize;\n const destFormatSize = this._componentCount * elementSize * this._arraySize;\n\n this.engine.wasm._wl_mesh_get_attribute_values(\n this._attribute,\n srcFormatSize,\n this._offset + index * this._stride,\n this._stride,\n destFormatSize,\n dest.byteOffset,\n destSize\n );\n\n for (let i = 0; i < len; ++i) out[i] = dest[i];\n return out;\n }\n\n /**\n * Set attribute element.\n *\n * @param i Index\n * @param v Value to set the element to\n *\n * `v.length` needs to be a multiple of the attributes component count, see\n * {@link MeshAttribute}. If `v.length` is more than one multiple, it will be\n * filled with the next n attribute elements, which can reduce overhead\n * of this call.\n *\n * @returns Reference to self (for method chaining)\n */\n set(i: number, v: Readonly) {\n if (v.length % this._componentCount !== 0) {\n throw new Error(\n `out.length, ${v.length} is not a multiple of the attribute vector components, ${this._componentCount}`\n );\n }\n\n /* Maximum number of values supported by the accessor, to avoid an out-of-bound access */\n const componentCount = this._componentCount * this._arraySize;\n const len = Math.min(v.length, componentCount * this.length);\n\n const elementSize = this._bufferType.BYTES_PER_ELEMENT;\n const srcSize = elementSize * len;\n const srcFormatSize = componentCount * elementSize;\n const destFormatSize = this._formatSize * this._arraySize;\n\n const wasm = this.engine.wasm;\n\n /* Unless we are already working with data from WASM heap, we\n * need to copy into temporary memory. */\n if ((v as Float32Array).buffer != wasm.HEAPU8.buffer) {\n const dest = this._tempBufferGetter(len);\n dest.set(v);\n v = dest;\n }\n\n wasm._wl_mesh_set_attribute_values(\n this._attribute,\n srcFormatSize,\n (v as Float32Array).byteOffset,\n srcSize,\n destFormatSize,\n this._offset + i * this._stride,\n this._stride\n );\n\n return this;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._engine;\n }\n}\n\n/**\n * Wrapper around a native font resource.\n *\n * @note Fonts are **per-engine**, they can thus be shared by multiple scenes.\n *\n * @since 1.2.0\n */\nexport class Font extends Resource {\n /** Em height in object space. Equivalent to line height. */\n get emHeight(): number {\n return this.engine.wasm._wl_font_get_emHeight(this._id);\n }\n\n /**\n * Cap height in object space. This is the typical height of capital\n * letters. Can be 0 if not defined by the font.\n */\n get capHeight(): number {\n return this.engine.wasm._wl_font_get_capHeight(this._id);\n }\n\n /**\n * X height in object space. This is the typical height of lowercase\n * letters. Can be 0 if not defined by the font.\n */\n get xHeight(): number {\n return this.engine.wasm._wl_font_get_xHeight(this._id);\n }\n\n /**\n * Outline size. This is the factor by which characters are expanded to\n * create the outline effect. Returns 0 if the font was compiled without\n * an outline.\n *\n * @since 1.2.1\n */\n get outlineSize(): number {\n return this.engine.wasm._wl_font_get_outlineSize(this._id);\n }\n}\n\n/**\n * Wrapper around a native particle effect resource.\n *\n * @note Particle effects are **per-engine**, they can thus be shared by\n * multiple scenes.\n *\n * @since 1.4.0\n */\nexport class ParticleEffect extends Resource {\n /**\n * Create a copy of the underlying native particle effect.\n *\n * @returns Particle effect clone.\n */\n clone(): ParticleEffect | null {\n const index = this.engine.wasm._wl_particleEffect_clone(this._id);\n return this.engine.particleEffects.wrap(index);\n }\n}\n\n/** Temporary canvas */\nlet temp2d: {\n canvas: HTMLCanvasElement;\n ctx: CanvasRenderingContext2D;\n} | null = null;\n\n/**\n * Wrapper around a native texture data.\n *\n * For more information about how to create meshes, have a look at the\n * {@link TextureManager} class.\n *\n * @note Textures are **per-engine**, they can thus be shared by multiple scenes.\n */\nexport class Texture extends Resource {\n /**\n * @deprecated Use {@link TextureManager.create} instead, accessible via\n * {@link WonderlandEngine.textures}:\n *\n * ```js\n * const image = new Image();\n * image.onload = () => {\n * const texture = engine.textures.create(image);\n * };\n * ```\n */\n constructor(engine: WonderlandEngine, param: ImageLike | number) {\n if (isImageLike(param)) {\n const texture = engine.textures.create(param);\n /* `super()` must be called as stated in the specification */\n super(engine, texture._index);\n return texture;\n }\n super(engine, param);\n }\n\n /** Whether this texture is valid. */\n get valid(): boolean {\n return !this.isDestroyed && this.width !== 0 && this.height !== 0;\n }\n\n /**\n * Index in this manager.\n *\n * @deprecated Use {@link Texture.index} instead.\n */\n get id(): number {\n return this.index;\n }\n\n /** Update the texture to match the HTML element (e.g. reflect the current frame of a video). */\n update() {\n const image = this._imageIndex;\n if (!this.valid || !image) return;\n\n this.engine.wasm._wl_image_markDirty(image);\n }\n\n /**\n * Width of the texture.\n *\n * @note If the texture has an {@link htmlElement}, this is the width used at creation.\n * If the actual element's width changed, it must be read from the element itself.\n */\n get width(): number {\n const element = this.htmlElement;\n if (element) {\n /** @todo(1.5.0): Remove, the size should be entirely read from the wasm. */\n return (element as HTMLVideoElement).videoWidth ?? element.width;\n }\n const wasm = this.engine.wasm;\n wasm._wl_image_size(this._imageIndex, wasm._tempMem);\n return wasm._tempMemUint32[0];\n }\n\n /**\n * Height of the texture.\n *\n * @note If the texture has an {@link htmlElement}, this is the height used at creation.\n * If the actual element's height changed, it must be read from the element itself.\n */\n get height(): number {\n const element = this.htmlElement;\n if (element) {\n /** @todo(1.5.0): Remove, the size should be entirely read from the wasm. */\n return (element as HTMLVideoElement).videoHeight ?? element.height;\n }\n const wasm = this.engine.wasm;\n wasm._wl_image_size(this._imageIndex, wasm._tempMem);\n return wasm._tempMemUint32[1];\n }\n\n /**\n * Returns the html element associated to this texture.\n *\n * @note This accessor will return `null` if the image is compressed.\n */\n get htmlElement(): ImageLike | null {\n const image = this._imageIndex;\n if (!image) return null;\n\n const wasm = this.engine.wasm;\n const jsImageIndex = wasm._wl_image_get_jsImage_index(image);\n\n /* Since the first element is `null`, no need to check for `jsImageIndex` */\n return wasm._images[jsImageIndex];\n }\n\n /**\n * Upload content to the texture to the GPU.\n *\n * Usage:\n *\n * ```js\n * // Size 128x64\n * const image = new Image();\n * texture.updateSubImage(28, 4, 100, 60, 0, 0, image);\n * ```\n *\n * @note The source is cropped if the destination offset and source dimensions\n * are out-of-bounds regarding the texture size.\n *\n * @note Uploading a texture without a source offset is significantly faster.\n *\n * #### ImageBitmap\n *\n * If you need to upload a subregion, it's recommended to use:\n *\n * ```js\n * createImageBitmap(image, sourceX, sourceY, sourceWidth, sourceHeight).then(bitmap => {\n * const destinationX = sourceX;\n * const destinationY = sourceY;\n * texture.updateSubImage(0, 0, sourceWidth, sourceHeight, destinationX, destinationY, bitmap);\n * });\n * ```\n *\n * You could use this method with a `src` offset, but the synchronous implementation\n * uses a temporary 2d canvas, negatively affecting performance for large content.\n *\n * @param srcX Horizontal pixel offset, in the source image.\n * @param srcY Vertical pixel offset, in the source image.\n * @param srcWidth Width of the sub image, in the source image.\n * @param srcHeight Height of the sub image, in the source image\n * @param dstX Horizontal pixel offset, in the destination image.\n * Defaults to `srcX` for backward compatibility with older API versions.\n * @param dstY Vertical pixel offset, in the destination image.\n * Defaults to `srcY` for backward compatibility with older API versions.\n * @param content `Image`, `HTMLCanvasElement`, `HTMLVideoElement`, or `ImageBitmap` to upload.\n * @returns `true` if the update went through, `false` otherwise.\n */\n updateSubImage(\n srcX: number,\n srcY: number,\n srcWidth: number,\n srcHeight: number,\n dstX: number = srcX,\n dstY: number = srcY,\n content?: ImageLike\n ): boolean {\n if (this.isDestroyed) return false;\n\n const image = this._imageIndex;\n if (!image) return false;\n\n let img = content ?? this.htmlElement;\n if (!img) return false;\n\n const isImageBitmap = img instanceof ImageBitmap;\n /* ImageBitmap can't be flipped upon upload to the GPU.\n * Thus, we need to skip flipping the temporary canvas to keep the same behavior\n * when uploading an ImageBitmap and uploading a blitted ImageBitmap. */\n const flipY = !isImageBitmap;\n\n if (srcX || srcY) {\n /* Lazy initialize temp canvas */\n if (!temp2d) {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error(\n 'Texture.updateSubImage(): Failed to obtain CanvasRenderingContext2D.'\n );\n }\n temp2d = {canvas, ctx};\n }\n temp2d.canvas.width = srcWidth;\n temp2d.canvas.height = srcHeight;\n\n temp2d.ctx.drawImage(\n img,\n srcX,\n isImageBitmap ? img.height - srcY - srcHeight : srcY,\n srcWidth,\n srcHeight,\n 0,\n 0,\n srcWidth,\n srcHeight\n );\n img = temp2d.canvas;\n }\n\n const wasm = this.engine.wasm;\n wasm._images[0] = img;\n\n /* Clamp the width/height to avoid a GL upload error */\n const width = srcWidth - Math.max(srcWidth + dstX - this.width, 0);\n const height = srcHeight - Math.max(srcHeight + dstY - this.height, 0);\n const dstReversedY = this.height - dstY - height;\n\n const ret = wasm._wl_renderer_updateImage(\n image,\n 0,\n width,\n height,\n dstX,\n dstReversedY,\n flipY\n );\n wasm._images[0] = null;\n\n return !!ret;\n }\n\n /**\n * Destroy and free the texture's texture altas space and memory.\n *\n * It is best practice to set the texture variable to `null` after calling\n * destroy to prevent accidental use of the invalid texture:\n *\n * ```js\n * texture.destroy();\n * texture = null;\n * ```\n *\n * @since 0.9.0\n */\n destroy(): void {\n const wasm = this.engine.wasm;\n\n const img = wasm._images[this._imageIndex];\n if (img instanceof ImageBitmap) img.close();\n\n wasm._wl_texture_destroy(this._id);\n this.engine.textures._destroy(this);\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Texture(destroyed)';\n }\n return `Texture(${this._index})`;\n }\n\n private get _imageIndex() {\n return this.engine.wasm._wl_texture_get_image_index(this._id);\n }\n}\n\n/**\n * Wrapper around a native animation.\n */\nexport class Animation extends SceneResource {\n /**\n * @param index Index in the manager\n */\n constructor(host: WonderlandEngine | Prefab = WL, index: number) {\n const scene = host instanceof Prefab ? host : host.scene;\n super(scene as Prefab, index);\n }\n\n /** Duration of this animation. */\n get duration(): number {\n return this.engine.wasm._wl_animation_get_duration(this._id);\n }\n\n /** Number of tracks in this animation. */\n get trackCount(): number {\n return this.engine.wasm._wl_animation_get_trackCount(this._id);\n }\n\n /**\n * Clone this animation retargeted to a new set of objects.\n *\n * The clone shares most of the data with the original and is therefore\n * light-weight.\n *\n * **Experimental:** This API might change in upcoming versions.\n *\n * If retargeting to {@link Skin}, the join names will be used to determine a mapping\n * from the previous skin to the new skin. The source skin will be retrieved from\n * the first track in the animation that targets a joint.\n *\n * @param newTargets New targets per track. Expected to have\n * {@link Animation#trackCount} elements or to be a {@link Skin}.\n * @returns The retargeted clone of this animation.\n */\n retarget(newTargets: Object3D[] | Skin): Animation {\n const wasm = this.engine.wasm;\n if (newTargets instanceof Skin) {\n const index = wasm._wl_animation_retargetToSkin(this._id, newTargets._id);\n return this._scene.animations.wrap(index)!;\n }\n\n if (newTargets.length != this.trackCount) {\n throw Error(\n 'Expected ' +\n this.trackCount.toString() +\n ' targets, but got ' +\n newTargets.length.toString()\n );\n }\n const ptr = wasm._malloc(2 * newTargets.length);\n for (let i = 0; i < newTargets.length; ++i) {\n const object3d = newTargets[i];\n this.scene.assertOrigin(object3d);\n /* Use local id here and **not** packed id, because the WASM\n * takes ownership and reinterprets the ids. */\n wasm.HEAPU16[(ptr >>> 1) + i] = newTargets[i].objectId;\n }\n const index = wasm._wl_animation_retarget(this._id, ptr);\n wasm._free(ptr);\n\n return this._scene.animations.wrap(index)!;\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Animation(destroyed)';\n }\n return `Animation(${this._index})`;\n }\n}\n\n/**\n * Scene graph object.\n *\n * Node in the scene graph or \"entity\". Consists of transformation and a reference\n * to its parent object. Usually holds components and is accessible by components\n * through {@link Component#object}.\n *\n * Objects are stored in a data oriented manner inside WebAssembly memory. This class\n * is a JavaScript API wrapper around this memory for more convenient use in\n * components.\n *\n * Objects can be created and added to a scene through {@link Prefab.addObject}:\n *\n * ```js\n * const parent = scene.addObject();\n * parent.name = 'parent`';\n * const child = scene.addObject(parent);\n * child.name = 'child`';\n * ```\n */\nexport class Object3D {\n /**\n * Packed object id, containing scene index and local id.\n *\n * @hidden\n */\n readonly _id: number = -1;\n /** Object id, relative to the scene manager. @hidden */\n readonly _localId: number = -1;\n\n /** Scene instance. @hidden */\n protected readonly _scene: Prefab;\n /** Wonderland Engine instance. @hidden */\n protected readonly _engine: WonderlandEngine;\n\n /**\n * @param o Object id to wrap.\n *\n * @deprecated Objects must be obtained via {@link Scene.addObject} or {@link Scene.wrap}:\n *\n * ```js\n * // Create a new object.\n * const obj = scene.addObject();\n *\n * // Wrap an object using its id. The id must be valid.\n * const obj = scene.wrap(0);\n * ```\n *\n * @hidden\n */\n constructor(scene: WonderlandEngine | Prefab, id: number) {\n scene = scene instanceof Prefab ? scene : scene.scene;\n this._localId = id;\n this._id = (scene._index << 22) | id;\n this._scene = scene;\n /** @todo: Remove and only keep getter. */\n this._engine = scene.engine;\n }\n\n /**\n * Name of the object.\n *\n * Useful for identifying objects during debugging.\n */\n get name(): string {\n const wasm = this._engine.wasm;\n return wasm.UTF8ToString(wasm._wl_object_name(this._id));\n }\n\n /**\n * Set the object's name.\n *\n * @param newName The new name to set.\n */\n set name(newName: string) {\n const wasm = this._engine.wasm;\n wasm._wl_object_set_name(this._id, wasm.tempUTF8(newName));\n }\n\n /**\n * Parent of this object or `null` if parented to root.\n */\n get parent(): Object3D | null {\n const p = this._engine.wasm._wl_object_parent(this._id);\n return p === 0 ? null : this._scene.wrap(p);\n }\n\n /**\n * Equivalent to {@link Object3D.getChildren}.\n *\n * @note Prefer to use {@link Object3D.getChildren} for performance.\n */\n get children(): Object3D[] {\n return this.getChildren();\n }\n\n /** The number of children of this object. */\n get childrenCount(): number {\n return this._engine.wasm._wl_object_get_children_count(this._id);\n }\n\n /**\n * Reparent object to given object.\n *\n * @note Reparenting is not trivial and might have a noticeable performance impact.\n *\n * @param newParent New parent or `null` to parent to root\n */\n set parent(newParent: Object3D | undefined | null) {\n if (this.markedDestroyed) {\n const strThis = this.toString();\n const strParent = newParent ? newParent : 'root';\n throw new Error(\n `Failed to attach ${strThis} to ${strParent}. ${strThis} is marked as destroyed.`\n );\n } else if (newParent?.markedDestroyed) {\n const strParent = newParent.toString();\n throw new Error(\n `Failed to attach ${this} to ${strParent}. ${strParent} is marked as destroyed.`\n );\n }\n this.scene.assertOrigin(newParent);\n this._engine.wasm._wl_object_set_parent(\n this._id,\n newParent == null ? 0 : newParent._id\n );\n }\n\n /** Local object id in the scene manager. */\n get objectId() {\n return this._localId;\n }\n\n /** Scene instance. */\n get scene() {\n return this._scene;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._engine;\n }\n\n /**\n * Add an object as a child of this instance.\n *\n * @returns A newly created object.\n */\n addChild(): Object3D {\n const objectId = this.engine.wasm._wl_scene_add_object(this.scene._index, this._id);\n const obj = this.scene.wrap(objectId);\n return obj;\n }\n\n /**\n * Clone this hierarchy into a new one.\n *\n * Cloning copies the hierarchy structure, object names,\n * as well as components.\n *\n * JavaScript components are cloned using {@link Component.copy}. You can\n * override this method in your components.\n *\n * @param parent The parent for the cloned hierarchy or `null` to clone\n * into the scene root. Defaults to `null`.\n *\n * @returns The clone of this object.\n */\n clone(parent: Object3D | null = null): Object3D {\n this.scene.assertOrigin(parent);\n const engine = this._engine;\n const id = engine.wasm._wl_object_clone(this._id, parent ? parent._id : 0);\n return this._scene.wrap(id);\n }\n\n /**\n * Children of this object.\n *\n * @note Child order is **undefined**. No assumptions should be made\n * about the index of a specific object.\n *\n * If you need to access a specific child of this object, you can\n * use {@link Object3D.findByName}.\n *\n * When the object exists in the scene at editor time, prefer passing it as\n * a component property.\n *\n * @note When providing an output array, only `this.childrenCount` elements will be written.\n * The rest of the array will not be modified by this method.\n *\n * @param out Destination array, expected to have at least `this.childrenCount` elements.\n * @returns The `out` parameter.\n */\n getChildren(out: Object3D[] = new Array(this.childrenCount)): Object3D[] {\n const childrenCount = this.childrenCount;\n if (childrenCount === 0) return out;\n\n const wasm = this._engine.wasm;\n wasm.requireTempMem(childrenCount * 2);\n\n this._engine.wasm._wl_object_get_children(\n this._id,\n wasm._tempMem,\n wasm._tempMemSize >> 1\n );\n\n for (let i = 0; i < childrenCount; ++i) {\n out[i] = this._scene.wrap(wasm._tempMemUint16[i]);\n }\n return out;\n }\n\n /**\n * Reset local transformation (translation, rotation and scaling) to identity.\n *\n * @returns Reference to self (for method chaining).\n */\n resetTransform(): this {\n this._engine.wasm._wl_object_reset_translation_rotation(this._id);\n this._engine.wasm._wl_object_reset_scaling(this._id);\n return this;\n }\n\n /**\n * Reset local position and rotation to identity.\n *\n * @returns Reference to self (for method chaining).\n */\n resetPositionRotation(): this {\n this._engine.wasm._wl_object_reset_translation_rotation(this._id);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.resetPositionRotation} instead. */\n resetTranslationRotation(): this {\n return this.resetPositionRotation();\n }\n\n /**\n * Reset local rotation, keep translation.\n *\n * @note To reset both rotation and translation, prefer\n * {@link resetTranslationRotation}.\n *\n * @returns Reference to self (for method chaining).\n */\n resetRotation(): this {\n this._engine.wasm._wl_object_reset_rotation(this._id);\n return this;\n }\n\n /**\n * Reset local translation, keep rotation.\n *\n * @note To reset both rotation and translation, prefer\n * {@link resetTranslationRotation}.\n *\n * @returns Reference to self (for method chaining).\n */\n resetPosition(): this {\n this._engine.wasm._wl_object_reset_translation(this._id);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.resetPosition} instead. */\n resetTranslation(): this {\n return this.resetPosition();\n }\n\n /**\n * Reset local scaling to identity (``[1.0, 1.0, 1.0]``).\n *\n * @returns Reference to self (for method chaining).\n */\n resetScaling(): this {\n this._engine.wasm._wl_object_reset_scaling(this._id);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.translateLocal} instead. */\n translate(v: Readonly): this {\n return this.translateLocal(v);\n }\n\n /**\n * Translate object by a vector in the parent's space.\n *\n * @param v Vector to translate by.\n *\n * @returns Reference to self (for method chaining).\n */\n translateLocal(v: Readonly): this {\n this._engine.wasm._wl_object_translate(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /**\n * Translate object by a vector in object space.\n *\n * @param v Vector to translate by.\n *\n * @returns Reference to self (for method chaining).\n */\n translateObject(v: Readonly): this {\n this._engine.wasm._wl_object_translate_obj(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /**\n * Translate object by a vector in world space.\n *\n * @param v Vector to translate by.\n *\n * @returns Reference to self (for method chaining).\n */\n translateWorld(v: Readonly): this {\n this._engine.wasm._wl_object_translate_world(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.rotateAxisAngleDegLocal} instead. */\n rotateAxisAngleDeg(a: Readonly, d: number): this {\n this.rotateAxisAngleDegLocal(a, d);\n return this;\n }\n\n /**\n * Rotate around given axis by given angle (degrees) in local space.\n *\n * @param a Vector representing the rotation axis.\n * @param d Angle in degrees.\n *\n * @note If the object is translated the rotation will be around\n * the parent. To rotate around the object origin, use\n * {@link rotateAxisAngleDegObject}\n *\n * @see {@link rotateAxisAngleRad}\n *\n * @returns Reference to self (for method chaining).\n */\n rotateAxisAngleDegLocal(a: Readonly, d: number): this {\n this._engine.wasm._wl_object_rotate_axis_angle(this._id, a[0], a[1], a[2], d);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.rotateAxisAngleRadLocal} instead. */\n rotateAxisAngleRad(a: Readonly, d: number): this {\n return this.rotateAxisAngleRadLocal(a, d);\n }\n\n /**\n * Rotate around given axis by given angle (radians) in local space.\n *\n * @param a Vector representing the rotation axis.\n * @param d Angle in radians.\n *\n * @note If the object is translated the rotation will be around\n * the parent. To rotate around the object origin, use\n * {@link rotateAxisAngleDegObject}\n *\n * @see {@link rotateAxisAngleDeg}\n *\n * @returns Reference to self (for method chaining).\n */\n rotateAxisAngleRadLocal(a: Readonly, d: number): this {\n this._engine.wasm._wl_object_rotate_axis_angle_rad(this._id, a[0], a[1], a[2], d);\n return this;\n }\n\n /**\n * Rotate around given axis by given angle (degrees) in object space.\n *\n * @param a Vector representing the rotation axis.\n * @param d Angle in degrees.\n *\n * Equivalent to prepending a rotation quaternion to the object's\n * local transformation.\n *\n * @see {@link rotateAxisAngleRadObject}\n *\n * @returns Reference to self (for method chaining).\n */\n rotateAxisAngleDegObject(a: Readonly, d: number): this {\n this._engine.wasm._wl_object_rotate_axis_angle_obj(this._id, a[0], a[1], a[2], d);\n return this;\n }\n\n /**\n * Rotate around given axis by given angle (radians) in object space\n * Equivalent to prepending a rotation quaternion to the object's\n * local transformation.\n *\n * @param a Vector representing the rotation axis\n * @param d Angle in degrees\n *\n * @see {@link rotateAxisAngleDegObject}\n *\n * @returns Reference to self (for method chaining).\n */\n rotateAxisAngleRadObject(a: Readonly, d: number): this {\n this._engine.wasm._wl_object_rotate_axis_angle_rad_obj(\n this._id,\n a[0],\n a[1],\n a[2],\n d\n );\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.rotateLocal} instead. */\n rotate(q: Readonly): this {\n this.rotateLocal(q);\n return this;\n }\n\n /**\n * Rotate by a quaternion.\n *\n * @param q the Quaternion to rotate by.\n *\n * @returns Reference to self (for method chaining).\n */\n rotateLocal(q: Readonly): this {\n this._engine.wasm._wl_object_rotate_quat(this._id, q[0], q[1], q[2], q[3]);\n return this;\n }\n\n /**\n * Rotate by a quaternion in object space.\n *\n * Equivalent to prepending a rotation quaternion to the object's\n * local transformation.\n *\n * @param q the Quaternion to rotate by.\n *\n * @returns Reference to self (for method chaining).\n */\n rotateObject(q: Readonly): this {\n this._engine.wasm._wl_object_rotate_quat_obj(this._id, q[0], q[1], q[2], q[3]);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.scaleLocal} instead. */\n scale(v: Readonly): this {\n this.scaleLocal(v);\n return this;\n }\n\n /**\n * Scale object by a vector in object space.\n *\n * @param v Vector to scale by.\n *\n * @returns Reference to self (for method chaining).\n */\n scaleLocal(v: Readonly): this {\n this._engine.wasm._wl_object_scale(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /** @overload */\n getPositionLocal(): Float32Array;\n /**\n * Compute local / object space position from transformation.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getPositionLocal(out: T): T;\n getPositionLocal(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this._engine.wasm;\n /* Translation is different than rotation & scaling.\n * We can't simply read the memory. */\n wasm._wl_object_get_translation_local(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n\n /** @overload */\n getTranslationLocal(): Float32Array;\n /** @deprecated Please use {@link Object3D.getPositionLocal} instead. */\n getTranslationLocal(out: T): T;\n getTranslationLocal(out: NumberArray = new Float32Array(3)): NumberArray {\n return this.getPositionLocal(out);\n }\n\n /** @overload */\n getPositionWorld(): Float32Array;\n /**\n * Compute world space position from transformation.\n *\n * May recompute transformations of the hierarchy of this object,\n * if they were changed by JavaScript components this frame.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @return The `out` parameter.\n */\n getPositionWorld(out: T): T;\n getPositionWorld(out: NumberArray = new Float32Array(3)): NumberArray {\n const wasm = this._engine.wasm;\n /* Translation is different than rotation & scaling.\n * We can't simply read the memory. */\n wasm._wl_object_get_translation_world(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n\n /** @overload */\n getTranslationWorld(): Float32Array;\n /** @deprecated Please use {@link Object3D.getPositionWorld} instead. */\n getTranslationWorld(out: T): T;\n getTranslationWorld(out: NumberArray = new Float32Array(3)): NumberArray {\n return this.getPositionWorld(out);\n }\n\n /**\n * Set local / object space position.\n *\n * Concatenates a new translation dual quaternion onto the existing rotation.\n *\n * @param v New local position array/vector, expected to have at least 3 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setPositionLocal(v: Readonly): this {\n this._engine.wasm._wl_object_set_translation_local(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.setPositionLocal} instead. */\n setTranslationLocal(v: Readonly): this {\n return this.setPositionLocal(v);\n }\n\n /**\n * Set world space position.\n *\n * Applies the inverse parent transform with a new translation dual quaternion\n * which is concatenated onto the existing rotation.\n *\n * @param v New world position array/vector, expected to have at least 3 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setPositionWorld(v: Readonly): this {\n this._engine.wasm._wl_object_set_translation_world(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /** @deprecated Please use {@link Object3D.setPositionWorld} instead. */\n setTranslationWorld(v: Readonly): this {\n return this.setPositionWorld(v);\n }\n\n /** @overload */\n getScalingLocal(): Float32Array;\n /**\n * Local / object space scaling.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @return The `out` parameter.\n *\n * @since 1.0.0\n */\n getScalingLocal(out: T): T;\n getScalingLocal(out: NumberArray = new Float32Array(3)) {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_scaling_local(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n return out;\n }\n\n /**\n * Set local / object space scaling.\n *\n * @param v New local scaling array/vector, expected to have at least 3 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setScalingLocal(v: Readonly): this {\n this._engine.wasm._wl_object_set_scaling_local(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /** @overload */\n getScalingWorld(): Float32Array;\n /**\n * World space scaling.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @return The `out` parameter.\n *\n * @since 1.0.0\n */\n getScalingWorld(out: T): T;\n getScalingWorld(out: NumberArray = new Float32Array(3)) {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_scaling_world(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n return out;\n }\n\n /**\n * Set World space scaling.\n *\n * @param v New world scaling array/vector, expected to have at least 3 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setScalingWorld(v: Readonly): this {\n this._engine.wasm._wl_object_set_scaling_world(this._id, v[0], v[1], v[2]);\n return this;\n }\n\n /** @overload */\n getRotationLocal(): Float32Array;\n /**\n * Local space rotation.\n *\n * @param out Destination array/vector, expected to have at least 4 elements.\n * @return The `out` parameter.\n *\n * @since 1.0.0\n */\n getRotationLocal(out: T): T;\n getRotationLocal(out: NumberArray = new Float32Array(4)) {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_trans_local(this._id) / 4; /* Align F32 */\n /* The first 4 floats represent the rotation quaternion. */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n out[3] = wasm.HEAPF32[ptr + 3];\n return out;\n }\n\n /**\n * Set local space rotation.\n *\n * @param v New world rotation array/vector, expected to have at least 4 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setRotationLocal(v: Readonly): this {\n this._engine.wasm._wl_object_set_rotation_local(this._id, v[0], v[1], v[2], v[3]);\n return this;\n }\n\n /** @overload */\n getRotationWorld(): Float32Array;\n /**\n * World space rotation.\n *\n * @param out Destination array/vector, expected to have at least 4 elements.\n * @return The `out` parameter.\n *\n * @since 1.0.0\n */\n getRotationWorld(out: T): T;\n getRotationWorld(out: NumberArray = new Float32Array(4)) {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_trans_world(this._id) / 4; /* Align F32 */\n /* The first 4 floats represent the rotation quaternion. */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n out[3] = wasm.HEAPF32[ptr + 3];\n return out;\n }\n\n /**\n * Set local space rotation.\n *\n * @param v New world rotation array/vector, expected to have at least 4 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setRotationWorld(v: Readonly): this {\n this._engine.wasm._wl_object_set_rotation_world(this._id, v[0], v[1], v[2], v[3]);\n return this;\n }\n\n /** @overload */\n getTransformLocal(): Float32Array;\n /**\n * Local space transformation.\n *\n * @param out Destination array/vector, expected to have at least 8 elements.\n * @return The `out` parameter.\n */\n getTransformLocal(out: T): T;\n getTransformLocal(out: NumberArray = new Float32Array(8)) {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_trans_local(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n out[3] = wasm.HEAPF32[ptr + 3];\n out[4] = wasm.HEAPF32[ptr + 4];\n out[5] = wasm.HEAPF32[ptr + 5];\n out[6] = wasm.HEAPF32[ptr + 6];\n out[7] = wasm.HEAPF32[ptr + 7];\n return out;\n }\n\n /**\n * Set local space rotation.\n *\n * @param v New local transform array, expected to have at least 8 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setTransformLocal(v: Readonly): this {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_trans_local(this._id) / 4; /* Align F32 */\n wasm.HEAPF32[ptr] = v[0];\n wasm.HEAPF32[ptr + 1] = v[1];\n wasm.HEAPF32[ptr + 2] = v[2];\n wasm.HEAPF32[ptr + 3] = v[3];\n wasm.HEAPF32[ptr + 4] = v[4];\n wasm.HEAPF32[ptr + 5] = v[5];\n wasm.HEAPF32[ptr + 6] = v[6];\n wasm.HEAPF32[ptr + 7] = v[7];\n this.setDirty();\n return this;\n }\n\n /** @overload */\n getTransformWorld(): Float32Array;\n /**\n * World space transformation.\n *\n * @param out Destination array, expected to have at least 8 elements.\n * @return The `out` parameter.\n */\n getTransformWorld(out: T): T;\n getTransformWorld(out: NumberArray = new Float32Array(8)) {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_trans_world(this._id) / 4; /* Align F32 */\n out[0] = wasm.HEAPF32[ptr];\n out[1] = wasm.HEAPF32[ptr + 1];\n out[2] = wasm.HEAPF32[ptr + 2];\n out[3] = wasm.HEAPF32[ptr + 3];\n out[4] = wasm.HEAPF32[ptr + 4];\n out[5] = wasm.HEAPF32[ptr + 5];\n out[6] = wasm.HEAPF32[ptr + 6];\n out[7] = wasm.HEAPF32[ptr + 7];\n return out;\n }\n\n /**\n * Set world space rotation.\n *\n * @param v New world transform array, expected to have at least 8 elements.\n *\n * @returns Reference to self (for method chaining).\n */\n setTransformWorld(v: Readonly): this {\n const wasm = this._engine.wasm;\n const ptr = wasm._wl_object_trans_world(this._id) / 4; /* Align F32 */\n wasm.HEAPF32[ptr] = v[0];\n wasm.HEAPF32[ptr + 1] = v[1];\n wasm.HEAPF32[ptr + 2] = v[2];\n wasm.HEAPF32[ptr + 3] = v[3];\n wasm.HEAPF32[ptr + 4] = v[4];\n wasm.HEAPF32[ptr + 5] = v[5];\n wasm.HEAPF32[ptr + 6] = v[6];\n wasm.HEAPF32[ptr + 7] = v[7];\n this._engine.wasm._wl_object_trans_world_to_local(this._id);\n return this;\n }\n\n /**\n * Local space transformation.\n *\n * @deprecated Please use {@link Object3D.setTransformLocal} and\n * {@link Object3D.getTransformLocal} instead.\n */\n get transformLocal(): Float32Array {\n const wasm = this._engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_object_trans_local(this._id),\n 8\n );\n }\n\n /**\n * Set local transform.\n *\n * @param t Local space transformation.\n *\n * @since 0.8.5\n *\n * @deprecated Please use {@link Object3D.setTransformLocal} and\n * {@link Object3D.getTransformLocal} instead.\n */\n set transformLocal(t: Readonly) {\n this.transformLocal.set(t);\n this.setDirty();\n }\n\n /**\n * Global / world space transformation.\n *\n * May recompute transformations of the hierarchy of this object,\n * if they were changed by JavaScript components this frame.\n *\n * @deprecated Please use {@link Object3D.setTransformWorld} and\n * {@link Object3D.getTransformWorld} instead.\n */\n get transformWorld(): Float32Array {\n const wasm = this._engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_object_trans_world(this._id),\n 8\n );\n }\n\n /**\n * Set world transform.\n *\n * @param t Global / world space transformation.\n *\n * @since 0.8.5\n *\n * @deprecated Please use {@link Object3D.setTransformWorld} and\n * {@link Object3D.getTransformWorld} instead.\n */\n set transformWorld(t: Readonly) {\n this.transformWorld.set(t);\n this._engine.wasm._wl_object_trans_world_to_local(this._id);\n }\n\n /**\n * Local / object space scaling.\n *\n * @deprecated Please use {@link Object3D.setScalingLocal} and\n * {@link Object3D.getScalingLocal} instead.\n */\n get scalingLocal(): Float32Array {\n const wasm = this._engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_object_scaling_local(this._id),\n 3\n );\n }\n\n /**\n * Set local space scaling.\n *\n * @param s Local space scaling.\n *\n * @since 0.8.7\n *\n * @deprecated Please use {@link Object3D.setScalingLocal} and\n * {@link Object3D.getScalingLocal} instead.\n */\n set scalingLocal(s: Readonly) {\n this.scalingLocal.set(s);\n this.setDirty();\n }\n\n /**\n * Global / world space scaling.\n *\n * May recompute transformations of the hierarchy of this object,\n * if they were changed by JavaScript components this frame.\n *\n * @deprecated Please use {@link Object3D.setScalingWorld} and\n * {@link Object3D.getScalingWorld} instead.\n */\n get scalingWorld(): Float32Array {\n const wasm = this._engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_object_scaling_world(this._id),\n 3\n );\n }\n\n /**\n * Set world space scaling.\n *\n * @param t World space scaling.\n *\n * @since 0.8.7\n *\n * @deprecated Please use {@link Object3D.setScalingWorld} and\n * {@link Object3D.getScalingWorld} instead.\n */\n set scalingWorld(s: Readonly) {\n this.scalingWorld.set(s);\n this._engine.wasm._wl_object_scaling_world_to_local(this._id);\n }\n\n /**\n * Local space rotation.\n *\n * @since 0.8.7\n *\n * @deprecated Please use {@link Object3D.getRotationLocal} and\n * {@link Object3D.setRotationLocal} instead.\n */\n get rotationLocal(): Float32Array {\n return this.transformLocal.subarray(0, 4);\n }\n\n /**\n * Global / world space rotation\n *\n * @since 0.8.7\n *\n * @deprecated Please use {@link Object3D.getRotationWorld} and\n * {@link Object3D.setRotationWorld} instead.\n */\n get rotationWorld(): Float32Array {\n return this.transformWorld.subarray(0, 4);\n }\n\n /**\n * Set local space rotation.\n *\n * @param r Local space rotation\n *\n * @since 0.8.7\n *\n * @deprecated Please use {@link Object3D.getRotationLocal} and\n * {@link Object3D.setRotationLocal} instead.\n */\n set rotationLocal(r: Readonly) {\n this._engine.wasm._wl_object_set_rotation_local(this._id, r[0], r[1], r[2], r[3]);\n }\n\n /**\n * Set world space rotation.\n *\n * @param r Global / world space rotation.\n *\n * @since 0.8.7\n *\n * @deprecated Please use {@link Object3D.getRotationWorld} and\n * {@link Object3D.setRotationWorld} instead.\n */\n set rotationWorld(r: Readonly) {\n this._engine.wasm._wl_object_set_rotation_world(this._id, r[0], r[1], r[2], r[3]);\n }\n\n /** @deprecated Please use {@link Object3D.getForwardWorld} instead. */\n getForward(out: T): T {\n return this.getForwardWorld(out);\n }\n\n /**\n * Compute the object's forward facing world space vector.\n *\n * The forward vector in object space is along the negative z-axis, i.e.,\n * `[0, 0, -1]`.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @return The `out` parameter.\n */\n getForwardWorld(out: T): T {\n out[0] = 0;\n out[1] = 0;\n out[2] = -1;\n this.transformVectorWorld(out);\n return out;\n }\n\n /** @deprecated Please use {@link Object3D.getUpWorld} instead. */\n getUp(out: T): T {\n return this.getUpWorld(out);\n }\n\n /**\n * Compute the object's up facing world space vector.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @return The `out` parameter.\n */\n getUpWorld(out: T): T {\n out[0] = 0;\n out[1] = 1;\n out[2] = 0;\n this.transformVectorWorld(out);\n return out;\n }\n\n /** @deprecated Please use {@link Object3D.getRightWorld} instead. */\n getRight(out: T): T {\n return this.getRightWorld(out);\n }\n\n /**\n * Compute the object's right facing world space vector.\n *\n * @param out Destination array/vector, expected to have at least 3 elements.\n * @return The `out` parameter.\n */\n getRightWorld(out: T): T {\n out[0] = 1;\n out[1] = 0;\n out[2] = 0;\n this.transformVectorWorld(out);\n return out;\n }\n\n /**\n * Transform a vector by this object's world transform.\n *\n * @param out Out vector\n * @param v Vector to transform, default `out`\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformVectorWorld(out: T, v: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = v[0];\n wasm._tempMemFloat[1] = v[1];\n wasm._tempMemFloat[2] = v[2];\n wasm._wl_object_transformVectorWorld(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n\n /**\n * Transform a vector by this object's local transform.\n *\n * @param out Out vector\n * @param v Vector to transform, default `out`\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformVectorLocal(out: T, v: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = v[0];\n wasm._tempMemFloat[1] = v[1];\n wasm._tempMemFloat[2] = v[2];\n wasm._wl_object_transformVectorLocal(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out;\n }\n\n /**\n * Transform a point by this object's world transform.\n *\n * @param out Out point.\n * @param p Point to transform, default `out`.\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformPointWorld(out: T, p: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = p[0];\n wasm._tempMemFloat[1] = p[1];\n wasm._tempMemFloat[2] = p[2];\n wasm._wl_object_transformPointWorld(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n\n return out;\n }\n\n /**\n * Transform a point by this object's local transform.\n *\n * @param out Out point.\n * @param p Point to transform, default `out`.\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformPointLocal(out: T, p: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = p[0];\n wasm._tempMemFloat[1] = p[1];\n wasm._tempMemFloat[2] = p[2];\n wasm._wl_object_transformPointLocal(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n\n return out;\n }\n\n /**\n * Transform a vector by this object's inverse world transform.\n *\n * @param out Out vector.\n * @param v Vector to transform, default `out`.\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformVectorInverseWorld(out: T, v: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = v[0];\n wasm._tempMemFloat[1] = v[1];\n wasm._tempMemFloat[2] = v[2];\n wasm._wl_object_transformVectorInverseWorld(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n\n return out;\n }\n\n /**\n * Transform a vector by this object's inverse local transform.\n *\n * @param out Out vector\n * @param v Vector to transform, default `out`\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformVectorInverseLocal(out: T, v: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = v[0];\n wasm._tempMemFloat[1] = v[1];\n wasm._tempMemFloat[2] = v[2];\n wasm._wl_object_transformVectorInverseLocal(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n\n return out;\n }\n\n /**\n * Transform a point by this object's inverse world transform.\n *\n * @param out Out point.\n * @param p Point to transform, default `out`.\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformPointInverseWorld(out: T, p: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat[0] = p[0];\n wasm._tempMemFloat[1] = p[1];\n wasm._tempMemFloat[2] = p[2];\n wasm._wl_object_transformPointInverseWorld(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n\n return out;\n }\n\n /**\n * Transform a point by this object's inverse local transform.\n *\n * @param out Out point.\n * @param p Point to transform, default `out`.\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n transformPointInverseLocal(out: T, p: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat.set(p);\n wasm._wl_object_transformPointInverseLocal(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n\n return out;\n }\n\n /**\n * Transform an object space dual quaternion into world space.\n *\n * @param out Out transformation.\n * @param q Local space transformation, default `out`.\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n toWorldSpaceTransform(out: T, q: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat.set(q);\n wasm._wl_object_toWorldSpaceTransform(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n out[3] = wasm._tempMemFloat[3];\n\n out[4] = wasm._tempMemFloat[4];\n out[5] = wasm._tempMemFloat[5];\n out[6] = wasm._tempMemFloat[6];\n out[7] = wasm._tempMemFloat[7];\n\n return out;\n }\n\n /**\n * Transform a world space dual quaternion into local space.\n *\n * @param out Out transformation\n * @param q World space transformation, default `out`\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n toLocalSpaceTransform(out: T, q: NumberArray = out): T {\n const p = this.parent;\n if (p) {\n p.toObjectSpaceTransform(out, q);\n return out;\n }\n if (out !== q) {\n out[0] = q[0];\n out[1] = q[1];\n out[2] = q[2];\n out[3] = q[3];\n out[4] = q[4];\n out[5] = q[5];\n out[6] = q[6];\n out[7] = q[7];\n }\n return out;\n }\n\n /**\n * Transform a world space dual quaternion into object space.\n *\n * @param out Out transformation.\n * @param q World space transformation, default `out`\n * @return The `out` parameter.\n *\n * @since 0.8.7\n */\n toObjectSpaceTransform(out: T, q: NumberArray = out): T {\n const wasm = this._engine.wasm;\n wasm._tempMemFloat.set(q);\n wasm._wl_object_toObjectSpaceTransform(this._id, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n out[3] = wasm._tempMemFloat[3];\n\n out[4] = wasm._tempMemFloat[4];\n out[5] = wasm._tempMemFloat[5];\n out[6] = wasm._tempMemFloat[6];\n out[7] = wasm._tempMemFloat[7];\n\n return out;\n }\n\n /**\n * Turn towards / look at target.\n *\n * Rotates the object so that its forward vector faces towards the target\n * position. The `up` vector acts as a hint to uniquely orient the object's\n * up direction. When orienting a view component, the projected `up` vector\n * faces upwards on the viewing plane.\n *\n * @param p Target position to turn towards, in world space.\n * @param up Up vector to align object with, in world space. Default is `[0, 1, 0]`.\n *\n * @returns Reference to self (for method chaining).\n */\n lookAt(p: NumberArray, up: NumberArray = UP_VECTOR): this {\n this._engine.wasm._wl_object_lookAt(\n this._id,\n p[0],\n p[1],\n p[2],\n up[0],\n up[1],\n up[2]\n );\n return this;\n }\n\n /** Destroy the object with all of its components and remove it from the scene */\n destroy(): void {\n if (this._id < 0) return;\n /* This will automatically call `scene._destroyObject` */\n this.engine.wasm._wl_object_remove(this._id);\n }\n\n /**\n * Mark transformation dirty.\n *\n * Causes an eventual recalculation of {@link transformWorld}, either\n * on next {@link getTranslationWorld}, {@link transformWorld} or\n * {@link scalingWorld} or the beginning of next frame, whichever\n * happens first.\n */\n setDirty(): void {\n this._engine.wasm._wl_object_set_dirty(this._id);\n }\n\n /**\n * Disable/enable all components of this object.\n *\n * @param b New state for the components.\n *\n * @since 0.8.5\n */\n set active(b: boolean) {\n const comps = this.getComponents();\n for (let c of comps) {\n c.active = b;\n }\n }\n\n /* `getComponent` overloads for native components. */\n\n /** @overload */\n getComponent(\n type: K,\n index?: number\n ): NativeComponents[K] | null;\n /** @overload */\n getComponent(typeOrClass: string, index?: number): Component | null;\n /**\n * Get a component attached to this object.\n *\n * @param typeOrClass Type name. It's also possible to give a class definition.\n * In this case, the method will use the `class.TypeName` field to find the component.\n * @param index=0 Index for component of given type. This can be used to access specific\n * components if the object has multiple components of the same type.\n * @returns The component or `null` if there is no such component on this object\n */\n getComponent(\n typeOrClass: ComponentConstructor,\n index?: number\n ): T | null;\n getComponent(\n typeOrClass: string | ComponentConstructor,\n index: number = 0\n ): Component | null {\n const wasm = this._engine.wasm;\n const type = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName;\n\n const scene = this._scene;\n const componentType = wasm._wl_scene_get_component_manager_index(\n scene._index,\n wasm.tempUTF8(type)\n );\n\n if (componentType < 0) {\n /* Not a native component, try js: */\n const typeIndex = wasm._componentTypeIndices[type];\n if (typeIndex === undefined) return null;\n const jsIndex = wasm._wl_get_js_component_index(this._id, typeIndex, index);\n if (jsIndex < 0) return null;\n\n const component = this._scene._jsComponents[jsIndex];\n return component.constructor !== BrokenComponent ? component : null;\n }\n\n const componentId = wasm._wl_get_component_id(this._id, componentType, index);\n return scene._components.wrapNative(componentType, componentId);\n }\n\n /* `getComponents` overloads for native components. */\n\n /** @overload */\n getComponents(): Component[];\n /** @overload */\n getComponents(type: K): NativeComponents[K][];\n /**\n * @param typeOrClass Type name, pass a falsey value (`undefined` or `null`) to retrieve all.\n * It's also possible to give a class definition. In this case, the method will use the `class.TypeName` field to\n * find the components.\n * @returns All components of given type attached to this object.\n *\n * @note As this function is non-trivial, avoid using it in `update()` repeatedly,\n * but rather store its result in `init()` or `start()`\n * @warning This method will currently return at most 341 components.\n */\n getComponents(typeOrClass: ComponentConstructor): T[];\n getComponents(\n typeOrClass?: string | ComponentConstructor | null\n ): T[] {\n const wasm = this._engine.wasm;\n const scene = this._scene;\n\n let manager = null;\n let type = null;\n if (typeOrClass) {\n type = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName;\n const nativeManager = scene._components.getNativeManager(type);\n manager = nativeManager !== null ? nativeManager : scene._components.js;\n }\n\n const components: Component[] = [];\n const maxComps = Math.floor((wasm._tempMemSize / 3) * 2);\n const componentsCount = wasm._wl_object_get_components(\n this._id,\n wasm._tempMem,\n maxComps\n );\n const offset = 2 * componentsCount;\n wasm._wl_object_get_component_types(this._id, wasm._tempMem + offset, maxComps);\n\n for (let i = 0; i < componentsCount; ++i) {\n const t = wasm._tempMemUint8[i + offset];\n const componentId = wasm._tempMemUint16[i];\n if (manager !== null && t !== manager) continue;\n\n const comp = this._scene._components.wrapAny(t, componentId);\n if (!comp) continue;\n if (type && type !== (comp.constructor as ComponentConstructor).TypeName)\n continue;\n components.push(comp);\n }\n return components as T[];\n }\n\n /* `addComponent` overloads for native components. */\n\n /** @overload */\n addComponent(type: 'collision', params?: Record): CollisionComponent;\n /** @overload */\n addComponent(type: 'text', params?: Record): TextComponent;\n /** @overload */\n addComponent(type: 'view', params?: Record): ViewComponent;\n /** @overload */\n addComponent(type: 'mesh', params?: Record): MeshComponent;\n /** @overload */\n addComponent(type: 'input', params?: Record): InputComponent;\n /** @overload */\n addComponent(type: 'light', params?: Record): LightComponent;\n /** @overload */\n addComponent(type: 'animation', params?: Record): AnimationComponent;\n /** @overload */\n addComponent(type: 'physx', params?: Record): PhysXComponent;\n /** @overload */\n addComponent(\n type: 'particle-effect',\n params?: Record\n ): ParticleEffectComponent;\n /** @overload */\n addComponent(type: string, params?: Record): Component;\n /**\n * Add component of given type to the object.\n *\n * You can use this function to clone components, see the example below.\n *\n * ```js\n * // Clone existing component (since 0.8.10)\n * let original = this.object.getComponent('mesh');\n * otherObject.addComponent('mesh', original);\n * // Create component from parameters\n * this.object.addComponent('mesh', {\n * mesh: someMesh,\n * material: someMaterial,\n * });\n * ```\n *\n * @param typeOrClass Typename to create a component of. Can be native or\n * custom JavaScript component type. It's also possible to give a class definition.\n * In this case, the method will use the `class.TypeName` field.\n * @param params Parameters to initialize properties of the new component,\n * can be another component to copy properties from.\n *\n * @returns The component or `null` if the type was not found\n */\n addComponent(\n typeClass: ComponentConstructor,\n params?: Record\n ): T;\n addComponent(\n typeOrClass: ComponentConstructor | string,\n params?: Record\n ): Component {\n if (this.markedDestroyed) {\n throw new Error(`Failed to add component. ${this} is marked as destroyed.`);\n }\n\n const wasm = this._engine.wasm;\n\n const type = isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName;\n const nativeManager = this._scene._components.getNativeManager(type);\n const isNative = nativeManager !== null;\n const manager = isNative ? nativeManager : this._scene._components.js;\n\n let componentId = -1;\n if (!isNative) {\n /* JavaScript component */\n if (!(type in wasm._componentTypeIndices)) {\n throw new TypeError(\"Unknown component type '\" + type + \"'\");\n }\n componentId = wasm._wl_object_add_js_component(\n this._id,\n wasm._componentTypeIndices[type]\n );\n } else {\n /* native component */\n componentId = wasm._wl_object_add_component(this._id, manager);\n }\n\n const component = this._scene._components.wrapAny(manager, componentId)!;\n\n if (params !== undefined) component.copy(params as Component);\n\n /* Explicitly initialize js components */\n if (!isNative) {\n component._triggerInit();\n /* start() is called through onActivate() */\n }\n\n /* If it was not explicitly requested by the user to leave the component inactive,\n * we activate it as a final step. This invalidates componentIndex! */\n if (!params || !('active' in params && !params.active)) {\n component.active = true;\n }\n\n return component;\n }\n\n /**\n * Search for descendants matching the name.\n *\n * This method is a wrapper around {@link Object3D.findByNameDirect} and\n * {@link Object3D.findByNameRecursive}.\n *\n * @param name The name to search for.\n * @param recursive If `true`, the method will look at all the descendants of this object.\n * If `false`, this method will only perform the search in direct children.\n * @returns An array of {@link Object3D} matching the name.\n *\n * @since 1.1.0\n */\n findByName(name: string, recursive = false): Object3D[] {\n return recursive ? this.findByNameRecursive(name) : this.findByNameDirect(name);\n }\n\n /**\n * Search for all **direct** children matching the name.\n *\n * @note Even though this method is heavily optimized, it does perform\n * a linear search to find the objects. Do not use in a hot path.\n *\n * @param name The name to search for.\n * @returns An array of {@link Object3D} matching the name.\n *\n * @since 1.1.0\n */\n findByNameDirect(name: string): Object3D[] {\n const wasm = this._engine.wasm;\n const id = this._id;\n\n /* Divide by 4 to get half as many ushort as possible */\n const tempSizeU16 = wasm._tempMemSize >> 2;\n const maxCount = tempSizeU16 - 2; /* Reserve two ushort */\n\n const buffer = wasm._tempMemUint16;\n buffer[maxCount] = 0; /* Index offset */\n buffer[maxCount + 1] = 0; /* child count */\n\n const bufferPtr = wasm._tempMem;\n const indexPtr = bufferPtr + maxCount * 2;\n const childCountPtr = bufferPtr + maxCount * 2 + 2;\n const namePtr = wasm.tempUTF8(name, (maxCount + 2) * 2);\n\n const result: Object3D[] = [];\n let read = 0;\n while (\n (read = wasm._wl_object_findByName(\n id,\n namePtr,\n indexPtr,\n childCountPtr,\n bufferPtr,\n maxCount\n ))\n ) {\n for (let i = 0; i < read; ++i) {\n result.push(this._scene.wrap(buffer[i]));\n }\n }\n\n return result;\n }\n\n /**\n * Search for **all descendants** matching the name.\n *\n * @note Even though this method is heavily optimized, it does perform\n * a linear search to find the objects. Do not use in a hot path.\n *\n * @param name The name to search for.\n * @returns An array of {@link Object3D} matching the name.\n *\n * @since 1.1.0\n */\n findByNameRecursive(name: string): Object3D[] {\n const wasm = this._engine.wasm;\n const id = this._id;\n\n /* Divide by 4 to get half as many ushort as possible */\n const tempSizeU16 = wasm._tempMemSize >> 2;\n const maxCount = tempSizeU16 - 1; /* Reserve one ushort */\n\n const buffer = wasm._tempMemUint16;\n buffer[maxCount] = 0; /* Index offset */\n\n const bufferPtr = wasm._tempMem;\n const indexPtr = bufferPtr + maxCount * 2;\n const namePtr = wasm.tempUTF8(name, (maxCount + 1) * 2);\n\n let read = 0;\n const result: Object3D[] = [];\n while (\n (read = wasm._wl_object_findByNameRecursive(\n id,\n namePtr,\n indexPtr,\n bufferPtr,\n maxCount\n ))\n ) {\n for (let i = 0; i < read; ++i) {\n result.push(this._scene.wrap(buffer[i]));\n }\n }\n\n return result;\n }\n\n /**\n * Whether given object's transformation has changed.\n */\n get changed(): boolean {\n return !!this._engine.wasm._wl_object_is_changed(this._id);\n }\n\n /**\n * `true` if the object is destroyed, `false` otherwise.\n *\n * If {@link WonderlandEngine.erasePrototypeOnDestroy} is `true`,\n * reading a custom property will not work:\n *\n * ```js\n * engine.erasePrototypeOnDestroy = true;\n *\n * const obj = scene.addObject();\n * obj.customParam = 'Hello World!';\n *\n * console.log(obj.isDestroyed); // Prints `false`\n * obj.destroy();\n * console.log(obj.isDestroyed); // Prints `true`\n * console.log(obj.customParam); // Throws an error\n * ```\n *\n * @since 1.1.1\n */\n get isDestroyed(): boolean {\n return this._id < 0;\n }\n\n /**\n * `true` if the object is marked as destroyed.\n *\n * This boolean will only ever be `true` when reading objects state\n * from the {@link Component.onDestroy} callback, i.e.,\n *\n * ```js\n * import {Component} from '@wonderlandengine/api';\n * class CleanupComponent extends Component {\n * onDestroy() {\n * if (this.object.markedDestroyed) {\n * // this object is getting removed\n * } else {\n * // The component is getting destroyed, the object will\n * // still exist after the destruction call\n * }\n * }\n * }\n * ```\n *\n * Certain operations are forbidden when an object is pending destruction:\n * - Reparenting the object via {@link parent}\n * - {@link addComponent}\n * - {@link Scene.addObject}\n * - {@link Scene.destroy}\n */\n get markedDestroyed(): boolean {\n const wasm = this.engine.wasm;\n return !!wasm._wl_object_markedDestroyed(this._id);\n }\n\n /**\n * Checks equality by comparing ids and **not** the JavaScript reference.\n *\n * @deprecate Use JavaScript reference comparison instead:\n *\n * ```js\n * const objectA = scene.addObject();\n * const objectB = scene.addObject();\n * const objectC = objectB;\n * console.log(objectA === objectB); // false\n * console.log(objectA === objectA); // true\n * console.log(objectB === objectC); // true\n * ```\n */\n equals(otherObject: Object3D | undefined | null): boolean {\n /** @todo(2.0.0): Remove this method. */\n if (!otherObject) return false;\n return this._id == otherObject._id;\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Object3D(destroyed)';\n }\n return `Object3D('${this.name}', ${this._localId})`;\n }\n}\n\n/**\n * Wrapper around a native skin data.\n */\nexport class Skin extends SceneResource {\n /** Amount of joints in this skin. */\n get jointCount() {\n return this.engine.wasm._wl_skin_get_joint_count(this._id);\n }\n\n /** Joints object ids for this skin */\n get jointIds(): Uint16Array {\n const wasm = this.engine.wasm;\n return new Uint16Array(\n wasm.HEAPU16.buffer,\n wasm._wl_skin_joint_ids(this._id),\n this.jointCount\n );\n }\n\n /**\n * Dual quaternions in a flat array of size 8 times {@link jointCount}.\n *\n * Inverse bind transforms of the skin.\n */\n get inverseBindTransforms(): Float32Array {\n const wasm = this.engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_skin_inverse_bind_transforms(this._id),\n 8 * this.jointCount\n );\n }\n\n /**\n * Vectors in a flat array of size 3 times {@link jointCount}.\n *\n * Inverse bind scalings of the skin.\n */\n get inverseBindScalings(): Float32Array {\n const wasm = this.engine.wasm;\n return new Float32Array(\n wasm.HEAPF32.buffer,\n wasm._wl_skin_inverse_bind_scalings(this._id),\n 3 * this.jointCount\n );\n }\n}\n\n/**\n * Wrapper around a native set of morph targets.\n *\n * ## Usage\n *\n * ```js\n * const mesh = object.getComponent('mesh');\n * const mouthTarget = mesh.morphTargets.getTargetIndex('mouth');\n * mesh.setMorphTargetWeight(mouthTarget, 0.5);\n * ```\n *\n * @since 1.2.0\n */\nexport class MorphTargets extends Resource {\n /** Amount of targets in this morph target set. */\n get count() {\n return this.engine.wasm._wl_morph_targets_get_target_count(this._id);\n }\n\n /** Returns the name of a given target */\n getTargetName(target: number): string {\n if (target >= this.count) {\n throw new Error(`Index ${target} is out of bounds for ${this.count} targets`);\n }\n const wasm = this.engine.wasm;\n return wasm.UTF8ToString(wasm._wl_morph_targets_get_target_name(this._id, target));\n }\n\n /**\n * Get the index for a given target name.\n *\n * Throws if no target with that name exists.\n *\n * @param name Name of the target.\n */\n getTargetIndex(name: string): number {\n const wasm = this.engine.wasm;\n const index = wasm._wl_morph_targets_get_target_index(\n this._id,\n wasm.tempUTF8(name)\n );\n if (index === -1) {\n throw Error(`Missing target '${name}'`);\n }\n return index;\n }\n}\n\n/* For backward compatibility with < 1.0.0. */\nexport {Object3D as Object};\n\n/**\n * Ray hit.\n *\n * Result of a {@link Scene.rayCast} or {@link Physics.rayCast}.\n *\n * @note this class wraps internal engine data and should only be created internally.\n */\nexport class RayHit {\n /** Scene instance. @hidden */\n readonly _scene: Scene;\n\n /** Pointer to the memory heap. */\n private _ptr: number;\n\n /**\n * @param ptr Pointer to the ray hits memory.\n */\n constructor(scene: Scene, ptr: number) {\n if ((ptr & 3) !== 0) {\n throw new Error('Misaligned pointer: please report a bug');\n }\n this._scene = scene;\n this._ptr = ptr;\n }\n\n /** @overload */\n getLocations(): Float32Array[];\n /**\n * Array of ray hit locations.\n *\n * #### Usage\n *\n * ```js\n * const hit = engine.physics.rayCast();\n * if (hit.hitCount > 0) {\n * const locations = hit.getLocations();\n * console.log(`Object hit at: ${locations[0][0]}, ${locations[0][1]}, ${locations[0][2]}`);\n * }\n * ```\n *\n * @param out Destination array of arrays/vectors, expected to have at least\n * `this.hitCount` elements. Each array is expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getLocations(out: T): T;\n getLocations(out?: NumberArray[]): NumberArray[] {\n out = out ?? Array.from({length: this.hitCount}, () => new Float32Array(3));\n\n const wasm = this.engine.wasm;\n const alignedPtr = this._ptr / 4; /* Align F32 */\n for (let i = 0; i < this.hitCount; ++i) {\n const locationPtr = alignedPtr + 3 * i;\n out[i][0] = wasm.HEAPF32[locationPtr];\n out[i][1] = wasm.HEAPF32[locationPtr + 1];\n out[i][2] = wasm.HEAPF32[locationPtr + 2];\n }\n return out;\n }\n\n /** @overload */\n getNormals(): Float32Array[];\n /**\n * Array of ray hit normals (only when using {@link Physics#rayCast}.\n *\n * @param out Destination array of arrays/vectors, expected to have at least\n * `this.hitCount` elements. Each array is expected to have at least 3 elements.\n * @returns The `out` parameter.\n */\n getNormals(out: T): T;\n getNormals(out?: NumberArray[]): NumberArray[] {\n out = out ?? Array.from({length: this.hitCount}, () => new Float32Array(3));\n\n const wasm = this.engine.wasm;\n const alignedPtr = (this._ptr + 48) / 4; /* Align F32 */\n for (let i = 0; i < this.hitCount; ++i) {\n const normalPtr = alignedPtr + 3 * i;\n out[i][0] = wasm.HEAPF32[normalPtr];\n out[i][1] = wasm.HEAPF32[normalPtr + 1];\n out[i][2] = wasm.HEAPF32[normalPtr + 2];\n }\n return out;\n }\n\n /** @overload */\n getDistances(): Float32Array;\n /**\n * Prefer these to recalculating the distance from locations.\n *\n * Distances of array hits to ray origin.\n *\n * @param out Destination array/vector, expected to have at least this.hitCount elements.\n * @returns The `out` parameter.\n */\n getDistances(out: T): T;\n getDistances(out: NumberArray = new Float32Array(this.hitCount)): NumberArray {\n const wasm = this.engine.wasm;\n const alignedPtr = (this._ptr + 48 * 2) / 4; /* Align F32 */\n for (let i = 0; i < this.hitCount; ++i) {\n const distancePtr = alignedPtr + i;\n out[i] = wasm.HEAPF32[distancePtr];\n }\n return out;\n }\n\n /**\n * Array of hit objects.\n *\n * @param out Destination array/vector, expected to have at least `this.hitCount` elements.\n * @returns The `out` parameter.\n */\n getObjects(out: Object3D[] = new Array(this.hitCount)): Object3D[] {\n const HEAPU16 = this.engine.wasm.HEAPU16;\n const alignedPtr = (this._ptr + (48 * 2 + 16)) >> 1;\n for (let i = 0; i < this.hitCount; ++i) {\n out[i] = this._scene.wrap(HEAPU16[alignedPtr + i]);\n }\n return out;\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._scene.engine;\n }\n\n /**\n * Equivalent to {@link RayHit.getLocations}.\n *\n * @note Prefer to use {@link RayHit.getLocations} for performance.\n */\n get locations(): Float32Array[] {\n return this.getLocations();\n }\n\n /**\n * Equivalent to {@link RayHit.getNormals}.\n *\n * @note Prefer to use {@link RayHit.getNormals} for performance.\n */\n get normals(): Float32Array[] {\n return this.getNormals();\n }\n\n /**\n * Equivalent to {@link RayHit.getDistances}.\n *\n * @note Prefer to use {@link RayHit.getDistances} for performance.\n */\n get distances(): Float32Array {\n return this.getDistances();\n }\n\n /**\n * Equivalent to {@link RayHit.getObjects}.\n *\n * @note Prefer to use {@link RayHit.getObjects} for performance.\n */\n get objects(): (Object3D | null)[] {\n /** @todo: Remove at 2.0.0, this is kept for backward compatibility. */\n const objects: (Object3D | null)[] = [null, null, null, null];\n return this.getObjects(objects as Object3D[]);\n }\n\n /** Number of hits (max 4) */\n get hitCount(): number {\n return Math.min(this.engine.wasm.HEAPU32[this._ptr / 4 + 30], 4);\n }\n}\n\nclass math {\n /** (Experimental!) Cubic Hermite spline interpolation for vector3 and quaternions.\n *\n * With `f == 0`, `out` will be `b`, if `f == 1`, `out` will be c.\n *\n * Whether a quaternion or vector3 interpolation is intended is determined by\n * length of `a`.\n *\n * @param out Array to write result to.\n * @param a First tangent/handle.\n * @param b First point or quaternion.\n * @param c Second point or quaternion.\n * @param d Second handle.\n * @param f Interpolation factor in [0; 1].\n * @returns The `out` parameter.\n *\n * @since 0.8.6\n */\n static cubicHermite(\n out: T,\n a: Readonly,\n b: Readonly,\n c: Readonly,\n d: Readonly,\n f: number,\n engine: WonderlandEngine = WL\n ) {\n const wasm = engine.wasm;\n wasm._tempMemFloat.subarray(0).set(a);\n wasm._tempMemFloat.subarray(4).set(b);\n wasm._tempMemFloat.subarray(8).set(c);\n wasm._tempMemFloat.subarray(12).set(d);\n\n const isQuat = a.length == 4;\n\n wasm._wl_math_cubicHermite(\n wasm._tempMem + 4 * 16,\n wasm._tempMem + 4 * 0,\n wasm._tempMem + 4 * 4,\n wasm._tempMem + 4 * 8,\n wasm._tempMem + 4 * 12,\n f,\n isQuat\n );\n out[0] = wasm._tempMemFloat[16];\n out[1] = wasm._tempMemFloat[17];\n out[2] = wasm._tempMemFloat[18];\n if (isQuat) out[3] = wasm._tempMemFloat[19];\n return out;\n }\n}\n\nexport {math};\n\n/**\n * Class for accessing internationalization (i18n) features.\n *\n * Allows {@link I18N.onLanguageChanged \"detecting language change\"},\n * {@link I18N.language \"setting the current language\"} or translating\n * {@link I18N.translate \"individual terms\"}.\n *\n * Internationalization works with terms,\n * a string type keyword that is linked to a different text for each language.\n *\n * Internally, string parameters for text and js components are\n * automatically swapped during language change, given they are linked to a term.\n * If manual text swapping is desired, {@link I18N.translate}\n * can be used to retrieve the current translation for any term.\n *\n * You can also use the {@link I18N.onLanguageChanged} to manually update text\n * when a language is changed to for example update a number in a string.\n *\n * @since 1.0.0\n */\nexport class I18N {\n /**\n * {@link Emitter} for language change events.\n *\n * First parameter to a listener is the old language index,\n * second parameter is the new language index.\n *\n * Usage from a within a component:\n *\n * ```js\n * this.engine.i18n.onLanguageChanged.add((oldLanguageIndex, newLanguageIndex) => {\n * const oldLanguage = this.engine.i18n.languageName(oldLanguageIndex);\n * const newLanguage = this.engine.i18n.languageName(newLanguageIndex);\n * console.log(\"Switched from\", oldLanguage, \"to\", newLanguage);\n * });\n * ```\n */\n readonly onLanguageChanged = new Emitter<[number, number]>();\n\n /** Wonderland Engine instance. @hidden */\n protected readonly _engine: WonderlandEngine;\n\n /** Previously set language index. @hidden */\n private _prevLanguageIndex: number = -1;\n\n /**\n * Constructor\n */\n constructor(engine: WonderlandEngine) {\n this._engine = engine;\n }\n\n /**\n * Set current language and apply translations to linked text parameters.\n *\n * @note This is equivalent to {@link I18N.setLanguage}.\n *\n * @param code Language code to switch to\n */\n set language(code: string | null) {\n this.setLanguage(code);\n }\n\n /** Get current language code. */\n get language(): string | null {\n const wasm = this._engine.wasm;\n const code = wasm._wl_i18n_currentLanguage();\n if (code === 0) return null;\n return wasm.UTF8ToString(code);\n }\n\n /**\n * Get the current language index.\n *\n * This method is more efficient than its equivalent:\n *\n * ```js\n * const index = i18n.languageIndex(i18n.language);\n * ```\n */\n get currentIndex(): number {\n return this._engine.wasm._wl_i18n_currentLanguageIndex();\n }\n\n /** Previous language index. */\n get previousIndex(): number {\n return this._prevLanguageIndex;\n }\n\n /**\n * Set current language and apply translations to linked text parameters.\n *\n * @param code The language code.\n * @returns A promise that resolves with the current index code when the\n * language is loaded.\n */\n async setLanguage(code: string | null): Promise {\n if (code == null) return Promise.resolve(this.currentIndex);\n const wasm = this._engine.wasm;\n this._prevLanguageIndex = this.currentIndex;\n wasm._wl_i18n_setLanguage(wasm.tempUTF8(code));\n\n const scene = this.engine.scene;\n\n const filename = wasm.UTF8ToString(wasm._wl_i18n_languageFile(this.currentIndex));\n const url = `${scene.baseURL}/locale/${filename}`;\n\n await scene._downloadDependency(url);\n\n this.onLanguageChanged.notify(this._prevLanguageIndex, this.currentIndex);\n\n return this.currentIndex;\n }\n\n /**\n * Get translated string for a term for the currently loaded language.\n *\n * @param term Term to translate\n */\n translate(term: string): string | null {\n const wasm = this._engine.wasm;\n const translation = wasm._wl_i18n_translate(wasm.tempUTF8(term));\n if (translation === 0) return null;\n return wasm.UTF8ToString(translation);\n }\n\n /**\n * Get the number of languages in the project.\n *\n */\n languageCount(): number {\n const wasm = this._engine.wasm;\n return wasm._wl_i18n_languageCount();\n }\n\n /**\n * Get a language code.\n *\n * @param index Index of the language to get the code from\n */\n languageIndex(code: string): number {\n const wasm = this._engine.wasm;\n return wasm._wl_i18n_languageIndex(wasm.tempUTF8(code));\n }\n\n /**\n * Get a language code.\n *\n * @param index Index of the language to get the code from\n */\n languageCode(index: number): string | null {\n const wasm = this._engine.wasm;\n const code = wasm._wl_i18n_languageCode(index);\n if (code === 0) return null;\n return wasm.UTF8ToString(code);\n }\n\n /**\n * Get a language name.\n *\n * @param index Index of the language to get the name from\n */\n languageName(index: number): string | null {\n const wasm = this._engine.wasm;\n const name = wasm._wl_i18n_languageName(index);\n if (name === 0) return null;\n return wasm.UTF8ToString(name);\n }\n\n /** Hosting engine instance. */\n get engine() {\n return this._engine;\n }\n}\n\n/**\n * Environment lighting properties\n *\n * @since 1.2.3\n */\nexport class Environment {\n /** Scene instance. @hidden */\n private readonly _scene: Scene;\n\n /** Constructor */\n constructor(scene: Scene) {\n this._scene = scene;\n }\n\n /**\n * Get intensity of environment lighting.\n *\n * Incoming environment lighting is multiplied by this factor.\n */\n get intensity() {\n return this._scene.engine.wasm._wl_scene_environment_get_intensity(\n this._scene._index\n );\n }\n\n /**\n * Set intensity of environment lighting.\n *\n * @param intensity New intensity.\n */\n set intensity(intensity: number) {\n this._scene.engine.wasm._wl_scene_environment_set_intensity(\n this._scene._index,\n intensity\n );\n }\n\n /** @overload */\n getTint(): Float32Array;\n /**\n * Get tint for environment lighting.\n *\n * Incoming environment lighting color channels are multiplied by these\n * values.\n *\n * @param out Preallocated array to write into, to avoid garbage,\n * otherwise will allocate a new Float32Array.\n * @returns Tint values - red, green, blue.\n */\n getTint(out: T): T;\n /** @overload */\n getTint(\n out: T | Float32Array = new Float32Array(3)\n ): T | Float32Array {\n const wasm = this._scene.engine.wasm;\n wasm._wl_scene_environment_get_tint(this._scene._index, wasm._tempMem);\n out[0] = wasm._tempMemFloat[0];\n out[1] = wasm._tempMemFloat[1];\n out[2] = wasm._tempMemFloat[2];\n return out as T;\n }\n\n /**\n * Equivalent to {@link getTint}.\n *\n * @note Prefer to use {@link getTint} for performance.\n */\n get tint(): Float32Array {\n return this.getTint();\n }\n\n /**\n * Set tint for environment lighting.\n *\n * @param v New tint value. Expects a 3 component array.\n */\n setTint(v: Readonly) {\n this._scene.engine.wasm._wl_scene_environment_set_tint(\n this._scene._index,\n v[0],\n v[1],\n v[2]\n );\n }\n\n /** Equivalent to {@link setTint}. */\n set tint(v: Readonly) {\n this.setTint(v);\n }\n\n /** @overload */\n getCoefficients(): Float32Array;\n /**\n * Get spherical harmonics coefficients for indirect lighting.\n *\n * These are 9 spherical harmonics coefficients for indirect diffuse\n * lighting.\n *\n * @param out Preallocated array to write into, to avoid garbage,\n * otherwise will allocate a new Float32Array.\n * @returns Spherical harmonics coefficients. Always 27 elements, every\n * consecutive 3 values representing the red, green, blue components of\n * a single coefficient. Unused/empty coefficients at the end can be 0.\n */\n getCoefficients(out: T): T;\n /** @overload */\n getCoefficients(\n out: T | Float32Array = new Float32Array(3 * 9)\n ): T | Float32Array {\n const wasm = this._scene.engine.wasm;\n wasm.requireTempMem(3 * 9 * 4);\n wasm._wl_scene_environment_get_coefficients(this._scene._index, wasm._tempMem);\n for (let i = 0; i < 3 * 9; ++i) {\n out[i] = wasm._tempMemFloat[i];\n }\n return out as T;\n }\n\n /**\n * Equivalent to {@link getCoefficients}.\n *\n * @note Prefer to use {@link getCoefficients} for performance.\n */\n get coefficients(): Float32Array {\n return this.getCoefficients();\n }\n\n /**\n * Set spherical harmonics coefficients for indirect lighting.\n *\n * @note The scene must have been packaged with environment lighting on for\n * this to take effect.\n *\n * @example\n *\n * Coefficients from [WebXR Lighting Estimation](https://www.w3.org/TR/webxr-lighting-estimation-1)\n * can be passed as follows:\n *\n * ```js\n * const probe = await engine.xr.session.requestLightProbe();\n * const estimate = engine.xr.frame.getLightEstimate(probe);\n * scene.environment.coefficients = estimate.sphericalHarmonicsCoefficients;\n * ```\n *\n * @param v A set of spherical harmonics coefficients, every 3 elements\n * constituting the red/green/blue components of a single coefficient.\n * Should be 0, 3, 12 or 27 **array elements**. Passing an empty array\n * disables indirect lighting.\n */\n setCoefficients(v: Readonly) {\n let count = v.length / 3;\n if (count > 9) count = 9;\n else if (count > 4 && count < 9) count = 4;\n else if (count > 1 && count < 4) count = 1;\n const wasm = this._scene.engine.wasm;\n wasm._tempMemFloat.set(v);\n wasm._wl_scene_environment_set_coefficients(\n this._scene._index,\n wasm._tempMem,\n count\n );\n }\n\n /** Equivalent to {@link setCoefficients}. */\n set coefficients(v: Readonly) {\n this.setCoefficients(v);\n }\n}\n", "/**\n * This file is automatically generated.\n *\n * **Do not** modify this file directly, but instead update\n * the 'write-version.mjs' script.\n */\n\n/**\n * Version type following a subset of the Semantic Versioning specification.\n */\nexport type Version = {major: number, minor: number, patch: number, rc: number};\n\n/** Version of this API. */\nexport const APIVersion: Version = {\n major: 1,\n minor: 4,\n patch: 2,\n rc: 0\n};\n", "import {WonderlandEngine} from '../engine.js';\nimport {NumberArray} from '../types.js';\nimport {capitalizeFirstUTF8} from '../utils/misc.js';\nimport {Texture} from '../wonderland.js';\n\nimport {Resource, ResourceManager} from './resource.js';\n\n/**\n * Material parameter type.\n */\nexport enum MaterialParamType {\n /** Unsigned integer parameter type. */\n UnsignedInt = 0,\n /** Integer parameter type. */\n Int = 1,\n /** 16-bit float parameter type. */\n HalfFloat = 2,\n /** Float parameter type. */\n Float = 3,\n /** Sampler resource parameter type, i.e., a {@link Texture}. */\n Sampler = 4,\n /**\n * Font resource parameter type.\n *\n * **Note**: Changing font isn't exposed yet and will raise an error.\n */\n Font = 5,\n}\n\n/**\n * Constructor parameters object for a {@link Material} instance.\n *\n * @deprecated Use {@link MaterialManager#getTemplate} instead:\n *\n * ```js\n * const PhongMaterial = engine.materials.getTemplate('Phong Opaque');\n * const material = new PhongMaterial();\n * material.setDiffuseColor([1, 0, 0]);\n * ```\n */\nexport interface MaterialParameters {\n /** The name of the pipeline. */\n pipeline: string;\n}\n\n/**\n * Material constructor.\n *\n * Material classes are automatically generated by the runtime based on the\n * loaded scene shaders.\n */\nexport interface MaterialConstructor {\n /**\n * Set of the dynamic parameters that exist on the definition.\n *\n * For instance, the set would contain elements such as `ambientColor`, `diffuseColor`\n * for a Phong material.\n */\n readonly Parameters: Set;\n /**\n * Create a new Material.\n *\n * The material is created from the pipeline associated to the material class.\n *\n * @note Creating material is expensive. Please use {@link Material#clone} to clone a material.\n * @note Do not use this constructor directly with an index, this is reserved for internal purposes.\n */\n new (index?: number): T;\n}\n\n/**\n * Wrapper around a native material.\n *\n * For more information about how to create materials, have a look at the\n * {@link MaterialManager} class.\n *\n * #### Properties\n *\n * The material properties are automatically converted into getters/setters:\n *\n * ```js\n * const material = new PhongMaterial();\n *\n * // Set the `diffuseColor` property\n * material.setDiffuseColor([1.0, 0.0, 0.0, 1.0]);\n * console.log(material.getDiffuseColor());\n * ```\n *\n * Getters for non-scalar types have an optional argument to skip an array\n * allocation:\n *\n * ```js\n * const material = new PhongMaterial();\n * const diffuse = [0, 0, 0, 0];\n * material.getDiffuseColor(diffuse);\n * console.log(diffuse) // Prints '[1.0, 1.0, 1.0, 1.0]'\n * ```\n *\n * @note Materials are **per-engine**, they can thus be shared by multiple scenes.\n *\n * #### TypeScript\n *\n * The Wonderland Editor can automatically generate material definitions (.d.ts)\n * from the project pipelines.\n *\n * To enable the generation, go to the `Project Settings > JavaScript` panel and\n * set `materialDefinitions` to a path, e.g., `materials.d.ts`.\n *\n * It's then possible to cast the material type using:\n *\n * ```ts\n * // Note the `.js` instead of `.d.ts`\n * import {PhongOpaque} from './materials.js';\n *\n * const mesh = object.getComponent('mesh');\n * const material = mesh.material as PhongOpaque;\n * material.setDiffuseColor([1, 0, 0, 1]); // Set a red diffuse\n * ```\n *\n * @since 1.2.0\n */\nexport class Material extends Resource {\n /**\n * @deprecated Use {@link MaterialManager#getTemplate} via {@link WonderlandEngine.materials}\n * to create a new material with a given pipeline:\n *\n * ```js\n * const PhongMaterial = engine.materials.getTemplate('Phong Opaque');\n * const material = new PhongMaterial();\n * material.setDiffuseColor([1, 0, 0]);\n * ```\n */\n constructor(engine: WonderlandEngine, params: number | MaterialParameters) {\n if (typeof params !== 'number') {\n if (!params?.pipeline) throw new Error(\"Missing parameter 'pipeline'\");\n const template = engine.materials.getTemplate(params.pipeline);\n const material = new template();\n super(engine, material._index);\n return material;\n }\n super(engine, params);\n }\n\n /**\n * Check whether a parameter exists on this material.\n *\n * @param name The name to check.\n * @returns `true` if the parameter with name `name` exists on this material,\n * `false` otherwise.\n */\n hasParameter(name: string) {\n const parameters = (this.constructor as MaterialConstructor).Parameters;\n return parameters && parameters.has(name);\n }\n\n /** @deprecated Use {@link pipeline} instead. */\n get shader(): string {\n return this.pipeline;\n }\n\n /** Name of the pipeline used by this material. */\n get pipeline(): string {\n const wasm = this.engine.wasm;\n return wasm.UTF8ToString(wasm._wl_material_get_pipeline(this._id));\n }\n\n /**\n * Create a copy of the underlying native material.\n *\n * @returns Material clone.\n */\n clone(): Material | null {\n const index = this.engine.wasm._wl_material_clone(this._id);\n return this.engine.materials.wrap(index);\n }\n\n toString() {\n if (this.isDestroyed) {\n return 'Material(destroyed)';\n }\n return `Material('${this.pipeline}', ${this._index})`;\n }\n\n /**\n * Wrap a native material index.\n *\n * @param engine Engine instance.\n * @param index The index.\n * @returns Material instance or `null` if index <= 0.\n *\n * @deprecated Use the {@link WonderlandEngine.materials} instead.\n */\n static wrap(engine: WonderlandEngine, index: number): Material | null {\n return engine.materials.wrap(index);\n }\n}\n\n/**\n * Manage materials.\n *\n * #### Creation\n *\n * To create a material, first retrieve the class associated to\n * the pipeline using {@link MaterialManager.getTemplate}:\n *\n * ```js\n * const PhongMaterial = engine.materials.getTemplate('Phong Opaque');\n * ```\n *\n * Creating a material is then done using the constructor:\n *\n * ```js\n * const material = new PhongMaterial();\n * material.setDiffuseColor([1.0, 0.0, 0.0, 1.0]);\n * ```\n */\nexport class MaterialManager extends ResourceManager {\n /** Material classes. @hidden. */\n private readonly _materialTemplates: MaterialConstructor[] = [];\n\n /** @hidden */\n constructor(engine: WonderlandEngine) {\n super(engine, Material);\n this._cacheDefinitions();\n }\n\n /** @override */\n wrap(index: number) {\n if (index <= 0) return null;\n\n const cached = this._cache[index];\n if (cached) return cached;\n\n const wasm = this.engine.wasm;\n const definition = wasm._wl_material_get_definition(index);\n\n const Template = this._materialTemplates[definition];\n const material = new Template(index);\n return this._wrapInstance(material);\n }\n\n /**\n * Get the material class with the given pipeline name.\n *\n * #### Usage\n *\n * ```js\n * const PhongMaterial = engine.materials.getTemplate('Phong Opaque');\n * const material = new PhongMaterial();\n * material.setDiffuseColor([1.0, 0.0, 0.0, 1.0]);\n * ```\n *\n * #### TypeScript\n *\n * This method provide a simple way to cast the constructor returned by `getTemplate`:\n *\n * ```ts\n * interface Phong {\n * getAmbientColor(out?: Float32Array): Float32Array;\n * setAmbientColor(value: NumberArray): void;\n * }\n * const PhongMaterial = engine.materials.getTemplate('Phong Opaque');\n * const mat = new PhongMaterial(); // `mat` is of type `Phong`\n * ```\n *\n * However, this means manually writing types for each pipeline.\n *\n * Fortunately, The Wonderland Editor can automatically generate material definitions (.d.ts)\n * from the project pipelines.\n *\n * To enable the generation, go to the `Project Settings > JavaScript` panel and\n * set `materialDefinitions` to a path, e.g., `materials.d.ts`.\n *\n * Material constructors will then be typed automatically when using a string literal pipeline name:\n *\n * ```ts\n * // Note the `.js` instead of `.d.ts`\n * import {PhongOpaque} from './materials.js';\n *\n * const PhongMaterial = engine.materials.getTemplate('Phong Opaque');\n * const mat = new PhongMaterial(); // `mat` is of type `PhongOpaque`\n * ```\n *\n * @param pipeline The pipeline name to search for.\n * @returns The material class.\n *\n * @throws `Error` if the material class doesn't exist.\n */\n getTemplate(pipeline: string): MaterialConstructor {\n const wasm = this.engine.wasm;\n const index = wasm._wl_get_material_definition_index(wasm.tempUTF8(pipeline));\n if (!index) {\n throw new Error(`Pipeline '${pipeline}' doesn't exist in the scene`);\n }\n return this._materialTemplates[index] as MaterialConstructor;\n }\n\n /**\n * Wrap a material instance.\n *\n * @todo: Remove at 2.0.0.\n *\n * @note Wrapping should only be called once per instance.\n *\n * @param instance The material instance.\n * @returns The new material, wrapped in a proxy.\n */\n _wrapInstance(instance: Material): Material {\n this._cache[instance.index] = instance;\n if (!this.engine.legacyMaterialSupport) return instance;\n\n /** @todo: Remove at 2.0.0. This is kept for backward compatibility. */\n const proxy = new Proxy(instance, {\n get(target: Material, prop: string) {\n if (!target.hasParameter(prop)) {\n return (target as Record)[prop];\n }\n /** This is slow, but users should migrate to `getParam` */\n const name = `get${capitalizeFirstUTF8(prop)}`;\n return (target as Record)[name]();\n },\n set(target, prop: string, value) {\n if (!target.hasParameter(prop)) {\n (target as Record)[prop] = value;\n return true;\n }\n /** This is slow, but users should migrate to `getParam` */\n const name = `set${capitalizeFirstUTF8(prop)}`;\n (target as Record)[name](value);\n return true;\n },\n });\n this._cache[instance.index] = proxy;\n return proxy;\n }\n\n /**\n * Cache all pipeline definitions.\n *\n * @hidden\n */\n private _cacheDefinitions() {\n const wasm = this.engine.wasm;\n const count = wasm._wl_get_material_definition_count();\n for (let i = 1; i < count; ++i) {\n this._materialTemplates[i] = this._createMaterialTemplate(i);\n }\n }\n\n /**\n * Create a material class from a definition index.\n *\n * @param wasm The WASM instance.\n * @param definitionIndex The definition index to wrap.\n * @returns The material class.\n */\n private _createMaterialTemplate(definitionIndex: number) {\n const engine = this.engine;\n const template = class CustomMaterial extends Material {\n static Parameters: Set = new Set();\n\n constructor(index?: number) {\n index = index ?? engine.wasm._wl_material_create(definitionIndex);\n super(engine, index);\n /** @todo(2.0.0): Unify with Material. We need to wrap in both since materials\n * can be created via the parent class, or via child classes.\n * In addition, wrapping returns a proxy, we want to support Proxy\n * as well as Proxy. */\n return engine.materials._wrapInstance(this);\n }\n };\n\n const wasm = this.engine.wasm;\n const nbParams = wasm._wl_material_definition_get_param_count(definitionIndex);\n for (let index = 0; index < nbParams; ++index) {\n const name = wasm.UTF8ToString(\n wasm._wl_material_definition_get_param_name(definitionIndex, index)\n );\n template.Parameters.add(name);\n\n const t = wasm._wl_material_definition_get_param_type(definitionIndex, index);\n const type = t & 0xff;\n const componentCount = (t >> 8) & 0xff;\n /* metaType could also be extracted using: (t >> 16) & 0xff */\n\n const capitalized = capitalizeFirstUTF8(name);\n const getterId = `get${capitalized}`;\n const setterId = `set${capitalized}`;\n\n const templateProto = template.prototype as Record;\n switch (type) {\n case MaterialParamType.UnsignedInt:\n templateProto[getterId] = uint32Getter(index, componentCount);\n /* For now, it's only possible to set scalar uint values */\n templateProto[setterId] = uint32Setter(index);\n break;\n case MaterialParamType.Int:\n templateProto[getterId] = int32Getter(index, componentCount);\n /* For now, it's only possible to set scalar int values. Integer values setter\n * is purposely shared with uint. */\n templateProto[setterId] = uint32Setter(index);\n break;\n case MaterialParamType.HalfFloat:\n case MaterialParamType.Float:\n templateProto[getterId] = float32Getter(index, componentCount);\n templateProto[setterId] = float32Setter(index, componentCount);\n break;\n case MaterialParamType.Sampler:\n templateProto[getterId] = samplerGetter(index);\n templateProto[setterId] = samplerSetter(index);\n break;\n case MaterialParamType.Font:\n templateProto[getterId] = fontGetter(index);\n /* For now, setting fonts is not supported */\n break;\n }\n }\n\n return template as MaterialConstructor;\n }\n}\n\n/** @todo: With this implementation, `_wl_material_get_param_value`\n * do not need to check for the parameter type anymore, we could use\n * explicit calls. */\n\nfunction uint32Getter(index: number, count: number) {\n if (count === 1) {\n return function (this: Material) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n return wasm._tempMemUint32[0];\n };\n }\n return function (this: Material, out: NumberArray = new Uint32Array(count)) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n for (let i = 0; i < out.length; ++i) {\n out[i] = wasm._tempMemUint32[i];\n }\n return out;\n };\n}\n\nfunction uint32Setter(index: number) {\n return function (this: Material, value: number) {\n const wasm = this.engine.wasm;\n wasm._wl_material_set_param_value_uint(this._id, index, value);\n };\n}\n\nfunction int32Getter(index: number, count: number) {\n if (count === 1) {\n return function (this: Material) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n return wasm._tempMemInt[0];\n };\n }\n return function (this: Material, out: NumberArray = new Int32Array(count)) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n for (let i = 0; i < out.length; ++i) {\n out[i] = wasm._tempMemInt[i];\n }\n return out;\n };\n}\n\nfunction float32Getter(index: number, count: number) {\n if (count === 1) {\n return function (this: Material) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n return wasm._tempMemFloat[0];\n };\n }\n return function (this: Material, out: NumberArray = new Float32Array(count)) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n for (let i = 0; i < out.length; ++i) {\n out[i] = wasm._tempMemFloat[i];\n }\n return out;\n };\n}\n\nfunction float32Setter(index: number, count: number) {\n return function (this: Material, value: number | NumberArray) {\n const wasm = this.engine.wasm;\n\n let actualCount = 1;\n if (typeof value === 'number') {\n wasm._tempMemFloat[0] = value;\n } else {\n actualCount = value.length;\n for (let i = 0; i < actualCount; ++i) wasm._tempMemFloat[i] = value[i];\n }\n\n if (actualCount < count) {\n throw new Error(`Expected an array ${count} values, got ${actualCount}`);\n }\n\n wasm._wl_material_set_param_value_float(this._id, index, wasm._tempMem, count);\n };\n}\n\nfunction samplerGetter(index: number) {\n return function (this: Material) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n return this.engine.textures.wrap(wasm._tempMemInt[0]);\n };\n}\n\nfunction samplerSetter(index: number) {\n return function (this: Material, value: Texture | null | undefined) {\n const wasm = this.engine.wasm;\n wasm._wl_material_set_param_value_uint(this._id, index, value?._id ?? 0);\n };\n}\n\nfunction fontGetter(index: number) {\n return function (this: Material) {\n const wasm = this.engine.wasm;\n wasm._wl_material_get_param_value(this._id, index, wasm._tempMem);\n return this.engine.fonts.wrap(wasm._tempMemInt[0]);\n };\n}\n", "import {WonderlandEngine} from '../engine.js';\nimport {Mesh, MeshIndexType, MeshParameters, MeshSkinningType} from '../wonderland.js';\n\nimport {ResourceManager} from './resource.js';\n\n/**\n * Manage meshes.\n *\n * #### Creation\n *\n * Creating a mesh is done using {@link MeshManager.create}:\n *\n * ```js\n * const mesh = engine.meshes.create({vertexCount: 3, indexData: [0, 1, 2]});\n * ```\n *\n * @since 1.2.0\n */\nexport class MeshManager extends ResourceManager {\n constructor(engine: WonderlandEngine) {\n super(engine, Mesh);\n }\n\n /**\n * Create a new mesh.\n *\n * @param params Vertex and index data. For more information, have a look\n * at the {@link MeshParameters} object.\n */\n create(params: Partial) {\n if (!params.vertexCount) throw new Error(\"Missing parameter 'vertexCount'\");\n\n const wasm = this.engine.wasm;\n\n let indexData = 0;\n let indexType = 0;\n let indexDataSize = 0;\n if (params.indexData) {\n indexType = params.indexType || MeshIndexType.UnsignedShort;\n indexDataSize = params.indexData.length * indexType;\n indexData = wasm._malloc(indexDataSize);\n /* Copy the index data into wasm memory */\n switch (indexType) {\n case MeshIndexType.UnsignedByte:\n wasm.HEAPU8.set(params.indexData, indexData);\n break;\n case MeshIndexType.UnsignedShort:\n wasm.HEAPU16.set(params.indexData, indexData >> 1);\n break;\n case MeshIndexType.UnsignedInt:\n wasm.HEAPU32.set(params.indexData, indexData >> 2);\n break;\n }\n }\n\n const {skinningType = MeshSkinningType.None} = params;\n\n const index = wasm._wl_mesh_create(\n indexData,\n indexDataSize,\n indexType,\n params.vertexCount,\n skinningType\n );\n const instance = new Mesh(this._host, index);\n this._cache[instance.index] = instance;\n return instance;\n }\n}\n", "import {WonderlandEngine} from '../engine.js';\nimport {ImageLike} from '../types.js';\nimport {Texture} from '../wonderland.js';\n\nimport {ResourceManager} from './resource.js';\n\nexport function needsFlipY(image: ImageLike) {\n /* WebGL can't flip ImageBitmap on upload.\n * For consistency with WebGPU, disable ImageBitmap flipping entirely and let the user use:\n * https://developer.mozilla.org/en-US/docs/Web/API/Window/createImageBitmap#options */\n return image instanceof ImageBitmap ? 0 : 1;\n}\n\n/**\n * Manage textures.\n *\n * #### Creation\n *\n * Creating a texture is done using {@link TextureManager.load}:\n *\n * ```js\n * const texture = await engine.textures.load('my-image.png');\n * ```\n *\n * Alternatively, textures can be created directly via a loaded image using\n * {@link TextureManager.create}.\n *\n * @since 1.2.0\n */\nexport class TextureManager extends ResourceManager {\n constructor(engine: WonderlandEngine) {\n super(engine, Texture);\n }\n\n /**\n * Create a new texture from an image or video.\n *\n * #### Usage\n *\n * ```js\n * const img = new Image();\n * img.load = function(img) {\n * const texture = engine.textures.create(img);\n * };\n * img.src = 'my-image.png';\n * ```\n *\n * @note The media must already be loaded. To automatically\n * load the media and create a texture, use {@link TextureManager.load} instead.\n *\n * @param image Media element to create the texture from.\n * @ret\\urns The new texture with the media content.\n */\n create(image: ImageLike): Texture {\n const wasm = this.engine.wasm;\n\n const jsImageIndex = wasm._images.length;\n wasm._images.push(image);\n\n if (image instanceof HTMLImageElement && !image.complete) {\n throw new Error('image must be ready to create a texture');\n }\n\n const width = (image as HTMLVideoElement).videoWidth ?? image.width;\n const height = (image as HTMLVideoElement).videoHeight ?? image.height;\n\n const imageIndex = wasm._wl_image_create(jsImageIndex);\n wasm._wl_image_markReady(imageIndex, width, height, needsFlipY(image));\n\n /* Required because the image isn't a resource by itself, but will eventually be one. */\n const index = wasm._wl_texture_create(imageIndex);\n\n const instance = new Texture(this.engine, index);\n this._cache[instance.index] = instance;\n\n return instance;\n }\n\n /**\n * Load an image from URL as {@link Texture}.\n *\n * #### Usage\n *\n * ```js\n * const texture = await engine.textures.load('my-image.png');\n * ```\n *\n * @param filename URL to load from.\n * @param crossOrigin Cross origin flag for the image object.\n * @returns Loaded texture.\n */\n load(filename: string, crossOrigin?: string): Promise {\n let image = new Image();\n image.crossOrigin = crossOrigin ?? image.crossOrigin;\n image.src = filename;\n return new Promise((resolve, reject) => {\n image.onload = () => {\n resolve(this.create(image));\n };\n image.onerror = function () {\n reject('Failed to load image. Not found or no read access');\n };\n });\n }\n}\n", "import {WASM} from './wasm.js';\nimport {Scene} from './scene.js';\n\nimport {Emitter, RetainEmitter} from './utils/event.js';\nimport {WonderlandEngine} from './engine.js';\nimport {InputType, ViewComponent} from './wonderland.js';\n\ntype WebXRFramebuffer = WebGLFramebuffer & {name: number};\ntype WebXRTexture = WebGLTexture & {name: number};\n\n/**\n * Options for {@link LoadRuntimeOptions.xrOfferSession}\n *\n * @since 1.1.5\n */\nexport interface XROfferSessionOptions {\n /**\n * Mode to offer XR session with. If set to `'auto'`, offers a session\n * with the first supported mode in the following order:\n * - VR\n * - AR\n * - inline\n */\n mode: XRSessionMode | 'auto';\n /** Required features for the offered XR session */\n features: string[];\n /** Optional features for the offered XR session */\n optionalFeatures: string[];\n}\n\n/** Properties of a WebXR session */\nexport class XRSessionState {\n /** Wonderland WASM bridge. @hidden */\n readonly #webxr: WebXR;\n\n /** Current WebXR session mode */\n sessionMode: XRSessionMode;\n\n /** Current WebXR session */\n session: XRSession;\n\n /** Current WebXR frame */\n frame: XRFrame | null = null;\n\n /**\n * Constructor.\n *\n * @param wasm Wasm bridge instance\n * @param mode Current XR session mode\n *\n * @hidden\n */\n constructor(webxr: WebXR, mode: XRSessionMode, session: XRSession) {\n this.#webxr = webxr;\n this.sessionMode = mode;\n this.session = session;\n }\n\n /** @overload */\n referenceSpaceForType(type: 'viewer'): XRReferenceSpace;\n /**\n * Get a WebXR reference space of a given reference space type.\n *\n * @param type Type of reference space to get\n * @returns Reference space, or `null` if there's no reference space\n * of the requested type available\n */\n referenceSpaceForType(type: XRReferenceSpaceType): XRReferenceSpace | null;\n referenceSpaceForType(type: XRReferenceSpaceType): XRReferenceSpace | null {\n return this.#webxr.referenceSpaceForType(type);\n }\n\n /** Set current reference space type used for retrieving eye, head, hand and joint poses */\n set currentReferenceSpace(refSpace: XRReferenceSpace) {\n this.#webxr.currentReferenceSpace = refSpace;\n }\n\n /** Current reference space type used for retrieving eye, head, hand and joint poses */\n get currentReferenceSpace(): XRReferenceSpace {\n return this.#webxr.currentReferenceSpace;\n }\n\n /** Current WebXR reference space type or `null` if not a default reference space */\n get currentReferenceSpaceType(): XRReferenceSpaceType {\n return this.#webxr.currentReferenceSpaceType;\n }\n\n /** Current WebXR base layer */\n get baseLayer(): XRProjectionLayer | XRWebGLLayer {\n return this.#webxr.baseLayer!;\n }\n\n /** Current WebXR framebuffer */\n get framebuffers(): WebGLFramebuffer[] {\n return this.#webxr.framebuffers;\n }\n}\n\n/**\n * WebXR API (internal)\n *\n * @hidden\n */\nexport class WebXR {\n /** Wonderland WASM bridge. @hidden */\n readonly #wasm: WASM;\n\n readonly engine: WonderlandEngine;\n\n sessionState: XRSessionState | null = null;\n\n colorFormat: number = 0x8058 /* RGBA8 */;\n depthFormat: number = 0x88f0 /* DEPTH24_STENCIL8 */;\n textureType: XRTextureType = 'texture';\n\n /**\n * {@link Emitter} for WebXR session end events.\n */\n readonly onSessionEnd = new Emitter();\n\n /**\n * {@link RetainEmitter} for WebXR session start events.\n */\n readonly onSessionStart: Emitter<[XRSession, XRSessionMode]> = new RetainEmitter<\n [XRSession, XRSessionMode]\n >();\n\n /**\n * {@link Emitter} notified before the first update and rendering on the first frame of a new XR session.\n */\n readonly onSessionFirstFrame: Emitter<[XRSession, XRSessionMode]> = new Emitter<\n [XRSession, XRSessionMode]\n >();\n\n /** Whether AR is supported by the browser. */\n arSupported: boolean = false;\n\n /** Whether VR is supported by the browser. */\n vrSupported: boolean = false;\n\n /**\n * Current WebXR base layer or `null` if no session active.\n */\n baseLayer: XRProjectionLayer | XRWebGLLayer | null = null;\n\n /** WebXR framebuffer scale factor. */\n framebufferScaleFactor: number = 1.0;\n\n private _webglBinding: XRWebGLBinding | null = null;\n\n private _initXR: boolean = false;\n private _inXR: boolean = false;\n\n /** WebXR reference spaces. */\n private _refSpaces: Record = {\n 'viewer': undefined,\n 'local': undefined,\n 'local-floor': undefined,\n 'bounded-floor': undefined,\n 'unbounded': undefined,\n };\n\n /** Current WebXR reference space. */\n private _refSpace: XRReferenceSpace | null = null;\n\n /** Current WebXR reference space type. */\n private _refSpaceType: XRReferenceSpaceType | null = null;\n\n /**\n * Emscripten WebXR framebuffer(s).\n *\n * @note fbo will not get overwritten if we are rendering to the\n * default framebuffer, e.g., when using WebXR emulator.\n */\n private _fbo: number | number[] = 0;\n\n private _allowLayers: boolean = false;\n private _requestAnimationFrameId: any = null;\n\n /**\n * Initial WebXR reference space type. See {@link init} for\n * more information.\n */\n private _initialReferenceSpaceType: XRReferenceSpaceType | null = null;\n\n private _tempPosition: Float32Array = new Float32Array(3);\n private _tempRotation: Float32Array = new Float32Array(4);\n\n private _tempPose(transform: XRRigidTransform) {\n const p = transform.position;\n this._tempPosition[0] = p.x;\n this._tempPosition[1] = p.y;\n this._tempPosition[2] = p.z;\n const o = transform.orientation;\n this._tempRotation[0] = o.x;\n this._tempRotation[1] = o.y;\n this._tempRotation[2] = o.z;\n this._tempRotation[3] = o.w;\n }\n\n /**\n * Constructor.\n *\n * @param engine Engine\n *\n * @hidden\n */\n constructor(engine: WonderlandEngine) {\n this.engine = engine;\n this.#wasm = engine.wasm;\n }\n\n /**\n * Check whether XR is supported and store the result in {@link arSupported} and {@link vrSupported}\n */\n checkXRSupport(): Promise {\n if (typeof navigator === 'undefined') {\n return Promise.resolve(false);\n }\n if (!navigator.xr) {\n const isLocalhost =\n location.hostname === 'localhost' || location.hostname === '127.0.0.1';\n const missingHTTPS = location.protocol !== 'https:' && !isLocalhost;\n console.warn(\n missingHTTPS\n ? 'WebXR is only supported with HTTPS or on localhost!'\n : 'WebXR unsupported in this browser.'\n );\n return Promise.resolve(false);\n }\n /* Those can throw if the application is embedded in an iframe without\n * xr-spatial-tracking permission. navigator.xr will be available, but\n * any access of it will throw. */\n const vrPromise = navigator.xr\n .isSessionSupported('immersive-vr')\n .then((supported) => (this.vrSupported = supported))\n .catch(() => (this.vrSupported = false));\n const arPromise = navigator.xr\n .isSessionSupported('immersive-ar')\n .then((supported) => (this.arSupported = supported))\n .catch(() => (this.arSupported = false));\n\n return Promise.all([vrPromise, arPromise]).then(\n () => this.vrSupported || this.arSupported\n );\n }\n\n /**\n * Initialize WebXR.\n *\n * @hidden\n */\n async init(\n framebufferScaleFactor: number,\n offerSessionOptions: XROfferSessionOptions | null\n ) {\n const supported = await this.checkXRSupport();\n if (!supported) return;\n\n this.framebufferScaleFactor = framebufferScaleFactor;\n\n /* Force the reference space to 'local'/'viewer' for the loading screen\n * to make sure the head input is at the origin. Doing it this way to\n * avoid adding JS components to the loading screen. */\n const onXRStart = () => {\n this._initialReferenceSpaceType = this.currentReferenceSpaceType;\n const newSpace = this._refSpaces['local'] ?? this._refSpaces['viewer']!;\n this.currentReferenceSpace = newSpace;\n };\n\n /* Not once() because the user can enter and exit XR several times\n * during a long loading screen */\n this.onSessionStart.add(onXRStart);\n\n /* This is called before all init()/start()/onActivate() so we avoid\n * overwriting a user-set reference space */\n this.engine.onLoadingScreenEnd.once(() => {\n this.onSessionStart.remove(onXRStart);\n\n if (!this._initialReferenceSpaceType) return;\n\n this.currentReferenceSpace =\n this._refSpaces[this._initialReferenceSpaceType] ??\n this._refSpaces['viewer']!;\n this._initialReferenceSpaceType = null;\n });\n\n if (offerSessionOptions !== null) {\n let mode = offerSessionOptions.mode;\n if (mode == 'auto') {\n if (this.vrSupported) {\n mode = 'immersive-vr';\n } else if (this.arSupported) {\n mode = 'immersive-ar';\n } else {\n mode = 'inline';\n }\n }\n\n const offerSession = () => {\n this.offerSession(\n mode as XRSessionMode,\n offerSessionOptions.features,\n offerSessionOptions.optionalFeatures\n )\n .then(\n /* When the session ends, offer to start a new one again */\n (s) => s.addEventListener('end', offerSession)\n )\n /* The browser may not suppoer offer session, or a previous request\n * was replaced by a new one. Not crucial, but inform the user. */\n .catch(console.warn);\n };\n\n offerSession();\n }\n }\n\n /** Get a WebXR reference space of a given reference space type. */\n referenceSpaceForType(type: XRReferenceSpaceType): XRReferenceSpace | null {\n return this._refSpaces[type] ?? null;\n }\n\n /** Set current reference space type used for retrieving eye, head, hand and joint poses */\n set currentReferenceSpace(refSpace: XRReferenceSpace) {\n this._refSpace! = refSpace;\n\n this._refSpaceType = null;\n for (const type of Object.keys(this._refSpaces) as XRReferenceSpaceType[]) {\n if (this._refSpaces[type] === refSpace) {\n /* Keep track of reference space type */\n this._refSpaceType = type;\n }\n }\n }\n\n /** Current reference space type used for retrieving eye, head, hand and joint poses */\n get currentReferenceSpace(): XRReferenceSpace {\n return this._refSpace!;\n }\n\n /** Current WebXR reference space type or `null` if not a default reference space */\n get currentReferenceSpaceType(): XRReferenceSpaceType {\n return this._refSpaceType!;\n }\n\n /** Current WebXR framebuffer */\n get framebuffers(): WebGLFramebuffer[] {\n if (!Array.isArray(this._fbo)) {\n return [this.#wasm.GL.framebuffers[this._fbo]];\n }\n return this._fbo.map((id) => this.#wasm.GL.framebuffers[id]);\n }\n\n /**\n * Set main/left/right eye view active according to XR mode.\n *\n * If @param inXR is `true`, disables the main view and enables the left and\n * right view. Otherwise, enables the main view and disables the left and\n * right view.\n *\n * @param inXR Whether an XR session is currently active\n */\n updateViewState(inXR: boolean | undefined) {\n const scene: Scene = this.engine.scene;\n\n inXR = inXR ?? this._inXR;\n\n if (scene.mainView) {\n scene.mainView.active = !inXR;\n }\n\n if (scene.leftEyeView && scene.rightEyeView) {\n /** We always have either no left/right eye views or both. Always enable\n * both since we can't know if this is single-view AR until we get the\n * view count in {@link nextFrame}. There the right eye view is\n * disabled if necessary. */\n scene.leftEyeView.active = inXR;\n scene.rightEyeView.active = inXR;\n }\n }\n\n /**\n * Set projection matrix parameters for the webxr session.\n *\n * @param near Distance of near clipping plane\n * @param far Distance of far clipping plane\n */\n updateProjectionParams(near: number | undefined, far: number | undefined) {\n if (!this.sessionState) return;\n\n const scene = this.engine.scene;\n near ??= scene.leftEyeView!.near;\n far ??= scene.leftEyeView!.far;\n\n /* If reverse-Z is enabled, we need to give WebXR swapped near and far\n * planes so it generates the correct projection matrices. This is\n * explicitly supported by the spec. */\n const reverseZ = !!this.engine.isReverseZEnabled;\n\n this.sessionState!.session.updateRenderState({\n depthNear: reverseZ ? far : near,\n depthFar: reverseZ ? near : far,\n });\n }\n\n /**\n * Request an XR session.\n *\n * @note Please use this call instead of directly calling `navigator.xr.requestSession()`.\n * Wonderland Engine requires to be aware that a session is started, and this\n * is done through this call.\n *\n * @param mode The XR mode.\n * @param features An array of required features, e.g., `['local-floor', 'hit-test']`.\n * @param optionalFeatures An array of optional features, e.g., `['bounded-floor', 'depth-sensing']`.\n * @returns A promise resolving with the `XRSession`, a string error message otherwise.\n */\n requestSession(\n mode: XRSessionMode,\n features: string[],\n optionalFeatures: string[] = []\n ): Promise {\n const options = this.sessionOptions(features, optionalFeatures);\n return navigator.xr!.requestSession(mode, options).then(async (s) => {\n /* Ensure our context can handle WebXR rendering */\n const gl = this.engine.canvas.getContext('webgl2')!;\n await gl.makeXRCompatible();\n this.startSession(s, mode);\n return s;\n });\n }\n\n /**\n * Offer an XR session.\n *\n * Adds an interactive UI element to the browser interface to start an XR\n * session. Browser support is optional, so it's advised to still allow\n * requesting a session with a UI element on the website itself.\n *\n * @note Please use this call instead of directly calling `navigator.xr.offerSession()`.\n * Wonderland Engine requires to be aware that a session is started, and this\n * is done through this call.\n *\n * @param mode The XR mode.\n * @param features An array of required features, e.g., `['local-floor', 'hit-test']`.\n * @param optionalFeatures An array of optional features, e.g., `['bounded-floor', 'depth-sensing']`.\n * @returns A promise resolving with the `XRSession`, a string error message otherwise.\n *\n * @since 1.1.5\n */\n offerSession(\n mode: XRSessionMode,\n features: string[],\n optionalFeatures: string[] = []\n ): Promise {\n /* @ts-ignore */\n if (!navigator.xr!.offerSession) {\n return Promise.reject('WebXR offerSession() unsupported in this browser.');\n }\n const options = this.sessionOptions(features, optionalFeatures);\n /* @ts-ignore */\n return navigator.xr!.offerSession(mode, options).then(async (s) => {\n /* Ensure our context can handle WebXR rendering */\n const gl = this.engine.canvas.getContext('webgl2')!;\n await gl.makeXRCompatible();\n this.startSession(s, mode);\n return s;\n });\n }\n\n async startSession(session: XRSession, mode: XRSessionMode) {\n /* Multiple calls to requestSession will result in the existing\n * session being passed to the callback */\n if (session == this.sessionState?.session) return;\n this.sessionState = new XRSessionState(this, mode, session);\n\n /* React to session ending */\n session.addEventListener('end', () => {\n this.endSession();\n });\n\n const gl = this.engine.canvas.getContext('webgl2')!;\n\n /* Create the base layer */\n let binding = null;\n const isEmulator = 'CustomWebXRPolyfill' in window;\n /* The WebXR emulator doesn't support layers, and will\n * simply throw an exception. Only way of checking seems\n * to be whether the custom polyfill is available */\n if (!isEmulator && 'XRWebGLBinding' in window) {\n binding = this._webglBinding = new XRWebGLBinding(session, gl);\n }\n const useLayers = this._allowLayers && binding;\n if (useLayers) {\n /* Use WebXR Layers */\n const layer = (this.baseLayer = binding!.createProjectionLayer({\n scaleFactor: this.framebufferScaleFactor,\n colorFormat: this.colorFormat,\n depthFormat: this.depthFormat,\n /* Always use 'texture-array' with multiview, otherwise default to it\n * if not set otherwise */\n textureType: !this.textureType ? 'texture-array' : this.textureType,\n }));\n session.updateRenderState({layers: [layer]});\n } else {\n const layer = (this.baseLayer = new XRWebGLLayer(session, gl, {\n framebufferScaleFactor: this.framebufferScaleFactor,\n }));\n session.updateRenderState({baseLayer: layer});\n }\n\n /* Request and cache available spaces. 'viewer' is always available. */\n const promises = [];\n for (const type of Object.keys(this._refSpaces) as XRReferenceSpaceType[]) {\n promises.push(\n session.requestReferenceSpace(type).then((refSpace) => {\n this._refSpaces[type] = refSpace;\n return refSpace;\n })\n );\n }\n\n await Promise.allSettled(promises).then((results) => {\n /* We prefer the reference space automatically in above order */\n for (let i = results.length - 1; i >= 0; --i) {\n const result = results[i];\n if (result.status === 'rejected') continue;\n /* Type is kept track of for the user to test against */\n this._refSpaceType = Object.keys(this._refSpaces)[\n i\n ] as XRReferenceSpaceType;\n this._refSpace = result.value;\n break;\n }\n });\n\n const isVR = mode == 'immersive-vr';\n\n const scene: Scene = this.engine.scene;\n\n if (isVR && (!scene.leftEyeView || !scene.rightEyeView)) {\n console.error(\n `sessionStart(): VR sessions require 1 classic view, a left and a right view`\n );\n return;\n }\n\n this.updateViewState(true);\n\n this.updateProjectionParams(undefined, undefined);\n\n this.onSessionStart.notify(session, this.sessionState!.sessionMode);\n\n this.#wasm._wl_reset_context();\n\n console.log(`WebXR ${isVR ? 'VR' : 'AR'} session started`);\n\n this.sessionState.session.addEventListener('visibilitychange', (event) => {\n switch (event.session.visibilityState) {\n case 'visible':\n this.#wasm._wl_xr_focus();\n case 'visible-blurred':\n this.#wasm._wl_xr_blur();\n case 'hidden':\n this.#wasm._wl_xr_hide();\n }\n });\n\n /* Wait until first frame to call _wl_xr_init() */\n this._initXR = true;\n\n this._requestAnimationFrameId = session.requestAnimationFrame(\n useLayers ? this.nextFrameLayers.bind(this) : this.nextFrameSingle.bind(this)\n );\n }\n\n sessionOptions(\n requiredFeatures: string[],\n optionalFeatures: string[] = []\n ): XRSessionInit {\n if (this._allowLayers && !optionalFeatures.includes('layers')) {\n optionalFeatures.push('layers');\n }\n\n const params: XRSessionInit = {\n requiredFeatures,\n optionalFeatures,\n };\n\n const hasDepthSensing =\n requiredFeatures.includes('depth-sensing') ||\n optionalFeatures.includes('depth-sensing');\n if (hasDepthSensing) {\n params.depthSensing = {\n usagePreference: ['gpu-optimized', 'cpu-optimized'],\n dataFormatPreference: ['float32', 'luminance-alpha'],\n };\n }\n return params;\n }\n\n endSession() {\n this.sessionState!.session.cancelAnimationFrame(this._requestAnimationFrameId);\n this._requestAnimationFrameId = null;\n this.sessionState = null;\n\n this.#wasm._wl_reset_context();\n\n if (this.onSessionStart instanceof RetainEmitter) this.onSessionStart.reset();\n this.onSessionEnd.notify();\n\n console.log('WebXR session ended');\n\n this._inXR = false;\n\n this.updateViewState(false);\n\n const scene: Scene = this.engine.scene;\n\n this.#wasm._wl_xr_exit();\n\n /* Restore projection matrices. They are temporarily overridden in XR and\n * view changes are applied when the session ends. */\n scene.leftEyeView!._generateProjectionMatrix();\n scene.rightEyeView!._generateProjectionMatrix();\n\n /* Meta WebXR emulator doesn't request animation frame. Quest does. */\n this.#wasm._wl_application_redraw();\n }\n\n nextFrame(time: number, frame: XRFrame) {\n this.sessionState!.frame = frame;\n\n const session = frame.session;\n\n const pose = frame.getViewerPose(this._refSpace!);\n if (!pose) return;\n\n const scene: Scene = this.engine.scene;\n\n if (this._initXR) {\n /* There's no way to tell how many views we have before rendering the first frame,\n * and AR is not a sufficient criteria for one eye (mobile AR, AR headsets) */\n scene.rightEyeView!.active = pose.views.length > 1;\n\n this.#wasm._wl_xr_init(pose.views.length, !this.baseLayer);\n this._initXR = false;\n this._inXR = true;\n\n this.onSessionFirstFrame.notify(session, this.sessionState!.sessionMode);\n }\n\n /* Head transformation */\n this._tempPose(pose.transform);\n scene._setInputTransformation(\n InputType.Head,\n this._tempPosition,\n this._tempRotation\n );\n\n /* Eyes transformations */\n for (let i = 0; i < pose.views.length && i < 2; ++i) {\n const view = pose.views[i];\n const viewIndex = view.eye == 'right' ? 1 : 0;\n this._tempPose(view.transform);\n scene._setInputTransformation(\n InputType.EyeLeft + viewIndex,\n this._tempPosition,\n this._tempRotation\n );\n }\n\n /* Controller transformations */\n for (const inputSource of session.inputSources) {\n let handedness = -1;\n if (inputSource.handedness == 'left') handedness = 0;\n else if (inputSource.handedness == 'right') handedness = 1;\n this._updateInputComponent(\n frame,\n inputSource.gripSpace,\n InputType.ControllerLeft + handedness\n );\n this._updateInputComponent(\n frame,\n inputSource.targetRaySpace,\n InputType.RayLeft + handedness\n );\n }\n\n this.#wasm._wl_nextFrame(0);\n }\n\n nextFrameSingle(time: number, frame: XRFrame) {\n const session = frame.session;\n /* Request next frame */\n if (this.sessionState != null) {\n this._requestAnimationFrameId = session.requestAnimationFrame(\n this.nextFrameSingle.bind(this)\n );\n }\n\n const pose = frame.getViewerPose(this._refSpace!);\n if (!pose) return;\n\n const gl = this.engine.canvas.getContext('webgl2')!;\n const GL = this.#wasm.GL as any;\n\n /* If framebuffer is non-null, compositor is enabled and we bind it.\n * If it's null, we need to avoid this call otherwise the canvas FBO is bound */\n const glLayer = session.renderState.baseLayer!;\n if (glLayer.framebuffer) {\n /* Make sure that FRAMEBUFFER_BINDING returns a valid value.\n * For that we create an id in the emscripten object tables\n * and add the framebuffer */\n const id = this._fbo || GL.getNewId(GL.framebuffers);\n (glLayer.framebuffer as WebXRFramebuffer).name = id;\n GL.framebuffers[id] = glLayer.framebuffer;\n this._fbo = id;\n gl.bindFramebuffer(gl.FRAMEBUFFER, GL.framebuffers[id]);\n }\n\n for (let i = 0; i < pose.views.length; ++i) {\n const view = pose.views[i];\n const viewport = glLayer.getViewport(view)!;\n\n let destView: ViewComponent;\n if (view.eye == 'left') {\n destView = this.engine.scene.leftEyeView!;\n } else if (view.eye == 'right') {\n destView = this.engine.scene.rightEyeView!;\n } else {\n destView = this.engine.scene._components.componentAt(ViewComponent, i);\n }\n destView._setViewport(viewport.x, viewport.y, viewport.width, viewport.height);\n destView._setProjectionMatrix(view.projectionMatrix);\n /* Patch projection matrix, currently only needed for reverse-Z.\n * If reverse-Z is enabled, we pass swapped near and far values to\n * updateRenderState(), giving us a matrix with inverted depth. But\n * we still need to map from NDC 1,-1 to 1,0. On WebGPU this won't\n * be necessary. */\n const ndcDepthIsZeroToOne = false;\n this.#wasm._wl_view_component_remapProjectionMatrix(\n destView._id,\n this.engine.isReverseZEnabled,\n ndcDepthIsZeroToOne\n );\n this.#wasm._wl_view_component_set_externalFramebuffer(\n destView._id,\n this._fbo as number\n );\n }\n\n this.nextFrame(time, frame);\n }\n\n nextFrameLayers(time: number, frame: XRFrame) {\n const session = frame.session;\n /* Request next frame */\n if (this.sessionState != null) {\n this._requestAnimationFrameId = session.requestAnimationFrame(\n this.nextFrameLayers.bind(this)\n );\n }\n\n const scene: Scene = this.engine.scene;\n\n const pose = frame.getViewerPose(this._refSpace!)!;\n\n const layer = this.baseLayer!;\n const binding = this._webglBinding;\n\n /* Make sure that FRAMEBUFFER_BINDING returns a valid value.\n * For that we create an id in the emscripten object tables\n * and add the framebuffer */\n const gl = this.engine.canvas.getContext('webgl2')!;\n const GL = this.#wasm.GL as any;\n\n const createFramebuffer = !this._fbo;\n const textureArray = layer.textureArrayLength == 2;\n\n if (createFramebuffer) {\n if (textureArray) {\n this._fbo = [GL.getNewId(GL.framebuffers), GL.getNewId(GL.framebuffers)];\n } else {\n /* Both eyes use the same FBO */\n const id = GL.getNewId(GL.framebuffers);\n this._fbo = [id, id];\n }\n this._fbo.forEach((id) => {\n const framebuffer = gl.createFramebuffer() as WebXRFramebuffer;\n GL.framebuffers[id] = framebuffer;\n framebuffer.name = id;\n });\n }\n\n const ids = this._fbo as number[];\n pose.views.forEach((view) => {\n const viewIndex = view.eye == 'right' ? 1 : 0;\n\n const subImage = binding!.getViewSubImage(layer, view);\n const viewport = subImage.viewport;\n\n let destView: ViewComponent;\n if (view.eye == 'right') {\n destView = this.engine.scene.rightEyeView!;\n } else {\n destView = this.engine.scene.leftEyeView!;\n }\n destView._setViewport(viewport.x, viewport.y, viewport.width, viewport.height);\n destView._setProjectionMatrix(view.projectionMatrix);\n const ndcDepthIsZeroToOne = false;\n this.#wasm._wl_view_component_remapProjectionMatrix(\n destView._id,\n this.engine.isReverseZEnabled,\n ndcDepthIsZeroToOne\n );\n this.#wasm._wl_view_component_set_externalFramebuffer(\n destView._id,\n ids[viewIndex]\n );\n\n if (!createFramebuffer) return;\n /* Only needs to be done for the first view on when using side-by-side\n * target texture */\n if (!textureArray && viewIndex != 0) return;\n\n /* Make WebXR API created textures known to emscripten */\n const colorTexture = subImage.colorTexture as WebXRTexture;\n const colorImageId = (colorTexture.name =\n colorTexture.name || GL.getNewId(GL.textures));\n GL.textures[colorImageId] = colorTexture;\n\n const depthStencilTexture = subImage.depthStencilTexture as WebXRTexture;\n const depthStencilImageId = (depthStencilTexture.name =\n depthStencilTexture.name || GL.getNewId(GL.textures));\n GL.textures[depthStencilImageId] = depthStencilTexture;\n\n /* Attach textures to framebuffer */\n gl.bindFramebuffer(gl.FRAMEBUFFER, GL.framebuffers[ids[viewIndex]]);\n\n if (textureArray) {\n /* @ts-ignore */\n gl.framebufferTextureLayer(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n subImage.colorTexture,\n 0,\n subImage.imageIndex\n );\n /* @ts-ignore */\n gl.framebufferTextureLayer(\n gl.FRAMEBUFFER,\n gl.DEPTH_STENCIL_ATTACHMENT,\n subImage.depthStencilTexture,\n 0,\n subImage.imageIndex\n );\n } else {\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n gl.TEXTURE_2D,\n subImage.colorTexture,\n 0\n );\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.DEPTH_STENCIL_ATTACHMENT,\n gl.TEXTURE_2D,\n subImage.depthStencilTexture,\n 0\n );\n }\n\n if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER)) {\n console.error('Target framebuffer for', view.eye, 'eye is incomplete.');\n }\n });\n\n this.nextFrame(time, frame);\n }\n\n private _updateInputComponent(\n frame: XRFrame,\n space: XRSpace | undefined,\n inputType: InputType\n ) {\n if (!space) return;\n const pose = frame.getPose(space, this._refSpace!);\n if (!pose || Number.isNaN(pose.transform.matrix[0])) return;\n this._tempPose(pose.transform);\n this.engine.scene._setInputTransformation(\n inputType,\n this._tempPosition,\n this._tempRotation\n );\n }\n}\n", "import {WonderlandEngine} from './engine.js';\nimport {InstantiateGltfResult, Scene} from './scene.js';\nimport {Prefab} from './prefab.js';\nimport {Object3D} from './wonderland.js';\n\n/** GLTF-specific loading options. */\nexport type GLTFOptions = {\n /** If `true`, extensions will be parsed. */\n extensions?: boolean;\n};\n\n/**\n * Extension data obtained from glTF files.\n */\nexport interface GLTFExtensionsInstance {\n /**\n * Mesh extension objects. Key is {@link Object3D.objectId}, value is JSON\n * data indexed by extension name.\n */\n mesh: Record>>;\n /**\n * Node extension objects. Key is {@link Object3D.objectId}, value is JSON\n * data indexed by extension name.\n */\n node: Record>>;\n /** Remapping from glTF node index to {@link Object3D.objectId}. */\n idMapping: number[];\n}\n\nexport class GLTFExtensions {\n objectCount: number;\n /** glTF root extensions object. JSON data indexed by extension name. */\n root: Record = {};\n /**\n * Mesh extension objects. Key is the gltf index, value is JSON\n * data indexed by extension name.\n */\n mesh: Record> = {};\n /**\n * Node extension objects. Key is a glTF index, value is JSON\n * data indexed by extension name.\n */\n node: Record> = {};\n\n constructor(count: number) {\n this.objectCount = count;\n }\n}\n\n/**\n * glTF scene.\n *\n * At the opposite of {@link Scene}, glTF scenes can be instantiated\n * in other scenes but can't:\n * - Be activated\n * - Be the destination of an instantiation\n *\n * #### Usage\n *\n * ```js\n * const prefab = await engine.loadGLTF('Zombie.glb');\n *\n * const scene = engine.scene;\n * for (let i = 0; i < 100; ++i) {\n * scene.instantiate(prefab);\n * }\n * ```\n *\n * Since this class inherits from {@link Prefab}, you can use the shared\n * API to modify the glTF before an instantiation:\n *\n * ```js\n * const prefab = await engine.loadGLTF('Zombie.glb');\n * const zombie = prefab.findByName('Zombie')[0];\n *\n * // The mesh is too small, we scale the root\n * zombie.setScalingWorld([2, 2, 2]);\n * // Add a custom js 'health' component to the root\n * zombie.addComponent('health', {value: 100});\n *\n * // 'Zombie' is wrapped in a new root added during instantiation\n * const {root} = engine.scene.instantiate(prefab);\n * const instanceZombie = root.children[0];\n * console.log(instanceZombie.getScalingWorld()); // Prints '[2, 2, 2]'\n * ```\n *\n * @category scene\n * @since 1.2.0\n */\nexport class PrefabGLTF extends Prefab {\n /**\n * Raw extensions read from the glTF file.\n *\n * The extensions will be mapped to the hierarchy upon instantiation.\n * For more information, have a look at the {@link InstantiateGltfResult} type.\n *\n * @note The glTF must be loaded with `extensions` enabled. If not, this\n * field will be set to `null`. For more information, have a look at the\n * {@link GLTFOptions} type.\n */\n extensions: GLTFExtensions | null = null;\n\n /**\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n constructor(engine: WonderlandEngine, index: number) {\n super(engine, index);\n this.extensions = this._readExtensions();\n }\n\n /**\n * Instantiate the glTF extensions on an active sub scene graph.\n *\n * @param id The root object id.\n * @param result The instantiation object result.\n *\n * @hidden\n */\n _processInstantiaton(dest: Prefab, root: Object3D, result: InstantiateGltfResult) {\n if (!this.extensions) return null;\n\n const wasm = this.engine.wasm;\n\n const count = this.extensions.objectCount;\n const idMapping: number[] = new Array(count);\n\n /** @todo: We need some check to ensure that the gltf layout didn't change to retarget extensions.\n * At least a simple scene graph size check should be required to avoid a segfault. */\n\n const activeRootIndex = wasm._wl_object_index(root._id);\n for (let i = 0; i < count; ++i) {\n const mappedId = wasm._wl_glTF_scene_extensions_gltfIndex_to_id(\n this._index,\n dest._index,\n activeRootIndex,\n i\n );\n idMapping[i] = mappedId;\n }\n\n const remapped: GLTFExtensionsInstance = {\n mesh: {},\n node: {},\n idMapping,\n };\n\n for (const gltfIndex in this.extensions.mesh) {\n const id = idMapping[gltfIndex];\n remapped.mesh[id] = this.extensions.mesh[gltfIndex];\n }\n for (const gltfIndex in this.extensions.node) {\n const id = idMapping[gltfIndex];\n remapped.node[id] = this.extensions.node[gltfIndex];\n }\n\n result.extensions = remapped;\n }\n\n /**\n * Unmarshalls gltf extensions.\n *\n * @hidden\n */\n private _readExtensions() {\n const wasm = this.engine.wasm;\n\n const ptr = wasm._wl_glTF_scene_get_extensions(this._index);\n if (!ptr) return null;\n\n let index = ptr / 4;\n const data = wasm.HEAPU32;\n const readString = () => {\n const strPtr = data[index++];\n const strLen = data[index++];\n return wasm.UTF8ViewToString(strPtr, strPtr + strLen);\n };\n\n const objectCount = data[index++];\n const extensions = new GLTFExtensions(objectCount);\n\n const meshExtensionsSize = data[index++];\n for (let i = 0; i < meshExtensionsSize; ++i) {\n const objectId = data[index++];\n extensions.mesh[objectId] = JSON.parse(readString());\n }\n const nodeExtensionsSize = data[index++];\n for (let i = 0; i < nodeExtensionsSize; ++i) {\n const objectId = data[index++];\n extensions.node[objectId] = JSON.parse(readString());\n }\n const rootExtensionsStr = readString();\n if (rootExtensionsStr) {\n extensions.root = JSON.parse(rootExtensionsStr);\n }\n\n return extensions;\n }\n}\n", "import {WonderlandEngine} from './engine.js';\nimport {WASM} from './wasm.js';\nimport {Emitter} from './utils/event.js';\nimport {fetchStreamWithProgress, fetchWithProgress, getBaseUrl} from './utils/fetch.js';\nimport {isString} from './utils/object.js';\nimport {\n Object3D,\n RayHit,\n Environment,\n ViewComponent,\n InputType,\n LogTag,\n} from './wonderland.js';\nimport {NumberArray} from './types.js';\nimport {Material} from './resources/material-manager.js';\nimport {InMemoryLoadOptions, Prefab, SceneLoadOptions} from './prefab.js';\nimport {GLTFExtensionsInstance, PrefabGLTF} from './scene-gltf.js';\n\nexport interface InstantiateResult {\n root: Object3D;\n}\n\nexport interface InstantiateGltfResult extends InstantiateResult {\n extensions: GLTFExtensionsInstance | null;\n}\n\n/** Options for scene activation. */\nexport interface ActivateOptions {\n /** If `true`, dispatches a ready event in the document. */\n dispatchReadyEvent?: boolean;\n /**\n * If `true`, the promise will resolve only once all dependencies are downloaded.\n *\n * If `false`, resolves as soon as possible, e.g., when the lowest\n * resolution textures are available.\n */\n waitForDependencies?: boolean;\n /** @hidden */\n legacyLoaded?: boolean;\n}\n\n/**\n * Legacy gltf extension type.\n *\n * @deprecated Use the new {@link WonderlandEngine.loadGLTF} API.\n */\nexport interface GLTFExtensionsLegacy {\n /** glTF root extensions object. JSON data indexed by extension name. */\n root: Record>;\n /**\n * Mesh extension objects. Key is {@link Object3D.objectId}, value is JSON\n * data indexed by extension name.\n */\n mesh: Record>>;\n /**\n * Node extension objects. Key is {@link Object3D.objectId}, value is JSON\n * data indexed by extension name.\n */\n node: Record>>;\n /** Remapping from glTF node index to {@link Object3D.objectId}. */\n idMapping: number[];\n}\n\n/** Options for {@link Scene.append}. */\nexport interface SceneAppendParameters {\n /** Whether to load glTF extension data */\n loadGltfExtensions: boolean;\n baseURL: string | undefined;\n}\n\n/**\n * Result obtained when appending a scene with {@link Scene.append} with gltf extensions.\n */\nexport type SceneAppendResultWithExtensions = {\n root: Object3D | null;\n extensions: GLTFExtensionsLegacy;\n};\n\n/**\n * Result obtained when appending a scene with {@link Scene.append}.\n */\nexport type SceneAppendResult = (Object3D | null) | SceneAppendResultWithExtensions;\n\n/**\n * Constants\n */\n\n/** Magic number for .bin */\nconst MAGIC_BIN = 'WLEV';\n\n/**\n * Scene type to load with {@link ChunkedSceneLoadSink}.\n *\n * @since 1.2.1\n * @hidden\n */\nexport enum SceneType {\n Prefab,\n Main,\n Dependency,\n}\n\n/**\n * Sink for `WritableStream` that loads the data as a .bin file.\n *\n * Data is loaded chunk by chunk to minimize the total WASM allocation size.\n *\n * @since 1.2.1\n * @hidden\n */\nexport class ChunkedSceneLoadSink implements UnderlyingSink {\n readonly #wasm: WASM;\n readonly #type: SceneType;\n readonly #closeParameters: any[];\n #offset = 0;\n #requested = 0;\n #firstChunk = true;\n\n _loadIndex = -1;\n sceneIndex = -1;\n\n /** Pointer to the staging buffer. */\n #ptr: number = 0;\n /** Staging buffer size, in **bytes**. */\n #bufferSize: number = 0;\n\n /** @overload */\n constructor(engine: WonderlandEngine, type: SceneType.Prefab, url: string);\n /** @overload */\n constructor(engine: WonderlandEngine, type: SceneType.Main, url: string);\n /** @overload */\n constructor(\n engine: WonderlandEngine,\n type: SceneType.Dependency,\n url: string,\n dependentSceneIndex: number\n );\n /**\n * Constructor\n * @param engine Engine instance\n * @param type Type of scene to load\n * @param url URL for the scene creation. Can be empty.\n * @param closeParameters Parameters for the final function to be called\n * during a successful {@link ChunkedSceneLoadSink.close}.\n */\n constructor(\n engine: WonderlandEngine,\n type: SceneType,\n url: string,\n ...closeParameters: any[]\n ) {\n this.#wasm = engine.wasm;\n this.#type = type;\n this.#closeParameters = closeParameters;\n this._loadIndex = this.#wasm._wl_scene_create_chunked_start(\n this.#wasm.tempUTF8(url)\n );\n /* Initial buffer for the header chunk */\n this.#requested = this.#wasm._wl_scene_create_chunked_buffer_size(this._loadIndex);\n this._resizeBuffer(this.#requested);\n }\n\n private _resizeBuffer(size: number) {\n if (this.#bufferSize > 0) {\n this.#wasm._free(this.#ptr);\n }\n this.#bufferSize = size;\n this.#ptr = size ? this.#wasm._malloc(size) : 0;\n }\n\n private _throwError(reason: string) {\n this.abort();\n throw new Error(reason);\n }\n\n /**\n * Write a single blob of data.\n *\n * @param blob Data to parse\n */\n write(blob: Uint8Array) {\n let read = 0;\n while (read < blob.length) {\n /* Fill the staging buffer */\n const toRead = Math.min(blob.length - read, this.#bufferSize - this.#offset);\n /* **Do not** attempt to create a HEAP8 view here, since the next wasm call might\n * re-allocate the heap, making this view dangling. */\n this.#wasm.HEAPU8.set(\n blob.subarray(read, read + toRead),\n this.#ptr + this.#offset\n );\n this.#offset += toRead;\n read += toRead;\n\n /* Not enough data, wait for more */\n if (this.#requested > this.#offset) continue;\n\n /* Send data. This loads as many chunks as possible and then tells\n * us how much data it's missing, and how much it has read. The\n * read count allows us to move incomplete chunk data to the start\n * of the buffer. This is necessary because the buffer is allocated\n * with just enough data to hold the largest chunk. */\n const readSizePtr = this.#wasm._tempMem;\n const requestedPtr = this.#wasm._tempMem + 4;\n let success: boolean;\n try {\n success = this.#wasm._wl_scene_create_chunked_next(\n this._loadIndex,\n this.#ptr,\n this.#offset,\n readSizePtr,\n requestedPtr\n );\n } catch {\n /* Catch calls to abort(), e.g. via asserts */\n success = false;\n }\n\n if (!success) {\n this._throwError('Chunk parsing failed');\n }\n\n const sentSize = this.#offset;\n const readSize = this.#wasm._tempMemUint32[0];\n this.#requested = this.#wasm._tempMemUint32[1];\n const extraSize = sentSize - readSize;\n\n /* #requested == 0 signals end of .bin file */\n if (extraSize > this.#requested) {\n this._throwError('Unexpected extra data');\n }\n\n /* Not enough data for a chunk, wait for more */\n if (!readSize) continue;\n\n if (extraSize > 0) {\n /* We passed one or more complete chunks plus incomplete extra\n * data for the next chunk. Move it to the buffer start. */\n this.#wasm.HEAPU8.copyWithin(\n this.#ptr,\n this.#ptr + readSize,\n this.#ptr + sentSize\n );\n }\n\n this.#offset = extraSize;\n\n if (this.#firstChunk) {\n /* First chunk is the header. At this point we can query for\n * the required buffer size for parsing the rest of the .bin.\n * The initial buffer is exactly the size of the header chunk\n * so #offset should be 0 here since we sent a complete chunk. */\n this._resizeBuffer(\n this.#wasm._wl_scene_create_chunked_buffer_size(this._loadIndex)\n );\n this.#firstChunk = false;\n }\n }\n }\n\n /** Called when all blobs of data have been written */\n close() {\n if (this.#requested > 0) {\n this._throwError('Unexpected end of data');\n }\n\n this._resizeBuffer(0);\n switch (this.#type) {\n case SceneType.Prefab:\n this.sceneIndex = this.#wasm._wl_scene_create_chunked_end_prefab(\n this._loadIndex\n );\n break;\n case SceneType.Main:\n this.#wasm._wl_scene_create_chunked_end_main(this._loadIndex);\n this.sceneIndex = 0;\n break;\n case SceneType.Dependency:\n [this.sceneIndex] = this.#closeParameters;\n this.#wasm._wl_scene_create_chunked_end_queued(\n this._loadIndex,\n this.sceneIndex\n );\n break;\n default:\n this.#wasm._wl_scene_create_chunked_abort(this._loadIndex);\n break;\n }\n this._loadIndex = -1;\n }\n\n /** Called when the stream is aborted */\n abort() {\n if (this._loadIndex === -1) return;\n this.#wasm._wl_scene_create_chunked_abort(this._loadIndex);\n this._loadIndex = -1;\n this._resizeBuffer(0);\n }\n\n /** Staging buffer size */\n get size() {\n return this.#bufferSize;\n }\n}\n\n/**\n * Wonderland Engine (.bin) scene.\n *\n * Wonderland Engine packages two types of scene:\n * - Activatable scene: Contains objects, components, views, resources, and rendering data\n * - Streamable scene: Contains objects, components, and resources\n *\n * #### Activation\n *\n * Some scenes are **activatable**, they can thus be attached to the renderer\n * to be updated and rendered on the canvas.\n *\n * For more information, have a look at the {@link WonderlandEngine.switchTo} method.\n *\n * #### Instantiation\n *\n * Besides activation, a scene can instantiate the content of another scene.\n *\n * For more information, have a look at the {@link Scene#instantiate} method.\n *\n * @category scene\n */\nexport class Scene extends Prefab {\n /** Called before rendering the scene */\n readonly onPreRender = new Emitter();\n /** Called after the scene has been rendered */\n readonly onPostRender = new Emitter();\n\n /** Ray hit pointer in WASM heap. @hidden */\n private _rayHit: number;\n /** Ray hit. @hidden */\n private _hit: RayHit;\n\n /** @hidden */\n private _environment: Environment;\n\n constructor(engine: WonderlandEngine, index: number) {\n super(engine, index);\n /** `this.engine?` is used for testing. */\n this._rayHit = this.engine?.wasm._malloc(4 * (3 * 4 + 3 * 4 + 4 + 2) + 4);\n this._hit = new RayHit(this, this._rayHit);\n this._environment = new Environment(this);\n }\n\n /** @overload */\n instantiate(scene: PrefabGLTF): InstantiateGltfResult;\n /**\n * Instantiate `scene` into this instance.\n *\n * Any scene can be instantiated into one another. It's thus possible\n * to instantiate a {@link PrefabGLTF} into this instance, or another\n * {@link Scene} instance.\n *\n * #### Usage\n *\n * ```js\n * const prefabScene = await engine.loadScene('Prefab.bin');\n * // Instantiate `prefabScene` into `scene`\n * engine.scene.instantiate(prefabScene);\n * ```\n *\n * #### Shared Resources\n *\n * Instantiating **does not** duplicate resources. Each instance will\n * reference the same assets stored in the {@link Scene}, e.g.,\n *\n * ```js\n * // `zombie` has one mesh and one material\n * const zombie = await engine.loadScene('Zombie.bin');\n *\n * for (let i = 0; i < 100; ++i) {\n * engine.scene.instantiate(zombie);\n * }\n *\n * console.log(engine.meshes.count) // Prints '1'\n * console.log(engine.materials.count) // Prints '1'\n * ```\n *\n * #### glTF extensions\n *\n * Instantiating a prefab loaded from a .glb automatically retargets the extensions\n * to the created hierarchy:\n *\n * ```js\n * const gltf = await engine.loadGLTF({url: 'Model.glb', extensions: true});\n * const {root, extensions} = engine.scene.instantiate(gltf);\n *\n * // Retrieve Object3D instance from extension nodes list\n * for (const key in extensions.node) {\n * const object3d = engine.scene.wrap(extensions.node[key]);\n * }\n * ```\n *\n * @param scene The scene to instantiate.\n * @returns An object containing the instantiated root {@link Object3D}.\n * When a glTF is instantiated, the result can contain extra metadata.\n * For more information, have a look at the {@link InstantiateResult} type.\n *\n * @since 1.2.0\n */\n instantiate(prefab: Prefab): InstantiateResult;\n instantiate(prefab: Prefab): InstantiateResult {\n if (prefab._index === this._index) {\n throw new Error(\"Can't instantiate scene into itself\");\n }\n\n const wasm = this.engine.wasm;\n const id = wasm._wl_scene_instantiate(prefab._index, this._index);\n\n const result: InstantiateResult = {root: this.wrap(id)};\n if (prefab instanceof PrefabGLTF) {\n const obj = this.wrap(id);\n prefab._processInstantiaton(this, obj, result as InstantiateGltfResult);\n }\n return result;\n }\n\n /** @todo: Add `instantiateBatch` to instantiate multiple chunks in a row. */\n\n /**\n * @todo Provide an API to delete all resources linked to a scene.\n *\n * Example:\n *\n * ```ts\n * const scene = await engine.loadScene('Scene.bin');\n * ...\n * scene.destroy({removeResources: true});\n * ```\n */\n\n /**\n * Destroy this scene and remove it from the engine.\n *\n * @note Destroying a scene **doesn't** remove the materials, meshes,\n * and textures it references in the engine. Those should be cleaned up either by loading\n * another main scene via {@link WonderlandEngine.loadMainScene}, or manually using {@link Mesh.destroy}.\n *\n * @throws If the scene is currently active.\n * */\n destroy() {\n if (this.isActive) {\n throw new Error(\n `Attempt to destroy ${this}, but destroying the active scene is not supported`\n );\n }\n\n /* Store before calling `super.destroy()` since the call\n * might destroy entirely the prototype. */\n const wasm = this.engine.wasm;\n const rayPtr = this._rayHit;\n\n super.destroy();\n\n wasm._free(rayPtr);\n }\n\n /**\n * View components.\n */\n get views(): ViewComponent[] {\n return this._components.components(ViewComponent, false);\n }\n\n /**\n * Active view components.\n */\n get activeViews(): ViewComponent[] {\n return this._components.components(ViewComponent, true);\n }\n\n /**\n * Main view.\n */\n get mainView(): ViewComponent | null {\n const id = this.engine.wasm._wl_scene_get_mainView(this._index);\n return this._components.wrapView(id);\n }\n\n /**\n * Left eye view.\n */\n get leftEyeView(): ViewComponent | null {\n const id = this.engine.wasm._wl_scene_get_leftView(this._index);\n return this._components.wrapView(id);\n }\n\n /**\n * Right eye view.\n */\n get rightEyeView(): ViewComponent | null {\n const id = this.engine.wasm._wl_scene_get_rightView(this._index);\n return this._components.wrapView(id);\n }\n\n /**\n * Set input transformation.\n *\n * @hidden\n */\n _setInputTransformation(\n type: InputType,\n position: Readonly,\n orientation: Readonly\n ) {\n const wasm = this.engine.wasm;\n wasm.requireTempMem(4 * 2 * 4);\n wasm._tempMemFloat.set(position);\n wasm._tempMemFloat.set(orientation, 4);\n wasm._wl_input_set_transformation(this._index, type, wasm._tempMem);\n }\n\n /**\n * Cast a ray through the scene and find intersecting collision components.\n *\n * The resulting ray hit will contain **up to 4** closest ray hits,\n * sorted by increasing distance.\n *\n * Example:\n *\n * ```js\n * const hit = engine.scene.rayCast(\n * [0, 0, 0],\n * [0, 0, 1],\n * 1 << 0 | 1 << 4, // Only check against components in groups 0 and 4\n * 25\n * );\n * if (hit.hitCount > 0) {\n * const locations = hit.getLocations();\n * console.log(`Object hit at: ${locations[0][0]}, ${locations[0][1]}, ${locations[0][2]}`);\n * }\n * ```\n *\n * @param o Ray origin.\n * @param d Ray direction.\n * @param groupMask Bitmask of collision groups to filter by: only objects\n * that are part of given groups are considered for the raycast.\n * @param maxDistance Maximum **inclusive** hit distance. Defaults to `100`.\n *\n * @returns The {@link RayHit} instance, cached by this class.\n *\n * @note The returned {@link RayHit} object is owned by the {@link Scene}\n * instance and will be reused with the next {@link Scene#rayCast} call.\n */\n rayCast(\n o: Readonly,\n d: Readonly,\n groupMask: number,\n maxDistance = 100.0\n ): RayHit {\n this.engine.wasm._wl_scene_ray_cast(\n this._index,\n o[0],\n o[1],\n o[2],\n d[0],\n d[1],\n d[2],\n groupMask,\n this._rayHit,\n maxDistance\n );\n return this._hit;\n }\n\n /**\n * Set the background clear color.\n *\n * @param color new clear color (RGBA).\n * @since 0.8.5\n */\n set clearColor(color: number[]) {\n this.engine.wasm._wl_scene_set_clearColor(color[0], color[1], color[2], color[3]);\n }\n\n /**\n * Set whether to clear the color framebuffer before drawing.\n *\n * This function is useful if an external framework (e.g. an AR tracking\n * framework) is responsible for drawing a camera frame before Wonderland\n * Engine draws the scene on top of it.\n *\n * @param b Whether to enable color clear.\n * @since 0.9.4\n */\n set colorClearEnabled(b: boolean) {\n this.engine.wasm._wl_scene_enableColorClear(b);\n }\n\n /**\n * Load a scene file (.bin).\n *\n * Will replace the currently active scene with the one loaded\n * from given file. It is assumed that JavaScript components required by\n * the new scene were registered in advance.\n *\n * Once the scene is loaded successfully and initialized,\n * {@link WonderlandEngine.onSceneLoaded} is notified.\n *\n * #### ArrayBuffer\n *\n * The `load()` method accepts an in-memory buffer:\n *\n * ```js\n * scene.load({\n * buffer: new ArrayBuffer(...),\n * baseURL: 'https://my-website/assets'\n * })\n * ```\n *\n * @note The `baseURL` is mandatory. It's used to fetch images and languages.\n *\n * Use {@link Scene.setLoadingProgress} to update the loading progress bar\n * when using an ArrayBuffer.\n *\n * @deprecated Use the new {@link WonderlandEngine.loadMainScene} API.\n *\n * @param options Path to the file to load, or an option object.\n * For more information about the options, see the {@link SceneLoadOptions} documentation.\n * @returns Promise that resolves when the scene was loaded.\n */\n async load(options: string | SceneLoadOptions): Promise {\n const engine = this.engine;\n\n /** @todo(2.0.0): Remove this method. */\n let dispatchReadyEvent = false;\n let opts: InMemoryLoadOptions;\n if (isString(options)) {\n opts = await Scene.loadBuffer(options, (bytes: number, size: number) => {\n this.engine.log.info(LogTag.Scene, `Scene downloading: ${bytes} / ${size}`);\n this.engine.setLoadingProgress(bytes / size);\n });\n } else {\n opts = options;\n dispatchReadyEvent = options.dispatchReadyEvent ?? false;\n }\n\n const scene = await engine.loadMainSceneFromBuffer({\n ...opts,\n dispatchReadyEvent,\n });\n /* Do not use `this`, since `loadMainScene` will destroy it.\n * `erasePrototypeOnDestroy` would make this instance unusable. */\n engine.onSceneLoaded.notify();\n return scene;\n }\n\n /**\n * Append a scene file.\n *\n * Loads and parses the file and its images and appends the result\n * to the currently active scene.\n *\n * Supported formats are streamable Wonderland scene files (.bin) and glTF\n * 3D scenes (.gltf, .glb).\n *\n * ```js\n * WL.scene.append(filename).then(root => {\n * // root contains the loaded scene\n * });\n * ```\n *\n * In case the `loadGltfExtensions` option is set to true, the response\n * will be an object containing both the root of the loaded scene and\n * any glTF extensions found on nodes, meshes and the root of the file.\n *\n * ```js\n * WL.scene.append(filename, { loadGltfExtensions: true }).then(({root, extensions}) => {\n * // root contains the loaded scene\n * // extensions.root contains any extensions at the root of glTF document\n * const rootExtensions = extensions.root;\n * // extensions.mesh and extensions.node contain extensions indexed by Object id\n * const childObject = root.children[0];\n * const meshExtensions = root.meshExtensions[childObject.objectId];\n * const nodeExtensions = root.nodeExtensions[childObject.objectId];\n * // extensions.idMapping contains a mapping from glTF node index to Object id\n * });\n * ```\n *\n * If the file to be loaded is located in a subfolder, it might be useful\n * to define the `baseURL` option. This will ensure any bin files\n * referenced by the loaded bin file are loaded at the correct path.\n *\n * ```js\n * WL.scene.append(filename, { baseURL: 'scenes' }).then(({root, extensions}) => {\n * // do stuff\n * });\n * ```\n *\n * @deprecated Use the new {@link Prefab} and {@link Scene} API.\n *\n * @param file The .bin, .gltf or .glb file to append. Should be a URL or\n * an `ArrayBuffer` with the file content.\n * @param options Additional options for loading.\n * @returns Promise that resolves when the scene was appended.\n */\n async append(\n file: string | ArrayBuffer,\n options: Partial = {}\n ): Promise {\n /** @todo(2.0.0): Remove this method. */\n\n const {baseURL = isString(file) ? getBaseUrl(file) : this.baseURL} = options;\n\n const buffer = isString(file) ? await fetchWithProgress(file) : file;\n const data = new Uint8Array(buffer);\n const isBinFile =\n data.byteLength > MAGIC_BIN.length &&\n data\n .subarray(0, MAGIC_BIN.length)\n .every((value, i) => value === MAGIC_BIN.charCodeAt(i));\n\n const scene = isBinFile\n ? this.engine.loadPrefabFromBuffer({buffer, baseURL})\n : this.engine.loadGLTFFromBuffer({\n buffer,\n baseURL,\n extensions: options.loadGltfExtensions,\n });\n\n const result = this.instantiate(scene)!;\n if (scene instanceof PrefabGLTF) {\n if (!scene.extensions) return result.root;\n return {\n root: result.root,\n extensions: {\n ...(result as InstantiateGltfResult).extensions,\n root: scene.extensions.root,\n },\n } as SceneAppendResultWithExtensions;\n }\n\n scene.destroy();\n return result.root;\n }\n\n /**\n * Update the loading screen progress bar.\n *\n * @param value Current loading percentage, in the range [0; 1].\n *\n * @deprecated Use {@link WonderlandEngine.setLoadingProgress}.\n */\n setLoadingProgress(percentage: number) {\n this.engine.setLoadingProgress(percentage);\n }\n\n /**\n * Dispatch an event 'wle-scene-ready' in the document.\n *\n * @note This is used for automatic testing.\n */\n dispatchReadyEvent() {\n document.dispatchEvent(\n new CustomEvent('wle-scene-ready', {\n detail: {filename: this.filename},\n })\n );\n }\n\n /**\n * Set the current material to render the sky.\n *\n * @note The sky needs to be enabled in the editor when creating the scene.\n * For more information, please refer to the background [tutorial](https://wonderlandengine.com/tutorials/background-effect/).\n */\n set skyMaterial(material: Material | null) {\n this.engine.wasm._wl_scene_set_sky_material(this._index, material?._id ?? 0);\n }\n\n /** Current sky material, or `null` if no sky is set. */\n get skyMaterial(): Material | null {\n const index = this.engine.wasm._wl_scene_get_sky_material(this._index);\n return this.engine.materials.wrap(index);\n }\n\n /**\n * Environment lighting properties.\n *\n * @since 1.2.3\n */\n get environment(): Environment {\n return this._environment;\n }\n\n /**\n * Relative url to the js components bundle\n *\n * Returns `null` if the scene was built without a components bundle.\n */\n get componentsBundle(): string | null {\n const wasm = this.engine.wasm;\n const ptr = wasm._wl_scene_get_componentsBundle(this._index);\n return ptr ? wasm.UTF8ToString(ptr) : null;\n }\n\n /**\n * Reset the scene.\n *\n * This method deletes all used and allocated objects, and components.\n *\n * @deprecated Load a new scene and activate it instead.\n */\n reset() {\n /** @todo(2.0.0): Remove this method. */\n }\n\n /**\n * Download and apply queued dependency files (.bin).\n *\n * @hidden\n */\n async _downloadDependency(url: string) {\n const sink = new ChunkedSceneLoadSink(\n this.engine,\n SceneType.Dependency,\n url,\n this._index\n );\n const stream = await fetchStreamWithProgress(url);\n return stream.pipeTo(new WritableStream(sink));\n }\n\n /**\n * Download and apply queued dependency files (.bin).\n *\n * @hidden\n */\n async _downloadDependencies() {\n const wasm = this.engine.wasm;\n\n const count = wasm._wl_scene_queued_bin_count(this._index);\n if (!count) return Promise.resolve();\n\n const urls = new Array(count).fill(0).map((_, i: number) => {\n const ptr = wasm._wl_scene_queued_bin_path(this._index, i);\n const url = wasm.UTF8ToString(ptr);\n return url;\n });\n\n wasm._wl_scene_clear_queued_bin_list(this._index);\n\n return Promise.all(urls.map((url: string) => this._downloadDependency(url)));\n }\n}\n", "import {\n Component,\n ComponentConstructor,\n Object as Object3D,\n Physics,\n I18N,\n MorphTargets,\n Texture,\n Font,\n DestroyedPrefabInstance,\n ParticleEffect,\n} from './wonderland.js';\n\nimport {Emitter, RetainEmitter} from './utils/event.js';\nimport {isString} from './utils/object.js';\nimport {APIVersion, Version} from './version.js';\nimport {WASM} from './wasm.js';\nimport {Logger} from './utils/logger.js';\nimport {clamp} from './utils/misc.js';\nimport {MaterialManager} from './resources/material-manager.js';\nimport {MeshManager} from './resources/mesh-manager.js';\nimport {ResourceManager} from './resources/resource.js';\nimport {needsFlipY, TextureManager} from './resources/texture-manager.js';\nimport {LogTag} from './wonderland.js';\nimport {Constructor, ImageLike, ProgressCallback} from './types.js';\nimport {XRSessionState, WebXR} from './webxr.js';\nimport {Prefab, InMemoryLoadOptions, LoadOptions, StreamLoadOptions} from './prefab.js';\nimport {Scene, ActivateOptions, ChunkedSceneLoadSink, SceneType} from './scene.js';\nimport {PrefabGLTF, GLTFOptions} from './scene-gltf.js';\nimport {ArrayBufferSource, onImageReady} from './utils/fetch.js';\n\n/**\n * Ensures that this API is compatible with the given\n * runtime version.\n *\n * We only enforce compatibility for major and minor components, i.e.,\n * the runtime and the API must both be of the form `x.y.*`.\n *\n * @throws If the major or the minor components are different.\n *\n * @param version The target version\n */\nexport function checkRuntimeCompatibility(version: Version) {\n const {major, minor} = version;\n\n let majorDiff = major - APIVersion.major;\n let minorDiff = minor - APIVersion.minor;\n /* Same version, so perfectly compatible. */\n if (!majorDiff && !minorDiff) return;\n\n const error =\n 'checkRuntimeCompatibility(): Version compatibility mismatch:\\n' +\n '\\t\u2192 API and runtime compatibility is enforced on a patch level (versions x.y.*)\\n';\n\n const isRuntimeOlder = majorDiff < 0 || (!majorDiff && minorDiff < 0);\n if (isRuntimeOlder) {\n /* Runtime is out of date. */\n throw new Error(\n `${error}\\t\u2192 Please use a Wonderland Engine editor version >= ${APIVersion.major}.${APIVersion.minor}.*`\n );\n }\n /* API is out of date. */\n throw new Error(\n `${error}\\t\u2192 Please use a new API version >= ${version.major}.${version.minor}.*`\n );\n}\n\n/**\n * Reload the browser tab.\n *\n * @note This method first ensures the XR session is terminated,\n * to avoid blocking the Oculus Quest page.\n *\n * @param engine The engine instance\n */\nfunction reloadPage(engine: WonderlandEngine) {\n /* End XR session for Oculus Quest blocking reload otherwise */\n const session = engine.xr?.session ?? null;\n if (!session) {\n location.reload();\n return;\n }\n session.end().then(() => location.reload());\n}\n\n/**\n * Main Wonderland Engine instance.\n *\n * Controls the canvas, rendering, and JS <-> WASM communication.\n */\nexport class WonderlandEngine {\n /**\n * {@link Emitter} for WebXR session end events.\n *\n * Usage from within a component:\n *\n * ```js\n * this.engine.onXRSessionEnd.add(() => console.log(\"XR session ended.\"));\n * ```\n */\n get onXRSessionEnd(): Emitter {\n return this.#webxr.onSessionEnd;\n }\n\n /**\n * {@link RetainEmitter} for WebXR session start events.\n *\n * Usage from within a component:\n *\n * ```js\n * this.engine.onXRSessionStart.add((session, mode) => console.log(session, mode));\n * ```\n *\n * By default, this emitter is retained and will automatically call any callback added\n * while a session is already started:\n *\n * ```js\n * // XR session is already active.\n * this.engine.onXRSessionStart.add((session, mode) => {\n * console.log(session, mode); // Triggered immediately.\n * });\n * ```\n */\n get onXRSessionStart(): Emitter<[XRSession, XRSessionMode]> {\n return this.#webxr.onSessionStart;\n }\n\n /**\n * {@link Emitter} for canvas / main framebuffer resize events.\n *\n * Usage from within a component:\n *\n * ```js\n * this.engine.onResize.add(() => {\n * const canvas = this.engine.canvas;\n * console.log(`New Size: ${canvas.width}, ${canvas.height}`);\n * });\n * ```\n *\n * @note The size of the canvas is in physical pixels, and is set via {@link WonderlandEngine.resize}.\n */\n readonly onResize: Emitter = new Emitter();\n\n /** Whether AR is supported by the browser. */\n get arSupported(): boolean {\n return this.#webxr.arSupported;\n }\n\n /** Whether VR is supported by the browser. */\n get vrSupported(): boolean {\n return this.#webxr.vrSupported;\n }\n\n /**\n * {@link RetainEmitter} signalling the end of the loading screen.\n *\n * Listeners get notified when the first call to {@link Scene#load()} is\n * invoked. At this point the new scene has not become active, and none of\n * its resources or components are initialized.\n *\n * Compared to {@link onSceneLoaded}, this does not wait for all components\n * to be fully initialized and activated. Any handler added inside\n * {@link Component#init()}, {@link Component#start()} or\n * {@link Component#onActivate()} will be called immediately.\n *\n * Usage:\n *\n * ```js\n * this.engine.onLoadingScreenEnd.add(() => console.log(\"Wait is over!\"));\n * ```\n */\n readonly onLoadingScreenEnd = new RetainEmitter();\n\n /**\n * {@link Emitter} for scene loaded events.\n *\n * Listeners get notified when a call to {@link Scene#load()} finishes. At\n * this point all resources are loaded and all components had their\n * {@link Component#init()} as well as (if active)\n * {@link Component#start()} and {@link Component#onActivate()} methods\n * called.\n *\n * Usage from within a component:\n *\n * ```js\n * this.engine.onSceneLoaded.add(() => console.log(\"Scene switched!\"));\n * ```\n *\n * @deprecated Use {@link onSceneActivated} instead.\n */\n readonly onSceneLoaded = new Emitter();\n\n /**\n * {@link Emitter} for scene activated events.\n *\n * This listener is notified with the old scene as first parameter, and\n * the new scene as second.\n *\n * This listener is notified after all resources are loaded and all components had their\n * {@link Component#init()} as well as (if active)\n * {@link Component#start()} and {@link Component#onActivate()} methods\n * called.\n *\n * Usage from within a component:\n *\n * ```js\n * this.engine.onSceneActivated.add((oldScene, newScene) => {\n * console.log(`Scene switch from ${oldScene.filename} to ${newScene.filename}`);\n * });\n * ```\n */\n readonly onSceneActivated = new Emitter<[Scene, Scene]>();\n\n /**\n * Triggered when a hot reload is requested.\n *\n * When using the Wonderland Editor, this will be triggered when packaging\n * the project.\n *\n * @note When {@link autoHotReload} is `true`, the emitter is notified after the\n * scene is reloaded.\n *\n * To customize the hot reload behavior, set the {@link autoHotReload} to `false`,\n * and use this emitter:\n *\n * ```js\n * this.engine.onHotReloadRequest.add(async (filename: string) => {\n * // This handler can be used to re-download a scene, download components,\n * // fetch an API, anything!\n * await this.loadMainScene(filename);\n * });\n * ```\n */\n readonly onHotReloadRequest: Emitter<[string]> = new Emitter();\n\n /**\n * Access to internationalization.\n */\n readonly i18n: I18N = new I18N(this);\n\n /**\n * WebXR related state, `null` if no XR session is active.\n */\n get xr(): XRSessionState | null {\n return this.#webxr.sessionState;\n }\n\n /**\n * If `true`, {@link Texture}, {@link Object3D}, and {@link Component}\n * instances have their prototype erased upon destruction.\n *\n * #### Object\n *\n * ```js\n * engine.erasePrototypeOnDestroy = true;\n *\n * const obj = engine.scene.addObject();\n * obj.name = 'iamalive';\n * console.log(obj.name); // Prints 'iamalive'\n *\n * obj.destroy();\n * console.log(obj.name); // Throws an error\n * ```\n *\n * #### Component\n *\n * Components will also be affected:\n *\n * ```js\n * class MyComponent extends Component {\n * static TypeName = 'my-component';\n * static Properties = {\n * alive: Property.bool(true)\n * };\n *\n * start() {\n * this.destroy();\n * console.log(this.alive) // Throws an error\n * }\n * }\n * ```\n *\n * A component is also destroyed if its ancestor gets destroyed:\n *\n * ```js\n * class MyComponent extends Component {\n * ...\n * start() {\n * this.object.parent.destroy();\n * console.log(this.alive) // Throws an error\n * }\n * }\n * ```\n *\n * @note Native components will not be erased if destroyed via object destruction:\n *\n * ```js\n * const mesh = obj.addComponent('mesh');\n * obj.destroy();\n * console.log(mesh.active) // Doesn't throw even if the mesh is destroyed\n * ```\n *\n * @since 1.1.1\n */\n erasePrototypeOnDestroy = false;\n\n /**\n * If `true`, the materials will be wrapped in a proxy to support pre-1.2.0\n * material access, i.e.,\n *\n * ```js\n * const material = new Material(engine, 'Phong Opaque');\n * material.diffuseColor = [1.0, 0.0, 0.0, 1.0];\n * ```\n *\n * If `false`, property accessors will not be available and material\n * properties should be accessed via methods, i.e.,\n *\n * ```js\n * const PhongOpaque = engine.materials.getTemplate('Phong Opaque');\n * const material = new PhongOpaque();\n * material.setDiffuseColor([1.0, 0.0, 0.0, 1.0]);\n * ...\n * const diffuse = material.getDiffuseColor();\n * ```\n *\n * When disabled, reading/writing to materials is slightly more efficient on the CPU.\n */\n legacyMaterialSupport = true;\n\n /**\n * If `true`, automatically reloads the scene sent by the editor socket.\n *\n * @note The {@link onHotReloadRequest} emitter is notified **after** the scene is re-loaded.\n *\n * To use custom hot-reload code, set this boolean to `false` and add the reloading code\n *\n */\n autoHotReload = true;\n\n /**\n * If `true`, uncompressed images will be automatically converted\n * to `ImageBitmap` before upload to the GPU.\n *\n * For more information, have a look at the\n * [ImageBitmap](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap) documentation.\n */\n loadUncompressedImagesAsBitmap = false;\n\n /**\n * Scene cache in scene manager.\n *\n * @hidden\n */\n _scenes: (Prefab | null)[] = [];\n\n /**\n * Currently active scene.\n *\n * @hidden\n */\n _scene: Scene;\n\n /** Incremented every time a new main scene is loaded. */\n _mainSceneVersion: number = 0;\n\n /** @hidden */\n private _textures!: TextureManager;\n /** @hidden */\n private _materials!: MaterialManager;\n /** @hidden */\n private _meshes!: MeshManager;\n /** @hidden */\n private _morphTargets!: ResourceManager;\n /** @hidden */\n private _fonts!: ResourceManager;\n /** @hidden */\n private _particleEffects!: ResourceManager;\n /**\n * Current loading uncompressed images.\n *\n * @note Indexed by js image index.\n *\n * @hidden\n */\n private _uncompressedPromises: Promise[] = [];\n\n /**\n * WebAssembly bridge.\n *\n * @hidden\n */\n #wasm: WASM;\n\n /**\n * Physics manager, only available when physx is enabled in the runtime.\n *\n * @hidden\n */\n #physics: Physics | null = null;\n\n /**\n * WebXR library.\n *\n * @hidden\n */\n #webxr: WebXR;\n\n /**\n * Resize observer to track for canvas size changes.\n *\n * @hidden\n */\n #resizeObserver: ResizeObserver | null = null;\n\n /**\n * Whether a components bundle has already been loaded or not.\n *\n * @hidden\n */\n #bundleCache: Set = new Set();\n\n /**\n * Create a new engine instance.\n *\n * @param wasm Wasm bridge instance\n * @param loadingScreen Loading screen .bin file data\n *\n * @hidden\n */\n constructor(wasm: WASM, loadingScreen: ArrayBuffer | null, withRenderer: boolean) {\n this.#wasm = wasm;\n this.#wasm['_setEngine'](this); /* String lookup to bypass private. */\n this.#wasm._loadingScreen = loadingScreen;\n\n const version = this.runtimeVersion;\n let versionString = `${version.major}.${version.minor}.${version.patch}`;\n if (version.rc > 0) {\n versionString += `-rc${version.rc}`;\n }\n this.log.info(LogTag.Engine, 'Wonderland Engine runtime version:', versionString);\n\n this.#webxr = new WebXR(this);\n\n checkRuntimeCompatibility(this.#wasm.runtimeVersion);\n\n /* Setup the error handler. This is used to to manage native errors. */\n this.#wasm._wl_set_error_callback(\n this.#wasm.addFunction((messagePtr: number) => {\n throw new Error(this.#wasm.UTF8ToString(messagePtr));\n }, 'vi')\n );\n\n this.#wasm._wl_application_create();\n\n this.#physics = null;\n\n if (this.#wasm.withPhysX) {\n this.#physics = new Physics(this);\n }\n\n this._init(withRenderer);\n\n this._scene = this._reload(0);\n\n this.canvas.addEventListener(\n 'webglcontextlost',\n (e) => this.log.error(LogTag.Engine, 'Context lost:', e),\n false\n );\n }\n\n /**\n * Start the engine if it's not already running.\n *\n * When using the {@link loadRuntime} function, this method is called\n * automatically.\n */\n start(): void {\n this.wasm._wl_application_start();\n }\n\n /**\n * Register a custom JavaScript component type.\n *\n * You can register a component directly using a class inheriting from {@link Component}:\n *\n * ```js\n * import { Component, Type } from '@wonderlandengine/api';\n *\n * export class MyComponent extends Component {\n * static TypeName = 'my-component';\n * static Properties = {\n * myParam: {type: Type.Float, default: 42.0},\n * };\n * init() {}\n * start() {}\n * update(dt) {}\n * onActivate() {}\n * onDeactivate() {}\n * onDestroy() {}\n * });\n *\n * // Here, we assume we have an engine already instantiated.\n * // In general, the registration occurs in the `index.js` file in your\n * // final application.\n * engine.registerComponent(MyComponent);\n * ```\n *\n * {@label CLASSES}\n * @param classes Custom component(s) extending {@link Component}.\n *\n * @since 1.0.0\n */\n registerComponent(...classes: ComponentConstructor[]) {\n for (const arg of classes) {\n this.wasm._registerComponent(arg);\n }\n }\n\n /**\n * Register a component bundle\n *\n * The module must be of the form:\n *\n * ```js\n * export default function(engine) {\n * engine.registerComponent(MyComponent);\n * ...\n * }\n * ```\n *\n * Alternatively, you can manually register a component bundle:\n *\n * ```js\n * const registerBundle = (await import(url)).default;\n * registerBundle(engine);\n * ```\n *\n * @param url The URL of the module to register.\n * @param nocache If `true`, force browser to re-download the file.\n */\n async registerBundle(url: string, nocache = false) {\n if (!nocache && this.#bundleCache.has(url)) return;\n this.#bundleCache.add(url);\n\n if (nocache) url += `?t=${Date.now()}`;\n const register = (await import(url)).default;\n if (typeof register !== 'function') {\n throw new Error(\n `The bundle '${url}' doesn't have a default exported registration function`\n );\n }\n register(this);\n }\n\n /**\n * Update the loading screen progress bar.\n *\n * @param value Current loading percentage, in the range [0; 1].\n *\n * @since 1.2.1\n */\n setLoadingProgress(percentage: number) {\n this.wasm._wl_set_loading_screen_progress(clamp(percentage, 0, 1));\n }\n\n /**\n * Switch the current active scene.\n *\n * Once active, the scene will be updated and rendered on the canvas.\n *\n * The currently active scene is accessed via {@link WonderlandEngine.scene}:\n *\n * ```js\n * import {Component} from '@wonderlandengine/api';\n *\n * class MyComponent extends Component{\n * start() {\n * console.log(this.scene === this.engine.scene); // Prints `true`\n * }\n * }\n * ```\n *\n * @note This method will throw if the scene isn't activatable.\n *\n * #### Component Lifecycle\n *\n * Marking a scene as active will:\n * * Call {@link Component#onDeactivate} for all active components of the previous scene\n * * Call {@link Component#onActivate} for all active components of the new scene\n *\n * #### Usage\n *\n * ```js\n * const scene = await engine.loadScene('Scene.bin');\n * engine.switchTo(scene);\n * ```\n *\n * @returns A promise that resolves once the scene is ready.\n *\n * @since 1.2.0\n */\n async switchTo(scene: Scene, opts: ActivateOptions = {}) {\n this.wasm._wl_deactivate_activeScene();\n\n /* Switch reference on engine **just** before activating, to allow\n * component to use `this.engine.scene` in `onActivate()`/`start()`. */\n const previous = this.scene;\n this._preactivate(scene);\n\n this.wasm._wl_scene_activate(scene._index);\n\n /* Enable/disable required views */\n this.#webxr.updateViewState(undefined);\n\n /* We loaded new views, update WebXR projection parameters */\n this.#webxr.updateProjectionParams(undefined, undefined);\n\n if (!this.onLoadingScreenEnd.isDataRetained) {\n this.onLoadingScreenEnd.notify();\n }\n\n /* For now, we always automatically download dependencies for\n * the user, i.e., separate textures.bin and languages.bin.\n *\n * In the future, we can eventually expose this in the chunk API\n * to give more control to the user if needed.\n *\n * We do not make the user wait until the textures download is complete. */\n const promise = scene._downloadDependencies();\n\n await this.i18n.setLanguage(this.i18n.languageCode(0));\n\n const {dispatchReadyEvent = false, waitForDependencies = false} = opts;\n\n if (waitForDependencies) await promise;\n\n this.onSceneActivated.notify(previous, scene);\n\n if (dispatchReadyEvent) scene.dispatchReadyEvent();\n }\n\n /**\n * Load the scene from a URL, as the main scene of a new {@link Scene}.\n *\n * #### Usage\n *\n * ```js\n * // The method returns the main scene\n * const scene = await engine.loadMainScene();\n * ```\n *\n * #### Destruction\n *\n * Loading a new main scene entirely resets the state of the engine, and destroys:\n * - All loaded scenes, prefabs, and gltf files\n * - Meshes\n * - Textures\n * - Materials\n *\n * @note This method can only load Wonderland Engine `.bin` files.\n *\n * @param options The URL pointing to the scene to load or an object\n * holding additional loading options.\n * @param progress Optional progress callback. When setting a custom\n * callback, you need to manually call {@link setLoadingProgress} to\n * get progress updates in the loading screen.\n * @returns The main scene of the new {@link Scene}.\n */\n async loadMainScene(\n options: LoadOptions & ActivateOptions,\n progress?: ProgressCallback\n ) {\n /**\n * @todo: It's currently possible to put the runtime in a limbo\n * state using hot-reload: Multiple reload requests are sent, and scene loading\n * ends up being interleaved.\n *\n * Add a system to cancel the last loading, if any.\n */\n\n progress ??= (bytes: number, size: number) => {\n this.log.info(LogTag.Scene, `Scene downloading: ${bytes} / ${size}`);\n this.setLoadingProgress(bytes / size);\n };\n\n const opts = Prefab.makeUrlLoadOptions(options);\n const {streamed = true, nocache = false} = opts;\n if (streamed) {\n const options = await Scene.loadStream(opts, progress);\n const {stream, url} = Prefab.validateStreamOptions(options);\n return this._loadMainScene(stream, url, nocache, options as ActivateOptions);\n } else {\n const options = await Scene.loadBuffer(opts, progress);\n return this.loadMainSceneFromBuffer(options);\n }\n }\n\n /**\n * Similar to {@link WonderlandEngine.loadMainScene}, but loading is done\n * from an `ArrayBuffer`.\n *\n * @param options An object containing the buffer and extra metadata.\n * @returns The main scene of the new {@link Scene}.\n */\n async loadMainSceneFromBuffer(options: InMemoryLoadOptions & ActivateOptions) {\n const {nocache = false} = options;\n const {buffer, url} = Prefab.validateBufferOptions(options);\n return this._loadMainScene(buffer, url, nocache, options);\n }\n\n /**\n * Load a {@link Prefab} from a URL.\n *\n * #### Usage\n *\n * ```js\n * const prefab = await engine.loadPrefab('Prefab.bin');\n * ```\n *\n * @note This method can only load Wonderland Engine `.bin` files.\n * @note This method is a wrapper around {@link WonderlandEngine.loadPrefabFromBuffer}.\n *\n * @param url The URL pointing to the prefab to load.\n * @param progress Optional progress callback.\n * @returns The loaded {@link Prefab}.\n */\n async loadPrefab(options: LoadOptions, progress?: ProgressCallback) {\n const opts = Prefab.makeUrlLoadOptions(options);\n const {streamed = true} = opts;\n if (streamed) {\n const options = await Scene.loadStream(opts, progress);\n const scene = await this._loadSceneFromStream(Prefab, options);\n this._validateLoadedPrefab(scene);\n scene._initialize();\n return scene;\n } else {\n const options = await Scene.loadBuffer(opts, progress);\n return this.loadPrefabFromBuffer(options);\n }\n }\n\n /**\n * Similar to {@link WonderlandEngine.loadPrefab}, but loading is done from\n * an `ArrayBuffer`.\n *\n * @param options An object containing the buffer and extra metadata.\n * @returns A new loaded {@link Prefab}.\n */\n loadPrefabFromBuffer(options: InMemoryLoadOptions) {\n const scene = this._loadSceneFromBuffer(Prefab, options);\n this._validateLoadedPrefab(scene);\n scene._initialize();\n return scene;\n }\n\n /**\n * Load a scene from a URL.\n *\n * At the opposite of {@link WonderlandEngine.loadMainScene}, the scene loaded\n * will be added to the list of existing scenes, and its resources will be made\n * available for other scenes/prefabs/gltf to use.\n *\n * #### Resources Sharing\n *\n * Upon loading, the scene resources are added in the engine, and references\n * to those resources are updated.\n *\n * It's impossible for a scene loaded with this method to import pipelines.\n * Thus, the loaded scene will reference existing pipelines in the main scene,\n * based on their names.\n *\n * #### Usage\n *\n * ```js\n * const scene = await engine.loadScene('Scene.bin');\n * ```\n *\n * @note This method can only load Wonderland Engine `.bin` files.\n *\n * @param options The URL pointing to the scene to load or an object\n * holding additional loading options.\n * @param progress Optional progress callback.\n * @returns A new loaded {@link Scene}.\n */\n async loadScene(options: LoadOptions, progress?: ProgressCallback) {\n const opts = Prefab.makeUrlLoadOptions(options);\n const {streamed = true} = opts;\n if (streamed) {\n const options = await Scene.loadStream(opts, progress);\n const scene = await this._loadSceneFromStream(Scene, options);\n this._validateLoadedScene(scene);\n scene._initialize();\n return scene;\n } else {\n const options = await Scene.loadBuffer(opts, progress);\n return this.loadSceneFromBuffer(options);\n }\n }\n\n /**\n * Create a glTF scene from a URL.\n *\n * @note Loading glTF files requires `enableRuntimeGltf` to be checked in\n * the editor Project Settings.\n *\n * @note This method is a wrapper around {@link WonderlandEngine.loadGLTFFromBuffer}.\n *\n * @param options The URL pointing to the scene to load or an object\n * holding additional loading options, optionally including\n * {@link GLTFOptions}. {@link LoadOptions.streamed} is ignored as\n * streamed glTF parsing isn't supported.\n * @param progress Optional progress callback.\n * @returns A new loaded {@link PrefabGLTF}.\n */\n async loadGLTF(opts: LoadOptions & GLTFOptions, progress?: ProgressCallback) {\n const memOptions = await Scene.loadBuffer(opts, progress);\n const options = isString(opts) ? memOptions : {...opts, ...memOptions};\n return this.loadGLTFFromBuffer(options);\n }\n\n /**\n * Similar to {@link WonderlandEngine.loadScene}, but loading is done from\n * an `ArrayBuffer`.\n *\n * @throws If the scene is streamable.\n *\n * @param options An object containing the buffer and extra metadata.\n * @returns A new loaded {@link Scene}.\n */\n loadSceneFromBuffer(options: InMemoryLoadOptions) {\n const scene = this._loadSceneFromBuffer(Scene, options);\n this._validateLoadedScene(scene);\n scene._initialize();\n return scene;\n }\n\n /**\n * Similar to {@link WonderlandEngine.loadGLTF}, but loading is done from\n * an `ArrayBuffer`.\n *\n * @note Loading glTF files requires `enableRuntimeGltf` to be checked in\n * the editor Project Settings.\n *\n * @param options An object containing the buffer and extra glTF metadata.\n * @returns A new loaded {@link PrefabGLTF}.\n */\n loadGLTFFromBuffer(options: InMemoryLoadOptions & GLTFOptions) {\n Scene.validateBufferOptions(options);\n const {buffer, extensions = false} = options;\n\n const wasm = this.wasm;\n\n if (!wasm._wl_glTF_scene_create) {\n throw new Error(\n 'Loading .gltf files requires `enableRuntimeGltf` to be checked in the editor Project Settings.'\n );\n }\n\n const ptr = wasm.copyBufferToHeap(buffer);\n\n try {\n const index = wasm._wl_glTF_scene_create(extensions, ptr, buffer.byteLength);\n const scene = new PrefabGLTF(this, index);\n this._scenes[scene._index] = scene;\n /* Start downloading uncompressed textures as soon as possible */\n if (this.runtimeVersion.patch) {\n this._loadUncompressedImages(scene._index);\n }\n return scene;\n } finally {\n /* Catch calls to abort(), e.g. via asserts */\n wasm._free(ptr);\n }\n }\n\n /**\n * Checks whether the given component is registered or not.\n *\n * @param typeOrClass A string representing the component typename (e.g., `'cursor-component'`),\n * or a component class (e.g., `CursorComponent`).\n * @returns `true` if the component is registered, `false` otherwise.\n */\n isRegistered(typeOrClass: string | ComponentConstructor) {\n return this.#wasm.isRegistered(\n isString(typeOrClass) ? typeOrClass : typeOrClass.TypeName\n );\n }\n\n /**\n * Retrieve the registered component from its type name.\n *\n * @param typename The component {@link Component.TypeName} property\n * @returns The class if a component was registered with given name, `null` otherwise.\n */\n getComponentClass(\n typename: string\n ): ComponentConstructor | null {\n const index = this.wasm._componentTypeIndices[typename];\n if (index === undefined) return null;\n return this.wasm._componentTypes[index] as ComponentConstructor;\n }\n\n /**\n * Resize the canvas and the rendering context.\n *\n * @note The `width` and `height` parameters will be scaled by the\n * `devicePixelRatio` value. By default, the pixel ratio used is\n * [window.devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio).\n *\n * @param width The width, in CSS pixels.\n * @param height The height, in CSS pixels.\n * @param devicePixelRatio The pixel ratio factor.\n */\n resize(width: number, height: number, devicePixelRatio = window.devicePixelRatio) {\n width = width * devicePixelRatio;\n height = height * devicePixelRatio;\n this.canvas.width = width;\n this.canvas.height = height;\n this.wasm._wl_application_resize(width, height);\n this.onResize.notify();\n }\n\n /**\n * Run the next frame.\n *\n * @param fixedDelta The elapsed time between this frame and the previous one.\n *\n * @note The engine automatically schedules next frames. You should only\n * use this method for testing.\n */\n nextFrame(fixedDelta: number = 0) {\n this.#wasm._wl_nextFrame(fixedDelta);\n }\n\n /**\n * Request an XR session.\n *\n * @note Please use this call instead of directly calling `navigator.xr.requestSession()`.\n * Wonderland Engine requires to be aware that a session is started, and this\n * is done through this call.\n *\n * @param mode The XR mode.\n * @param features An array of required features, e.g., `['local-floor', 'hit-test']`.\n * @param optionalFeatures An array of optional features, e.g., `['bounded-floor', 'depth-sensing']`.\n * @returns A promise resolving with the `XRSession`, a string error message otherwise.\n */\n requestXRSession(\n mode: XRSessionMode,\n features: string[],\n optionalFeatures: string[] = []\n ): Promise {\n return this.#webxr.requestSession(mode, features, optionalFeatures);\n }\n\n /**\n * Offer an XR session.\n *\n * Adds an interactive UI element to the browser interface to start an XR\n * session. Browser support is optional, so it's advised to still allow\n * requesting a session with a UI element on the website itself.\n *\n * @note Please use this call instead of directly calling `navigator.xr.offerSession()`.\n * Wonderland Engine requires to be aware that a session is started, and this\n * is done through this call.\n *\n * @param mode The XR mode.\n * @param features An array of required features, e.g., `['local-floor', 'hit-test']`.\n * @param optionalFeatures An array of optional features, e.g., `['bounded-floor', 'depth-sensing']`.\n * @returns A promise resolving with the `XRSession`, a string error message otherwise.\n *\n * @since 1.1.5\n */\n offerXRSession(\n mode: XRSessionMode,\n features: string[],\n optionalFeatures: string[] = []\n ): Promise {\n return this.#webxr.offerSession(mode, features, optionalFeatures);\n }\n\n /**\n * Wrap an object ID using {@link Object}.\n *\n * @note This method performs caching and will return the same\n * instance on subsequent calls.\n *\n * @param objectId ID of the object to create.\n * @returns The object\n *\n * @deprecated Use {@link Scene#wrap} instead.\n *\n */\n wrapObject(objectId: number): Object3D {\n return this.scene.wrap(objectId);\n }\n\n toString() {\n return 'engine';\n }\n\n /* Public Getters & Setter */\n\n /** Currently active scene. */\n get scene(): Scene {\n return this._scene;\n }\n\n /**\n * WebAssembly bridge.\n *\n * @note Use with care. This object is used to communicate\n * with the WebAssembly code throughout the api.\n *\n * @hidden\n */\n get wasm(): WASM {\n return this.#wasm;\n }\n\n /**\n * WebXR api.\n *\n * @hidden\n */\n get webxr(): WebXR {\n return this.#webxr;\n }\n\n /** Canvas element that Wonderland Engine renders to. */\n get canvas(): HTMLCanvasElement {\n return this.#wasm.canvas;\n }\n\n /**\n * Current WebXR session or `null` if no session active.\n *\n * @deprecated Use {@link XRSessionState.session} on the {@link xr}\n * object instead.\n */\n get xrSession(): XRSession | null {\n return this.xr?.session ?? null;\n }\n\n /**\n * Current WebXR frame or `null` if no session active.\n *\n * @deprecated Use {@link XRSessionState.frame} on the {@link xr}\n * object instead.\n */\n get xrFrame(): XRFrame | null {\n return this.xr?.frame ?? null;\n }\n\n /**\n * Current WebXR base layer or `null` if no session active.\n *\n * @deprecated Use {@link XRSessionState.baseLayer} on the {@link xr}\n * object instead.\n */\n get xrBaseLayer(): XRProjectionLayer | XRWebGLLayer | null {\n return this.xr?.baseLayer ?? null;\n }\n\n /**\n * Current WebXR framebuffer or `null` if no session active.\n *\n * @deprecated Use {@link XRSessionState.framebuffers} on the {@link xr}\n * object instead.\n */\n get xrFramebuffer(): WebGLFramebuffer | null {\n return this.xr?.framebuffers[0] ?? null;\n }\n\n /**\n * WebXR framebuffer scale factor.\n */\n get xrFramebufferScaleFactor() {\n return this.#webxr.framebufferScaleFactor;\n }\n\n set xrFramebufferScaleFactor(value: number) {\n this.#webxr.framebufferScaleFactor = value;\n }\n\n /** Physics manager, only available when physx is enabled in the runtime. */\n get physics() {\n return this.#physics;\n }\n\n /** Texture resources */\n get textures() {\n return this._textures;\n }\n\n /** Material resources */\n get materials() {\n return this._materials;\n }\n\n /** Mesh resources */\n get meshes() {\n return this._meshes;\n }\n\n /** Morph target set resources */\n get morphTargets() {\n return this._morphTargets;\n }\n\n /** Font resources */\n get fonts() {\n return this._fonts;\n }\n\n /** Particle effect resources */\n get particleEffects() {\n return this._particleEffects;\n }\n\n /** Get all uncompressed images. */\n get images(): ImageLike[] {\n const wasm = this.wasm;\n const max = wasm._tempMemSize >> 1;\n const count = wasm._wl_get_images(wasm._tempMem, max);\n const result = new Array(count);\n for (let i = 0; i < count; ++i) {\n const index = wasm._tempMemUint16[i];\n result[i] = wasm._images[index];\n }\n return result;\n }\n\n /**\n * Promise that resolve once all uncompressed images are loaded.\n *\n * This is equivalent to calling {@link WonderlandEngine.images}, and wrapping each\n * `load` listener into a promise.\n */\n get imagesPromise(): Promise {\n const wasm = this.wasm;\n const max = wasm._tempMemSize >> 1;\n const count = wasm._wl_get_images(wasm._tempMem, max);\n const result = new Array(count);\n for (let i = 0; i < count; ++i) {\n const index = wasm._tempMemUint16[i];\n const loading = this._uncompressedPromises[index];\n result[i] = (loading ?? Promise.resolve()).then(() => wasm._images[index]);\n }\n return Promise.all(result);\n }\n\n /**\n * `true` if the texture streaming is currently idle, i.e.,\n * not attempting to upload any textures.\n *\n * @hidden\n */\n get isTextureStreamingIdle() {\n return !!this.wasm._wl_renderer_streaming_idle();\n }\n\n /**\n * `true` if reverse-Z rendering is supported and enabled.\n *\n * @hidden\n */\n get isReverseZEnabled() {\n return !!this.wasm._wl_renderer_isReverseZEnabled();\n }\n\n /*\n * Enable or disable the mechanism to automatically resize the canvas.\n *\n * Internally, the engine uses a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).\n * Changing the canvas css will thus automatically be tracked by the engine.\n */\n set autoResizeCanvas(flag: boolean) {\n const state = !!this.#resizeObserver;\n if (state === flag) return;\n\n if (!flag) {\n this.#resizeObserver?.unobserve(this.canvas);\n this.#resizeObserver = null;\n return;\n }\n this.#resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === this.canvas) {\n this.resize(entry.contentRect.width, entry.contentRect.height);\n }\n }\n });\n this.#resizeObserver.observe(this.canvas);\n }\n\n /** `true` if the canvas is automatically resized by the engine. */\n get autoResizeCanvas() {\n return this.#resizeObserver !== null;\n }\n\n /** Retrieves the runtime version. */\n get runtimeVersion(): Version {\n return this.#wasm.runtimeVersion;\n }\n\n /** Engine {@link Logger}. Use it to turn on / off logging. */\n get log(): Logger {\n return this.#wasm._log;\n }\n\n /* Internal-Only Methods */\n\n /**\n * Initialize the engine.\n *\n * @note Should be called after the WebAssembly is fully loaded.\n *\n * @hidden\n */\n _init(withRenderer: boolean): boolean {\n const success = this.#wasm._wl_application_init(withRenderer);\n if (!success) {\n throw new Error(`Failed to initializing Wonderland runtime`);\n }\n\n this.resize(this.canvas.clientWidth, this.canvas.clientHeight);\n return true;\n }\n\n /**\n * Reset the runtime state, including:\n * - Component cache\n * - Images\n * - Callbacks\n *\n * @note This api is meant to be used internally.\n *\n * @hidden\n */\n _reset() {\n this.wasm.reset();\n this._scenes.length = 0;\n this._scene = this._reload(0);\n this.#bundleCache.clear();\n return this.switchTo(this._scene);\n }\n\n /**\n * Attempt to hot reload the engine.\n *\n * @param filename URL of the file to use for the reload.\n * If `null`, forces a full page reload.\n *\n * @hidden\n */\n async _reloadRequest(filename: string | null) {\n if (filename === null) {\n reloadPage(this);\n return;\n }\n\n try {\n if (this.autoHotReload)\n await this.loadMainScene({url: filename, nocache: true});\n } catch (e) {\n console.error('Hot reload request failed to load main scene, reason:', e);\n setTimeout(() => reloadPage(this), 1000);\n return;\n }\n\n this.onHotReloadRequest.notify(filename);\n }\n\n /**\n * Add an empty scene.\n *\n * @returns The newly created scene\n *\n * @hidden\n */\n _createEmpty(): Scene {\n const wasm = this.wasm;\n const index = wasm._wl_scene_create_empty();\n const scene = new Scene(this, index);\n this._scenes[index] = scene;\n return scene;\n }\n\n /** @hidden */\n _destroyScene(instance: Prefab) {\n const wasm = this.wasm;\n wasm._wl_scene_destroy(instance._index);\n\n const index = instance._index;\n (instance._index as number) = -1;\n if (this.erasePrototypeOnDestroy) {\n Object.setPrototypeOf(instance, DestroyedPrefabInstance);\n }\n\n this._scenes[index] = null;\n }\n\n /**\n * Reload the state of the engine with a new main scene.\n *\n * @param index Scene index.\n *\n * @hidden\n */\n private _reload(index: number) {\n const scene = new Scene(this, index);\n this._scenes[index] = scene;\n\n this._textures = new TextureManager(this);\n this._materials = new MaterialManager(this);\n this._meshes = new MeshManager(this);\n this._morphTargets = new ResourceManager(this, MorphTargets);\n this._fonts = new ResourceManager(this, Font);\n this._particleEffects = new ResourceManager(this, ParticleEffect);\n this._uncompressedPromises.length = 0;\n\n return scene;\n }\n\n /**\n * Helper to load a main scene from an `ArrayBuffer` or `ReadableStream`.\n *\n * @param data Buffer or stream.\n * @param url Base URL.\n * @param nocache If `true`, force a browser reload of files to download.\n * Only affects components loading for now.\n * @param options Activation options.\n * @returns The loaded main scene.\n *\n * @hidden\n */\n private async _loadMainScene(\n data: ArrayBuffer | ReadableStream,\n url: string,\n nocache: boolean,\n options: ActivateOptions\n ) {\n if (nocache) url += `?t=${Date.now()}`;\n\n const wasm = this.#wasm;\n\n /*\n * - Deactivate currently active scene\n * - Destroy all scenes\n * - Mark all resources as destroyed\n * - Activation of main scene to prevent the runtime to be in limbo\n */\n\n wasm._wl_deactivate_activeScene();\n\n ++this._mainSceneVersion;\n\n /* Only destroy all scene once the current active one is disabled */\n for (let i = this._scenes.length - 1; i >= 0; --i) {\n const scene = this._scenes[i];\n if (scene) scene.destroy();\n }\n\n /* Mark all resources as destroyed */\n this._textures._clear();\n this._materials._clear();\n this._meshes._clear();\n this._morphTargets._clear();\n this._uncompressedPromises.length = 0;\n\n const stream =\n data instanceof ReadableStream\n ? data\n : new ReadableStream(new ArrayBufferSource(data));\n const sink = new ChunkedSceneLoadSink(this, SceneType.Main, url);\n await stream.pipeTo(new WritableStream(sink));\n\n const mainScene = this._reload(sink.sceneIndex);\n /* Start downloading uncompressed textures as soon as possible */\n this._loadUncompressedImages(mainScene._index);\n\n /**\n * @todo(2.0.0)\n *\n * Backward compatibility: We need to set this instance reference on the\n * engine **before** component creation occurs, since users have been accessing\n * the scene with `this.engine.scene` in `init()`.\n */\n this._preactivate(mainScene);\n\n const componentsBundle = mainScene.componentsBundle;\n if (componentsBundle) {\n /* Relative URL are resolved from the script that imports, and **not** from\n * the document. We assume that the components bundle is located alongside\n * other resources, relative to the .bin.\n *\n * @note: `componentsBundle` already contains the same baseURL as the .bin. */\n const bundleURL = new URL(componentsBundle, document.baseURI);\n const url = nocache ? bundleURL.href.split('?')[0] : bundleURL.href;\n await this.registerBundle(url, nocache);\n }\n mainScene._initialize();\n\n await this.switchTo(mainScene, options);\n\n return mainScene;\n }\n\n /**\n * Helper to load prefab and activatable scene from an `ArrayBuffer`.\n *\n * @param PrefabClass Scene constructor.\n * @param options Loading options.\n * @returns The loaded prefab.\n *\n * @hidden\n */\n private _loadSceneFromBuffer(\n PrefabClass: Constructor,\n options: InMemoryLoadOptions\n ) {\n const {buffer, url} = Scene.validateBufferOptions(options);\n\n const sink = new ChunkedSceneLoadSink(this, SceneType.Prefab, url);\n sink.write(new Uint8Array(buffer));\n sink.close();\n const index = sink.sceneIndex;\n\n /* The scene was successfully loaded on the wasm side, we need to\n * add it to the scene cache, since the wasm might read it. */\n const scene = new PrefabClass(this, index);\n this._scenes[index] = scene;\n /* Start downloading uncompressed textures as soon as possible */\n this._loadUncompressedImages(scene._index);\n\n return scene;\n }\n\n /**\n * Helper to load prefab and activatable scene from a `ReadableStream`.\n *\n * @param PrefabClass Scene constructor.\n * @param options Loading options.\n * @returns The loaded prefab.\n *\n * @hidden\n */\n private async _loadSceneFromStream(\n PrefabClass: Constructor,\n options: StreamLoadOptions\n ) {\n const {stream, url} = Scene.validateStreamOptions(options);\n const sink = new ChunkedSceneLoadSink(this, SceneType.Prefab, url);\n await stream.pipeTo(new WritableStream(sink));\n const index = sink.sceneIndex;\n\n /* The scene was successfully loaded on the wasm side, we need to\n * add it to the scene cache, since the wasm might read it. */\n const scene = new PrefabClass(this, index);\n this._scenes[index] = scene;\n /* Start downloading uncompressed textures as soon as possible */\n this._loadUncompressedImages(scene._index);\n\n return scene;\n }\n\n /**\n * Checks if the loaded scene is a prefab and throws an error if not.\n *\n * @param scene The loaded scene\n *\n * @hidden\n */\n private _validateLoadedPrefab(scene: Prefab) {\n if (this.wasm._wl_scene_activatable(scene._index)) {\n this.wasm._wl_scene_destroy(scene._index);\n throw new Error(\n 'File is not a prefab. To load a scene, use loadScene() instead'\n );\n }\n }\n\n /**\n * Checks if the loaded scene is a scene (and not a prefab) and throws an\n * error if not.\n *\n * @param scene The loaded scene\n *\n * @hidden\n */\n private _validateLoadedScene(scene: Scene) {\n if (!this.wasm._wl_scene_activatable(scene._index)) {\n this.wasm._wl_scene_destroy(scene._index);\n throw new Error(\n 'File is not a scene. To load a prefab, use loadPrefab() instead'\n );\n }\n }\n\n /**\n * Set the scene data on the engine, before the activation occurs.\n *\n * @hidden\n */\n private _preactivate(scene: Scene) {\n this._scene = scene;\n /* Update the current active scene in the physx manager */\n if (this.physics) (this.physics._hit._scene as Scene) = scene;\n }\n\n /**\n * Notify the runtime when images are ready to be uploaded.\n *\n * @param sceneIndex Index of the scene where the image originated\n * @hidden\n */\n private _loadUncompressedImages(sceneIndex: number) {\n const mainSceneVersion = this._mainSceneVersion;\n const wasm = this.wasm;\n\n const bitmapOptions: ImageBitmapOptions = {\n colorSpaceConversion: 'none',\n };\n\n const imageCount = wasm._wl_image_count();\n for (let i = 0; i < imageCount; ++i) {\n if (wasm._wl_image_originalScene(i) !== sceneIndex) continue;\n\n const jsImageIndex = wasm._wl_image_get_jsImage_index(i);\n\n const image = wasm._images[jsImageIndex];\n if (!image) continue;\n\n let promise: Promise = onImageReady(image);\n if (this.loadUncompressedImagesAsBitmap) {\n promise = promise.then((img) => createImageBitmap(img, bitmapOptions));\n }\n this._uncompressedPromises[jsImageIndex] = promise\n .then((img) => {\n /* Discard the promise if the main scene has been reloaded */\n if (this._mainSceneVersion === mainSceneVersion) {\n wasm._wl_image_markReady(i, img.width, img.height, needsFlipY(img));\n }\n })\n .catch((e) => {\n /* Discard the promise if the main scene has been reloaded */\n if (this._mainSceneVersion === mainSceneVersion) {\n this.log.error(\n LogTag.Scene,\n 'Failed to load uncompressed image',\n e\n );\n }\n });\n }\n }\n}\n", "/**\n * Assert that the given bit index is less than 32.\n *\n * @param bit The bit to test.\n */\nfunction assert(bit: number): void {\n if (bit >= 32) {\n throw new Error(`BitSet.enable(): Value ${bit} is over 31`);\n }\n}\n\n/**\n * Stores a bit pattern to quickly test if an index is enabled / disabled.\n *\n * This class can store up to **32** different values in the range [0; 31].\n *\n * #### Usage\n *\n * ```js\n * const bitset = new BitSet();\n * bitset.enable(10); // Enable bit at index `10`.\n * console.log(bitset.enabled(10)); // Prints 'true'.\n * ```\n *\n * #### TypeScript\n *\n * The set can be typed over an enum:\n *\n * ```ts\n * enum Tag {\n * First = 0,\n * Second = 1,\n * }\n *\n * const bitset = new BitSet();\n * bitset.enable(Tag.First);\n * ```\n */\nexport class BitSet {\n /** Enabled bits. @hidden */\n private _bits: number = 0;\n\n /**\n * Enable the bit at the given index.\n *\n * @param bits A spread of all the bits to enable.\n * @returns Reference to self (for method chaining).\n */\n enable(...bits: T[]) {\n for (const bit of bits) {\n assert(bit);\n /* Casts the result to an unsigned integer */\n this._bits |= (1 << bit) >>> 0;\n }\n return this;\n }\n\n /**\n * Enable all bits.\n *\n * @returns Reference to self (for method chaining).\n */\n enableAll() {\n this._bits = ~0;\n return this;\n }\n\n /**\n * Disable the bit at the given index.\n *\n * @param bits A spread of all the bits to disable.\n * @returns Reference to self (for method chaining).\n */\n disable(...bits: T[]) {\n for (const bit of bits) {\n assert(bit);\n /* Casts the result to an unsigned integer */\n this._bits &= ~((1 << bit) >>> 0);\n }\n return this;\n }\n\n /**\n * Disable all bits.\n *\n * @returns Reference to self (for method chaining).\n */\n disableAll() {\n this._bits = 0;\n return this;\n }\n\n /**\n * Checker whether the bit is set or not.\n *\n * @param bit The bit to check.\n * @returns `true` if it's enabled, `false` otherwise.\n */\n enabled(bit: T) {\n return !!(this._bits & ((1 << bit) >>> 0));\n }\n}\n", "import {BitSet} from './bitset.js';\nimport {Emitter} from './event.js';\n\n/**\n * Logging levels supported by {@link Logger}.\n */\nexport enum LogLevel {\n Info = 0,\n Warn = 1,\n Error = 2,\n}\n\n/**\n * Logging wrapper.\n *\n * This is used to allow turning on/off:\n * - `console.log`\n * - `console.warn`\n * - `console.error`\n *\n * #### Usage\n *\n * ```js\n * import {Logger, LogLevel, LogTag} from '@wonderlandengine/api';\n *\n * // Create a logger with only the \"error\" and \"warn\" levels activated\n * const logger = new Logger(LogLevel.Warn, LogLevel.Error);\n *\n * // Only the \"error\" and \"warn\" levels are activated,\n * // this message isn't logged.\n * logger.info(LogTag.Component, 'This message is shushed')\n *\n * // Prints 'Hello Error!'\n * logger.error(LogTag.Component, 'Hello Error!');\n *\n * // Prints 'Hello Warning!'\n * logger.warn(LogTag.Component, 'Hello Warning!');\n * ```\n *\n * The log levels can be changed at anytime using the {@link BitSet} api:\n *\n * ```js\n * // Enable the \"info\" level\n * logger.levels.enable(LogLevel.Info);\n * * // Disable the \"warn\" level\n * logger.levels.disable(LogLevel.Warn);\n * ```\n *\n * #### Tags\n *\n * In addition, the logger supports tagging messages:\n *\n * ```js\n * import {Logger, LogLevel, LogTag} from '@wonderlandengine/api';\n *\n * const logger = new Logger(LogLevel.Info);\n *\n * logger.tags.disableAll();\n *\n * // All tags are off, this message isn't logged\n * logger.info(LogTag.Component, 'This message is shushed');\n *\n * logger.tags.enable(LogTag.Component);\n * logger.info(LogTag.Component, 'Hello World!') // Prints 'Hello World!'\n * ```\n *\n * The tagging system gives another layer of control to enable / disable\n * some specific logs.\n */\nexport class Logger {\n /**\n * Bitset of enabled levels.\n *\n * @hidden\n */\n levels: BitSet = new BitSet();\n\n /**\n * Bitset of enabled tags.\n *\n * @hidden\n */\n tags: BitSet = new BitSet().enableAll();\n\n /**\n * Notified when an `info`, `warn`, or `error` message is logged.\n *\n * @note The emitter is always notified, even if the logger levels\n * and tags are disabled.\n *\n * @note The message is passed as an array, similar to `console.log`.\n *\n * #### Usage\n *\n * ```js\n * import {LogLevel} from '@wonderlandengine/api';\n *\n * engine.logger.onLog.add((level, messages, tag) => {\n * // Only process error messages\n * if (level !== LogLevel.Error) return;\n *\n * // Only process engine logs\n * if (tag !== LogTag.Engine) return;\n *\n * const str = messages.join(' ');\n * sendAnalytics(str);\n * });\n * ```\n */\n onLog: Emitter<[LogLevel, unknown[], number]> = new Emitter();\n\n /**\n * Create a new logger instance.\n *\n * @param levels Default set of levels to enable.\n */\n constructor(...levels: LogLevel[]) {\n this.levels.enable(...levels);\n }\n\n /**\n * Log the message(s) using `console.log`.\n *\n * @param tag Tag represented by a positive integer.\n * @param msg A spread of message to log.\n * @returns Reference to self (for method chaining).\n */\n info(tag: number, ...msg: unknown[]): this {\n if (this.levels.enabled(LogLevel.Info) && this.tags.enabled(tag)) {\n console.log(...msg);\n }\n this.onLog.notify(LogLevel.Info, msg, tag);\n return this;\n }\n\n /**\n * Log the message(s) using `console.warn`.\n *\n * @param tag Tag represented by a positive integer.\n * @param msg A spread of message to log.\n * @returns Reference to self (for method chaining).\n */\n warn(tag: number, ...msg: unknown[]): this {\n if (this.levels.enabled(LogLevel.Warn) && this.tags.enabled(tag)) {\n console.warn(...msg);\n }\n this.onLog.notify(LogLevel.Warn, msg, tag);\n return this;\n }\n\n /**\n * Log the message(s) using `console.error`.\n *\n * @param tag Tag represented by a positive integer.\n * @param msg A spread of message to log.\n * @returns Reference to self (for method chaining).\n */\n error(tag: number, ...msg: unknown[]): this {\n if (this.levels.enabled(LogLevel.Error) && this.tags.enabled(tag)) {\n console.error(...msg);\n }\n this.onLog.notify(LogLevel.Error, msg, tag);\n return this;\n }\n}\n", "import {Version} from './version.js';\nimport {WonderlandEngine} from './engine.js';\nimport {ComponentProperty, Type, defaultPropertyCloner} from './property.js';\nimport {CBORReader, CBORType, getType} from './utils/cbor.js';\nimport {Logger} from './utils/logger.js';\nimport {\n inheritProperties,\n Component,\n ComponentConstructor,\n ComponentProto,\n BrokenComponent,\n AnimationComponent,\n LogTag,\n} from './wonderland.js';\nimport {ImageLike} from './types.js';\nimport {Scene} from './scene.js';\nimport {Prefab} from './prefab.js';\nimport {ComponentPropertyDecoder, setupComponentClass} from './component.js';\n\n/**\n * Low-level wrapper to interact with the WebAssembly code.\n *\n * @hidden\n */\nexport class WASM {\n /**\n * Emscripten wasm field.\n *\n * @note This api is meant to be used internally.\n */\n readonly wasm: ArrayBuffer = null!;\n\n /**\n * Emscripten canvas.\n *\n * @note This api is meant to be used internally.\n */\n readonly canvas: HTMLCanvasElement = null!;\n\n /**\n * WebGPU device.\n *\n * @note This api is meant to be used internally.\n */\n readonly preinitializedWebGPUDevice: any = null;\n\n /**\n * Convert a WASM memory view to a JavaScript string.\n *\n * @param ptr Pointer start\n * @param ptrEnd Pointer end\n * @returns JavaScript string\n */\n UTF8ViewToString: (ptr: number, ptrEnd: number) => string;\n\n /** Logger instance. */\n readonly _log: Logger = new Logger();\n\n /** If `true`, logs will not spam the console on error. */\n _deactivate_component_on_error: boolean = false;\n\n /** Temporary memory pointer. */\n _tempMem: number = null!;\n /** Temporary memory size. */\n _tempMemSize: number = 0;\n /** Temporary float memory view. */\n _tempMemFloat: Float32Array = null!;\n /** Temporary int memory view. */\n _tempMemInt: Int32Array = null!;\n /** Temporary uint8 memory view. */\n _tempMemUint8: Uint8Array = null!;\n /** Temporary uint32 memory view. */\n _tempMemUint32: Uint32Array = null!;\n /** Temporary uint16 memory view. */\n _tempMemUint16: Uint16Array = null!;\n\n /** Loading screen .bin file data */\n _loadingScreen: ArrayBuffer | null = null;\n\n /** List of callbacks triggered when the scene is loaded. */\n readonly _sceneLoadedCallback: any[] = [];\n\n /** Image cache. */\n _images: (ImageLike | null)[] = [null];\n\n /** Component instances. */\n private _components: Component[] = null!;\n\n /** Component Type info. */\n _componentTypes: ComponentConstructor[] = [];\n\n /** Index per component type name. */\n _componentTypeIndices: Record = {};\n\n /** Wonderland engine instance. */\n private _engine: WonderlandEngine = null!;\n\n /**\n * `true` if this runtime is using physx.\n *\n * @note This api is meant to be used internally.\n */\n private _withPhysX: boolean = false;\n\n /** Decoder for UTF8 `ArrayBuffer` to JavaScript string. */\n private readonly _utf8Decoder = new TextDecoder('utf8');\n\n /**\n * Registration index of {@link BrokenComponent}.\n *\n * This is used to return dummy instances when a component\n * isn't registered.\n *\n * @hidden\n */\n private readonly _brokenComponentIndex = 0;\n\n /**\n * Create a new instance of the WebAssembly <> API bridge.\n *\n * @param threads `true` if the runtime used has threads support\n */\n constructor(threads: boolean) {\n if (threads) {\n this.UTF8ViewToString = (s: number, e: number) => {\n if (!s) return '';\n return this._utf8Decoder.decode(this.HEAPU8.slice(s, e));\n };\n } else {\n this.UTF8ViewToString = (s: number, e: number) => {\n if (!s) return '';\n return this._utf8Decoder.decode(this.HEAPU8.subarray(s, e));\n };\n }\n\n (this._brokenComponentIndex as number) = this._registerComponent(BrokenComponent);\n }\n\n /** Retrieves the runtime version. */\n get runtimeVersion(): Version {\n /** We cannot use _tempMem because we are meant to be called before it's initialized */\n const tempBuf = this._malloc(4 * 2);\n const tempVersion = new Uint16Array(this.HEAP8.buffer, tempBuf, 4);\n this._wl_runtime_version(tempBuf);\n const version = {\n major: tempVersion[0],\n minor: tempVersion[1],\n patch: tempVersion[2],\n rc: tempVersion[3],\n };\n this._free(tempBuf);\n return version;\n }\n\n /**\n * Reset the cache of the library.\n *\n * @note Should only be called when tearing down the runtime.\n */\n reset() {\n /* Called first to perform cleanup. */\n this._wl_reset();\n\n this._components = null!;\n this._images.length = 1;\n this.allocateTempMemory(1024);\n\n this._componentTypes = [];\n this._componentTypeIndices = {};\n (this._brokenComponentIndex as number) = this._registerComponent(BrokenComponent);\n }\n\n /**\n * Checks whether the given component is registered or not.\n *\n * @param ctor A string representing the component typename (e.g., `'cursor-component'`).\n * @returns `true` if the component is registered, `false` otherwise.\n */\n isRegistered(type: string) {\n return type in this._componentTypeIndices;\n }\n\n /**\n * Register a legacy component in this Emscripten instance.\n *\n * @note This api is meant to be used internally.\n *\n * @param typeName The name of the component.\n * @param params An object containing the parameters (properties).\n * @param object The object's prototype.\n * @returns The registration index\n */\n _registerComponentLegacy(\n typeName: string,\n params: Record,\n object: ComponentProto\n ) {\n const ctor = class CustomComponent extends Component {};\n ctor.TypeName = typeName;\n ctor.Properties = params;\n Object.assign(ctor.prototype, object);\n return this._registerComponent(ctor);\n }\n\n /**\n * Register a class component in this Emscripten instance.\n *\n * @note This api is meant to be used internally.\n *\n * @param ctor The class to register.\n * @returns The registration index.\n */\n _registerComponent(ctor: ComponentConstructor) {\n if (!ctor.TypeName) throw new Error('no name provided for component.');\n if (!ctor.prototype._triggerInit) {\n throw new Error(\n `registerComponent(): Component ${ctor.TypeName} must extend Component`\n );\n }\n\n setupComponentClass(ctor);\n\n const typeIndex =\n ctor.TypeName in this._componentTypeIndices\n ? this._componentTypeIndices[ctor.TypeName]\n : this._componentTypes.length;\n this._componentTypes[typeIndex] = ctor;\n this._componentTypeIndices[ctor.TypeName] = typeIndex;\n\n if (ctor === BrokenComponent) return typeIndex;\n\n this._log.info(\n LogTag.Engine,\n 'Registered component',\n ctor.TypeName,\n `(class ${ctor.name})`,\n 'with index',\n typeIndex\n );\n\n if (ctor.onRegister) ctor.onRegister(this._engine);\n\n return typeIndex;\n }\n\n /**\n * Allocate the requested amount of temporary memory\n * in this WASM instance.\n *\n * @param size The number of bytes to allocate\n */\n allocateTempMemory(size: number) {\n this._log.info(LogTag.Engine, 'Allocating temp mem:', size);\n this._tempMemSize = size;\n if (this._tempMem) this._free(this._tempMem);\n this._tempMem = this._malloc(this._tempMemSize);\n this.updateTempMemory();\n }\n\n /**\n * @todo: Delete this and only keep `allocateTempMemory`\n *\n * @param size Number of bytes to allocate\n */\n requireTempMem(size: number) {\n if (this._tempMemSize >= size) return;\n /* Grow in 1kb increments */\n this.allocateTempMemory(Math.ceil(size / 1024) * 1024);\n }\n\n /**\n * Update the temporary memory views. This must be called whenever the\n * temporary memory address changes.\n *\n * @note This api is meant to be used internally.\n */\n updateTempMemory() {\n this._tempMemFloat = new Float32Array(\n this.HEAP8.buffer,\n this._tempMem,\n this._tempMemSize >> 2\n );\n this._tempMemInt = new Int32Array(\n this.HEAP8.buffer,\n this._tempMem,\n this._tempMemSize >> 2\n );\n this._tempMemUint32 = new Uint32Array(\n this.HEAP8.buffer,\n this._tempMem,\n this._tempMemSize >> 2\n );\n this._tempMemUint16 = new Uint16Array(\n this.HEAP8.buffer,\n this._tempMem,\n this._tempMemSize >> 1\n );\n this._tempMemUint8 = new Uint8Array(\n this.HEAP8.buffer,\n this._tempMem,\n this._tempMemSize\n );\n }\n\n /**\n * Returns a uint8 buffer view on temporary WASM memory.\n *\n * **Note**: this method might allocate if the requested memory is bigger\n * than the current temporary memory allocated.\n *\n * @param count The number of **elements** required\n * @returns A {@link TypedArray} over the WASM memory\n */\n getTempBufferU8(count: number): Uint8Array {\n this.requireTempMem(count);\n return this._tempMemUint8;\n }\n\n /**\n * Returns a uint16 buffer view on temporary WASM memory.\n *\n * **Note**: this method might allocate if the requested memory is bigger\n * than the current temporary memory allocated.\n *\n * @param count The number of **elements** required\n * @returns A {@link TypedArray} over the WASM memory\n */\n getTempBufferU16(count: number): Uint16Array {\n this.requireTempMem(count * 2);\n return this._tempMemUint16;\n }\n\n /**\n * Returns a uint32 buffer view on temporary WASM memory.\n *\n * **Note**: this method might allocate if the requested memory is bigger\n * than the current temporary memory allocated.\n *\n * @param count The number of **elements** required.\n * @returns A {@link TypedArray} over the WASM memory.\n */\n getTempBufferU32(count: number): Uint32Array {\n this.requireTempMem(count * 4);\n return this._tempMemUint32;\n }\n\n /**\n * Returns a int32 buffer view on temporary WASM memory.\n *\n * **Note**: this method might allocate if the requested memory is bigger\n * than the current temporary memory allocated.\n *\n * @param count The number of **elements** required.\n * @returns A {@link TypedArray} over the WASM memory.\n */\n getTempBufferI32(count: number): Int32Array {\n this.requireTempMem(count * 4);\n return this._tempMemInt;\n }\n\n /**\n * Returns a float32 buffer view on temporary WASM memory.\n *\n * **Note**: this method might allocate if the requested memory is bigger\n * than the current temporary memory allocated.\n *\n * @param count The number of **elements** required.\n * @returns A {@link TypedArray} over the WASM memory.\n */\n getTempBufferF32(count: number): Float32Array {\n this.requireTempMem(count * 4);\n return this._tempMemFloat;\n }\n\n /**\n * Copy the string into temporary WASM memory and retrieve the pointer.\n *\n * @note This method will compute the strlen and append a `\\0`.\n *\n * @note The result should be used **directly** otherwise it might get\n * overridden by any next call modifying the temporary memory.\n *\n * @param str The string to write to temporary memory\n * @param byteOffset The starting byte offset in the temporary memory at which\n * the string should be written. This is useful when using multiple temporaries.\n * @return The temporary pointer onto the WASM memory\n */\n tempUTF8(str: string, byteOffset = 0): number {\n const strLen = this.lengthBytesUTF8(str) + 1;\n this.requireTempMem(strLen + byteOffset);\n const ptr = this._tempMem + byteOffset;\n this.stringToUTF8(str, ptr, strLen);\n return ptr;\n }\n\n /**\n * Copy the buffer into the WASM heap.\n *\n * @note The returned pointer must be freed.\n *\n * @param buffer The buffer to copy into the heap.\n * @returns An allocated pointer, that must be free after use.\n */\n copyBufferToHeap(buffer: ArrayBuffer): number {\n const size = buffer.byteLength;\n const ptr = this._malloc(size);\n this.HEAPU8.set(new Uint8Array(buffer), ptr);\n return ptr;\n }\n\n /**\n * Returns `true` if the runtime supports physx or not.\n */\n get withPhysX(): boolean {\n return this._withPhysX;\n }\n\n /**\n * Set the engine instance holding this bridge.\n *\n * @note This api is meant to be used internally.\n *\n * @param engine The engine instance.\n */\n protected _setEngine(engine: WonderlandEngine): void {\n this._engine = engine;\n }\n\n /* WebAssembly to JS call bridge. */\n\n protected _wljs_reload(filenamePtr: number) {\n const filename = filenamePtr ? this.UTF8ToString(filenamePtr) : null;\n this._engine._reloadRequest(filename);\n }\n protected _wljs_init(withPhysX: boolean) {\n this._withPhysX = withPhysX;\n\n /* Target memory for JS API functions that return arrays */\n this.allocateTempMemory(1024);\n }\n protected _wljs_scene_switch(index: number) {\n const scene = this._engine._scenes[index] as Scene | null;\n /* Scene can be null during testing with `engine.reset()` */\n this._components = scene?._jsComponents ?? null!;\n }\n protected _wljs_destroy_image(index: number) {\n const img = this._images[index];\n if (!img) return;\n\n this._images[index] = null;\n\n if ((img as HTMLImageElement).src !== undefined) {\n (img as HTMLImageElement).src = '';\n }\n if ((img as HTMLImageElement).onload !== undefined) {\n (img as HTMLImageElement).onload = null;\n }\n if ((img as HTMLImageElement).onerror !== undefined) {\n (img as HTMLImageElement).onerror = null;\n }\n }\n protected _wljs_objects_markDestroyed(\n sceneIndex: number,\n idsPtr: number,\n count: number\n ) {\n const scene = this._engine._scenes[sceneIndex] as Scene;\n const start = idsPtr >>> 1;\n for (let i = 0; i < count; ++i) {\n const id = this.HEAPU16[start + i];\n scene._destroyObject(id);\n }\n }\n protected _wljs_scene_initialize(\n sceneIndex: number,\n idsPtr: number,\n idsEnd: number,\n paramDataPtr: number,\n paramDataEndPtr: number,\n offsetsPtr: number,\n offsetsEndPtr: number\n ) {\n const cborEncoded = this.HEAPU8.subarray(paramDataPtr, paramDataEndPtr);\n const offsets = this.HEAPU32.subarray(offsetsPtr >>> 2, offsetsEndPtr >>> 2);\n const ids = this.HEAPU16.subarray(idsPtr >>> 1, idsEnd >>> 1);\n\n const engine = this._engine;\n const scene = engine._scenes[sceneIndex] as Scene;\n const components = scene._jsComponents;\n\n const cbor = new CBORReader(cborEncoded);\n\n let componentsCount = 0;\n {\n const typeInfo = cbor.readTypeInfo();\n if (getType(typeInfo) !== CBORType.Array) {\n this._log.error(LogTag.Engine, 'Parameters data must be an array');\n return;\n }\n componentsCount = cbor.readArrayLength(typeInfo) as number;\n }\n if (componentsCount !== ids.length) {\n this._log.error(\n LogTag.Engine,\n `Parameters set for ${componentsCount} components, but expected ${ids.length}`\n );\n return;\n }\n\n const decoder = new ComponentPropertyDecoder(scene, offsets);\n for (let i = 0; i < componentsCount; ++i) {\n const id = Component._pack(sceneIndex, ids[i]);\n const index = this._wl_get_js_component_index_for_id(id);\n const component = components[index];\n decoder.decode(cbor, component);\n }\n }\n\n protected _wljs_set_component_param_translation(\n scene: number,\n component: number,\n param: number,\n valuePtr: number,\n valueEndPtr: number\n ) {\n const components = (this._engine._scenes[scene] as Scene)._jsComponents;\n const comp = components[component];\n\n const value = this.UTF8ViewToString(valuePtr, valueEndPtr);\n const ctor = comp.constructor as ComponentConstructor;\n const paramName = ctor._propertyOrder[param];\n (comp as Record)[paramName] = value;\n }\n\n protected _wljs_get_component_type_index(namePtr: number, nameEndPtr: number) {\n const typename = this.UTF8ViewToString(namePtr, nameEndPtr);\n const index = this._componentTypeIndices[typename];\n if (index === undefined) {\n return this._brokenComponentIndex;\n }\n return index;\n }\n protected _wljs_component_create(\n sceneIndex: number,\n index: number,\n id: number,\n type: number,\n object: number\n ) {\n const scene = this._engine._scenes[sceneIndex] as Scene;\n scene._components.createJs(index, id, type, object);\n }\n protected _wljs_component_init(scene: number, component: number) {\n const components = (this._engine._scenes[scene] as Scene)._jsComponents;\n const c = components[component];\n c._triggerInit();\n }\n protected _wljs_component_update(component: number, dt: number) {\n const c = this._components[component];\n c._triggerUpdate(dt);\n }\n protected _wljs_component_onActivate(component: number) {\n const c = this._components[component];\n c._triggerOnActivate();\n }\n protected _wljs_component_onDeactivate(component: number) {\n const c = this._components[component];\n c._triggerOnDeactivate();\n }\n protected _wljs_component_markDestroyed(\n sceneIndex: number,\n manager: number,\n componentId: number\n ) {\n const scene = this._engine._scenes[sceneIndex] as Prefab;\n scene._destroyComponent(manager, componentId);\n }\n protected _wljs_swap(scene: number, a: number, b: number) {\n const components = (this._engine._scenes[scene] as Scene)._jsComponents;\n const componentA = components[a];\n components[a] = components[b];\n components[b] = componentA;\n }\n protected _wljs_copy(\n srcSceneIndex: number,\n srcIndex: number,\n dstSceneIndex: number,\n dstIndex: number,\n offsetsPtr: number,\n copyInfoPtr: number\n ) {\n const srcScene = this._engine._scenes[srcSceneIndex] as Scene;\n const dstScene = this._engine._scenes[dstSceneIndex] as Scene;\n const destComp = dstScene._jsComponents[dstIndex];\n const srcComp = srcScene._jsComponents[srcIndex];\n destComp._copy(srcComp, offsetsPtr, copyInfoPtr);\n }\n /**\n * Forward an animation event to a corresponding\n * {@link AnimationComponent}\n *\n * @note This api is meant to be used internally. Please have a look at\n * {@link AnimationComponent.onEvent} instead.\n *\n * @param componentId Component id in the manager\n * @param namePtr Pointer to UTF8 event name\n * @param nameEndPtr Pointer to end of UTF8 event name\n */\n protected _wljs_trigger_animationEvent(\n componentId: number,\n namePtr: number,\n nameEndPtr: number\n ) {\n const scene = this._engine.scene;\n const comp = scene._components.wrapAnimation(componentId);\n const nameStr = this.UTF8ViewToString(namePtr, nameEndPtr);\n comp.onEvent.notify(nameStr);\n }\n}\n\n/* Extends the WASM class with the WebAssembly typings */\n\nexport interface WASM {\n HEAP8: Int8Array;\n HEAPU8: Uint8Array;\n HEAPU16: Uint16Array;\n HEAP16: Int16Array;\n HEAPU32: Uint32Array;\n HEAP32: Int32Array;\n HEAPF32: Float32Array;\n HEAPF64: Float64Array;\n\n GL: {\n framebuffers: WebGLFramebuffer[];\n };\n\n assert: (condition: boolean, msg?: string) => void;\n _free: (ptr: number) => void;\n _malloc: (size: number) => number;\n lengthBytesUTF8: (str: string) => number;\n stringToUTF8: (str: string, outPtr: number, len: number) => void;\n UTF8ToString: (ptr: number) => string;\n addFunction: (func: Function, sig: string) => number;\n removeFunction: (ptr: number) => void;\n\n _wl_runtime_version: (out: number) => void;\n _wl_set_error_callback: (cbPtr: number) => void;\n _wl_application_create: () => void;\n _wl_application_init: (withRenderer: boolean) => boolean;\n _wl_application_start: () => void;\n _wl_application_redraw: () => void;\n _wl_application_resize: (width: number, height: number) => void;\n\n _wl_nextUpdate: (delta: number) => void;\n _wl_nextFrame: (delta: number) => void;\n _wl_reset: () => void;\n _wl_reset_context: () => void;\n _wl_xr_init: (viewCount: number, useLayers: boolean) => void;\n _wl_xr_focus: () => void;\n _wl_xr_blur: () => void;\n _wl_xr_hide: () => void;\n _wl_xr_exit: () => void;\n\n _wl_deactivate_activeScene: () => void;\n\n _wl_renderer_set_mesh_layout: (layout: number) => void;\n _wl_renderer_streaming_idle: () => number;\n _wl_renderer_isReverseZEnabled: () => number;\n\n _wl_load_main_scene: (ptr: number, size: number, url: number) => number;\n _wl_get_images: (out: number, max: number) => number;\n _wl_get_material_definition_count: () => number;\n _wl_get_material_definition_index: (ptr: number) => number;\n\n _wl_scene_get_active: (root: number) => number;\n _wl_scene_create: (ptr: number, size: number, url: number) => number;\n _wl_scene_create_chunked_start: (url: number) => number;\n _wl_scene_create_chunked_buffer_size: (index: number) => number;\n _wl_scene_create_chunked_next: (\n index: number,\n ptr: number,\n size: number,\n readSizePtr: number,\n requiredSizePtr: number\n ) => boolean;\n _wl_scene_create_chunked_abort: (index: number) => void;\n _wl_scene_create_chunked_end_prefab: (index: number) => number;\n _wl_scene_create_chunked_end_main: (index: number) => void;\n _wl_scene_create_chunked_end_queued: (\n index: number,\n dependentSceneIndex: number\n ) => void;\n _wl_scene_create_empty: () => number;\n _wl_scene_initialize: (index: number) => number;\n _wl_scene_destroy: (index: number) => void;\n _wl_scene_instantiate: (src: number, dst: number) => number;\n _wl_scene_activate: (index: number) => void;\n _wl_scene_queued_bin_count: (index: number) => number;\n _wl_scene_queued_bin_path: (sceneIndex: number, index: number) => number;\n _wl_scene_clear_queued_bin_list: (sceneIndex: number) => void;\n _wl_scene_load_queued_bin: (index: number, ptr: number, size: number) => boolean;\n _wl_scene_activatable: (index: number) => boolean;\n _wl_scene_active: (index: number) => boolean;\n _wl_scene_get_baseURL: (index: number) => number;\n _wl_scene_get_filename: (index: number) => number;\n _wl_scene_get_componentsBundle: (index: number) => number;\n\n _wl_scene_get_component_manager_count: (scene: number) => number;\n _wl_scene_get_component_manager_index: (scene: number, ptr: number) => number;\n\n _wl_scene_get_mainView: (scene: number) => number;\n _wl_scene_get_leftView: (scene: number) => number;\n _wl_scene_get_rightView: (scene: number) => number;\n _wl_scene_get_components: (\n scene: number,\n typeIndex: number,\n subTypeIndex: number,\n active: boolean,\n offset: number,\n count: number,\n ptr: number\n ) => number;\n _wl_scene_get_component: (scene: number, typeIndex: number, index: number) => number;\n\n _wl_scene_ray_cast: (\n scene: number,\n x: number,\n y: number,\n z: number,\n dx: number,\n dy: number,\n dz: number,\n groupMask: number,\n maxDistance: number,\n outPtr: number\n ) => void;\n _wl_scene_add_object: (scene: number, parentId: number) => number;\n _wl_scene_add_objects: (\n scene: number,\n parentId: number,\n count: number,\n componentCountHint: number,\n ptr: number,\n size: number\n ) => number;\n _wl_scene_reserve_objects: (\n scene: number,\n objectCount: number,\n _tempMem: number\n ) => void;\n _wl_scene_set_sky_material: (index: number, id: number) => void;\n _wl_scene_get_sky_material: (index: number) => number;\n _wl_scene_environment_set_intensity: (index: number, intensity: number) => void;\n _wl_scene_environment_get_intensity: (index: number) => number;\n _wl_scene_environment_set_tint: (\n index: number,\n r: number,\n g: number,\n b: number\n ) => void;\n _wl_scene_environment_get_tint: (index: number, ptr: number) => void;\n _wl_scene_environment_set_coefficients: (\n index: number,\n ptr: number,\n count: number\n ) => void;\n _wl_scene_environment_get_coefficients: (index: number, ptr: number) => void;\n _wl_scene_set_clearColor: (r: number, g: number, b: number, a: number) => void;\n _wl_scene_enableColorClear: (b: boolean) => void;\n\n _wl_set_loading_screen_progress: (ratio: number) => void;\n\n _wl_glTF_scene_create: (extensions: boolean, ptr: number, ptrEnd: number) => number;\n _wl_glTF_scene_get_extensions: (index: number) => number;\n _wl_glTF_scene_extensions_gltfIndex_to_id: (\n gltfScene: number,\n destScene: number,\n objectIndex: number,\n gltfIndex: number\n ) => number;\n\n _wl_component_get_object: (manager: number, id: number) => number;\n _wl_component_setActive: (manager: number, id: number, active: boolean) => void;\n _wl_component_isActive: (manager: number, id: number) => number;\n _wl_component_remove: (manager: number, id: number) => void;\n\n _wl_collision_component_get_collider: (id: number) => number;\n _wl_collision_component_set_collider: (id: number, collider: number) => void;\n _wl_collision_component_get_extents: (id: number) => number;\n _wl_collision_component_get_group: (id: number) => number;\n _wl_collision_component_set_group: (id: number, group: number) => void;\n _wl_collision_component_query_overlaps: (\n id: number,\n outPtr: number,\n outCount: number\n ) => number;\n _wl_text_component_get_horizontal_alignment: (id: number) => number;\n _wl_text_component_set_horizontal_alignment: (id: number, alignment: number) => void;\n _wl_text_component_get_vertical_alignment: (id: number) => number;\n _wl_text_component_set_vertical_alignment: (\n id: number,\n verticalAlignment: number\n ) => void;\n _wl_text_component_get_justified: (id: number) => number;\n _wl_text_component_set_justified: (id: number, justified: boolean) => void;\n _wl_text_component_get_character_spacing: (id: number) => number;\n _wl_text_component_set_character_spacing: (id: number, spacing: number) => void;\n _wl_text_component_get_line_spacing: (id: number) => number;\n _wl_text_component_set_line_spacing: (id: number, spacing: number) => void;\n _wl_text_component_get_effect: (id: number) => number;\n _wl_text_component_get_effectOffset: (id: number, outPtr: number) => void;\n _wl_text_component_set_effect: (id: number, effect: number) => void;\n _wl_text_component_set_effectOffset: (id: number, offsetPtr: number) => void;\n _wl_text_component_get_wrapMode: (id: number) => number;\n _wl_text_component_set_wrapMode: (id: number, mode: number) => void;\n _wl_text_component_get_wrapWidth: (id: number) => number;\n _wl_text_component_set_wrapWidth: (id: number, width: number) => void;\n _wl_text_component_get_text: (id: number) => number;\n _wl_text_component_set_text: (id: number, ptr: number) => void;\n _wl_text_component_set_material: (id: number, materialId: number) => void;\n _wl_text_component_get_material: (id: number) => number;\n _wl_text_component_get_boundingBox: (\n id: number,\n textPtr: number,\n resultPtr: number\n ) => number;\n _wl_view_component_get_projectionType: (id: number) => number;\n _wl_view_component_set_projectionType: (id: number, type: number) => void;\n _wl_view_component_get_projectionMatrix: (id: number, ptr: number) => void;\n _wl_view_component_set_projectionMatrix: (id: number, ptr: number) => void;\n _wl_view_component_remapProjectionMatrix: (\n id: number,\n inverseDepth: boolean,\n depthZeroToOne: boolean\n ) => void;\n _wl_view_component_generate_projectionMatrix: (id: number) => void;\n _wl_view_component_get_near: (id: number) => number;\n _wl_view_component_set_near: (id: number, near: number) => void;\n _wl_view_component_get_far: (id: number) => number;\n _wl_view_component_set_far: (id: number, far: number) => void;\n _wl_view_component_get_fov: (id: number) => number;\n _wl_view_component_set_fov: (id: number, fov: number) => void;\n _wl_view_component_get_viewport: (id: number) => number;\n _wl_view_component_set_viewport: (\n id: number,\n x: number,\n y: number,\n width: number,\n height: number\n ) => void;\n _wl_view_component_get_extent: (id: number) => number;\n _wl_view_component_set_extent: (id: number, fov: number) => void;\n _wl_view_component_set_externalFramebuffer: (id: number, fb: number) => void;\n _wl_input_component_get_type: (id: number) => number;\n _wl_input_component_set_type: (id: number, type: number) => void;\n _wl_input_set_transformation: (sceneIndex: number, type: number, ptr: number) => void;\n _wl_light_component_get_color: (id: number) => number;\n _wl_light_component_get_type: (id: number) => number;\n _wl_light_component_set_type: (id: number, type: number) => void;\n _wl_light_component_get_intensity: (id: number) => number;\n _wl_light_component_set_intensity: (id: number, intensity: number) => void;\n _wl_light_component_get_outerAngle: (id: number) => number;\n _wl_light_component_set_outerAngle: (id: number, angle: number) => void;\n _wl_light_component_get_innerAngle: (id: number) => number;\n _wl_light_component_set_innerAngle: (id: number, angle: number) => void;\n _wl_light_component_get_shadows: (id: number) => number;\n _wl_light_component_set_shadows: (id: number, shadows: boolean) => void;\n _wl_light_component_get_shadowRange: (id: number) => number;\n _wl_light_component_set_shadowRange: (id: number, range: number) => void;\n _wl_light_component_get_shadowBias: (id: number) => number;\n _wl_light_component_set_shadowBias: (id: number, bias: number) => void;\n _wl_light_component_get_shadowNormalBias: (id: number) => number;\n _wl_light_component_set_shadowNormalBias: (id: number, bias: number) => void;\n _wl_light_component_get_shadowTexelSize: (id: number) => number;\n _wl_light_component_set_shadowTexelSize: (id: number, size: number) => void;\n _wl_light_component_get_cascadeCount: (id: number) => number;\n _wl_light_component_set_cascadeCount: (id: number, count: number) => void;\n _wl_animation_component_get_animation: (id: number) => number;\n _wl_animation_component_set_animation: (id: number, animId: number) => void;\n _wl_animation_component_get_playCount: (id: number) => number;\n _wl_animation_component_set_playCount: (id: number, count: number) => void;\n _wl_animation_component_get_speed: (id: number) => number;\n _wl_animation_component_set_speed: (id: number, speed: number) => void;\n _wl_animation_component_play: (id: number) => void;\n _wl_animation_component_stop: (id: number) => void;\n _wl_animation_component_pause: (id: number) => void;\n _wl_animation_component_state: (id: number) => number;\n _wl_animation_component_getGraphParamValue: (\n id: number,\n paramIndex: number,\n outPtr: number\n ) => number;\n _wl_animation_component_setGraphParamValue: (\n id: number,\n paramIndex: number,\n valuePtr: number\n ) => void;\n _wl_animation_component_get_rootMotionMode: (id: number) => number;\n _wl_animation_component_set_rootMotionMode: (id: number, value: number) => void;\n _wl_animation_component_get_rootMotion_translation: (\n id: number,\n outPtr: number\n ) => number;\n _wl_animation_component_get_rootMotion_rotation: (id: number, outPtr: number) => number;\n _wl_animation_component_getGraphParamIndex: (id: number, paramName: number) => number;\n _wl_animation_component_get_iteration: (id: number) => number;\n _wl_animation_component_get_position: (id: number) => number;\n _wl_animation_component_get_duration: (id: number) => number;\n _wl_mesh_component_get_material: (id: number) => number;\n _wl_mesh_component_set_material: (id: number, materialId: number) => void;\n _wl_mesh_component_get_mesh: (id: number) => number;\n _wl_mesh_component_set_mesh: (id: number, meshId: number) => void;\n _wl_mesh_component_get_skin: (id: number) => number;\n _wl_mesh_component_set_skin: (id: number, skinId: number) => void;\n _wl_mesh_component_get_morph_targets: (id: number) => number;\n _wl_mesh_component_set_morph_targets: (id: number, morphTargetSetId: number) => void;\n _wl_mesh_component_get_morph_target_weight: (id: number, index: number) => number;\n _wl_mesh_component_get_morph_target_weights: (id: number, ptr: number) => number;\n _wl_mesh_component_set_morph_target_weight: (\n id: number,\n index: number,\n weight: number\n ) => void;\n _wl_mesh_component_set_morph_target_weights: (\n id: number,\n ptr: number,\n count: number\n ) => void;\n _wl_particleEffect_component_get_particleEffect: (id: number) => number;\n _wl_particleEffect_component_set_particleEffect: (\n id: number,\n particleEffectId: number\n ) => void;\n _wl_physx_component_get_static: (id: number) => number;\n _wl_physx_component_set_static: (id: number, flag: boolean) => void;\n _wl_physx_component_get_kinematic: (id: number) => number;\n _wl_physx_component_set_kinematic: (id: number, kinematic: boolean) => void;\n _wl_physx_component_get_gravity: (id: number) => number;\n _wl_physx_component_set_gravity: (id: number, gravity: boolean) => void;\n _wl_physx_component_get_simulate: (id: number) => number;\n _wl_physx_component_set_simulate: (id: number, simulation: boolean) => void;\n _wl_physx_component_get_allowSimulation: (id: number) => number;\n _wl_physx_component_set_allowSimulation: (id: number, allowSimulation: boolean) => void;\n _wl_physx_component_get_allowQuery: (id: number) => number;\n _wl_physx_component_set_allowQuery: (id: number, allowQuery: boolean) => void;\n _wl_physx_component_get_trigger: (id: number) => number;\n _wl_physx_component_set_trigger: (id: number, trigger: boolean) => void;\n _wl_physx_component_get_shape: (id: number) => number;\n _wl_physx_component_set_shape: (id: number, shape: number) => void;\n _wl_physx_component_get_shape_data: (id: number) => number;\n _wl_physx_component_set_shape_data: (id: number, shapeIndex: number) => void;\n _wl_physx_component_get_extents: (id: number) => number;\n _wl_physx_component_get_staticFriction: (id: number) => number;\n _wl_physx_component_set_staticFriction: (id: number, value: number) => void;\n _wl_physx_component_get_dynamicFriction: (id: number) => number;\n _wl_physx_component_set_dynamicFriction: (id: number, value: number) => void;\n _wl_physx_component_get_bounciness: (id: number) => number;\n _wl_physx_component_set_bounciness: (id: number, value: number) => void;\n _wl_physx_component_get_linearDamping: (id: number) => number;\n _wl_physx_component_set_linearDamping: (id: number, value: number) => void;\n _wl_physx_component_get_angularDamping: (id: number) => number;\n _wl_physx_component_set_angularDamping: (id: number, value: number) => void;\n _wl_physx_component_get_linearVelocity: (id: number, ptr: number) => number;\n _wl_physx_component_set_linearVelocity: (\n id: number,\n x: number,\n y: number,\n z: number\n ) => void;\n _wl_physx_component_get_angularVelocity: (id: number, ptr: number) => number;\n _wl_physx_component_set_angularVelocity: (\n id: number,\n x: number,\n y: number,\n z: number\n ) => void;\n _wl_physx_component_get_groupsMask: (id: number) => number;\n _wl_physx_component_set_groupsMask: (id: number, flags: number) => void;\n _wl_physx_component_get_blocksMask: (id: number) => number;\n _wl_physx_component_set_blocksMask: (id: number, flags: number) => void;\n _wl_physx_component_get_linearLockAxis: (id: number) => number;\n _wl_physx_component_set_linearLockAxis: (id: number, lock: number) => void;\n _wl_physx_component_get_angularLockAxis: (id: number) => number;\n _wl_physx_component_set_angularLockAxis: (id: number, lock: number) => void;\n _wl_physx_component_get_mass: (id: number) => number;\n _wl_physx_component_set_mass: (id: number, value: number) => void;\n _wl_physx_component_get_offsetTranslation: (id: number, out: number) => void;\n _wl_physx_component_get_offsetTransform: (id: number) => number;\n _wl_physx_component_set_offsetTranslation: (\n id: number,\n x: number,\n y: number,\n z: number\n ) => void;\n _wl_physx_component_set_offsetRotation: (\n id: number,\n x: number,\n y: number,\n z: number,\n w: number\n ) => void;\n _wl_physx_component_set_sleepOnActivate: (id: number, flag: boolean) => void;\n _wl_physx_component_get_sleepOnActivate: (id: number) => number;\n _wl_physx_component_set_massSpaceInertiaTensor: (\n id: number,\n x: number,\n y: number,\n z: number\n ) => void;\n _wl_physx_component_addForce: (\n id: number,\n x: number,\n y: number,\n z: number,\n mode: number,\n localForce: boolean\n ) => void;\n _wl_physx_component_addForceAt: (\n id: number,\n x: number,\n y: number,\n z: number,\n mode: number,\n localForce: boolean,\n posX: number,\n posY: number,\n posZ: number,\n local: boolean\n ) => void;\n _wl_physx_component_addTorque: (\n id: number,\n x: number,\n y: number,\n z: number,\n mode: number\n ) => void;\n _wl_physx_component_addCallback: (id: number, otherId: number) => number;\n _wl_physx_component_removeCallback: (id: number, callbackId: number) => number;\n _wl_physx_update_global_pose: (object: number, component: number) => void;\n _wl_physx_ray_cast: (\n scene: number,\n x: number,\n y: number,\n z: number,\n dx: number,\n dy: number,\n dz: number,\n groupMask: number,\n outPtr: number,\n maxDistance: number\n ) => void;\n _wl_physx_set_collision_callback: (callback: number) => void;\n _wl_mesh_create: (\n indicesPtr: number,\n indicesSize: number,\n indexType: number,\n vertexCount: number,\n skinningType: number\n ) => number;\n _wl_mesh_get_vertexCount: (index: number) => number;\n _wl_mesh_get_indexData: (index: number, outPtr: number, count: number) => number;\n _wl_mesh_update: (index: number) => void;\n _wl_mesh_get_boundingSphere: (index: number, outPtr: number) => void;\n _wl_mesh_get_attribute: (index: number, attribute: number, outPtr: number) => void;\n _wl_mesh_destroy: (index: number) => void;\n _wl_mesh_get_attribute_values: (\n attribute: number,\n srcFormatSize: number,\n srcPtr: number,\n srcStride: number,\n dstFormatSize: number,\n destPtr: number,\n dstSize: number\n ) => void;\n _wl_mesh_set_attribute_values: (\n attribute: number,\n srcFormatSize: number,\n srcPtr: number,\n srcSize: number,\n dstFormatSize: number,\n destPtr: number,\n destStride: number\n ) => void;\n _wl_font_get_emHeight: (index: number) => number;\n _wl_font_get_capHeight: (index: number) => number;\n _wl_font_get_xHeight: (index: number) => number;\n _wl_font_get_outlineSize: (index: number) => number;\n _wl_material_create: (definitionIndex: number) => number;\n _wl_material_get_definition: (index: number) => number;\n _wl_material_definition_get_param_count: (index: number) => number;\n _wl_material_definition_get_param_name: (index: number, paramIndex: number) => number;\n _wl_material_definition_get_param_type: (index: number, paramIndex: number) => number;\n _wl_material_get_pipeline: (index: number) => number;\n _wl_material_clone: (index: number) => number;\n _wl_material_get_param_index: (index: number, namePtr: number) => number;\n _wl_material_get_param_type: (index: number, paramIndex: number) => number;\n _wl_material_get_param_value: (\n index: number,\n paramIndex: number,\n outPtr: number\n ) => number;\n _wl_material_set_param_value_uint: (\n index: number,\n paramId: number,\n valueId: number\n ) => void;\n _wl_material_set_param_value_float: (\n index: number,\n paramId: number,\n ptr: number,\n count: number\n ) => void;\n _wl_image_create: (jsImage: number) => number;\n _wl_image_size: (index: number, out: number) => number;\n _wl_image_get_jsImage_index: (index: number) => number;\n _wl_image_compressed: (index: number) => number;\n _wl_image_originalScene: (index: number) => number;\n _wl_image_markDirty: (index: number) => void;\n _wl_image_markReady: (\n index: number,\n width: number,\n height: number,\n flipY: number\n ) => void;\n _wl_image_count: () => number;\n\n _wl_texture_create: (image: number) => number;\n _wl_texture_get_image_index: (index: number) => number;\n _wl_texture_destroy: (id: number) => void;\n\n _wl_renderer_updateImage: (\n imageIndex: number,\n jsImageIndex: number,\n srcWidth: number,\n srcHeight: number,\n xOffset: number,\n yOffset: number,\n flipY: boolean\n ) => number;\n\n _wl_animation_get_duration: (id: number) => number;\n _wl_animation_get_trackCount: (id: number) => number;\n _wl_animation_retargetToSkin: (id: number, targetId: number) => number;\n _wl_animation_retarget: (id: number, ptr: number) => number;\n _wl_particleEffect_clone: (index: number) => number;\n _wl_object_id: (scene: number, index: number) => number;\n _wl_object_index: (id: number) => number;\n _wl_object_name: (id: number) => number;\n _wl_object_set_name: (id: number, ptr: number) => void;\n _wl_object_remove: (id: number) => void;\n _wl_object_markedDestroyed: (id: number) => boolean;\n _wl_object_parent: (id: number) => number;\n _wl_object_get_children_count: (id: number) => number;\n _wl_object_get_children: (id: number, outPtr: number, count: number) => number;\n _wl_object_set_parent: (id: number, parentId: number) => void;\n _wl_object_clone: (id: number, parentId: number) => number;\n _wl_object_reset_scaling: (id: number) => void;\n _wl_object_reset_translation_rotation: (id: number) => void;\n _wl_object_reset_rotation: (id: number) => void;\n _wl_object_reset_translation: (id: number) => void;\n _wl_object_translate: (id: number, x: number, y: number, z: number) => void;\n _wl_object_translate_obj: (id: number, x: number, y: number, z: number) => void;\n _wl_object_translate_world: (id: number, x: number, y: number, z: number) => void;\n _wl_object_rotate_axis_angle: (\n id: number,\n x: number,\n y: number,\n z: number,\n deg: number\n ) => void;\n _wl_object_rotate_axis_angle_rad: (\n id: number,\n x: number,\n y: number,\n z: number,\n rad: number\n ) => void;\n _wl_object_rotate_axis_angle_obj: (\n id: number,\n x: number,\n y: number,\n z: number,\n deg: number\n ) => void;\n _wl_object_rotate_axis_angle_rad_obj: (\n id: number,\n x: number,\n y: number,\n z: number,\n rad: number\n ) => void;\n _wl_object_rotate_quat: (\n id: number,\n x: number,\n y: number,\n z: number,\n w: number\n ) => void;\n _wl_object_rotate_quat_obj: (\n id: number,\n x: number,\n y: number,\n z: number,\n w: number\n ) => void;\n _wl_object_scale: (id: number, x: number, y: number, z: number) => void;\n _wl_object_trans_local: (id: number) => number;\n _wl_object_get_translation_local: (id: number, outPtr: number) => void;\n _wl_object_set_translation_local: (id: number, x: number, y: number, z: number) => void;\n _wl_object_get_translation_world: (id: number, outPtr: number) => void;\n _wl_object_set_translation_world: (id: number, x: number, y: number, z: number) => void;\n _wl_object_trans_world: (id: number) => number;\n _wl_object_trans_world_to_local: (id: number) => number;\n _wl_object_scaling_local: (id: number) => number;\n _wl_object_scaling_world: (id: number) => number;\n _wl_object_set_scaling_local: (id: number, x: number, y: number, z: number) => void;\n _wl_object_set_scaling_world: (id: number, x: number, y: number, z: number) => void;\n _wl_object_scaling_world_to_local: (id: number) => number;\n _wl_object_set_rotation_local: (\n id: number,\n x: number,\n y: number,\n z: number,\n w: number\n ) => void;\n _wl_object_set_rotation_world: (\n id: number,\n x: number,\n y: number,\n z: number,\n w: number\n ) => void;\n _wl_object_transformVectorWorld: (id: number, ptr: number) => number;\n _wl_object_transformVectorLocal: (id: number, ptr: number) => number;\n _wl_object_transformPointWorld: (id: number, ptr: number) => number;\n _wl_object_transformPointLocal: (id: number, ptr: number) => number;\n _wl_object_transformVectorInverseWorld: (id: number, ptr: number) => number;\n _wl_object_transformVectorInverseLocal: (id: number, ptr: number) => number;\n _wl_object_transformPointInverseWorld: (id: number, ptr: number) => number;\n _wl_object_transformPointInverseLocal: (id: number, ptr: number) => number;\n _wl_object_toWorldSpaceTransform: (id: number, ptr: number) => number;\n _wl_object_toObjectSpaceTransform: (id: number, ptr: number) => number;\n _wl_object_lookAt: (\n id: number,\n x: number,\n y: number,\n z: number,\n upX: number,\n upY: number,\n upZ: number\n ) => void;\n _wl_object_set_dirty: (id: number) => void;\n _wl_get_js_component_index: (id: number, outPtr: number, count: number) => number;\n _wl_get_js_component_index_for_id: (id: number) => number;\n _wl_get_component_id: (id: number, managerId: number, index: number) => number;\n _wl_object_get_components: (id: number, outPtr: number, count: number) => number;\n _wl_object_get_component_types: (id: number, outPtr: number, count: number) => void;\n _wl_object_add_js_component: (id: number, typeId: number) => number;\n _wl_object_add_component: (id: number, typeId: number) => number;\n _wl_object_is_changed: (id: number) => number;\n _wl_object_findByName: (\n obj: number,\n name: number,\n indexPtr: number,\n childPtr: number,\n outPtr: number,\n count: number\n ) => number;\n _wl_object_findByNameRecursive: (\n obj: number,\n name: number,\n indexPtr: number,\n outPtr: number,\n count: number\n ) => number;\n _wl_component_manager_name: (scene: number, id: number) => number;\n _wl_skin_get_joint_count: (id: number) => number;\n _wl_skin_joint_ids: (id: number) => number;\n _wl_skin_inverse_bind_transforms: (id: number) => number;\n _wl_skin_inverse_bind_scalings: (id: number) => number;\n _wl_morph_targets_get_target_count: (id: number) => number;\n _wl_morph_targets_get_target_name: (id: number, target: number) => number;\n _wl_morph_targets_get_target_index: (id: number, namePtr: number) => number;\n _wl_morph_target_weights_get_weight: (id: number, target: number) => number;\n _wl_morph_target_weights_set_weight: (\n id: number,\n target: number,\n weight: number\n ) => void;\n _wl_math_cubicHermite: (\n a: number,\n b: number,\n c: number,\n d: number,\n f: number,\n e: number,\n isQuat: boolean\n ) => void;\n _wl_i18n_setLanguage: (ptr: number) => void;\n _wl_i18n_currentLanguage: () => number;\n _wl_i18n_currentLanguageIndex: () => number;\n _wl_i18n_translate: (ptr: number) => number;\n _wl_i18n_languageCount: () => number;\n _wl_i18n_languageIndex: (ptr: number) => number;\n _wl_i18n_languageCode: (index: number) => number;\n _wl_i18n_languageName: (index: number) => number;\n _wl_i18n_languageFile: (index: number) => number;\n}\n\n/*\n * Api <> Runtime compatibility.\n *\n * Some features exposed in the API are only available from a specific\n * runtime version.\n *\n * Every added feature must throw by default. If the runtime loaded\n * has the specific feature, the method will be overwritten by Emscripten\n * upon loading.\n */\n\n/**\n * Throwing function used for features added in a patch version.\n *\n * #### Usage\n *\n * ```ts\n * const requireRuntime1_1_1 = throwInvalidRuntime('1.1.1');\n * WASM.prototype._wl_new_function = requireRuntime1_1_1;\n * ```\n *\n * @param version The version in which the feature was added.\n * @returns A function that will throw when called.\n */\nfunction throwInvalidRuntime(version: string) {\n return function () {\n throw new Error(\n `Feature added in version ${version}.` +\n `\\n\\t\u2192 Please use a Wonderland Engine editor version >= ${version}`\n );\n };\n}\n", "import {threads} from 'wasm-feature-detect';\nimport {WonderlandEngine} from './engine.js';\nimport {XROfferSessionOptions} from './webxr.js';\nimport {LogLevel} from './utils/logger.js';\nimport {WASM} from './wasm.js';\n\nimport {getBaseUrl} from './utils/fetch.js';\n\nexport * from './utils/index.js';\nexport * from './decorators.js';\nexport {NativeComponents} from './component.js';\nexport * from './wonderland.js';\nexport * from './engine.js';\nexport * from './webxr.js';\nexport * from './property.js';\nexport * from './prefab.js';\nexport * from './scene.js';\nexport * from './scene-gltf.js';\nexport * from './resources/resource.js';\nexport * from './resources/material-manager.js';\nexport * from './resources/mesh-manager.js';\nexport {TextureManager} from './resources/texture-manager.js';\nexport * from './types.js';\nexport * from './version.js';\nexport * from './wasm.js';\n\n/** Relative default path for the loading screen. */\nconst LOADING_SCREEN_PATH = 'WonderlandRuntime-LoadingScreen.bin';\n\nfunction loadScript(scriptURL: string): Promise {\n return new Promise((resolve, reject) => {\n const s = document.createElement('script');\n const node = document.body.appendChild(s);\n s.onload = () => {\n document.body.removeChild(node);\n resolve();\n };\n s.onerror = (e) => {\n document.body.removeChild(node);\n reject(e);\n };\n s.src = scriptURL;\n });\n}\n\n/**\n * Checks for support of WebAssembly features.\n *\n * @returns An object containing booleans for supported features.\n */\nasync function detectFeatures(): Promise<{\n threadsSupported: boolean;\n}> {\n let threadsSupported = await threads();\n if (threadsSupported) {\n if (self.crossOriginIsolated) {\n console.log('WASM Threads is supported');\n } else {\n console.warn(\n 'WASM Threads is supported, but the page is not crossOriginIsolated, therefore thread support is disabled.'\n );\n }\n } else {\n console.warn('WASM Threads is not supported');\n }\n\n threadsSupported = threadsSupported && self.crossOriginIsolated;\n return {\n threadsSupported,\n };\n}\n\n/**\n * Options to forward to {@link loadRuntime}\n */\nexport interface LoadRuntimeOptions {\n /**\n * @deprecated Ignored, SIMD in the runtime is always enabled as it's supported on\n * all modern browsers.\n */\n simd: boolean;\n /**\n * If `true`, forces the runtime to load the threads-compatible version.\n * If `undefined`, performs browser feature detection to check whether threads are supported or not.\n */\n threads: boolean;\n /**\n * If `true`, forces the runtime to load the webgpu-compatible version.\n */\n webgpu: boolean;\n /**\n * If `true`, forces the runtime to load a physx-compatible version.\n *\n * **Note**: If your scene uses physx, you **must** enable this option.\n */\n physx: boolean;\n /**\n * If `true`, forces the runtime to load a loader-compatible version.\n *\n * This option allows to load gltf data at runtime.\n */\n loader: boolean;\n\n /**\n * Whether to instantiate the runtime with a renderer.\n *\n * Disabling this is mostly meant for testing purposes (increased speed).\n *\n * @hidden\n */\n renderer: boolean;\n\n /**\n * Path to the loading screen. If `undefined`, defaults to 'WonderlandRuntime-LoadingScreen.bin'.\n * Beware that these are special .bin files signed by Wonderland. Customizing\n * requires an enterprise license, please reach out for more information.\n */\n loadingScreen: string;\n\n /**\n * Default framebuffer scale factor. This can later be changed using\n * {@link WonderlandEngine.xrFramebufferScaleFactor}\n */\n xrFramebufferScaleFactor: number;\n\n /**\n * Whether to advertise AR/VR session support to the browser.\n *\n * Adds an interactive UI element to the browser interface to start an XR\n * session. Browser support is optional, so it's advised to still allow\n * requesting a session with a UI element on the website itself.\n *\n * If `undefined`, no XR session is automatically offered to the browser.\n *\n * @since 1.1.5\n */\n xrOfferSession: XROfferSessionOptions;\n\n /**\n * Canvas id or element. If this is `undefined`, looks for a canvas with id 'canvas'.\n */\n canvas: string;\n\n /**\n * Logging level(s) to enable.\n *\n * By default, all levels are enabled.\n */\n logs: LogLevel[];\n}\n\n/**\n * Load the runtime using the WASM and JS files.\n *\n * @param runtime The runtime base string, e.g,: `WonderlandRuntime-loader-physx`.\n * @param options Options to modify the loading behaviour.\n *\n * @returns A promise that resolves when the engine is ready to be used.\n */\nexport async function loadRuntime(\n runtime: string,\n options: Partial = {}\n): Promise {\n const baseURL = getBaseUrl(runtime);\n\n const {threadsSupported} = await detectFeatures();\n const {\n threads = threadsSupported,\n webgpu = false,\n physx = false,\n loader = false,\n renderer = true,\n xrFramebufferScaleFactor = 1.0,\n xrOfferSession = null,\n loadingScreen = baseURL ? `${baseURL}/${LOADING_SCREEN_PATH}` : LOADING_SCREEN_PATH,\n canvas = 'canvas',\n logs = [LogLevel.Info, LogLevel.Warn, LogLevel.Error],\n } = options;\n\n const variant = [];\n if (loader) variant.push('loader');\n if (physx) variant.push('physx');\n if (threads) variant.push('threads');\n if (webgpu) variant.push('webgpu');\n\n const variantStr = variant.join('-');\n\n let filename = runtime;\n if (variantStr) filename = `${filename}-${variantStr}`;\n\n const download = function (\n filename: string,\n errorMessage: string\n ): Promise {\n return fetch(filename)\n .then((r) => {\n if (!r.ok) return Promise.reject(errorMessage);\n return r.arrayBuffer();\n })\n .catch((_) => Promise.reject(errorMessage));\n };\n\n const [wasmData, loadingScreenData] = await Promise.all([\n download(`${filename}.wasm`, `Failed to fetch runtime .wasm file: ${filename}`),\n download(loadingScreen, 'Failed to fetch loading screen file'),\n ]);\n\n const canvasElement = document.getElementById(canvas) as HTMLCanvasElement;\n if (!canvasElement) {\n throw new Error(`loadRuntime(): Failed to find canvas with id '${canvas}'`);\n }\n if (!(canvasElement instanceof HTMLCanvasElement)) {\n throw new Error(`loadRuntime(): HTML element '${canvas}' must be a canvas`);\n }\n\n const wasm = new WASM(threads);\n (wasm.wasm as ArrayBuffer) = wasmData;\n (wasm.canvas as HTMLCanvasElement) = canvasElement;\n wasm._log.levels.enable(...logs);\n\n if (!window._WL) {\n window._WL = {runtimes: {}};\n }\n const runtimes = window._WL.runtimes;\n\n /* Global identifier of this runtime in `window`. */\n const runtimeGlobalId = variantStr ? variantStr : 'default';\n /* Only load the runtime if not previously loaded in the page */\n if (!runtimes[runtimeGlobalId]) {\n await loadScript(`${filename}.js`);\n runtimes[runtimeGlobalId] = window.instantiateWonderlandRuntime!;\n window.instantiateWonderlandRuntime = undefined;\n }\n await runtimes[runtimeGlobalId](wasm);\n\n if (webgpu) {\n const WebGPU = (wasm as any).WebGPU;\n const adapter = await (navigator as any).gpu.requestAdapter();\n const adapterId = WebGPU.mgrAdapter.create(adapter);\n const desc = {\n requiredFeatures: ['texture-compression-bc', 'depth32float-stencil8'],\n };\n const device = await adapter.requestDevice(desc);\n const deviceId = WebGPU.mgrDevice.create(adapter);\n (wasm.preinitializedWebGPUDevice as any) = device;\n\n const context = canvasElement.getContext('webgpu')! as any;\n\n context.configure({\n device: device,\n format: (navigator as any).gpu.getPreferredCanvasFormat(),\n alphaMode: 'premultiplied',\n });\n }\n\n const engine = new WonderlandEngine(wasm, loadingScreenData, renderer);\n\n await engine.webxr.init(xrFramebufferScaleFactor, xrOfferSession);\n\n engine.autoResizeCanvas = true;\n engine.start();\n\n return engine;\n}\n", "import { Component, Type } from '@wonderlandengine/api';\n/**\n * 8thwall camera component.\n *\n * Sets up the 8thwall pipeline and retrieves tracking events to place an\n * object at the location of the tracked AR camera / mobile device.\n *\n * Use this for SLAM tracking based on 8thwall.\n *\n * Make sure to enable 8thwall in \"Project Settings\" > \"AR\". See also the\n * [AR Getting Started Guide](/getting-started/quick-start-ar)\n *\n *\n * Currently only supports world-tracking (SLAM) using BACK camera.\n *\n * - remove any occurrences of the old 8thwall-camera component in the editor\n * - Add this component to NonVrCamera\n *\n * IMPORTANT!\n * - until it get's fixed, select 'customIndexHtml' in the project settings\n * - open a generated index.html and crossorigin=\"anonymous\" attribute to the + + + + + + MyWonderland + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Zumbies Mortos: 0 +
+ + +
+ + + + +
+ + diff --git a/deploy/music/happy-funny-kids-111912.mp3 b/deploy/music/happy-funny-kids-111912.mp3 new file mode 100644 index 0000000..eac2376 Binary files /dev/null and b/deploy/music/happy-funny-kids-111912.mp3 differ diff --git a/deploy/music/level-win-6416.mp3 b/deploy/music/level-win-6416.mp3 new file mode 100644 index 0000000..27b396e Binary files /dev/null and b/deploy/music/level-win-6416.mp3 differ diff --git a/deploy/sfx/9mm-pistol-shoot-short-reverb-7152.mp3 b/deploy/sfx/9mm-pistol-shoot-short-reverb-7152.mp3 new file mode 100644 index 0000000..9893129 Binary files /dev/null and b/deploy/sfx/9mm-pistol-shoot-short-reverb-7152.mp3 differ diff --git a/deploy/sfx/click.wav b/deploy/sfx/click.wav new file mode 100644 index 0000000..e21139d Binary files /dev/null and b/deploy/sfx/click.wav differ diff --git a/deploy/sfx/cows-short.mp3 b/deploy/sfx/cows-short.mp3 new file mode 100644 index 0000000..4de6a9a Binary files /dev/null and b/deploy/sfx/cows-short.mp3 differ diff --git a/deploy/sfx/critter-40645.mp3 b/deploy/sfx/critter-40645.mp3 new file mode 100644 index 0000000..a642ef7 Binary files /dev/null and b/deploy/sfx/critter-40645.mp3 differ diff --git a/deploy/sfx/ducks-short.mp3 b/deploy/sfx/ducks-short.mp3 new file mode 100644 index 0000000..219ea20 Binary files /dev/null and b/deploy/sfx/ducks-short.mp3 differ diff --git a/deploy/sfx/high-pitched-aha-103125.mp3 b/deploy/sfx/high-pitched-aha-103125.mp3 new file mode 100644 index 0000000..8e718b5 Binary files /dev/null and b/deploy/sfx/high-pitched-aha-103125.mp3 differ diff --git a/deploy/sfx/pig_grunts_snorts_breathing_hackney_city_farm-73959.mp3 b/deploy/sfx/pig_grunts_snorts_breathing_hackney_city_farm-73959.mp3 new file mode 100644 index 0000000..f7e40d6 Binary files /dev/null and b/deploy/sfx/pig_grunts_snorts_breathing_hackney_city_farm-73959.mp3 differ diff --git a/deploy/sfx/pop-94319.mp3 b/deploy/sfx/pop-94319.mp3 new file mode 100644 index 0000000..fff12a8 Binary files /dev/null and b/deploy/sfx/pop-94319.mp3 differ diff --git a/deploy/sfx/punch-boxing-02wav-14897.mp3 b/deploy/sfx/punch-boxing-02wav-14897.mp3 new file mode 100644 index 0000000..5bb7910 Binary files /dev/null and b/deploy/sfx/punch-boxing-02wav-14897.mp3 differ diff --git a/deploy/sfx/sheep-short.mp3 b/deploy/sfx/sheep-short.mp3 new file mode 100644 index 0000000..5b7483b Binary files /dev/null and b/deploy/sfx/sheep-short.mp3 differ diff --git a/deploy/sfx/unclick.wav b/deploy/sfx/unclick.wav new file mode 100644 index 0000000..65f89cf Binary files /dev/null and b/deploy/sfx/unclick.wav differ diff --git a/js/bg-music.js b/js/bg-music.js index b968ae3..27d3d1d 100644 --- a/js/bg-music.js +++ b/js/bg-music.js @@ -20,35 +20,35 @@ export class BgMusic extends Component { static Properties = {}; init() { - state.bgMusic = this.object.addComponent('howler-audio-source', { - src: 'music/happy-funny-kids-111912.mp3', - loop: true, - volume: 0.4 - }); - state.bgMusic.play(); - this.bgDucks = this.object.addComponent('howler-audio-source', { - src: 'sfx/ducks-short.mp3', - loop: true, - volume: 1.3 - }); - this.bgDucks.play(); - this.bgCow = this.object.addComponent('howler-audio-source', { - src: 'sfx/cows-short.mp3', - loop: true, - volume: 1.0 - }); - this.bgCow.play(); - this.bgSheep = this.object.addComponent('howler-audio-source', { - src: 'sfx/sheep-short.mp3', - loop: true, - volume: 1.0 - }); - this.bgSheep.play(); - this.bgPig = this.object.addComponent('howler-audio-source', { - src: 'sfx/pig_grunts_snorts_breathing_hackney_city_farm-73959.mp3', - loop: true, - volume: 1.0 - }); - this.bgPig.play(); + // state.bgMusic = this.object.addComponent('howler-audio-source', { + // src: 'music/happy-funny-kids-111912.mp3', + // loop: true, + // volume: 0.4 + // }); + // state.bgMusic.play(); + // this.bgDucks = this.object.addComponent('howler-audio-source', { + // src: 'sfx/ducks-short.mp3', + // loop: true, + // volume: 1.3 + // }); + // this.bgDucks.play(); + // this.bgCow = this.object.addComponent('howler-audio-source', { + // src: 'sfx/cows-short.mp3', + // loop: true, + // volume: 1.0 + // }); + // this.bgCow.play(); + // this.bgSheep = this.object.addComponent('howler-audio-source', { + // src: 'sfx/sheep-short.mp3', + // loop: true, + // volume: 1.0 + // }); + // this.bgSheep.play(); + // this.bgPig = this.object.addComponent('howler-audio-source', { + // src: 'sfx/pig_grunts_snorts_breathing_hackney_city_farm-73959.mp3', + // loop: true, + // volume: 1.0 + // }); + // this.bgPig.play(); } }; diff --git a/js/bullet-physics.js b/js/bullet-physics.js index ffeb4a4..cc6574f 100644 --- a/js/bullet-physics.js +++ b/js/bullet-physics.js @@ -52,6 +52,9 @@ export class BulletPhysics extends Component { return; } + // Se já marcou ponto, não faz mais nada + if (this.scored) return; + //update position this.object.getPositionWorld(this.position); //deactivate bullet if through the floor @@ -71,21 +74,37 @@ export class BulletPhysics extends Component { this.object.setPositionLocal(this.position); let overlaps = this.collision.queryOverlaps(); + for (let i = 0; i < overlaps.length; ++i) { let t = overlaps[i].object.getComponent("score-trigger"); - if (t && !this.scored) { - t.onHit(); - this.destroyBullet(0); - return; + + // this.destroyBullet(0); + // se eu movo this.destroyBullet(0); pra aqui funciona e ele desaparece quando bate em algo mas n funciona o hit tipo o tiro n ativa botões nem mata bichos + + if (t) { + t.onHit(() => this.destroyBullet(0)); // Ativar ação do alvo (ponto, morte, etc) + this.destroyBullet(0); // Só então destruir + break; } } } destroyBullet(time) { - if (time == 0) { - this.object.destroy(); + // 1) desativa render e colisão na hora + const meshComp = this.object.getComponent("mesh", 0); + if (meshComp) meshComp.active = false; + if (this.collision) this.collision.active = false; + + // 2) opcional: desativa o próprio objeto pra sair da árvore imediatamente + this.object.active = false; + + // 3) destrói de fato (ou remove da cena) + if (time === 0) { + // remove imediatamente + this.engine.scene.removeObject(this.object); + // ou: this.object.destroy(); } else { setTimeout(() => { - this.object.destroy() + this.engine.scene.removeObject(this.object); }, time); } } diff --git a/js/game.js b/js/game.js index c26088e..5195afa 100644 --- a/js/game.js +++ b/js/game.js @@ -1,10 +1,9 @@ - export const state = { shotCount: 0, /* Global function used to update the score display */ updateCounter: null, updateScore: null, - gameOver: false, + gameOver: true, score: 0, resetButton: null, bgMusic: null, @@ -21,37 +20,144 @@ export const state = { despawnTarget: null, firstShot: false, launch: null, + paused: false, + isFirstGameOver : false, + zombieCount: 0, + + // Wave system properties + currentWave: 1, + zombiesKilledInWave: 0, + waveProperties: { + baseZombies: 10, // Starting number of zombies in wave 1 + zombiesIncrease: 10, // How many more zombies per wave + baseSpeed: 2.5, // Starting speed in wave 1 (increased from 1.5) + speedIncreasePercent: 50 // Speed increase percentage per wave (increased to 50%) + }, + + getCurrentWave() { + return this.currentWave; + }, + + getWaveZombieCount() { + return this.waveProperties.baseZombies + + (this.currentWave - 1) * this.waveProperties.zombiesIncrease; + }, + + getWaveSpeed() { + // + const speedMultiplier = 1 + ((this.currentWave - 1) * this.waveProperties.speedIncreasePercent / 100); + const speed = this.waveProperties.baseSpeed * speedMultiplier; + console.log(`Wave ${this.currentWave} speed: ${speed.toFixed(2)} (multiplier: ${speedMultiplier.toFixed(2)})`); + return speed * ((this.zombieCount * 0.001) + 1) + }, + + getRemainingZombies() { + const total = this.getWaveZombieCount(); + const killed = this.zombiesKilledInWave; + console.log(`Wave ${this.currentWave}: ${killed}/${total} zombies killed`); + return total - killed; + }, + + updateWaveDisplay() { + const remaining = this.getRemainingZombies(); + const nextWaveZombies = this.waveProperties.baseZombies + + (this.currentWave) * this.waveProperties.zombiesIncrease; + const speed = this.getWaveSpeed() + this.updateScore( + `Wave ${this.currentWave} - Remaining: ${remaining} zombies - Speed: ${speed.toFixed(1)} - Next Wave: ${nextWaveZombies} zombies` + ); + }, + + zombieKilled() { + this.zombiesKilledInWave++; + this.score++; + + // Check if wave is complete + const remaining = this.getRemainingZombies(); + if (remaining <= 0) { + console.log(`Wave ${this.currentWave} complete! Starting next wave...`); + this.nextWave(); + } else { + this.updateWaveDisplay(); + } + }, + + nextWave() { + this.currentWave++; + this.zombiesKilledInWave = 0; + + // Update max targets for the new wave + const newZombieCount = this.getWaveZombieCount(); + if (this.mouseSpawner) { + console.log(`Setting new wave ${this.currentWave} with ${newZombieCount} zombies`); + this.mouseSpawner.maxTargets = newZombieCount; + this.mouseSpawner.reset(); // Clear existing zombies and start new wave + } + + // Update display with new wave info + this.updateWaveDisplay(); + }, + + loseGame() { + console.log("Game Over triggered"); + this.gameOver = true; + this.isFirstGameOver = true; + + // Stop music first + if (this.mouseSound) { + this.mouseSound.stop(); + } - incrementScore() { - ++this.score; + // Clear all existing zombies + if (this.mouseSpawner) { + this.mouseSpawner.reset(); + } - let scoreString = ""; - if (this.maxTargets != this.score) { - scoreString = `${this.score} rats down, ${(this.maxTargets - this.score)} left`; + // Show reset button and ensure it's visible + if (this.resetButton) { + console.log("Showing reset button"); + this.resetButton.unhide(); + // Force button to be visible + this.resetButton.object.children[0].getComponent("mesh").active = true; + this.resetButton.object.children[1].getComponent("text").active = true; + this.resetButton.active = true; } else { - scoreString = "Congrats, you got all the rats!"; - this.victoryMusic.play(); - this.bgMusic.stop(); - this.mouseSound.stop(); - this.resetButton.unhide(); - this.gameOver = true; + console.log("Reset button not found!"); } - this.updateScore(scoreString); + // Update score display + this.updateScore(`Game Over! You survived ${this.currentWave} waves and eliminated ${this.score} zombies!`); }, restart() { - this.mouseSpawner.targets = []; - this.mouseSpawner.reset(); + if (this.mouseSpawner) { + this.mouseSpawner.reset(); + } + + if (this.isFirstGameOver) { + window.location.reload() + } + + // atualiza o zombieCount para 0 + this.zombieCount = 0; + + // document.getElementById("pontos-test").innerHTML = 0; this.victoryMusic.stop(); - this.bgMusic.play(); this.gameOver = false; this.shotCount = 0; this.score = 0; - this.targetsSpawned = 0; + this.currentWave = 1; + this.zombiesKilledInWave = 0; + + // Reset max targets for wave 1 + if (this.mouseSpawner) { + const initialZombies = this.getWaveZombieCount(); + console.log(`Restarting game with ${initialZombies} zombies in wave 1`); + this.mouseSpawner.maxTargets = initialZombies; + } this.updateCounter(); - this.updateScore(`Eliminate all ${this.maxTargets} rats.`); + this.updateWaveDisplay(); } }; diff --git a/js/index.js b/js/index.js index a725889..841febd 100644 --- a/js/index.js +++ b/js/index.js @@ -23,7 +23,6 @@ import {VrModeActiveSwitch} from '@wonderlandengine/components'; import {BgMusic} from './bg-music.js'; import {BulletSpawner} from './bullet-spawner.js'; import {ConfettiParticles} from './confetti-particles.js'; -import {GameLogo} from './game-logo.js'; import {HudControl} from './hud-control.js'; import {JoystickMovement} from './joystick-movement.js'; import {MouseLookCustom} from './mouse-look-custom.js'; @@ -32,7 +31,6 @@ import {PlayAgainButton} from './play-again-button.js'; import {PlayerLocation} from './player-location.js'; import {ScoreDisplay} from './score-display.js'; import {ShotCounter} from './shot-counter.js'; -import {SpawnMover} from './spawn-mover.js'; import {TeleportCustom} from './teleport-custom.js'; import {WasdControlsCustom} from './wasd-controls-custom.js'; /* wle:auto-imports:end */ @@ -41,18 +39,23 @@ import { loadRuntime } from "@wonderlandengine/api"; import * as API from "@wonderlandengine/api"; // Deprecated: Backward compatibility. /* wle:auto-constants:start */ -const RuntimeOptions = { - physx: false, - loader: false, - xrFramebufferScaleFactor: 1, - canvas: 'canvas', -}; const Constants = { ProjectName: 'MyWonderland', RuntimeBaseName: 'WonderlandRuntime', WebXRRequiredFeatures: ['local',], WebXROptionalFeatures: ['local','local-floor','hand-tracking','hit-test',], }; +const RuntimeOptions = { + physx: false, + loader: false, + xrFramebufferScaleFactor: 1, + xrOfferSession: { + mode: 'auto', + features: Constants.WebXRRequiredFeatures, + optionalFeatures: Constants.WebXROptionalFeatures, + }, + canvas: 'canvas', +}; /* wle:auto-constants:end */ const engine = await loadRuntime(Constants.RuntimeBaseName, RuntimeOptions); @@ -108,7 +111,6 @@ engine.registerComponent(VrModeActiveSwitch); engine.registerComponent(BgMusic); engine.registerComponent(BulletSpawner); engine.registerComponent(ConfettiParticles); -engine.registerComponent(GameLogo); engine.registerComponent(HudControl); engine.registerComponent(JoystickMovement); engine.registerComponent(MouseLookCustom); @@ -117,7 +119,6 @@ engine.registerComponent(PlayAgainButton); engine.registerComponent(PlayerLocation); engine.registerComponent(ScoreDisplay); engine.registerComponent(ShotCounter); -engine.registerComponent(SpawnMover); engine.registerComponent(TeleportCustom); engine.registerComponent(WasdControlsCustom); /* wle:auto-register:end */ diff --git a/js/joystick-movement.js b/js/joystick-movement.js index 9921e11..0d28716 100644 --- a/js/joystick-movement.js +++ b/js/joystick-movement.js @@ -1,39 +1,39 @@ -/* - Copyright 2021. Futurewei Technologies Inc. All rights reserved. - 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. -*/ import { Component, Type } from "@wonderlandengine/api"; import { vec3 } from "gl-matrix"; + /** - * Basic movement with W/A/S/D keys. + * Basic movement with W/A/S/D keys and VR 'leafblower' push on trigger (left hand). + * Fluid push with acceleration/deceleration, ground and gravity constraints. + * Directional impulse follows cursor forward vector in any direction (including up/down). */ export class JoystickMovement extends Component { static TypeName = "joystick-movement"; static Properties = { - /** Movement speed in m/s. */ - moveSpeed: { type: Type.Float, default: 0.1 }, - turnAngle: { type: Type.Float, default: 30.0 }, - /** Object of which the orientation is used to determine forward direction */ - headObject: { type: Type.Object } + moveSpeed: { type: Type.Float, default: 0.1 }, // speed for joystick movement + turnAngle: { type: Type.Float, default: 30.0 }, // turning speed deg/s + headObject: { type: Type.Object }, // object to move/rotate + blowForce: { type: Type.Float, default: 1.0 }, // impulse magnitude (m/s) + friction: { type: Type.Float, default: 0.95 }, // horizontal damping [0-1] + gravity: { type: Type.Float, default: 9.81 }, // gravity m/s² + cursorObject: { type: Type.Object }, // object containing `cursor` component + blowDelay: { type: Type.Float, default: 0.5 }, // delay between blows in seconds + turnSpeedMultiplier: { type: Type.Float, default: 1.0 }, // multiplier for turn speed + maxAirJumps: { type: Type.Int, default: 3 }, // maximum number of air jumps + horizontalForceMultiplier: { type: Type.Float, default: 10.0 }, // multiplier for horizontal force + maxBlowDuration: { type: Type.Float, default: 3.0 } // maximum continuous blow duration in seconds } init() { - this.lastTurnTime = 0; - this.anglePerTurn = 30; - this.turnIntervalTime = 100; - - this.up = false; - this.right = false; - this.down = false; - this.left = false; + this.lastTime = Date.now(); + this.velocity = vec3.fromValues(0, 0, 0); // current velocity in m/s + this.gamepadLeft = null; + this.session = null; + this.lastBlowTime = 0; + this.continuousBlow = false; + this.isGrounded = true; + this.remainingAirJumps = this.maxAirJumps; + this.blowStartTime = 0; + this.currentBlowDuration = 0; } start() { @@ -42,82 +42,163 @@ export class JoystickMovement extends Component { } update() { - if (this.gamepad && this.gamepad.axes) { - // x-axis (right is positive) - let currentTime = Date.now(); - let lastTurnTimeGap = Math.abs(currentTime - this.lastTurnTime); - - this.left = false; - this.right = false; - if (this.gamepad.axes[2] > 0) { - this.left = true; - } else if (this.gamepad.axes[2] < 0) { - this.right = true; - } + const now = Date.now(); + const dt = (now - this.lastTime) / 1000; // convert ms to seconds + this.lastTime = now; + // Check if player is grounded + const worldPos = this.headObject.getTranslationWorld(); + const wasGrounded = this.isGrounded; + this.isGrounded = worldPos[1] <= 0.01; // Consider grounded if very close to ground - // y-axis (up is negative) - this.up = false; - this.down = false; - if (this.gamepad.axes[3] > 0) { - this.down = true; - } else if (this.gamepad.axes[3] < 0) { - this.up = true; - } + // Reset air jumps when touching ground + if (!wasGrounded && this.isGrounded) { + this.remainingAirJumps = this.maxAirJumps; + } - let direction = [0, 0, 0]; + // Joystick: forward/back movement and rotation + if (this.gamepadLeft?.axes) { + const x = this.gamepadLeft.axes[2]; + const y = this.gamepadLeft.axes[3]; + let dir = vec3.fromValues(0, 0, 0); let angle = 0; - if (this.up) direction[2] -= 1.0; - if (this.down) direction[2] += 1.0; - if (this.left) angle = -this.anglePerTurn; - if (this.right) angle = this.anglePerTurn; - - let xStrength = Math.abs(this.gamepad.axes[2]); - let yStrength = Math.abs(this.gamepad.axes[3]); - - if (yStrength > xStrength) { - vec3.normalize(direction, direction); - direction[2] *= this.moveSpeed; - vec3.transformQuat(direction, direction, this.headObject.transformWorld); - this.headObject.translate(direction); - } else if (lastTurnTimeGap > this.turnIntervalTime) { - this.headObject.rotateAxisAngleDegObject([0, 1, 0], angle); - this.lastTurnTime = currentTime; + // determine desired direction + if (y < -0.1) dir[2] = -1; + else if (y > 0.1) dir[2] = 1; + + // Apply turn speed multiplier to rotation + if (x > 0.1) angle = -this.turnAngle * this.turnSpeedMultiplier; + else if (x < -0.1) angle = this.turnAngle * this.turnSpeedMultiplier; + + // move or turn + if (Math.abs(y) > Math.abs(x)) { + vec3.normalize(dir, dir); + vec3.scale(dir, dir, this.moveSpeed * dt); + vec3.transformQuat(dir, dir, this.headObject.transformWorld); + this.headObject.translate(dir); + } else if (Math.abs(x) > 0.1) { + this.headObject.rotateAxisAngleDegObject([0,1,0], angle * dt); } } - } - setupVREvents(s) { - /* If in VR, one-time bind the listener */ - this.session = s; - - s.addEventListener('end', function () { - /* Reset cache once the session ends to rebind select etc, in case - * it starts again */ - this.gamepad = null; - this.session = null; - }.bind(this)); - - if (s.inputSources && s.inputSources.length) { - for (let i = 0; i < s.inputSources.length; i++) { - let inputSource = s.inputSources[i]; - - if (inputSource.handedness == "left") { - this.gamepad = inputSource.gamepad; + + // Handle continuous blow + if (this.continuousBlow && this.gamepadLeft?.buttons[0].pressed) { + if (this.blowStartTime === 0) { + this.blowStartTime = now; + this.currentBlowDuration = 0; + } + + this.currentBlowDuration = (now - this.blowStartTime) / 1000; + + if (this.currentBlowDuration <= this.maxBlowDuration && + (this.isGrounded || this.remainingAirJumps > 0)) { + this.applyBlowForce(); + + // Add continuous haptic feedback + if (this.gamepadLeft.hapticActuators) { + this.gamepadLeft.hapticActuators[0].pulse(0.3, 50); } } + } else { + this.blowStartTime = 0; + this.currentBlowDuration = 0; } - s.addEventListener('inputsourceschange', function (e) { - if (e.added && e.added.length) { - for (let i = 0; i < e.added.length; i++) { - let inputSource = e.added[i]; - if (inputSource.handedness == "left") { - this.gamepad = inputSource.gamepad; - console.log("left gamepad added"); - } - } + // apply gravity to vertical velocity + if (!this.isGrounded) { + this.velocity[1] -= this.gravity * dt; + } + + // apply velocity movement + const delta = vec3.clone(this.velocity); + vec3.scale(delta, delta, dt); + this.headObject.translate(delta); + + // ground clamp + const newPos = this.headObject.getTranslationWorld(); + if (newPos[1] < 0) { + newPos[1] = 0; + this.headObject.setTranslationWorld(newPos); + this.velocity[1] = 0; + this.isGrounded = true; + this.remainingAirJumps = this.maxAirJumps; + } + + // horizontal friction - smoother deceleration + const friction = Math.pow(this.friction, dt * 60); // Scale friction with frame rate + this.velocity[0] *= friction; + this.velocity[2] *= friction; + } + + setupVREvents(session) { + this.session = session; + session.addEventListener('end', () => { this.gamepadLeft = null; this.session = null; }); + for (const src of session.inputSources || []) { + if (src.handedness === 'left') this.gamepadLeft = src.gamepad; + } + session.addEventListener('inputsourceschange', e => { + for (const src of e.added || []) { + if (src.handedness === 'left') this.gamepadLeft = src.gamepad; + } + }); + session.addEventListener('selectstart', this.onTriggerDown.bind(this)); + session.addEventListener('selectend', this.onTriggerUp.bind(this)); + } + + onTriggerDown(event) { + const src = event.inputSource; + if (src.handedness !== 'left' || !this.gamepadLeft) return; + + this.continuousBlow = true; + this.blowStartTime = Date.now(); + this.currentBlowDuration = 0; + + // Only apply initial blow if grounded or has remaining air jumps + if (this.isGrounded || this.remainingAirJumps > 0) { + this.applyBlowForce(); + if (!this.isGrounded) { + this.remainingAirJumps--; } - }.bind(this)); + } + } + + onTriggerUp(event) { + const src = event.inputSource; + if (src.handedness !== 'left') return; + + this.continuousBlow = false; + this.blowStartTime = 0; + this.currentBlowDuration = 0; + } + + applyBlowForce() { + if (!this.cursorObject) return; + const cursor = this.cursorObject.getComponent('cursor'); + if (!cursor) return; + const rayObj = cursor.cursorRayObject || cursor.object; + const dir = vec3.create(); + rayObj.getForward(dir); + + // Normalize the direction vector + vec3.normalize(dir, dir); + + // Calculate the horizontal component (XZ plane) + const horizontalDir = vec3.fromValues(dir[0], 0, dir[2]); + vec3.normalize(horizontalDir, horizontalDir); + + // Calculate the vertical component + const verticalDir = vec3.fromValues(0, dir[1], 0); + + // Combine horizontal and vertical components with different weights + const finalDir = vec3.create(); + vec3.scale(horizontalDir, horizontalDir, this.horizontalForceMultiplier); // Increased horizontal force + vec3.scale(verticalDir, verticalDir, 1.0); // Keep vertical force as is + vec3.add(finalDir, horizontalDir, verticalDir); + vec3.normalize(finalDir, finalDir); + + // Apply the impulse in the opposite direction + vec3.scale(finalDir, finalDir, -this.blowForce); + vec3.add(this.velocity, this.velocity, finalDir); } -}; \ No newline at end of file +} diff --git a/js/mouse-mover.js b/js/mouse-mover.js index 467e1d0..b502496 100644 --- a/js/mouse-mover.js +++ b/js/mouse-mover.js @@ -15,165 +15,62 @@ import { vec3 } from "gl-matrix"; import { state } from "./game"; /** -@brief Moves and rotates the mice in random directions at set intervals. +@brief Moves the mouse directly towards the player each frame. */ - - export class MouseMover extends Component { - static TypeName = "mouse-mover"; - static Properties = { - speed: { type: Type.Float, default: 1.0 }, - }; - - time = 0; - currentPos = [0, 0, 0]; - pointA = [0, 0, 0]; - pointB = [0, 0, 0]; - moveDuration = 2; - - savedAngle = 0; - previousAngle = 0; - newAngle = 0 + static Properties = {}; init() { - this.object.getPositionLocal(this.currentPos); - - this.rotateMoveRatio = 2; - this.moveDuration = (Math.random() * 0.7) + 1.3; - this.time = this.moveDuration / this.rotateMoveRatio; - this.speed = 1.5; - this.travelDistance = this.moveDuration * this.speed; - - vec3.add(this.pointA, this.pointA, this.currentPos); - vec3.add(this.pointB, this.currentPos, [0, 0, 1.5]); - - this.speedLevel1 = true; - this.speedLevel2 = true; - this.speedLevel3 = true; - - - this.minDistanceFromPlayer = 7; - - state.updateMoveDuration = function (firstShot = false) { - let targetsLeft = state.score / state.maxTargets; - if (firstShot) { - this.moveDuration *= 0.3; - this.rotateMoveRatio++; - } else if (targetsLeft > 0.2 && this.speedLevel1) { - this.moveDuration *= 0.5; - this.rotateMoveRatio++; - this.speedLevel1 = false; - } else if (targetsLeft > 0.5 && this.speedLevel2) { - this.moveDuration *= 0.7; - this.rotateMoveRatio++; - this.speedLevel2 = false; - } else if (targetsLeft > 0.8 && this.speedLevel3) { - this.moveDuration *= 0.7; - this.rotateMoveRatio++; - this.speedLevel3 = false; - } - }.bind(this); + // nothing needed here for direct follow } + update(dt) { if (isNaN(dt)) return; + if(state.gameOver || state.paused) return; // skip if game is over or paused - // increment time on movement cycle - this.time += dt; - - // get new angle and move location only when movement cycle is complete - if (this.time >= this.moveDuration) { - this.time = 0; - - this.pointA = this.currentPos; + // Get current speed from wave system + const currentSpeed = state.getWaveSpeed() ; - // new position will always be the hypotenuse of x and z triangle - let x = Math.random() * this.travelDistance; - let z = Math.abs(Math.sqrt(Math.pow(this.travelDistance, 2) - Math.pow(x, 2))); + // get current and player positions + const currentPos = vec3.create(); + this.object.getPositionLocal(currentPos); + const playerPos = state.getPlayerLocation(); - let playerLocation = [0, 0, 0] - playerLocation = state.getPlayerLocation(); - - let xNegBoundary = this.pointA[0] < -8; - let xPosBoundary = this.pointA[0] > 13; - let yNegBoundary = this.pointA[2] < -8; - let yPosBoundary = this.pointA[2] > 12; - - // keep mice within farm fence - if (xNegBoundary || xPosBoundary || yNegBoundary || yPosBoundary) { - if (xPosBoundary) { - x *= -1; - } - if (yPosBoundary) { - z *= -1; - } - // move away from player if too close - } else if (this.isPlayerClose()) { - if (this.pointA[0] < playerLocation[0]) { - x *= -1; - } - if (this.pointA[2] < playerLocation[2]) { - z *= -1; - } - // otherwise move in random direction - } else { - const randomNegative1 = Math.round(Math.random()) * 2 - 1; - const randomNegative2 = Math.round(Math.random()) * 2 - 1; - x *= randomNegative1 - z *= randomNegative2; - } - - vec3.add(this.pointB, this.pointA, [x, 0, z]); - - this.newAngle = Math.floor(Math.random() * 180); - this.previousAngle = this.savedAngle; - } - - this.object.resetPosition(); - let rotateTime = this.moveDuration / this.rotateMoveRatio; - let moveTime = this.time - this.moveDuration / this.rotateMoveRatio; - // rotation phase - if (this.time < rotateTime) { - this.object.resetRotation(); - this.savedAngle = (this.time * this.newAngle) + this.previousAngle; - this.object.rotateAxisAngleDegLocal([0, 0, 1], this.savedAngle); - this.object.rotateAxisAngleDegLocal([1, 0, 0], 90); - } - // linear interpolation between old and new position - else { - vec3.lerp(this.currentPos, this.pointA, this.pointB, moveTime); + // Check if zombie is close enough to player to trigger game over + if (vec3.dist(currentPos, playerPos) < 0.5) { + console.log("Zombie reached player, game over"); + state.loseGame(); + return; } - // movement phase - this.object.translateLocal(this.currentPos); - } - // check if player is too close - isPlayerClose() { - let distanceFromPlayer = vec3.dist(this.pointA, state.getPlayerLocation()); - if (distanceFromPlayer < this.minDistanceFromPlayer) { - return true; - } else { - return false; - } - } - // Check if player is too close and run away. Puts update() into "runAwayNow" mode. - runFromPlayer(playerLocation) { - if (this.isPlayerClose()) { - - this.pointA = this.currentPos; - let x = Math.random() * this.travelDistance; - let z = Math.abs(Math.sqrt(Math.pow(this.travelDistance, 2) - Math.pow(x, 2))); - - if (this.pointA[0] < playerLocation[0]) { - x *= -1; - } - if (this.pointA[2] < playerLocation[2]) { - z *= -1; - } - - vec3.add(this.pointB, this.pointA, [x, 0, z]); - // increment time to bypass rotate phase - this.time = this.moveDuration / this.rotateMoveRatio; - } + // compute direction vector in XZ plane + const dir = vec3.fromValues( + playerPos[0] - currentPos[0], + 0, + playerPos[2] - currentPos[2] + ); + if (vec3.len(dir) < 0.01) return; // already at player + vec3.normalize(dir, dir); + + // move towards player with wave-adjusted speed + const moveVec = vec3.scale(vec3.create(), dir, currentSpeed * dt); + // update position + vec3.add(currentPos, currentPos, moveVec); + this.object.setPositionLocal(currentPos); + // ensure Y position is at floor height + currentPos[1] = state.floorHeight; + this.object.setPositionLocal(currentPos); + // update the object position again to ensure Y is set + this.object.setPositionLocal(currentPos); + // update the collider position + this.object.getComponent("collision").setPositionLocal(currentPos); + // ensure the collider is at the correct height + this.object.getComponent("collision").setHeight(state.floorHeight); + + // rotate to face the player (Y-axis rotation) + const angleDeg = Math.atan2(dir[0], dir[2]) * (180 / Math.PI); + this.object.resetRotation(); + this.object.rotateAxisAngleDegLocal([0, 1, 0], angleDeg); } -}; +} diff --git a/js/mouse-spawner.js b/js/mouse-spawner.js index be74485..6e36ad7 100644 --- a/js/mouse-spawner.js +++ b/js/mouse-spawner.js @@ -16,7 +16,7 @@ import { state } from "./game"; import { MouseMover } from "./mouse-mover"; import { ScoreTrigger } from "./score-trigger"; /** -@brief Spawns in mice at set intervals until maxTargets is reached. +@brief Spawns waves of 6 mice per side (±X, ±Z). Após 6 spawns, escolhe um novo lado aleatório. */ const tempQuat2 = new Float32Array(8); @@ -24,28 +24,36 @@ const tempQuat2 = new Float32Array(8); export class MouseSpawner extends Component { static TypeName = "mouse-spawner"; static Properties = { - targetMesh: { type: Type.Mesh }, - targetMaterial: { type: Type.Material }, - spawnAnimation: { type: Type.Animation }, - maxTargets: { type: Type.Int, default: 20 }, - particles: { type: Type.Object }, - spawnIntervalCeiling: { type: Type.Float, default: 3.0 }, - spawnIntervalFloor: { type: Type.Float, default: 1.0 }, - bulletMesh: { type: Type.Mesh }, - bulletMaterial: { type: Type.Material }, + targetMesh: { type: Type.Mesh }, + targetMaterial: { type: Type.Material }, + spawnAnimation: { type: Type.Animation }, + particles: { type: Type.Object }, + initialSpawnInterval: { type: Type.Float, default: 2.0 }, + minSpawnInterval: { type: Type.Float, default: 0.5 }, + spawnIntervalDecreaseRate: { type: Type.Float, default: 0.95 }, + // Raio mínimo/ máximo para spawn + spawnRadiusMin: { type: Type.Float, default: 15.0 }, + spawnRadiusMax: { type: Type.Float, default: 30.0 }, }; + time = 0; + spawnInterval = 1.2; + targets = []; + + // Para controle de waves por lado + sides = ["+X", "-X", "+Z", "-Z"]; + currentSide = null; + sideCount = 0; + waveNumber = 1; + static onRegister(engine) { engine.registerComponent(ScoreTrigger); engine.registerComponent(HowlerAudioSource); - engine.registerComponent(MouseMover); + // engine.registerComponent(MouseMover); } - time = 0; - spawnInterval = 3; - targets = []; - init() { + // Função global para despawn state.despawnTarget = function (obj) { for (let i = 0; i < this.targets.length; i++) { if (obj.objectId == this.targets[i].objectId) { @@ -59,77 +67,124 @@ export class MouseSpawner extends Component { start() { state.mouseSpawner = this; - state.mouseSound = this.object.addComponent(HowlerAudioSource, { - src: "sfx/critter-40645.mp3", - loop: true, - volume: 1.0, - }); - - state.maxTargets = this.maxTargets; - state.updateScore(`Eliminate all ${state.maxTargets} rats.`); - this.spawnInterval = this.spawnIntervalCeiling; + this.spawnInterval = this.initialSpawnInterval; + this.pickNewSide(); // escolhe o primeiro lado this.spawnTarget(); } update(dt) { + if (state.gameOver || state.paused) return; + this.time += dt; - if (state.targetsSpawned >= this.maxTargets) return; - if (this.time >= this.spawnInterval) { this.time = 0; this.spawnTarget(); - //gradually increases spawn interval until it hits the floor - if (this.spawnInterval > this.spawnIntervalFloor) { - this.spawnInterval *= .8; + + // Diminui o intervalo + if (this.spawnInterval > this.minSpawnInterval) { + this.spawnInterval *= this.spawnIntervalDecreaseRate; } } } reset() { - for (let i = 0; i < this.targets.length; i++) { - this.targets[i].destroy(); + // Destroi todos os alvos atuais + for (let obj of this.targets) { + obj.destroy(); } this.targets = []; + this.time = 0; + this.spawnInterval = this.initialSpawnInterval; + this.waveNumber = 1; + this.sideCount = 0; + this.pickNewSide(); this.object.resetPosition(); } + pickNewSide() { + // Escolhe um lado aleatório (pode repetir) + const idx = Math.floor(Math.random() * this.sides.length); + this.currentSide = this.sides[idx]; + this.sideCount = 0; + // Atualiza texto de wave + state.updateScore(`Wave ${this.waveNumber} – Lado ${this.currentSide}`); + } + spawnTarget() { + // Se já geramos 6 no lado atual, passamos para o próximo + if (this.sideCount >= 6) { + this.waveNumber++; + this.pickNewSide(); + } + + // Cria o objeto no ponto do spawner const obj = this.engine.scene.addObject(); obj.setTransformLocal(this.object.getTransformWorld(tempQuat2)); - obj.scaleLocal([0.1, 0.1, 0.1]); - const mesh = obj.addComponent('mesh'); - mesh.mesh = this.targetMesh; + // Gera deslocamento com base no lado atual + const minR = this.spawnRadiusMin; + const maxR = this.spawnRadiusMax; + let dx = 0, dz = 0; + + switch (this.currentSide) { + case "+X": + dx = minR + Math.random() * (maxR - minR); + dz = (Math.random() * 2 - 1) * maxR; + break; + case "-X": + dx = - (minR + Math.random() * (maxR - minR)); + dz = (Math.random() * 2 - 1) * maxR; + break; + case "+Z": + dz = minR + Math.random() * (maxR - minR); + dx = (Math.random() * 2 - 1) * maxR; + break; + case "-Z": + dz = - (minR + Math.random() * (maxR - minR)); + dx = (Math.random() * 2 - 1) * maxR; + break; + } + obj.translateLocal([dx, 0, dz]); + + // Configura mesh, material e escala + obj.scaleLocal([1,1,1]); + const mesh = obj.addComponent("mesh"); + mesh.mesh = this.targetMesh; mesh.material = this.targetMaterial; - mesh.active = true; + mesh.active = true; + + // Movimento obj.addComponent(MouseMover); + // Corrige rotação + obj.rotateAxisAngleDegLocal([1,0,0], 180); + + // Animação de spawn if (this.spawnAnimation) { const anim = obj.addComponent("animation"); anim.playCount = 1; anim.animation = this.spawnAnimation; - anim.active = true; + anim.active = true; anim.play(); } - /* Add scoring trigger */ + // Gatilho de pontuação const trigger = this.engine.scene.addObject(obj); trigger.addComponent("collision", { collider: WL.Collider.Sphere, - extents: [0.6, 0, 0], - group: 1 << 0, - active: true, + extents: [1.5,1.5,1.5], + group: 1 << 0, + active: true, }); - - trigger.translateLocal([0, 0.7, 0]); + trigger.translateLocal([0,1,0]); trigger.addComponent(ScoreTrigger, { particles: this.particles }); obj.setDirty(); - - state.targetsSpawned++; this.targets.push(obj); - state.mouseSound.play(); + + // Incrementa contador deste lado + this.sideCount++; } -}; +} diff --git a/js/play-again-button.js b/js/play-again-button.js index 9089d2d..500f91b 100644 --- a/js/play-again-button.js +++ b/js/play-again-button.js @@ -35,33 +35,51 @@ export class PlayAgainButton extends Component { }); state.resetButton = this; - this.hide(); + this.unhide(); } restart() { + console.log("Restarting game"); state.restart(); this.hide(); } hide() { - this.object.children[0].getComponent("mesh").active = false; - this.object.children[1].getComponent("text").active = false; + console.log("Hiding button"); + if (this.object.children[0]) { + const mesh = this.object.children[0].getComponent("mesh"); + if (mesh) mesh.active = false; + } + if (this.object.children[1]) { + const text = this.object.children[1].getComponent("text"); + if (text) text.active = false; + } this.active = false; } unhide() { - this.object.children[0].getComponent("mesh").active = true; - this.object.children[1].getComponent("text").active = true; + console.log("Unhiding button"); + if (this.object.children[0]) { + const mesh = this.object.children[0].getComponent("mesh"); + if (mesh) mesh.active = true; + } + if (this.object.children[1]) { + const text = this.object.children[1].getComponent("text"); + if (text) text.active = true; + } this.active = true; } update(dt) { + if (!this.active) return; + let overlaps = this.collision.queryOverlaps(); for (let i = 0; i < overlaps.length; ++i) { let p = overlaps[i].object.getComponent("bullet-physics"); if (p) { this.restart(); this.soundPop.play(); + break; } } } diff --git a/js/player-location.js b/js/player-location.js index 74a5c68..6b40dd7 100644 --- a/js/player-location.js +++ b/js/player-location.js @@ -41,14 +41,14 @@ export class PlayerLocation extends Component { let locationDistance = vec3.dist(currentLocation, this.playerLocation); if (locationDistance != 0) { this.playerLocation = currentLocation; - try { - for (let i = 0; i < state.mouseSpawner.targets.length; i++) { - let mouseMoverComponent = state.mouseSpawner.targets[i].getComponent(MouseMover); - mouseMoverComponent.runFromPlayer(currentLocation);; - } - } catch (e) { - console.log("player-location >> get mouse >> ", e); - } + // try { + // for (let i = 0; i < state.mouseSpawner.targets.length; i++) { + // let mouseMoverComponent = state.mouseSpawner.targets[i].getComponent(MouseMover); + // // mouseMoverComponent.runFromPlayer(currentLocation);; + // } + // } catch (e) { + // console.log("player-location >> get mouse >> ", e); + // } } } }; diff --git a/js/score-trigger.js b/js/score-trigger.js index 4af335e..18b913b 100644 --- a/js/score-trigger.js +++ b/js/score-trigger.js @@ -25,35 +25,54 @@ objects, see `mouse-spawner`. const tempQuat2 = new Float32Array(8); export class ScoreTrigger extends Component { - static TypeName = "score-trigger"; - static Properties = { - particles: { type: Type.Object }, - }; - - init() { - this.collision = this.object.getComponent('collision'); - this.soundHit = this.object.addComponent("howler-audio-source", { - src: "sfx/high-pitched-aha-103125.mp3", - volume: 1.9, - }); - this.soundPop = this.object.addComponent("howler-audio-source", { - src: "sfx/pop-94319.mp3", - volume: 1.9, - }); - state.victoryMusic = this.object.addComponent("howler-audio-source", { - src: "music/level-win-6416.mp3", - volume: 1.9, - }); - } + static TypeName = "score-trigger"; + static Properties = { + particles: { type: Type.Object }, + }; + + init() { + this.collision = this.object.getComponent("collision"); + this.soundHit = this.object.addComponent("howler-audio-source", { + src: "sfx/high-pitched-aha-103125.mp3", + volume: 1.9, + }); + this.soundPop = this.object.addComponent("howler-audio-source", { + src: "sfx/pop-94319.mp3", + volume: 1.9, + }); + state.victoryMusic = this.object.addComponent("howler-audio-source", { + src: "music/level-win-6416.mp3", + volume: 1.9, + }); + } - onHit() { - this.particles.setTransformWorld(this.object.getTransformWorld(tempQuat2)); + onHit(callback) { + if (typeof callback === "function") { + this.particles.setTransformWorld( + this.object.getTransformWorld(tempQuat2) + ); this.particles.getComponent("confetti-particles").burst(); + state.zombiesKilledInWave++; + state.zombieCount++; + // document.getElementById("pontos-test").innerHTML = state.zombieCount; + const elementPontos = document.getElementById("pontos-test") + if(elementPontos) { + elementPontos.innerHTML = state.zombieCount + } - state.despawnTarget(this.object.parent); - state.incrementScore(); + state.despawnTarget(this.object.parent); - this.soundHit.play(); - this.soundPop.play(); + callback(); // chama o callback com argumento + } else { + console.log("Nenhum callback fornecido."); } -}; + this.particles.setTransformWorld(this.object.getTransformWorld(tempQuat2)); + this.particles.getComponent("confetti-particles").burst(); + + state.despawnTarget(this.object.parent); + state.incrementScore(); + + this.soundHit.play(); + this.soundPop.play(); + } +} diff --git a/js/spawn-mover.js b/js/spawn-mover.js index 37b2229..1bf3901 100644 --- a/js/spawn-mover.js +++ b/js/spawn-mover.js @@ -1,16 +1,3 @@ -/* - Copyright 2021. Futurewei Technologies Inc. All rights reserved. - 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. -*/ - import { Component, Type } from "@wonderlandengine/api"; import { vec3, quat2 } from "gl-matrix"; @@ -32,7 +19,7 @@ export class SpawnMover extends Component { this.pointB = [0, 0, 0]; this.moveDuration = 1; - this.speed = 3; + this.speed = 0.2; this.travelDistance = this.moveDuration * this.speed; quat2.getTranslation(this.currentPos, this.object.transformLocal); @@ -42,37 +29,37 @@ export class SpawnMover extends Component { } update(dt) { - if (isNaN(dt)) return; + // if (isNaN(dt)) return; - this.time += dt; - if (this.time >= this.moveDuration) { - this.time -= this.moveDuration; + // this.time += dt; + // if (this.time >= this.moveDuration) { + // this.time -= this.moveDuration; - this.pointA = this.currentPos; - let x = Math.random() * this.travelDistance; - let z = Math.sqrt(Math.pow(this.travelDistance, 2) - Math.pow(x, 2)); + // this.pointA = this.currentPos; + // let x = Math.random() * this.travelDistance; + // let z = Math.sqrt(Math.pow(this.travelDistance, 2) - Math.pow(x, 2)); - let distanceFromOrigin = vec3.length(this.pointA); - if (distanceFromOrigin > 20) { - if (this.pointA[0] >= 14) { - x *= -1; - } - if (this.pointA[2] >= 14) { - z *= -1; - } - } else { - const randomNegative1 = Math.round(Math.random()) * 2 - 1; - const randomNegative2 = Math.round(Math.random()) * 2 - 1; - x *= randomNegative1 - z *= randomNegative2; - } - vec3.add(this.pointB, this.pointA, [x, 0, z]); - } + // let distanceFromOrigin = vec3.length(this.pointA); + // if (distanceFromOrigin > 20) { + // if (this.pointA[0] >= 14) { + // x *= -1; + // } + // if (this.pointA[2] >= 14) { + // z *= -1; + // } + // } else { + // const randomNegative1 = Math.round(Math.random()) * 2 - 1; + // const randomNegative2 = Math.round(Math.random()) * 2 - 1; + // x *= randomNegative1 + // z *= randomNegative2; + // } + // vec3.add(this.pointB, this.pointA, [x, 0, z]); + // } - this.object.resetPosition(); - vec3.lerp(this.currentPos, this.pointA, this.pointB, this.time); - this.object.translateLocal(this.currentPos); + // this.object.resetPosition(); + // vec3.lerp(this.currentPos, this.pointA, this.pointB, this.time); + // this.object.translateLocal(this.currentPos); } }; diff --git a/js/wasd-controls-custom.js b/js/wasd-controls-custom.js index 1ea3083..983531d 100644 --- a/js/wasd-controls-custom.js +++ b/js/wasd-controls-custom.js @@ -1,126 +1,107 @@ -/* - Copyright 2021. Futurewei Technologies Inc. All rights reserved. - 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. -*/ import { Component, Type } from "@wonderlandengine/api"; import { vec3 } from "gl-matrix"; -/** - * Basic movement with W/A/S/D keys. - */ + export class WasdControlsCustom extends Component { static TypeName = "wasd-controls-custom"; static Properties = { - /** Movement speed in m/s. */ speed: { type: Type.Float, default: 0.1 }, - /** Object of which the orientation is used to determine forward direction */ - headObject: { type: Type.Object } + headObject: { type: Type.Object }, + boundaryLimit: { type: Type.Float, default: 12.0 }, + gravity: { type: Type.Float, default: 9.8 }, + jumpForce: { type: Type.Float, default: 8.0 }, + groundLevel: { type: Type.Float, default: 0.0 }, + maxJumps: { type: Type.Int, default: 2 } // número de pulos extras permitidos no ar } - init() { this.up = false; this.right = false; this.down = false; this.left = false; + this.verticalVelocity = 0; + this.isJumping = false; + this.jumpsRemaining = this.maxJumps + 1; // 1 do chão + extras + + this.centerPos = vec3.create(); + if (this.headObject) { + this.headObject.getPositionLocal(this.centerPos); + } + window.addEventListener('keydown', this.press.bind(this)); window.addEventListener('keyup', this.release.bind(this)); } - update() { - - if (this.up || this.right || this.down || this.left /* only move if key is pressed */) { - let direction = [0, 0, 0]; - - let forwardVec = []; - this.object.getForward(forwardVec); /* Get player forward */ - forwardVec[1] = 0; /* Ignore height */ - let backVec = [0, 0, 0]; - - if (forwardVec[2] == 1 /* if z = +1 */) { - forwardVec[2] = this.speed; - backVec = [0, 0, -this.speed]; - } else if (forwardVec[2] == -1 /* if z = -1 */) { - forwardVec[2] = -this.speed; - backVec = [0, 0, this.speed]; - } else /* calculate for all other cases */ { - let angle = vec3.angle([0, 0, -1], forwardVec); /* Get angle relative to -Z */ - - let xPolarity = -1; - let zPolarity = -1; - - if (forwardVec[0] > 0) xPolarity = 1; - if (forwardVec[2] > 0) zPolarity = 1; - - //set x-magnitude for forward and back - forwardVec[0] = xPolarity * Math.abs(Math.sin(angle)) * this.speed; - backVec[0] = -forwardVec[0]; - //set z-magnitude for forward and back - forwardVec[2] = zPolarity * Math.abs(Math.cos(angle)) * this.speed; - backVec[2] = -forwardVec[2]; - } - let rightVec = []; - this.object.getRight(rightVec); - rightVec[1] = 0; - let leftVec = [0, 0, 0]; - if (rightVec[0] == 1) { - rightVec[0] = this.speed; - leftVec = [-this.speed, 0, 0]; - } else if (rightVec[0] == -1) { - rightVec[0] = -this.speed; - leftVec = [this.speed, 0, 0]; - } else { - let angle = vec3.angle([-1, 0, 0], rightVec); - let xPolarity = -1; - let zPolarity = -1; - //set x-magnitude for forward and back - if (rightVec[0] > 0) xPolarity = 1; - rightVec[0] = xPolarity * Math.abs(Math.cos(angle)) * this.speed; - leftVec[0] = -rightVec[0]; - //set z-magnitude for forward and back - if (rightVec[2] > 0) zPolarity = 1; - rightVec[2] = zPolarity * Math.abs(Math.sin(angle)) * this.speed; - leftVec[2] = -rightVec[2]; - } - - if (this.up) vec3.add(direction, direction, forwardVec); - if (this.down) vec3.add(direction, direction, backVec); - if (this.left) vec3.add(direction, direction, leftVec); - if (this.right) vec3.add(direction, direction, rightVec); - - this.headObject.translate(direction); + update(dt) { + if (!this.headObject) return; + + const direction = vec3.create(); + + // Movimento horizontal + if (this.up || this.right || this.down || this.left) { + const forward = vec3.fromValues(0, 0, 0); + this.object.getForward(forward); + forward[1] = 0; + vec3.normalize(forward, forward); + vec3.scale(forward, forward, this.speed); + + const rightV = vec3.fromValues(0, 0, 0); + this.object.getRight(rightV); + rightV[1] = 0; + vec3.normalize(rightV, rightV); + vec3.scale(rightV, rightV, this.speed); + + if (this.up) vec3.add(direction, direction, forward); + if (this.down) vec3.sub(direction, direction, forward); + if (this.right) vec3.add(direction, direction, rightV); + if (this.left) vec3.sub(direction, direction, rightV); + } + + const newPos = vec3.create(); + this.headObject.getPositionLocal(newPos); + vec3.add(newPos, newPos, direction); + + // Gravidade + this.verticalVelocity -= this.gravity * dt; + newPos[1] += this.verticalVelocity * dt; + + // Chegou no chão + if (newPos[1] <= this.groundLevel) { + newPos[1] = this.groundLevel; + this.verticalVelocity = 0; + this.jumpsRemaining = this.maxJumps + 1; // reset ao tocar o chão + this.isJumping = false; + } else { + this.isJumping = true; } + + // Clamping X/Z + const minX = this.centerPos[0] - this.boundaryLimit; + const maxX = this.centerPos[0] + this.boundaryLimit; + const minZ = this.centerPos[2] - this.boundaryLimit; + const maxZ = this.centerPos[2] + this.boundaryLimit; + newPos[0] = Math.min(Math.max(newPos[0], minX), maxX); + newPos[2] = Math.min(Math.max(newPos[2], minZ), maxZ); + + this.headObject.setPositionLocal(newPos); } press(e) { - if (e.keyCode === 38 /* up */ || e.keyCode === 87 /* w */ || e.keyCode === 90 /* z */) { - this.up = true - } else if (e.keyCode === 39 /* right */ || e.keyCode === 68 /* d */) { - this.right = true - } else if (e.keyCode === 40 /* down */ || e.keyCode === 83 /* s */) { - this.down = true - } else if (e.keyCode === 37 /* left */ || e.keyCode === 65 /* a */ || e.keyCode === 81 /* q */) { - this.left = true + if (e.keyCode === 38 || e.keyCode === 87 || e.keyCode === 90) this.up = true; + else if (e.keyCode === 39 || e.keyCode === 68) this.right = true; + else if (e.keyCode === 40 || e.keyCode === 83) this.down = true; + else if (e.keyCode === 37 || e.keyCode === 65 || e.keyCode === 81) this.left = true; + else if (e.keyCode === 32 && this.jumpsRemaining > 0) { + // Executa o pulo imediatamente se houver pulos disponíveis + this.verticalVelocity = this.jumpForce; + this.jumpsRemaining--; } } release(e) { - if (e.keyCode === 38 /* up */ || e.keyCode === 87 /* w */ || e.keyCode === 90 /* z */) { - this.up = false - } else if (e.keyCode === 39 /* right */ || e.keyCode === 68 /* d */) { - this.right = false - } else if (e.keyCode === 40 /* down */ || e.keyCode === 83 /* s */) { - this.down = false - } else if (e.keyCode === 37 /* left */ || e.keyCode === 65 /* a */ || e.keyCode === 81 /* q */) { - this.left = false - } + if (e.keyCode === 38 || e.keyCode === 87 || e.keyCode === 90) this.up = false; + else if (e.keyCode === 39 || e.keyCode === 68) this.right = false; + else if (e.keyCode === 40 || e.keyCode === 83) this.down = false; + else if (e.keyCode === 37 || e.keyCode === 65 || e.keyCode === 81) this.left = false; } -}; \ No newline at end of file +}; diff --git a/models/Zombie.glb b/models/Zombie.glb new file mode 100644 index 0000000..573a710 Binary files /dev/null and b/models/Zombie.glb differ diff --git a/package-lock.json b/package-lock.json index 71c8043..e8d2f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,124 +1,112 @@ { - "name": "MyWonderland", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "MyWonderland", - "version": "1.0.0", - "dependencies": { - "@wonderlandengine/api": "^1.0.0", - "@wonderlandengine/components": "^1.0.3", - "gl-matrix": "^3.4.3" - } + "name": "MyWonderland", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "MyWonderland", + "version": "1.0.0", + "dependencies": { + "@wonderlandengine/api": ">=1.4.0 <1.5.0", + "@wonderlandengine/components": "^1.0.3", + "gl-matrix": "^3.4.3" + } + }, + "node_modules/@types/earcut": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.1.tgz", + "integrity": "sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ==" + }, + "node_modules/@types/webxr": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz", + "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==" + }, + "node_modules/@wonderlandengine/api": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@wonderlandengine/api/-/api-1.4.2.tgz", + "integrity": "sha512-l9BER6npfjokcEquZbz5rPSCxoaxHlbGvLfUfgZIX2Rmv+z7vOso/e6jLezH9wmZ+PjF1o3fGuRtdwPDdC3kgg==", + "dependencies": { + "@types/webxr": "^0.5.0" + } + }, + "node_modules/@wonderlandengine/components": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wonderlandengine/components/-/components-1.0.3.tgz", + "integrity": "sha512-Swp8Lh1Arwm+V4y3IUL2WcXiIJWb8QeRS40d1/kmIjAIcXLQnv6SBv6zoVgXVVZAg3gb6eHxb8+1RGwEHPtihA==", + "dependencies": { + "@types/earcut": "^2.1.1", + "@types/webxr": "^0.5.0", + "earcut": "^2.2.4", + "gl-matrix": "^3.3.0", + "howler": "^2.2.1" + }, + "peerDependencies": { + "@wonderlandengine/api": "^1.0.0" + } + }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "node_modules/howler": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz", + "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==" + } }, - "node_modules/@types/earcut": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.1.tgz", - "integrity": "sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ==" - }, - "node_modules/@types/webxr": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz", - "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==" - }, - "node_modules/@wonderlandengine/api": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@wonderlandengine/api/-/api-1.0.0.tgz", - "integrity": "sha512-aqjeIgNTa823quW/QYhrTlo6gRMgY9ZT1Q+Oh48K1AFzljb1eo6qb53h1989BKkTIZ5kFhKpltQRt/NBR88dxg==", - "dependencies": { - "@types/webxr": "^0.5.0", - "wasm-feature-detect": "^1.3.0" - } - }, - "node_modules/@wonderlandengine/components": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@wonderlandengine/components/-/components-1.0.3.tgz", - "integrity": "sha512-Swp8Lh1Arwm+V4y3IUL2WcXiIJWb8QeRS40d1/kmIjAIcXLQnv6SBv6zoVgXVVZAg3gb6eHxb8+1RGwEHPtihA==", - "dependencies": { - "@types/earcut": "^2.1.1", - "@types/webxr": "^0.5.0", - "earcut": "^2.2.4", - "gl-matrix": "^3.3.0", - "howler": "^2.2.1" - }, - "peerDependencies": { - "@wonderlandengine/api": "^1.0.0" - } - }, - "node_modules/earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" - }, - "node_modules/gl-matrix": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", - "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" - }, - "node_modules/howler": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz", - "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==" - }, - "node_modules/wasm-feature-detect": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.5.1.tgz", - "integrity": "sha512-GHr23qmuehNXHY4902/hJ6EV5sUANIJC3R/yMfQ7hWDg3nfhlcJfnIL96R2ohpIwa62araN6aN4bLzzzq5GXkg==" - } - }, - "dependencies": { - "@types/earcut": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.1.tgz", - "integrity": "sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ==" - }, - "@types/webxr": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz", - "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==" - }, - "@wonderlandengine/api": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@wonderlandengine/api/-/api-1.0.0.tgz", - "integrity": "sha512-aqjeIgNTa823quW/QYhrTlo6gRMgY9ZT1Q+Oh48K1AFzljb1eo6qb53h1989BKkTIZ5kFhKpltQRt/NBR88dxg==", - "requires": { - "@types/webxr": "^0.5.0", - "wasm-feature-detect": "^1.3.0" - } - }, - "@wonderlandengine/components": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@wonderlandengine/components/-/components-1.0.3.tgz", - "integrity": "sha512-Swp8Lh1Arwm+V4y3IUL2WcXiIJWb8QeRS40d1/kmIjAIcXLQnv6SBv6zoVgXVVZAg3gb6eHxb8+1RGwEHPtihA==", - "requires": { - "@types/earcut": "^2.1.1", - "@types/webxr": "^0.5.0", - "earcut": "^2.2.4", - "gl-matrix": "^3.3.0", - "howler": "^2.2.1" - } - }, - "earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" - }, - "gl-matrix": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", - "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" - }, - "howler": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz", - "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==" - }, - "wasm-feature-detect": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.5.1.tgz", - "integrity": "sha512-GHr23qmuehNXHY4902/hJ6EV5sUANIJC3R/yMfQ7hWDg3nfhlcJfnIL96R2ohpIwa62araN6aN4bLzzzq5GXkg==" + "dependencies": { + "@types/earcut": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.1.tgz", + "integrity": "sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ==" + }, + "@types/webxr": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz", + "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==" + }, + "@wonderlandengine/api": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@wonderlandengine/api/-/api-1.4.2.tgz", + "integrity": "sha512-l9BER6npfjokcEquZbz5rPSCxoaxHlbGvLfUfgZIX2Rmv+z7vOso/e6jLezH9wmZ+PjF1o3fGuRtdwPDdC3kgg==", + "requires": { + "@types/webxr": "^0.5.0" + } + }, + "@wonderlandengine/components": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wonderlandengine/components/-/components-1.0.3.tgz", + "integrity": "sha512-Swp8Lh1Arwm+V4y3IUL2WcXiIJWb8QeRS40d1/kmIjAIcXLQnv6SBv6zoVgXVVZAg3gb6eHxb8+1RGwEHPtihA==", + "requires": { + "@types/earcut": "^2.1.1", + "@types/webxr": "^0.5.0", + "earcut": "^2.2.4", + "gl-matrix": "^3.3.0", + "howler": "^2.2.1" + } + }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "howler": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz", + "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==" + } } - } } diff --git a/package.json b/package.json index ad18f0a..9709cca 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,19 @@ { - "name": "MyWonderland", - "version": "1.0.0", - "description": "My Wonderland project", - "main": "js/index.js", - "type": "module", - "module": "js/index.js", - "keywords": [ - "wonderland-engine" - ], - "dependencies": { - "@wonderlandengine/api": "^1.0.0", - "@wonderlandengine/components": "^1.0.3", - "gl-matrix": "^3.4.3" - } -} + "name": "MyWonderland", + "version": "1.0.0", + "description": "My Wonderland project", + "main": "js/index.js", + "type": "module", + "module": "js/index.js", + "keywords": [ + "wonderland-engine" + ], + "dependencies": { + "@wonderlandengine/api": ">=1.4.0 <1.5.0", + "@wonderlandengine/components": "^1.0.3", + "gl-matrix": "^3.4.3" + }, + "overrides": { + "@wonderlandengine/api": "$@wonderlandengine/api" + } +} \ No newline at end of file