@playcanvas/supersplat-viewer
Version:
Viewer for https://superspl.at
5 lines (3 loc) • 3.59 MB
JavaScript
var index$2 = "* {\n margin: 0;\n padding: 0;\n touch-action: none;\n}\nbody {\n overflow: hidden;\n font-family: 'Arial', sans-serif;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n}\n.hidden {\n display: none !important;\n}\n#ui {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n}\n#ui * {\n pointer-events: auto;\n}\n#infoPanel {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n}\n#infoPanel > #infoPanelContent {\n position: absolute;\n top: 40px;\n left: 50%;\n transform: translate(-50%, 0);\n min-height: 280px;\n min-width: 320px;\n padding: 8px;\n border-radius: 24px;\n color: black;\n background-color: #eeed;\n display: flex;\n flex-direction: column;\n}\n#infoPanel > #infoPanelContent > #header {\n font-size: 24px;\n font-weight: bold;\n padding-bottom: 16px;\n color: #444;\n display: none;\n}\n#infoPanel > #infoPanelContent > #tabs {\n display: flex;\n gap: 16px;\n background-color: #666;\n padding: 8px;\n border-radius: 22px;\n}\n#infoPanel > #infoPanelContent > #tabs > .tab {\n padding: 8px;\n border-radius: 16px;\n cursor: pointer;\n flex-grow: 1;\n text-align: center;\n font-weight: bold;\n font-size: 14px;\n color: #222;\n transition: background-color 250ms ease;\n}\n#infoPanel > #infoPanelContent > #tabs > .tab:hover {\n background-color: #eee;\n}\n#infoPanel > #infoPanelContent > #tabs > .tab.active {\n background-color: #eee;\n}\n#infoPanel > #infoPanelContent > #infoPanels {\n padding: 16px;\n}\n#infoPanel > #infoPanelContent .control-item {\n display: flex;\n justify-content: space-between;\n gap: 32px;\n line-height: 1.5;\n}\n#infoPanel > #infoPanelContent > #infoPanels h1 {\n font-size: 14px;\n font-weight: bold;\n padding: 0 0 6px 0;\n}\n#infoPanel > #infoPanelContent .control-item > .control-action {\n text-align: left;\n color: #333;\n}\n#infoPanel > #infoPanelContent .control-item > .control-key {\n text-align: right;\n color: #666;\n}\n#infoPanel > #infoPanelContent .control-spacer {\n border-bottom: 1px dashed #666;\n margin: 10px 0;\n}\n\n/* loadingWrap */\n\n#loadingWrap {\n position: fixed;\n bottom: 120px;\n left: 50%;\n transform: translate(-50%, 0);\n width: 380px;\n\n display: flex;\n flex-direction: column;\n\n padding: 16px;\n}\n#loadingWrap > #loadingText {\n font-size: 18px;\n color: white;\n text-align: center;\n text-shadow: 0 0 4px rgba(0, 0, 0, 0.5);\n}\n#loadingWrap > #loadingBar {\n width: 100%;\n height: 10px;\n margin-top: 8px;\n border-radius: 4px;\n overflow: hidden;\n}\n\n/* controlsWrap */\n\n#controlsWrap {\n position: absolute;\n left: max(16px, env(safe-area-inset-left));\n right: max(16px, env(safe-area-inset-right));\n bottom: max(16px, env(safe-area-inset-bottom));\n\n display: flex;\n flex-direction: column;\n}\n\n#controlsWrap.faded-in {\n visibility: visible;\n opacity: 1;\n transition: opacity 0.5s ease-out;\n}\n#controlsWrap.faded-out {\n visibility: hidden;\n opacity: 0;\n transition: visibility 0s 0.5s, opacity 0.5s ease-out;\n}\n\n#controlsWrap > #timelineContainer {\n height: 30px;\n cursor: pointer;\n}\n#controlsWrap > #timelineContainer > #line {\n width: 100%;\n height: 50%;\n border-bottom: 1px solid #8888;\n}\n#controlsWrap > #timelineContainer > #handle {\n position: absolute;\n top: 15.5px;\n width: 10px;\n height: 10px;\n transform: translate(-50%, -50%);\n border: 1px solid black;\n border-radius: 50%;\n background-color: #fff8;\n}\n#controlsWrap > #timelineContainer > #time {\n position: absolute;\n top: 0;\n padding: 2px 4px;\n transform: translate(-50%, -100%);\n font-size: 12px;\n color: white;\n background-color: rgba(0, 0, 0, 0.5);\n border-radius: 4px;\n}\n#controlsWrap > #buttonContainer {\n display: flex;\n gap: 8px;\n}\n\n/* spacer */\n\n.spacer {\n flex-grow: 1;\n}\n\n/* settingsPanel */\n\n#settingsPanel {\n position: fixed;\n right: max(16px, env(safe-area-inset-right));\n bottom: calc(max(16px, env(safe-area-inset-bottom)) + 70px);\n padding: 10px;\n color: #eee;\n background-color: rgba(0, 0, 0, 0.3);\n border-radius: 8px;\n\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n\n font-size: 14px;\n}\n#settingsPanel > .settingsRow {\n display: flex;\n gap: 4px;\n width: 100%;\n \n}\n#settingsPanel > .settingsRow > .button {\n padding: 10px 0px;\n flex-grow: 1;\n}\n\n/* toggleWrap */\n\n.toggleWrap {\n position: relative;\n background-color: #dddd;\n border: 1px solid #888;\n border-radius: 8px;\n height: 40px;\n width: 120px;\n}\n.toggleWrap > .toggleHighlight {\n position: absolute;\n left: 0;\n width: 50%;\n height: 100%;\n background-color: #eee;\n border-radius: 8px;\n transition: left 0.1s ease-out;\n}\n.toggleWrap > .toggleHighlight.right {\n left: 60px;\n}\n.toggleWrap > button {\n position: absolute;\n width: 60px;\n height: 100%;\n border: 0;\n border-radius: 8px;\n\n cursor: pointer;\n font-size: 14px;\n letter-spacing: -0.25px;\n\n color: #222;\n background-color: transparent;\n\n appearance: none;\n}\n.toggleWrap > button:hover {\n color: black;\n}\n.toggleWrap > button.left {\n left: 0;\n}\n.toggleWrap > button.right {\n right: 0;\n}\n\n/* button */\n.button {\n width: 40px;\n height: 40px;\n padding: 0;\n margin: 0;\n color: #222;\n background-color: #dddd;\n border: 1px solid #888;\n border-radius: 8px;\n cursor: pointer;\n\n /* font-weight: bold; */\n font-size: 14px;\n letter-spacing: -0.25px;\n}\n.button:hover {\n background-color: #eee;\n}\n.button > svg {\n display: block;\n margin: auto;\n}\n#poster {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-size: cover;\n background-position: center;\n background-repeat: no-repeat;\n}\nbutton {\n color: #222;\n}\nbutton:active {\n outline: none;\n}\n#joystickBase {\n position: absolute;\n width: 96px;\n height: 96px;\n transform: translate(-50%, -50%);\n border-radius: 50%;\n touch-action: none;\n\n background: radial-gradient(circle at center, #0000 50%, #000f 100%);\n background-color: #0002;\n}\n\n#joystickBase > #joystick {\n position: absolute;\n width: 48px;\n height: 48px;\n transform: translate(-50%, -50%);\n border-radius: 50%;\n touch-action: none;\n\n background-color: #fff8;\n}";
var index$1 = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>SuperSplat Viewer</title>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover\">\n <base href=\"\">\n <link rel=\"stylesheet\" href=\"./index.css\">\n <script type=\"module\">\n const url = new URL(location.href);\n const settingsUrl = url.searchParams.has('settings') ? url.searchParams.get('settings') : './settings.json';\n const contentUrl = url.searchParams.has('content') ? url.searchParams.get('content') : './scene.compressed.ply';\n const params = {};\n\n // apply url parameter overrides\n if (url.searchParams.has('noui')) params.noui = true;\n if (url.searchParams.has('noanim')) params.noanim = true;\n if (url.searchParams.has('poster')) params.posterUrl = url.searchParams.get('poster');\n if (url.searchParams.has('skybox')) params.skyboxUrl = url.searchParams.get('skybox');\n if (url.searchParams.has('ministats')) params.ministats = true;\n\n const createImage = (url) => {\n const img = new Image();\n img.src = url;\n return img;\n };\n\n window.sse = {\n poster: params.posterUrl && createImage(params.posterUrl),\n settings: fetch(settingsUrl).then(response => response.json()),\n contentUrl,\n contents: fetch(contentUrl),\n params\n };\n </script>\n <script type=\"module\" src=\"./index.js\"></script>\n </head>\n <body>\n <pc-app antialias=\"false\" depth=\"false\" high-resolution=\"true\" stencil=\"false\">\n <pc-scene>\n <!-- Camera (with XR support) -->\n <pc-entity name=\"camera root\">\n <pc-entity name=\"camera\">\n <pc-camera near-clip=\"0.001\" far-clip=\"5000\" horizontal-fov=\"true\" tonemap=\"none\"></pc-camera>\n </pc-entity>\n <!-- XR controllers will be added here -->\n <pc-scripts>\n </pc-scripts>\n </pc-entity>\n <!-- Light (for XR controllers) -->\n <pc-entity name=\"light\" rotation=\"35 45 0\">\n <pc-light color=\"white\" intensity=\"1.5\"></pc-light>\n </pc-entity>\n <!-- Splat -->\n <pc-entity name=\"splat\" rotation=\"0 0 180\">\n </pc-entity>\n </pc-scene>\n </pc-app>\n\n <div id=\"ui\">\n <div id=\"poster\"></div>\n\n <!-- Loading Indicator -->\n <div id=\"loadingWrap\">\n <div id=\"loadingText\"></div>\n <div id=\"loadingBar\"></div>\n </div>\n\n <div id=\"controlsWrap\" class=\"faded-in\">\n\n <!-- Timeline Panel -->\n <div id=\"timelineContainer\" class=\"hidden\">\n <div id=\"line\"></div>\n <div id=\"handle\"></div>\n <div id=\"time\" class=\"hidden\">0:00</div>\n </div>\n\n <!-- Buttons Panel -->\n <div id=\"buttonContainer\">\n <button id=\"play\" class=\"button hidden\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24px\" height=\"24px\" viewBox=\"4 4 16 16\" fill=\"currentColor\"><path d=\"M15 12.3301L9 16.6603L9 8L15 12.3301Z\"/></svg>\n </button>\n <button id=\"pause\" class=\"button hidden\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24px\" height=\"24px\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M5 16V4h3v12H5zm7-12h3v12h-3V4z\"/></svg>\n </button>\n <div class=\"spacer\"></div>\n <button id=\"arMode\" class=\"button hidden\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M440-181 240-296q-19-11-29.5-29T200-365v-230q0-22 10.5-40t29.5-29l200-115q19-11 40-11t40 11l200 115q19 11 29.5 29t10.5 40v230q0 22-10.5 40T720-296L520-181q-19 11-40 11t-40-11Zm0-92v-184l-160-93v185l160 92Zm80 0 160-92v-185l-160 93v184ZM80-680v-120q0-33 23.5-56.5T160-880h120v80H160v120H80ZM280-80H160q-33 0-56.5-23.5T80-160v-120h80v120h120v80Zm400 0v-80h120v-120h80v120q0 33-23.5 56.5T800-80H680Zm120-600v-120H680v-80h120q33 0 56.5 23.5T880-800v120h-80ZM480-526l158-93-158-91-158 91 158 93Zm0 45Zm0-45Zm40 69Zm-80 0Z\"/></svg>\n </button>\n <button id=\"vrMode\" class=\"button hidden\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M300-240q-66 0-113-47t-47-113v-163q0-51 32-89.5t82-47.5q57-11 113-15.5t113-4.5q57 0 113.5 4.5T706-700q50 10 82 48t32 89v163q0 66-47 113t-113 47h-40q-13 0-26-1.5t-25-6.5l-64-22q-12-5-25-5t-25 5l-64 22q-12 5-25 6.5t-26 1.5h-40Zm0-80h40q7 0 13.5-1t12.5-3q29-9 56.5-19t57.5-10q30 0 58 9.5t56 19.5q6 2 12.5 3t13.5 1h40q33 0 56.5-23.5T740-400v-163q0-22-14-38t-35-21q-52-11-104.5-14.5T480-640q-54 0-106 4t-105 14q-21 4-35 20.5T220-563v163q0 33 23.5 56.5T300-320ZM40-400v-160h60v160H40Zm820 0v-160h60v160h-60Zm-380-80Z\"/></svg>\n </button>\n <button id=\"info\" class=\"button\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z\"/></svg>\n </button>\n <button id=\"orbitSettings\" class=\"button\">\n <!-- <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h480q33 0 56.5 23.5T720-720v180l160-160v440L720-420v180q0 33-23.5 56.5T640-160H160Zm0-80h480v-480H160v480Zm0 0v-480 480Z\"/></svg> -->\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"2 2 20 20\" width=\"24\" fill=\"currentColor\">\n <path d=\"m 7.1442743,11.663766 q -0.9582938,0 -1.8114091,-0.368125 Q 4.47975,10.927516 3.8428352,10.290601 3.2059204,9.6536865 2.8377954,8.8005713 2.4696703,7.947456 2.4696703,6.9891622 q 0,-0.9699803 0.3681251,-1.8172523 0.368125,-0.847272 1.0050398,-1.4841868 0.6369148,-0.6369148 1.49003,-1.0050398 0.8531153,-0.3681251 1.8114091,-0.3681251 0.9699803,0 1.8172523,0.3681251 0.847272,0.368125 1.4841864,1.0050398 0.636915,0.6369148 1.00504,1.4841868 0.368125,0.847272 0.368125,1.8172523 0,0.9582938 -0.368125,1.8114091 -0.368125,0.8531152 -1.00504,1.4900297 -0.6369144,0.636915 -1.4841864,1.00504 -0.847272,0.368125 -1.8172523,0.368125 z m 0,-0.958294 Q 7.4481236,10.284758 7.6701673,9.8289841 7.8922109,9.3732102 8.0324491,8.8590038 H 6.2560995 Q 6.3963377,9.3732102 6.6183814,9.8289841 6.840425,10.284758 7.1442743,10.705472 Z M 5.9288773,10.518488 Q 5.7185201,10.132833 5.5607522,9.7179623 5.4029843,9.3030912 5.2978057,8.8590038 H 3.9187975 q 0.3389088,0.5843255 0.847272,1.0167264 0.5083632,0.4324008 1.1628078,0.6427578 z m 2.430794,0 Q 9.0141159,10.308131 9.5224791,9.8757302 10.030842,9.4433293 10.369751,8.8590038 H 8.9907429 Q 8.8855643,9.3030912 8.7277964,9.7179623 8.5700285,10.132833 8.3596713,10.518488 Z M 3.5214562,7.924083 h 1.5893654 q -0.03506,-0.2337302 -0.052589,-0.4616171 -0.01753,-0.227887 -0.01753,-0.4733037 0,-0.2454167 0.01753,-0.4733037 0.01753,-0.2278869 0.052589,-0.4616171 H 3.5214562 q -0.058432,0.2337302 -0.087649,0.4616171 -0.029216,0.227887 -0.029216,0.4733037 0,0.2454167 0.029216,0.4733037 0.029216,0.2278869 0.087649,0.4616171 z m 2.5242862,0 h 2.1970638 q 0.03506,-0.2337302 0.052589,-0.4616171 0.01753,-0.227887 0.01753,-0.4733037 0,-0.2454167 -0.01753,-0.4733037 -0.01753,-0.2278869 -0.052589,-0.4616171 H 6.0457424 q -0.03506,0.2337302 -0.052589,0.4616171 -0.01753,0.227887 -0.01753,0.4733037 0,0.2454167 0.01753,0.4733037 0.01753,0.2278869 0.052589,0.4616171 z m 3.1319846,0 h 1.589365 q 0.05843,-0.2337302 0.08765,-0.4616171 0.02922,-0.227887 0.02922,-0.4733037 0,-0.2454167 -0.02922,-0.4733037 -0.02922,-0.2278869 -0.08765,-0.4616171 H 9.177727 q 0.03506,0.2337302 0.052589,0.4616171 0.01753,0.227887 0.01753,0.4733037 0,0.2454167 -0.01753,0.4733037 Q 9.212786,7.6903528 9.177727,7.924083 Z M 8.9907429,5.1193206 H 10.369751 Q 10.030842,4.5349951 9.5224791,4.1025942 9.0141159,3.6701934 8.3596713,3.4598362 8.5700285,3.845491 8.7277964,4.2603621 8.8855643,4.6752332 8.9907429,5.1193206 Z m -2.7346434,0 H 8.0324491 Q 7.8922109,4.6051142 7.6701673,4.1493403 7.4481236,3.6935664 7.1442743,3.272852 6.840425,3.6935664 6.6183814,4.1493403 6.3963377,4.6051142 6.2560995,5.1193206 Z m -2.337302,0 H 5.2978057 Q 5.4029843,4.6752332 5.5607522,4.2603621 5.7185201,3.845491 5.9288773,3.4598362 5.2744327,3.6701934 4.7660695,4.1025942 4.2577063,4.5349951 3.9187975,5.1193206 Z\"/>\n <path d=\"m 15.71996,21.990604 -4.663443,-4.853407 a 1.3461538,1.5192307 46.143522 0 1 0.162796,-2.023287 l 1.095483,-1.052606 -4.0563428,0.163849 6.5728988,-6.3156334 -0.32559,4.0465734 1.095484,-1.052605 a 1.3461538,1.5192307 46.143522 0 1 2.028171,-0.08193 l 4.663442,4.85341 a 1.3461538,1.5192307 46.143522 0 1 -0.162794,2.023289 L 17.748132,21.90868 A 1.3461538,1.5192307 46.143522 0 1 15.71996,21.9906 Z m 1.095484,-1.052606 4.381933,-4.210422 -4.663442,-4.853407 -4.381934,4.210422 z\"/>\n </svg>\n </button>\n <button id=\"flySettings\" class=\"button hidden\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"2 2 20 20\" width=\"24\" fill=\"currentColor\">\n <path d=\"M 9.8906998,16.233103 5.1313273,11.47373 a 1.3461538,1.5192307 45 0 1 0.122383,-2.0261336 l 1.0742595,-1.074259 -4.0522666,0.244769 6.4455506,-6.445549 -0.244768,4.052264 1.074259,-1.074259 a 1.3461538,1.5192307 45 0 1 2.0261302,-0.122383 l 4.759374,4.7593716 a 1.3461538,1.5192307 45 0 1 -0.122384,2.026135 l -4.297034,4.297033 A 1.3461538,1.5192307 45 0 1 9.8906998,16.233103 Z M 10.964957,15.158844 15.261991,10.861811 10.502618,6.1024374 6.2055848,10.399472 Z\"/>\n <path d=\"m 14.224293,15.714388 c 3.299831,3.005202 5.165738,4.544311 5.165738,4.544311 l -3.240906,1.355289 -3.633673,-5.8996 z\"/>\n <path d=\"m 15.993848,12.784194 2.558715,1.345379 1.867746,2.763273 2.180246,-2.121321 -4.83894,-2.694438 z\"/>\n <path d=\"m 14.340359,14.30196 3.720169,3.629993 1.885618,-0.942809 -4.014797,-2.687184 z\"/>\n </svg>\n </button>\n <button id=\"enterFullscreen\" class=\"button\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M120-120v-200h80v120h120v80H120Zm520 0v-80h120v-120h80v200H640ZM120-640v-200h200v80H200v120h-80Zm640 0v-120H640v-80h200v200h-80Z\"/></svg>\n </button>\n <button id=\"exitFullscreen\" class=\"button hidden\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M240-120v-120H120v-80h200v200h-80Zm400 0v-200h200v80H720v120h-80ZM120-640v-80h120v-120h80v200H120Zm520 0v-200h80v120h120v80H640Z\"/></svg>\n </button>\n </div>\n </div>\n\n <!-- Settings Panel -->\n <div id=\"settingsPanel\" class=\"hidden\">\n <div>Camera Mode</div>\n <div id=\"cameraToggle\" class=\"toggleWrap\">\n <div id=\"cameraToggleHighlight\" class=\"toggleHighlight\"></div>\n <button id=\"orbit\" class=\"left\">Orbit</button>\n <button id=\"fly\" class=\"right\">Fly</button>\n </div>\n <div>Render Quality</div>\n <div id=\"qualityToggle\" class=\"toggleWrap\">\n <div id=\"qualityToggleHighlight\" class=\"toggleHighlight\"></div>\n <button id=\"low\" class=\"left\">Low</button>\n <button id=\"high\" class=\"right\">High</button>\n </div>\n <div>View</div>\n <div class=\"settingsRow\">\n <button id=\"frame\" class=\"button\">Frame</button>\n <button id=\"reset\" class=\"button\">Reset</button>\n </div>\n </div>\n\n <!-- Info Panel -->\n <div id=\"infoPanel\" class=\"hidden\">\n <div id=\"infoPanelContent\" onpointerdown=\"event.stopPropagation()\">\n <div id=\"header\">Controls</div>\n <div id=\"tabs\">\n <div id=\"desktopTab\" class=\"tab active\">Desktop</div>\n <div id=\"touchTab\" class=\"tab\">Touch</div>\n </div>\n <div id=\"infoPanels\">\n <div id=\"desktopInfoPanel\">\n <div class=\"control-spacer\"></div>\n <h1>Orbit Mode</h1>\n <div class=\"control-item\">\n <span class=\"control-action\">Orbit</span>\n <span class=\"control-key\">Left Mouse</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Pan</span>\n <span class=\"control-key\">Right Mouse</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Zoom</span>\n <span class=\"control-key\">Mouse Wheel</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Set Focus</span>\n <span class=\"control-key\">Double Click</span>\n </div>\n <div class=\"control-spacer\"></div>\n <h1>Fly Mode</h1>\n <div class=\"control-item\">\n <span class=\"control-action\">Look Around</span>\n <span class=\"control-key\">Left Mouse</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Fly</span>\n <span class=\"control-key\">W,S,A,D</span>\n </div>\n <div class=\"control-spacer\"></div>\n <div class=\"control-item\">\n <span class=\"control-action\">Frame Scene</span>\n <span class=\"control-key\">F</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Reset Camera</span>\n <span class=\"control-key\">R</span>\n </div>\n </div>\n <div id=\"touchInfoPanel\" class=\"hidden\">\n <div class=\"control-spacer\"></div>\n <h1>Orbit Mode</h1>\n <div class=\"control-item\">\n <span class=\"control-action\">Orbit</span>\n <span class=\"control-key\">One Finger Drag</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Pan</span>\n <span class=\"control-key\">Two Finger Drag</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Zoom</span>\n <span class=\"control-key\">Pinch</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Set Focus</span>\n <span class=\"control-key\">Double Tap</span>\n </div>\n <div class=\"control-spacer\"></div>\n <h1>Fly Mode</h1>\n <div class=\"control-item\">\n <span class=\"control-action\">Look Around</span>\n <span class=\"control-key\">Touch on Right</span>\n </div>\n <div class=\"control-item\">\n <span class=\"control-action\">Fly</span>\n <span class=\"control-key\">Touch on Left</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Touch Joystick -->\n <div id=\"joystickBase\" class=\"hidden\">\n <div id=\"joystick\"></div>\n </div>\n </div>\n <script type=\"module\">\n // Load the poster image if available\n const poster = window.sse?.poster;\n if (poster) {\n const element = document.getElementById('poster');\n element.style.backgroundImage = `url(${poster.src})`;\n element.style.display = 'block';\n element.style.filter = 'blur(40px)';\n }\n </script>\n </body>\n</html>\n";
var index = "const TRACEID_GPU_TIMINGS = 'GpuTimings';\n\nconst version = '2.11.3';\nconst revision = '99c96dd';\nfunction extend(target, ex) {\n\t\tfor(const prop in ex){\n\t\t\t\tconst copy = ex[prop];\n\t\t\t\tif (Array.isArray(copy)) {\n\t\t\t\t\t\ttarget[prop] = extend([], copy);\n\t\t\t\t} else if (copy && typeof copy === 'object') {\n\t\t\t\t\t\ttarget[prop] = extend({}, copy);\n\t\t\t\t} else {\n\t\t\t\t\t\ttarget[prop] = copy;\n\t\t\t\t}\n\t\t}\n\t\treturn target;\n}\n\nconst guid = {\n\t\tcreate () {\n\t\t\t\treturn 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c)=>{\n\t\t\t\t\t\tconst r = Math.random() * 16 | 0;\n\t\t\t\t\t\tconst v = c === 'x' ? r : r & 0x3 | 0x8;\n\t\t\t\t\t\treturn v.toString(16);\n\t\t\t\t});\n\t\t}\n};\n\nconst path = {\n\t\tdelimiter: '/',\n\t\tjoin (...sections) {\n\t\t\t\tlet result = sections[0];\n\t\t\t\tfor(let i = 0; i < sections.length - 1; i++){\n\t\t\t\t\t\tconst one = sections[i];\n\t\t\t\t\t\tconst two = sections[i + 1];\n\t\t\t\t\t\tif (two[0] === path.delimiter) {\n\t\t\t\t\t\t\t\tresult = two;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (one && two && one[one.length - 1] !== path.delimiter && two[0] !== path.delimiter) {\n\t\t\t\t\t\t\t\tresult += path.delimiter + two;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tresult += two;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t},\n\t\tnormalize (pathname) {\n\t\t\t\tconst lead = pathname.startsWith(path.delimiter);\n\t\t\t\tconst trail = pathname.endsWith(path.delimiter);\n\t\t\t\tconst parts = pathname.split('/');\n\t\t\t\tlet result = '';\n\t\t\t\tlet cleaned = [];\n\t\t\t\tfor(let i = 0; i < parts.length; i++){\n\t\t\t\t\t\tif (parts[i] === '') continue;\n\t\t\t\t\t\tif (parts[i] === '.') continue;\n\t\t\t\t\t\tif (parts[i] === '..' && cleaned.length > 0) {\n\t\t\t\t\t\t\t\tcleaned = cleaned.slice(0, cleaned.length - 2);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i > 0) cleaned.push(path.delimiter);\n\t\t\t\t\t\tcleaned.push(parts[i]);\n\t\t\t\t}\n\t\t\t\tresult = cleaned.join('');\n\t\t\t\tif (!lead && result[0] === path.delimiter) {\n\t\t\t\t\t\tresult = result.slice(1);\n\t\t\t\t}\n\t\t\t\tif (trail && result[result.length - 1] !== path.delimiter) {\n\t\t\t\t\t\tresult += path.delimiter;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t},\n\t\tsplit (pathname) {\n\t\t\t\tconst lastDelimiterIndex = pathname.lastIndexOf(path.delimiter);\n\t\t\t\tif (lastDelimiterIndex !== -1) {\n\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\tpathname.substring(0, lastDelimiterIndex),\n\t\t\t\t\t\t\t\tpathname.substring(lastDelimiterIndex + 1)\n\t\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\treturn [\n\t\t\t\t\t\t'',\n\t\t\t\t\t\tpathname\n\t\t\t\t];\n\t\t},\n\t\tgetBasename (pathname) {\n\t\t\t\treturn path.split(pathname)[1];\n\t\t},\n\t\tgetDirectory (pathname) {\n\t\t\t\treturn path.split(pathname)[0];\n\t\t},\n\t\tgetExtension (pathname) {\n\t\t\t\tconst ext = pathname.split('?')[0].split('.').pop();\n\t\t\t\tif (ext !== pathname) {\n\t\t\t\t\t\treturn `.${ext}`;\n\t\t\t\t}\n\t\t\t\treturn '';\n\t\t},\n\t\tisRelativePath (pathname) {\n\t\t\t\treturn pathname.charAt(0) !== '/' && pathname.match(/:\\/\\//) === null;\n\t\t},\n\t\textractPath (pathname) {\n\t\t\t\tlet result = '';\n\t\t\t\tconst parts = pathname.split('/');\n\t\t\t\tlet i = 0;\n\t\t\t\tif (parts.length > 1) {\n\t\t\t\t\t\tif (path.isRelativePath(pathname)) {\n\t\t\t\t\t\t\t\tif (parts[0] === '.') {\n\t\t\t\t\t\t\t\t\t\tfor(i = 0; i < parts.length - 1; ++i){\n\t\t\t\t\t\t\t\t\t\t\t\tresult += i === 0 ? parts[i] : `/${parts[i]}`;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else if (parts[0] === '..') {\n\t\t\t\t\t\t\t\t\t\tfor(i = 0; i < parts.length - 1; ++i){\n\t\t\t\t\t\t\t\t\t\t\t\tresult += i === 0 ? parts[i] : `/${parts[i]}`;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tresult = '.';\n\t\t\t\t\t\t\t\t\t\tfor(i = 0; i < parts.length - 1; ++i){\n\t\t\t\t\t\t\t\t\t\t\t\tresult += `/${parts[i]}`;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfor(i = 0; i < parts.length - 1; ++i){\n\t\t\t\t\t\t\t\t\t\tresult += i === 0 ? parts[i] : `/${parts[i]}`;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t}\n};\n\nconst detectPassiveEvents = ()=>{\n\t\tlet result = false;\n\t\ttry {\n\t\t\t\tconst opts = Object.defineProperty({}, 'passive', {\n\t\t\t\t\t\tget: function() {\n\t\t\t\t\t\t\t\tresult = true;\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\twindow.addEventListener('testpassive', null, opts);\n\t\t\t\twindow.removeEventListener('testpassive', null, opts);\n\t\t} catch (e) {}\n\t\treturn result;\n};\nconst ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';\nconst environment = typeof window !== 'undefined' ? 'browser' : typeof global !== 'undefined' ? 'node' : 'worker';\nconst platformName = /android/i.test(ua) ? 'android' : /ip(?:[ao]d|hone)/i.test(ua) ? 'ios' : /windows/i.test(ua) ? 'windows' : /mac os/i.test(ua) ? 'osx' : /linux/i.test(ua) ? 'linux' : /cros/i.test(ua) ? 'cros' : null;\nconst browserName = environment !== 'browser' ? null : /Chrome\\/|Chromium\\/|Edg.*\\//.test(ua) ? 'chrome' : /Safari\\//.test(ua) ? 'safari' : /Firefox\\//.test(ua) ? 'firefox' : 'other';\nconst touch = environment === 'browser' && ('ontouchstart' in window || 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0);\nconst passiveEvents = detectPassiveEvents();\nconst platform = {\n\t\tname: platformName,\n\t\tenvironment: environment,\n\t\tbrowser: environment === 'browser',\n\t\tworker: environment === 'worker',\n\t\tandroid: platformName === 'android',\n\t\ttouch: touch,\n\t\tpassiveEvents: passiveEvents,\n\t\tbrowserName: browserName\n};\n\nconst ASCII_LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';\nconst ASCII_UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\nconst ASCII_LETTERS = ASCII_LOWERCASE + ASCII_UPPERCASE;\nconst HIGH_SURROGATE_BEGIN = 0xD800;\nconst HIGH_SURROGATE_END = 0xDBFF;\nconst LOW_SURROGATE_BEGIN = 0xDC00;\nconst LOW_SURROGATE_END = 0xDFFF;\nconst ZERO_WIDTH_JOINER = 0x200D;\nconst REGIONAL_INDICATOR_BEGIN = 0x1F1E6;\nconst REGIONAL_INDICATOR_END = 0x1F1FF;\nconst FITZPATRICK_MODIFIER_BEGIN = 0x1F3FB;\nconst FITZPATRICK_MODIFIER_END = 0x1F3FF;\nconst DIACRITICAL_MARKS_BEGIN = 0x20D0;\nconst DIACRITICAL_MARKS_END = 0x20FF;\nconst VARIATION_MODIFIER_BEGIN = 0xFE00;\nconst VARIATION_MODIFIER_END = 0xFE0F;\nfunction getCodePointData(string, i = 0) {\n\t\tconst size = string.length;\n\t\tif (i < 0 || i >= size) {\n\t\t\t\treturn null;\n\t\t}\n\t\tconst first = string.charCodeAt(i);\n\t\tif (size > 1 && first >= HIGH_SURROGATE_BEGIN && first <= HIGH_SURROGATE_END) {\n\t\t\t\tconst second = string.charCodeAt(i + 1);\n\t\t\t\tif (second >= LOW_SURROGATE_BEGIN && second <= LOW_SURROGATE_END) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcode: (first - HIGH_SURROGATE_BEGIN) * 0x400 + second - LOW_SURROGATE_BEGIN + 0x10000,\n\t\t\t\t\t\t\t\tlong: true\n\t\t\t\t\t\t};\n\t\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\t\tcode: first,\n\t\t\t\tlong: false\n\t\t};\n}\nfunction isCodeBetween(string, begin, end) {\n\t\tif (!string) {\n\t\t\t\treturn false;\n\t\t}\n\t\tconst codeData = getCodePointData(string);\n\t\tif (codeData) {\n\t\t\t\tconst code = codeData.code;\n\t\t\t\treturn code >= begin && code <= end;\n\t\t}\n\t\treturn false;\n}\nfunction numCharsToTakeForNextSymbol(string, index) {\n\t\tif (index === string.length - 1) {\n\t\t\t\treturn 1;\n\t\t}\n\t\tif (isCodeBetween(string[index], HIGH_SURROGATE_BEGIN, HIGH_SURROGATE_END)) {\n\t\t\t\tconst first = string.substring(index, index + 2);\n\t\t\t\tconst second = string.substring(index + 2, index + 4);\n\t\t\t\tif (isCodeBetween(second, FITZPATRICK_MODIFIER_BEGIN, FITZPATRICK_MODIFIER_END) || isCodeBetween(first, REGIONAL_INDICATOR_BEGIN, REGIONAL_INDICATOR_END) && isCodeBetween(second, REGIONAL_INDICATOR_BEGIN, REGIONAL_INDICATOR_END)) {\n\t\t\t\t\t\treturn 4;\n\t\t\t\t}\n\t\t\t\tif (isCodeBetween(second, VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) {\n\t\t\t\t\t\treturn 3;\n\t\t\t\t}\n\t\t\t\treturn 2;\n\t\t}\n\t\tif (isCodeBetween(string[index + 1], VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) {\n\t\t\t\treturn 2;\n\t\t}\n\t\treturn 1;\n}\nconst string = {\n\t\tASCII_LOWERCASE: ASCII_LOWERCASE,\n\t\tASCII_UPPERCASE: ASCII_UPPERCASE,\n\t\tASCII_LETTERS: ASCII_LETTERS,\n\t\tformat (s, ...args) {\n\t\t\t\tfor(let i = 0; i < args.length; i++){\n\t\t\t\t\t\ts = s.replace(`{${i}}`, args[i]);\n\t\t\t\t}\n\t\t\t\treturn s;\n\t\t},\n\t\tgetCodePoint (string, i) {\n\t\t\t\tconst codePointData = getCodePointData(string, i);\n\t\t\t\treturn codePointData && codePointData.code;\n\t\t},\n\t\tgetCodePoints (string) {\n\t\t\t\tif (typeof string !== 'string') {\n\t\t\t\t\t\tthrow new TypeError('Not a string');\n\t\t\t\t}\n\t\t\t\tlet i = 0;\n\t\t\t\tconst arr = [];\n\t\t\t\tlet codePoint;\n\t\t\t\twhile(!!(codePoint = getCodePointData(string, i))){\n\t\t\t\t\t\tarr.push(codePoint.code);\n\t\t\t\t\t\ti += codePoint.long ? 2 : 1;\n\t\t\t\t}\n\t\t\t\treturn arr;\n\t\t},\n\t\tgetSymbols (string) {\n\t\t\t\tif (typeof string !== 'string') {\n\t\t\t\t\t\tthrow new TypeError('Not a string');\n\t\t\t\t}\n\t\t\t\tlet index = 0;\n\t\t\t\tconst length = string.length;\n\t\t\t\tconst output = [];\n\t\t\t\tlet take = 0;\n\t\t\t\tlet ch;\n\t\t\t\twhile(index < length){\n\t\t\t\t\t\ttake += numCharsToTakeForNextSymbol(string, index + take);\n\t\t\t\t\t\tch = string[index + take];\n\t\t\t\t\t\tif (isCodeBetween(ch, DIACRITICAL_MARKS_BEGIN, DIACRITICAL_MARKS_END)) {\n\t\t\t\t\t\t\t\tch = string[index + take++];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (isCodeBetween(ch, VARIATION_MODIFIER_BEGIN, VARIATION_MODIFIER_END)) {\n\t\t\t\t\t\t\t\tch = string[index + take++];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch && ch.charCodeAt(0) === ZERO_WIDTH_JOINER) {\n\t\t\t\t\t\t\t\tch = string[index + take++];\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst char = string.substring(index, index + take);\n\t\t\t\t\t\toutput.push(char);\n\t\t\t\t\t\tindex += take;\n\t\t\t\t\t\ttake = 0;\n\t\t\t\t}\n\t\t\t\treturn output;\n\t\t},\n\t\tfromCodePoint (...args) {\n\t\t\t\treturn args.map((codePoint)=>{\n\t\t\t\t\t\tif (codePoint > 0xFFFF) {\n\t\t\t\t\t\t\t\tcodePoint -= 0x10000;\n\t\t\t\t\t\t\t\treturn String.fromCharCode((codePoint >> 10) + 0xD800, codePoint % 0x400 + 0xDC00);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn String.fromCharCode(codePoint);\n\t\t\t\t}).join('');\n\t\t}\n};\n\nclass EventHandle {\n\t\tconstructor(handler, name, callback, scope, once = false){\n\t\t\t\tthis._removed = false;\n\t\t\t\tthis.handler = handler;\n\t\t\t\tthis.name = name;\n\t\t\t\tthis.callback = callback;\n\t\t\t\tthis.scope = scope;\n\t\t\t\tthis._once = once;\n\t\t}\n\t\toff() {\n\t\t\t\tif (this._removed) return;\n\t\t\t\tthis.handler.offByHandle(this);\n\t\t}\n\t\ton(name, callback, scope = this) {\n\t\t\t\treturn this.handler._addCallback(name, callback, scope, false);\n\t\t}\n\t\tonce(name, callback, scope = this) {\n\t\t\t\treturn this.handler._addCallback(name, callback, scope, true);\n\t\t}\n\t\tset removed(value) {\n\t\t\t\tif (!value) return;\n\t\t\t\tthis._removed = true;\n\t\t}\n\t\tget removed() {\n\t\t\t\treturn this._removed;\n\t\t}\n\t\ttoJSON(key) {\n\t\t\t\treturn undefined;\n\t\t}\n}\n\nclass EventHandler {\n\t\tinitEventHandler() {\n\t\t\t\tthis._callbacks = new Map();\n\t\t\t\tthis._callbackActive = new Map();\n\t\t}\n\t\t_addCallback(name, callback, scope, once) {\n\t\t\t\tif (!this._callbacks.has(name)) {\n\t\t\t\t\t\tthis._callbacks.set(name, []);\n\t\t\t\t}\n\t\t\t\tif (this._callbackActive.has(name)) {\n\t\t\t\t\t\tconst callbackActive = this._callbackActive.get(name);\n\t\t\t\t\t\tif (callbackActive && callbackActive === this._callbacks.get(name)) {\n\t\t\t\t\t\t\t\tthis._callbackActive.set(name, callbackActive.slice());\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst evt = new EventHandle(this, name, callback, scope, once);\n\t\t\t\tthis._callbacks.get(name).push(evt);\n\t\t\t\treturn evt;\n\t\t}\n\t\ton(name, callback, scope = this) {\n\t\t\t\treturn this._addCallback(name, callback, scope, false);\n\t\t}\n\t\tonce(name, callback, scope = this) {\n\t\t\t\treturn this._addCallback(name, callback, scope, true);\n\t\t}\n\t\toff(name, callback, scope) {\n\t\t\t\tif (name) {\n\t\t\t\t\t\tif (this._callbackActive.has(name) && this._callbackActive.get(name) === this._callbacks.get(name)) {\n\t\t\t\t\t\t\t\tthis._callbackActive.set(name, this._callbackActive.get(name).slice());\n\t\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t\tfor (const [key, callbacks] of this._callbackActive){\n\t\t\t\t\t\t\t\tif (!this._callbacks.has(key)) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (this._callbacks.get(key) !== callbacks) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthis._callbackActive.set(key, callbacks.slice());\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!name) {\n\t\t\t\t\t\tfor (const callbacks of this._callbacks.values()){\n\t\t\t\t\t\t\t\tfor(let i = 0; i < callbacks.length; i++){\n\t\t\t\t\t\t\t\t\t\tcallbacks[i].removed = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis._callbacks.clear();\n\t\t\t\t} else if (!callback) {\n\t\t\t\t\t\tconst callbacks = this._callbacks.get(name);\n\t\t\t\t\t\tif (callbacks) {\n\t\t\t\t\t\t\t\tfor(let i = 0; i < callbacks.length; i++){\n\t\t\t\t\t\t\t\t\t\tcallbacks[i].removed = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthis._callbacks.delete(name);\n\t\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t\tconst callbacks = this._callbacks.get(name);\n\t\t\t\t\t\tif (!callbacks) {\n\t\t\t\t\t\t\t\treturn this;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor(let i = 0; i < callbacks.length; i++){\n\t\t\t\t\t\t\t\tif (callbacks[i].callback !== callback) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (scope && callbacks[i].scope !== scope) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcallbacks[i].removed = true;\n\t\t\t\t\t\t\t\tcallbacks.splice(i, 1);\n\t\t\t\t\t\t\t\ti--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (callbacks.length === 0) {\n\t\t\t\t\t\t\t\tthis._callbacks.delete(name);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t}\n\t\toffByHandle(handle) {\n\t\t\t\tconst name = handle.name;\n\t\t\t\thandle.removed = true;\n\t\t\t\tif (this._callbackActive.has(name) && this._callbackActive.get(name) === this._callbacks.get(name)) {\n\t\t\t\t\t\tthis._callbackActive.set(name, this._callbackActive.get(name).slice());\n\t\t\t\t}\n\t\t\t\tconst callbacks = this._callbacks.get(name);\n\t\t\t\tif (!callbacks) {\n\t\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tconst ind = callbacks.indexOf(handle);\n\t\t\t\tif (ind !== -1) {\n\t\t\t\t\t\tcallbacks.splice(ind, 1);\n\t\t\t\t\t\tif (callbacks.length === 0) {\n\t\t\t\t\t\t\t\tthis._callbacks.delete(name);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t}\n\t\tfire(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {\n\t\t\t\tif (!name) {\n\t\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tconst callbacksInitial = this._callbacks.get(name);\n\t\t\t\tif (!callbacksInitial) {\n\t\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tlet callbacks;\n\t\t\t\tif (!this._callbackActive.has(name)) {\n\t\t\t\t\t\tthis._callbackActive.set(name, callbacksInitial);\n\t\t\t\t} else if (this._callbackActive.get(name) !== callbacksInitial) {\n\t\t\t\t\t\tcallbacks = callbacksInitial.slice();\n\t\t\t\t}\n\t\t\t\tfor(let i = 0; (callbacks || this._callbackActive.get(name)) && i < (callbacks || this._callbackActive.get(name)).length; i++){\n\t\t\t\t\t\tconst evt = (callbacks || this._callbackActive.get(name))[i];\n\t\t\t\t\t\tif (!evt.callback) continue;\n\t\t\t\t\t\tevt.callback.call(evt.scope, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);\n\t\t\t\t\t\tif (evt._once) {\n\t\t\t\t\t\t\t\tconst existingCallback = this._callbacks.get(name);\n\t\t\t\t\t\t\t\tconst ind = existingCallback ? existingCallback.indexOf(evt) : -1;\n\t\t\t\t\t\t\t\tif (ind !== -1) {\n\t\t\t\t\t\t\t\t\t\tif (this._callbackActive.get(name) === existingCallback) {\n\t\t\t\t\t\t\t\t\t\t\t\tthis._callbackActive.set(name, this._callbackActive.get(name).slice());\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tconst callbacks = this._callbacks.get(name);\n\t\t\t\t\t\t\t\t\t\tif (!callbacks) continue;\n\t\t\t\t\t\t\t\t\t\tcallbacks[ind].removed = true;\n\t\t\t\t\t\t\t\t\t\tcallbacks.splice(ind, 1);\n\t\t\t\t\t\t\t\t\t\tif (callbacks.length === 0) {\n\t\t\t\t\t\t\t\t\t\t\t\tthis._callbacks.delete(name);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!callbacks) {\n\t\t\t\t\t\tthis._callbackActive.delete(name);\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t}\n\t\thasEvent(name) {\n\t\t\t\treturn !!this._callbacks.get(name)?.length;\n\t\t}\n\t\tconstructor(){\n\t\t\t\tthis._callbacks = new Map();\n\t\t\t\tthis._callbackActive = new Map();\n\t\t}\n}\n\nclass IndexedList {\n\t\tpush(key, item) {\n\t\t\t\tif (this._index[key]) {\n\t\t\t\t\t\tthrow Error(`Key already in index ${key}`);\n\t\t\t\t}\n\t\t\t\tconst location = this._list.push(item) - 1;\n\t\t\t\tthis._index[key] = location;\n\t\t}\n\t\thas(key) {\n\t\t\t\treturn this._index[key] !== undefined;\n\t\t}\n\t\tget(key) {\n\t\t\t\tconst location = this._index[key];\n\t\t\t\tif (location !== undefined) {\n\t\t\t\t\t\treturn this._list[location];\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t}\n\t\tremove(key) {\n\t\t\t\tconst location = this._index[key];\n\t\t\t\tif (location !== undefined) {\n\t\t\t\t\t\tthis._list.splice(location, 1);\n\t\t\t\t\t\tdelete this._index[key];\n\t\t\t\t\t\tfor(key in this._index){\n\t\t\t\t\t\t\t\tconst idx = this._index[key];\n\t\t\t\t\t\t\t\tif (idx > location) {\n\t\t\t\t\t\t\t\t\t\tthis._index[key] = idx - 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t}\n\t\tlist() {\n\t\t\t\treturn this._list;\n\t\t}\n\t\tclear() {\n\t\t\t\tthis._list.length = 0;\n\t\t\t\tfor(const prop in this._index){\n\t\t\t\t\t\tdelete this._index[prop];\n\t\t\t\t}\n\t\t}\n\t\tconstructor(){\n\t\t\t\tthis._list = [];\n\t\t\t\tthis._index = {};\n\t\t}\n}\n\nconst cachedResult = (func)=>{\n\t\tconst uninitToken = {};\n\t\tlet result = uninitToken;\n\t\treturn ()=>{\n\t\t\t\tif (result === uninitToken) {\n\t\t\t\t\t\tresult = func();\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t};\n};\nclass Impl {\n\t\tstatic{\n\t\t\t\tthis.modules = {};\n\t\t}\n\t\tstatic{\n\t\t\t\tthis.wasmSupported = cachedResult(()=>{\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function') {\n\t\t\t\t\t\t\t\t\t\tconst module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));\n\t\t\t\t\t\t\t\t\t\tif (module instanceof WebAssembly.Module) {\n\t\t\t\t\t\t\t\t\t\t\t\treturn new WebAssembly.Instance(module) instanceof WebAssembly.Instance;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (e) {}\n\t\t\t\t\t\treturn false;\n\t\t\t\t});\n\t\t}\n\t\tstatic loadScript(url, callback) {\n\t\t\t\tconst s = document.createElement(\"script\");\n\t\t\t\ts.setAttribute('src', url);\n\t\t\t\ts.onload = ()=>{\n\t\t\t\t\t\tcallback(null);\n\t\t\t\t};\n\t\t\t\ts.onerror = ()=>{\n\t\t\t\t\t\tcallback(`Failed to load script='${url}'`);\n\t\t\t\t};\n\t\t\t\tdocument.body.appendChild(s);\n\t\t}\n\t\tstatic loadWasm(moduleName, config, callback) {\n\t\t\t\tconst loadUrl = Impl.wasmSupported() && config.glueUrl && config.wasmUrl ? config.glueUrl : config.fallbackUrl;\n\t\t\t\tif (loadUrl) {\n\t\t\t\t\t\tImpl.loadScript(loadUrl, (err)=>{\n\t\t\t\t\t\t\t\tif (err) {\n\t\t\t\t\t\t\t\t\t\tcallback(err, null);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconst module = window[moduleName];\n\t\t\t\t\t\t\t\t\t\twindow[moduleName] = undefined;\n\t\t\t\t\t\t\t\t\t\tmodule({\n\t\t\t\t\t\t\t\t\t\t\t\tlocateFile: ()=>config.wasmUrl,\n\t\t\t\t\t\t\t\t\t\t\t\tonAbort: ()=>{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcallback('wasm module aborted.');\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}).then((instance)=>{\n\t\t\t\t\t\t\t\t\t\t\t\tcallback(null, instance);\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t\tcallback('No supported wasm modules found.', null);\n\t\t\t\t}\n\t\t}\n\t\tstatic getModule(name) {\n\t\t\t\tif (!Impl.modules.hasOwnProperty(name)) {\n\t\t\t\t\t\tImpl.modules[name] = {\n\t\t\t\t\t\t\t\tconfig: null,\n\t\t\t\t\t\t\t\tinitializing: false,\n\t\t\t\t\t\t\t\tinstance: null,\n\t\t\t\t\t\t\t\tcallbacks: []\n\t\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn Impl.modules[name];\n\t\t}\n\t\tstatic initialize(moduleName, module) {\n\t\t\t\tif (module.initializing) {\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst config = module.config;\n\t\t\t\tif (config.glueUrl || config.wasmUrl || config.fallbackUrl) {\n\t\t\t\t\t\tmodule.initializing = true;\n\t\t\t\t\t\tImpl.loadWasm(moduleName, config, (err, instance)=>{\n\t\t\t\t\t\t\t\tif (err) {\n\t\t\t\t\t\t\t\t\t\tif (config.errorHandler) {\n\t\t\t\t\t\t\t\t\t\t\t\tconfig.errorHandler(err);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\tconsole.error(`failed to initialize module=${moduleName} error=${err}`);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tmodule.instance = instance;\n\t\t\t\t\t\t\t\t\t\tmodule.callbacks.forEach((callback)=>{\n\t\t\t\t\t\t\t\t\t\t\t\tcallback(instance);\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t}\n}\nclass WasmModule {\n\t\tstatic setConfig(moduleName, config) {\n\t\t\t\tconst module = Impl.getModule(moduleName);\n\t\t\t\tmodule.config = config;\n\t\t\t\tif (module.callbacks.length > 0) {\n\t\t\t\t\t\tImpl.initialize(moduleName, module);\n\t\t\t\t}\n\t\t}\n\t\tstatic getConfig(moduleName) {\n\t\t\t\treturn Impl.modules?.[moduleName]?.config;\n\t\t}\n\t\tstatic getInstance(moduleName, callback) {\n\t\t\t\tconst module = Impl.getModule(moduleName);\n\t\t\t\tif (module.instance) {\n\t\t\t\t\t\tcallback(module.instance);\n\t\t\t\t} else {\n\t\t\t\t\t\tmodule.callbacks.push(callback);\n\t\t\t\t\t\tif (module.config) {\n\t\t\t\t\t\t\t\tImpl.initialize(moduleName, module);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t}\n}\n\nclass ReadStream {\n\t\tconstructor(arraybuffer){\n\t\t\t\tthis.offset = 0;\n\t\t\t\tthis.arraybuffer = arraybuffer;\n\t\t\t\tthis.dataView = new DataView(arraybuffer);\n\t\t}\n\t\tget remainingBytes() {\n\t\t\t\treturn this.dataView.byteLength - this.offset;\n\t\t}\n\t\treset(offset = 0) {\n\t\t\t\tthis.offset = offset;\n\t\t}\n\t\tskip(bytes) {\n\t\t\t\tthis.offset += bytes;\n\t\t}\n\t\talign(bytes) {\n\t\t\t\tthis.offset = this.offset + bytes - 1 & ~(bytes - 1);\n\t\t}\n\t\t_inc(amount) {\n\t\t\t\tthis.offset += amount;\n\t\t\t\treturn this.offset - amount;\n\t\t}\n\t\treadChar() {\n\t\t\t\treturn String.fromCharCode(this.dataView.getUint8(this.offset++));\n\t\t}\n\t\treadChars(numChars) {\n\t\t\t\tlet result = '';\n\t\t\t\tfor(let i = 0; i < numChars; ++i){\n\t\t\t\t\t\tresult += this.readChar();\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t}\n\t\treadU8() {\n\t\t\t\treturn this.dataView.getUint8(this.offset++);\n\t\t}\n\t\treadU16() {\n\t\t\t\treturn this.dataView.getUint16(this._inc(2), true);\n\t\t}\n\t\treadU32() {\n\t\t\t\treturn this.dataView.getUint32(this._inc(4), true);\n\t\t}\n\t\treadU64() {\n\t\t\t\treturn this.readU32() + 2 ** 32 * this.readU32();\n\t\t}\n\t\treadU32be() {\n\t\t\t\treturn this.dataView.getUint32(this._inc(4), false);\n\t\t}\n\t\treadArray(result) {\n\t\t\t\tfor(let i = 0; i < result.length; ++i){\n\t\t\t\t\t\tresult[i] = this.readU8();\n\t\t\t\t}\n\t\t}\n\t\treadLine() {\n\t\t\t\tconst view = this.dataView;\n\t\t\t\tlet result = '';\n\t\t\t\twhile(true){\n\t\t\t\t\t\tif (this.offset >= view.byteLength) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst c = String.fromCharCode(this.readU8());\n\t\t\t\t\t\tif (c === '\\n') {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresult += c;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t}\n}\n\nclass SortedLoopArray {\n\t\tconstructor(args){\n\t\t\t\tthis.items = [];\n\t\t\t\tthis.length = 0;\n\t\t\t\tthis.loopIndex = -1;\n\t\t\t\tthis._sortBy = args.sortBy;\n\t\t\t\tthis._sortHandler = this._doSort.bind(this);\n\t\t}\n\t\t_binarySearch(item) {\n\t\t\t\tlet left = 0;\n\t\t\t\tlet right = this.items.lengt