@splnlss/3d-story-viewer 0.8.4 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -163,7 +163,7 @@ class CSS2DRenderer {
163
163
  }
164
164
  let sparkPromise = null;
165
165
  function loadSpark() {
166
- sparkPromise ?? (sparkPromise = import("./spark.module-OeW1hboi.js").catch((error) => {
166
+ sparkPromise ?? (sparkPromise = import("./spark.module-SbwV3up9.js").catch((error) => {
167
167
  sparkPromise = null;
168
168
  throw error;
169
169
  }));
@@ -7718,11 +7718,10 @@ class TileDecoders {
7718
7718
  this.disposed = false;
7719
7719
  }
7720
7720
  getDraco() {
7721
- var _a3, _b2;
7722
7721
  if (this.disposed) throw new Error("Tile decoders disposed");
7723
7722
  if (!this.draco) {
7724
7723
  const loader = new DRACOLoader();
7725
- if ((_b2 = (_a3 = globalThis.navigator) == null ? void 0 : _a3.userAgent) == null ? void 0 : _b2.includes("OculusBrowser/")) loader.decoderConfig.type = "js";
7724
+ loader.decoderConfig.type = "js";
7726
7725
  this.draco = managedLoader(loader, "_initDecoder", "decodeGeometry", "decoderPending");
7727
7726
  }
7728
7727
  return this.draco;
@@ -13210,6 +13209,7 @@ class GlobeManager extends EventDispatcher {
13210
13209
  this.destinationTileCamera = null;
13211
13210
  this.destinationTileResolution = 0;
13212
13211
  this.previousAutoDisableRendererCulling = null;
13212
+ this.parseQueueTimer = null;
13213
13213
  this.dracoLoader = null;
13214
13214
  this.ownsDecoders = !options.decoders;
13215
13215
  this.decoders = options.decoders ?? new TileDecoders(renderer);
@@ -13220,6 +13220,7 @@ class GlobeManager extends EventDispatcher {
13220
13220
  }
13221
13221
  }
13222
13222
  initTiles() {
13223
+ this.stopParseQueuePump();
13223
13224
  if (this.tiles) {
13224
13225
  this.setDestinationTilePrefetch(null);
13225
13226
  this.scene.remove(this.tiles.group);
@@ -13243,6 +13244,19 @@ class GlobeManager extends EventDispatcher {
13243
13244
  this.scene.add(this.tiles.group);
13244
13245
  updateTilesCamera(this.tiles, this.camera, this.renderer);
13245
13246
  this.attachTileLoadListener();
13247
+ this.startParseQueuePump();
13248
+ }
13249
+ startParseQueuePump() {
13250
+ this.stopParseQueuePump();
13251
+ this.parseQueueTimer = setInterval(() => {
13252
+ var _a3;
13253
+ const queue = (_a3 = this.tiles) == null ? void 0 : _a3.parseQueue;
13254
+ if ((queue == null ? void 0 : queue.items.length) > 0 && queue.currJobs < queue.maxJobs) queue.tryRunJobs();
13255
+ }, 100);
13256
+ }
13257
+ stopParseQueuePump() {
13258
+ if (this.parseQueueTimer !== null) clearInterval(this.parseQueueTimer);
13259
+ this.parseQueueTimer = null;
13246
13260
  }
13247
13261
  attachTileLoadListener() {
13248
13262
  this.tiles.addEventListener("load-model", ({ scene }) => {
@@ -13259,6 +13273,8 @@ class GlobeManager extends EventDispatcher {
13259
13273
  }
13260
13274
  /** The renderer shares one cache and request scheduler between both registered cameras. */
13261
13275
  setDestinationTilePrefetch(target) {
13276
+ const timeoutMs = (target == null ? void 0 : target.timeoutMs) ?? 12e3;
13277
+ if (target && (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 3e4)) return;
13262
13278
  this.clearDestinationTilePrefetch();
13263
13279
  if (!target || !this.tiles) return;
13264
13280
  let position;
@@ -13282,7 +13298,7 @@ class GlobeManager extends EventDispatcher {
13282
13298
  this.destinationTileResolution = target.resolutionPixels;
13283
13299
  this.tiles.setCamera(camera);
13284
13300
  this.updateDestinationTileResolution();
13285
- __privateSet(this, _destinationTileTimeout, setTimeout(() => this.clearDestinationTilePrefetch(), 12e3));
13301
+ __privateSet(this, _destinationTileTimeout, setTimeout(() => this.clearDestinationTilePrefetch(), timeoutMs));
13286
13302
  }
13287
13303
  updateDestinationTileResolution() {
13288
13304
  var _a3, _b2;
@@ -13306,12 +13322,15 @@ class GlobeManager extends EventDispatcher {
13306
13322
  }
13307
13323
  update() {
13308
13324
  if (!this.tiles) return;
13325
+ const parseQueue = this.tiles.parseQueue;
13326
+ if ((parseQueue == null ? void 0 : parseQueue.items.length) > 0 && parseQueue.currJobs < parseQueue.maxJobs) parseQueue.tryRunJobs();
13309
13327
  updateTilesCamera(this.tiles, this.camera, this.renderer);
13310
13328
  this.updateDestinationTileResolution();
13311
13329
  this.tiles.errorTarget = 20;
13312
13330
  this.tiles.update();
13313
13331
  }
13314
13332
  dispose() {
13333
+ this.stopParseQueuePump();
13315
13334
  this.clearDestinationTilePrefetch();
13316
13335
  if (this.tiles) {
13317
13336
  this.scene.remove(this.tiles.group);
@@ -14375,86 +14394,32 @@ function RGBEPromise(url, options = {}) {
14375
14394
  );
14376
14395
  });
14377
14396
  }
14378
- var stats_min$1 = { exports: {} };
14379
- var stats_min = stats_min$1.exports;
14380
- var hasRequiredStats_min;
14381
- function requireStats_min() {
14382
- if (hasRequiredStats_min) return stats_min$1.exports;
14383
- hasRequiredStats_min = 1;
14384
- (function(module, exports2) {
14385
- (function(f2, e) {
14386
- module.exports = e();
14387
- })(stats_min, function() {
14388
- var f2 = function() {
14389
- function e(a3) {
14390
- c3.appendChild(a3.dom);
14391
- return a3;
14392
- }
14393
- function u2(a3) {
14394
- for (var d2 = 0; d2 < c3.children.length; d2++) c3.children[d2].style.display = d2 === a3 ? "block" : "none";
14395
- l3 = a3;
14396
- }
14397
- var l3 = 0, c3 = document.createElement("div");
14398
- c3.style.cssText = "position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";
14399
- c3.addEventListener("click", function(a3) {
14400
- a3.preventDefault();
14401
- u2(++l3 % c3.children.length);
14402
- }, false);
14403
- var k2 = (performance || Date).now(), g2 = k2, a2 = 0, r2 = e(new f2.Panel("FPS", "#0ff", "#002")), h2 = e(new f2.Panel("MS", "#0f0", "#020"));
14404
- if (self.performance && self.performance.memory) var t2 = e(new f2.Panel("MB", "#f08", "#201"));
14405
- u2(0);
14406
- return { REVISION: 16, dom: c3, addPanel: e, showPanel: u2, begin: function() {
14407
- k2 = (performance || Date).now();
14408
- }, end: function() {
14409
- a2++;
14410
- var c4 = (performance || Date).now();
14411
- h2.update(c4 - k2, 200);
14412
- if (c4 > g2 + 1e3 && (r2.update(1e3 * a2 / (c4 - g2), 100), g2 = c4, a2 = 0, t2)) {
14413
- var d2 = performance.memory;
14414
- t2.update(d2.usedJSHeapSize / 1048576, d2.jsHeapSizeLimit / 1048576);
14415
- }
14416
- return c4;
14417
- }, update: function() {
14418
- k2 = this.end();
14419
- }, domElement: c3, setMode: u2 };
14420
- };
14421
- f2.Panel = function(e, f3, l3) {
14422
- var c3 = Infinity, k2 = 0, g2 = Math.round, a2 = g2(window.devicePixelRatio || 1), r2 = 80 * a2, h2 = 48 * a2, t2 = 3 * a2, v2 = 2 * a2, d2 = 3 * a2, m2 = 15 * a2, n2 = 74 * a2, p2 = 30 * a2, q2 = document.createElement("canvas");
14423
- q2.width = r2;
14424
- q2.height = h2;
14425
- q2.style.cssText = "width:80px;height:48px";
14426
- var b2 = q2.getContext("2d");
14427
- b2.font = "bold " + 9 * a2 + "px Helvetica,Arial,sans-serif";
14428
- b2.textBaseline = "top";
14429
- b2.fillStyle = l3;
14430
- b2.fillRect(0, 0, r2, h2);
14431
- b2.fillStyle = f3;
14432
- b2.fillText(e, t2, v2);
14433
- b2.fillRect(d2, m2, n2, p2);
14434
- b2.fillStyle = l3;
14435
- b2.globalAlpha = 0.9;
14436
- b2.fillRect(d2, m2, n2, p2);
14437
- return { dom: q2, update: function(h3, w2) {
14438
- c3 = Math.min(c3, h3);
14439
- k2 = Math.max(k2, h3);
14440
- b2.fillStyle = l3;
14441
- b2.globalAlpha = 1;
14442
- b2.fillRect(0, 0, r2, m2);
14443
- b2.fillStyle = f3;
14444
- b2.fillText(g2(h3) + " " + e + " (" + g2(c3) + "-" + g2(k2) + ")", t2, v2);
14445
- b2.drawImage(q2, d2 + a2, m2, n2 - a2, p2, d2, m2, n2 - a2, p2);
14446
- b2.fillRect(d2 + n2 - a2, m2, a2, p2);
14447
- b2.fillStyle = l3;
14448
- b2.globalAlpha = 0.9;
14449
- b2.fillRect(d2 + n2 - a2, m2, a2, g2((1 - h3 / w2) * p2));
14450
- } };
14451
- };
14452
- return f2;
14453
- });
14454
- })(stats_min$1);
14455
- return stats_min$1.exports;
14397
+ const finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
14398
+ function readLoadingDiagnostics(viewer) {
14399
+ var _a3, _b2, _c, _d, _e, _f, _g, _h;
14400
+ const tiles = (_b2 = (_a3 = viewer.globeManager) == null ? void 0 : _a3.tiles) == null ? void 0 : _b2.stats;
14401
+ const pager = (_c = viewer.sparkRenderer) == null ? void 0 : _c.pager;
14402
+ const pendingTiles = tiles && [tiles.queued, tiles.downloading, tiles.parsing].map(finite);
14403
+ let pendingRad = null;
14404
+ if (pager && [pager.fetchPriority, pager.fetchers, pager.fetched].every(Array.isArray)) {
14405
+ const pending = /* @__PURE__ */ new Map();
14406
+ for (const { splats, chunk } of [...pager.fetchPriority, ...pager.fetchers, ...pager.fetched]) {
14407
+ if (!splats || !Number.isInteger(chunk) || ((_d = pager.getSplatsChunk) == null ? void 0 : _d.call(pager, splats, chunk))) continue;
14408
+ if (!pending.has(splats)) pending.set(splats, /* @__PURE__ */ new Set());
14409
+ pending.get(splats).add(chunk);
14410
+ }
14411
+ pendingRad = [...pending.values()].reduce((sum, chunks) => sum + chunks.size, 0);
14412
+ }
14413
+ return {
14414
+ fps: finite(viewer.diagnosticFps),
14415
+ tilesVisible: finite(tiles == null ? void 0 : tiles.visible),
14416
+ tilesPending: (pendingTiles == null ? void 0 : pendingTiles.every((value) => value !== null)) ? pendingTiles.reduce((sum, value) => sum + value, 0) : null,
14417
+ tilesFailed: finite(tiles == null ? void 0 : tiles.failed),
14418
+ radPages: finite((_e = pager == null ? void 0 : pager.pageLru) == null ? void 0 : _e.size),
14419
+ radPending: pendingRad,
14420
+ gpuTextures: finite((_h = (_g = (_f = viewer.renderer) == null ? void 0 : _f.info) == null ? void 0 : _g.memory) == null ? void 0 : _h.textures)
14421
+ };
14456
14422
  }
14457
- requireStats_min();
14458
14423
  function computeUpVector(position, target) {
14459
14424
  const forward = new Vector3().subVectors(target, position).normalize();
14460
14425
  const referenceUp = position.clone().normalize();
@@ -14623,6 +14588,7 @@ class TilesLayer extends ContentLayer {
14623
14588
  super();
14624
14589
  this.type = "TilesLayer";
14625
14590
  this.isTilesLayer = true;
14591
+ this.disposed = false;
14626
14592
  const renderOrder = (options == null ? void 0 : options.renderOrder) ?? 0;
14627
14593
  const implicit = (options == null ? void 0 : options.implicit) ?? false;
14628
14594
  const overlayMode = (options == null ? void 0 : options.blendeMode) == "overlay" ? true : false;
@@ -14662,6 +14628,7 @@ class TilesLayer extends ContentLayer {
14662
14628
  updateTilesCamera(tiles, this.camera, this.renderer);
14663
14629
  tiles.addEventListener("load-model", (event) => {
14664
14630
  var _a3;
14631
+ if (this.disposed) return;
14665
14632
  if (hasOffset && !offsetApplied) {
14666
14633
  try {
14667
14634
  const bounds = new Sphere();
@@ -14715,6 +14682,8 @@ class TilesLayer extends ContentLayer {
14715
14682
  }
14716
14683
  }
14717
14684
  dispose() {
14685
+ if (this.disposed) return;
14686
+ this.disposed = true;
14718
14687
  super.dispose();
14719
14688
  if (this.tiles) {
14720
14689
  this.tiles.dispose();
@@ -14724,23 +14693,392 @@ class TilesLayer extends ContentLayer {
14724
14693
  if (this.ownsDecoders) this.decoders.dispose();
14725
14694
  }
14726
14695
  }
14696
+ var MeshoptDecoder = function() {
14697
+ var wasm_base = "b9H79Tebbbe8Fv9Gbb9Gvuuuuueu9Giuuub9Geueu9Giuuueuixkbeeeddddillviebeoweuecj:Gdkr;Neqo9TW9T9VV95dbH9F9F939H79T9F9J9H229F9Jt9VV7bb8A9TW79O9V9Wt9F9KW9J9V9KW9wWVtW949c919M9MWVbeY9TW79O9V9Wt9F9KW9J9V9KW69U9KW949c919M9MWVbdE9TW79O9V9Wt9F9KW9J9V9KW69U9KW949tWG91W9U9JWbiL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9p9JtblK9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9r919HtbvL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVT949WboY9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVJ9V29VVbrl79IV9Rbwq:VZkdbk:XYi5ud9:du8Jjjjjbcj;kb9Rgv8Kjjjjbc9:hodnalTmbcuhoaiRbbgrc;WeGc:Ge9hmbarcsGgwce0mbc9:hoalcufadcd4cbawEgDadfgrcKcaawEgqaraq0Egk6mbaicefhxcj;abad9Uc;WFbGcjdadca0EhmaialfgPar9Rgoadfhsavaoadz:jjjjbgzceVhHcbhOdndninaeaO9nmeaPax9RaD6mdamaeaO9RaOamfgoae6EgAcsfglc9WGhCabaOad2fhXaAcethQaxaDfhiaOaeaoaeao6E9RhLalcl4cifcd4hKazcj;cbfaAfhYcbh8AazcjdfhEaHh3incbh5dnawTmbaxa8Acd4fRbbh5kcbh8Eazcj;cbfhqinaih8Fdndndndna5a8Ecet4ciGgoc9:fPdebdkaPa8F9RaA6mrazcj;cbfa8EaA2fa8FaAz:jjjjb8Aa8FaAfhixdkazcj;cbfa8EaA2fcbaAz:kjjjb8Aa8FhixekaPa8F9RaK6mva8FaKfhidnaCTmbaPai9RcK6mbaocdtc:q:G:cjbfcj:G:cjbawEhaczhrcbhlinargoc9Wfghaqfhrdndndndndndnaaa8Fahco4fRbbalcoG4ciGcdtfydbPDbedvivvvlvkar9cb83bwar9cb83bbxlkarcbaiRbdai8Xbb9c:c:qj:bw9:9c:q;c1:I1e:d9c:b:c:e1z9:gg9cjjjjjz:dg8J9qE86bbaqaofgrcGfcbaicdfa8J9c8N1:NfghRbbag9cjjjjjw:dg8J9qE86bbarcVfcbaha8J9c8M1:NfghRbbag9cjjjjjl:dg8J9qE86bbarc7fcbaha8J9c8L1:NfghRbbag9cjjjjjd:dg8J9qE86bbarctfcbaha8J9c8K1:NfghRbbag9cjjjjje:dg8J9qE86bbarc91fcbaha8J9c8J1:NfghRbbag9cjjjj;ab:dg8J9qE86bbarc4fcbaha8J9cg1:NfghRbbag9cjjjja:dg8J9qE86bbarc93fcbaha8J9ch1:NfghRbbag9cjjjjz:dgg9qE86bbarc94fcbahag9ca1:NfghRbbai8Xbe9c:c:qj:bw9:9c:q;c1:I1e:d9c:b:c:e1z9:gg9cjjjjjz:dg8J9qE86bbarc95fcbaha8J9c8N1:NfgiRbbag9cjjjjjw:dg8J9qE86bbarc96fcbaia8J9c8M1:NfgiRbbag9cjjjjjl:dg8J9qE86bbarc97fcbaia8J9c8L1:NfgiRbbag9cjjjjjd:dg8J9qE86bbarc98fcbaia8J9c8K1:NfgiRbbag9cjjjjje:dg8J9qE86bbarc99fcbaia8J9c8J1:NfgiRbbag9cjjjj;ab:dg8J9qE86bbarc9:fcbaia8J9cg1:NfgiRbbag9cjjjja:dg8J9qE86bbarcufcbaia8J9ch1:NfgiRbbag9cjjjjz:dgg9qE86bbaiag9ca1:NfhixikaraiRblaiRbbghco4g8Ka8KciSg8KE86bbaqaofgrcGfaiclfa8Kfg8KRbbahcl4ciGg8La8LciSg8LE86bbarcVfa8Ka8Lfg8KRbbahcd4ciGg8La8LciSg8LE86bbarc7fa8Ka8Lfg8KRbbahciGghahciSghE86bbarctfa8Kahfg8KRbbaiRbeghco4g8La8LciSg8LE86bbarc91fa8Ka8Lfg8KRbbahcl4ciGg8La8LciSg8LE86bbarc4fa8Ka8Lfg8KRbbahcd4ciGg8La8LciSg8LE86bbarc93fa8Ka8Lfg8KRbbahciGghahciSghE86bbarc94fa8Kahfg8KRbbaiRbdghco4g8La8LciSg8LE86bbarc95fa8Ka8Lfg8KRbbahcl4ciGg8La8LciSg8LE86bbarc96fa8Ka8Lfg8KRbbahcd4ciGg8La8LciSg8LE86bbarc97fa8Ka8Lfg8KRbbahciGghahciSghE86bbarc98fa8KahfghRbbaiRbigico4g8Ka8KciSg8KE86bbarc99faha8KfghRbbaicl4ciGg8Ka8KciSg8KE86bbarc9:faha8KfghRbbaicd4ciGg8Ka8KciSg8KE86bbarcufaha8KfgrRbbaiciGgiaiciSgiE86bbaraifhixdkaraiRbwaiRbbghcl4g8Ka8KcsSg8KE86bbaqaofgrcGfaicwfa8Kfg8KRbbahcsGghahcsSghE86bbarcVfa8KahfghRbbaiRbeg8Kcl4g8La8LcsSg8LE86bbarc7faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarctfaha8KfghRbbaiRbdg8Kcl4g8La8LcsSg8LE86bbarc91faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc4faha8KfghRbbaiRbig8Kcl4g8La8LcsSg8LE86bbarc93faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc94faha8KfghRbbaiRblg8Kcl4g8La8LcsSg8LE86bbarc95faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc96faha8KfghRbbaiRbvg8Kcl4g8La8LcsSg8LE86bbarc97faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc98faha8KfghRbbaiRbog8Kcl4g8La8LcsSg8LE86bbarc99faha8LfghRbba8KcsGg8Ka8KcsSg8KE86bbarc9:faha8KfghRbbaiRbrgicl4g8Ka8KcsSg8KE86bbarcufaha8KfgrRbbaicsGgiaicsSgiE86bbaraifhixekarai8Pbw83bwarai8Pbb83bbaiczfhikdnaoaC9pmbalcdfhlaoczfhraPai9RcL0mekkaoaC6moaimexokaCmva8FTmvkaqaAfhqa8Ecefg8Ecl9hmbkdndndndnawTmbasa8Acd4fRbbgociGPlbedrbkaATmdaza8Afh8Fazcj;cbfhhcbh8EaEhaina8FRbbhraahocbhlinaoahalfRbbgqce4cbaqceG9R7arfgr86bbaoadfhoaAalcefgl9hmbkaacefhaa8Fcefh8FahaAfhha8Ecefg8Ecl9hmbxikkaATmeaza8Afhaazcj;cbfhhcbhoceh8EaYh8FinaEaofhlaa8Vbbhrcbhoinala8FaofRbbcwtahaofRbbgqVc;:FiGce4cbaqceG9R7arfgr87bbaladfhlaLaocefgofmbka8FaQfh8FcdhoaacdfhaahaQfhha8EceGhlcbh8EalmbxdkkaATmbaocl4h8Eaza8AfRbbhqcwhoa3hlinalRbbaotaqVhqalcefhlaocwfgoca9hmbkcbhhaEh8FaYhainazcj;cbfahfRbbhrcwhoaahlinalRbbaotarVhralaAfhlaocwfgoca9hmbkara8E94aq7hqcbhoa8Fhlinalaqao486bbalcefhlaocwfgoca9hmbka8Fadfh8FaacefhaahcefghaA9hmbkkaEclfhEa3clfh3a8Aclfg8Aad6mbkaXazcjdfaAad2z:jjjjb8AazazcjdfaAcufad2fadz:jjjjb8AaAaOfhOaihxaimbkc9:hoxdkcbc99aPax9RakSEhoxekc9:hokavcj;kbf8Kjjjjbaok:ysezu8Jjjjjbc;ae9Rgv8Kjjjjbc9:hodnalaeci9UgrcHf6mbcuhoaiRbbgwc;WeGc;Ge9hmbawcsGgDce0mbavc;abfcFecjez:kjjjb8Aav9cu83iUav9cu83i8Wav9cu83iyav9cu83iaav9cu83iKav9cu83izav9cu83iwav9cu83ibaialfc9WfhqaicefgwarfhldnaeTmbcmcsaDceSEhkcbhxcbhmcbhrcbhicbhoindnalaq9nmbc9:hoxikdndnawRbbgDc;Ve0mbavc;abfaoaDcu7gPcl4fcsGcitfgsydlhzasydbhHdndnaDcsGgsak9pmbavaiaPfcsGcdtfydbaxasEhDaxasTgOfhxxekdndnascsSmbcehOasc987asamffcefhDxekalcefhDal8SbbgscFeGhPdndnascu9mmbaDhlxekalcvfhlaPcFbGhPcrhsdninaD8SbbgOcFbGastaPVhPaOcu9kmeaDcefhDascrfgsc8J9hmbxdkkaDcefhlkcehOaPce4cbaPceG9R7amfhDkaDhmkavc;abfaocitfgsaDBdbasazBdlavaicdtfaDBdbavc;abfaocefcsGcitfgsaHBdbasaDBdlaocdfhoaOaifhidnadcd9hmbabarcetfgsaH87ebasclfaD87ebascdfaz87ebxdkabarcdtfgsaHBdbascwfaDBdbasclfazBdbxekdnaDcpe0mbavaiaqaDcsGfRbbgscl4gP9RcsGcdtfydbaxcefgOaPEhDavaias9RcsGcdtfydbaOaPTgzfgOascsGgPEhsaPThPdndnadcd9hmbabarcetfgHax87ebaHclfas87ebaHcdfaD87ebxekabarcdtfgHaxBdbaHcwfasBdbaHclfaDBdbkavaicdtfaxBdbavc;abfaocitfgHaDBdbaHaxBdlavaicefgicsGcdtfaDBdbavc;abfaocefcsGcitfgHasBdbaHaDBdlavaiazfgicsGcdtfasBdbavc;abfaocdfcsGcitfgDaxBdbaDasBdlaocifhoaiaPfhiaOaPfhxxekaxcbalRbbgsEgHaDc;:eSgDfhOascsGhAdndnascl4gCmbaOcefhzxekaOhzavaiaC9RcsGcdtfydbhOkdndnaAmbazcefhxxekazhxavaias9RcsGcdtfydbhzkdndnaDTmbalcefhDxekalcdfhDal8SbegPcFeGhsdnaPcu9kmbalcofhHascFbGhscrhldninaD8SbbgPcFbGaltasVhsaPcu9kmeaDcefhDalcrfglc8J9hmbkaHhDxekaDcefhDkasce4cbasceG9R7amfgmhHkdndnaCcsSmbaDhsxekaDcefhsaD8SbbglcFeGhPdnalcu9kmbaDcvfhOaPcFbGhPcrhldninas8SbbgDcFbGaltaPVhPaDcu9kmeascefhsalcrfglc8J9hmbkaOhsxekascefhskaPce4cbaPceG9R7amfgmhOkdndnaAcsSmbashlxekascefhlas8SbbgDcFeGhPdnaDcu9kmbascvfhzaPcFbGhPcrhDdninal8SbbgscFbGaDtaPVhPascu9kmealcefhlaDcrfgDc8J9hmbkazhlxekalcefhlkaPce4cbaPceG9R7amfgmhzkdndnadcd9hmbabarcetfgDaH87ebaDclfaz87ebaDcdfaO87ebxekabarcdtfgDaHBdbaDcwfazBdbaDclfaOBdbkavc;abfaocitfgDaOBdbaDaHBdlavaicdtfaHBdbavc;abfaocefcsGcitfgDazBdbaDaOBdlavaicefgicsGcdtfaOBdbavc;abfaocdfcsGcitfgDaHBdbaDazBdlavaiaCTaCcsSVfgicsGcdtfazBdbaiaATaAcsSVfhiaocifhokawcefhwaocsGhoaicsGhiarcifgrae6mbkkcbc99alaqSEhokavc;aef8Kjjjjbaok:clevu8Jjjjjbcz9Rhvdnalaecvf9pmbc9:skdnaiRbbc;:eGc;qeSmbcuskav9cb83iwaicefhoaialfc98fhrdnaeTmbdnadcdSmbcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcdtfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgiBdbalaiBdbawcefgwae9hmbxdkkcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcetfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgi87ebalaiBdbawcefgwae9hmbkkcbc99aoarSEk:Lvoeue99dud99eud99dndnadcl9hmbaeTmeindndnabcdfgd8Sbb:Yab8Sbbgi:Ygl:l:tabcefgv8Sbbgo:Ygr:l:tgwJbb;:9cawawNJbbbbawawJbbbb9GgDEgq:mgkaqaicb9iEalMgwawNakaqaocb9iEarMgqaqNMM:r:vglNJbbbZJbbb:;aDEMgr:lJbbb9p9DTmbar:Ohixekcjjjj94hikadai86bbdndnaqalNJbbbZJbbb:;aqJbbbb9GEMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkavad86bbdndnawalNJbbbZJbbb:;awJbbbb9GEMgw:lJbbb9p9DTmbaw:Ohdxekcjjjj94hdkabad86bbabclfhbaecufgembxdkkaeTmbindndnabclfgd8Ueb:Yab8Uebgi:Ygl:l:tabcdfgv8Uebgo:Ygr:l:tgwJb;:FSawawNJbbbbawawJbbbb9GgDEgq:mgkaqaicb9iEalMgwawNakaqaocb9iEarMgqaqNMM:r:vglNJbbbZJbbb:;aDEMgr:lJbbb9p9DTmbar:Ohixekcjjjj94hikadai87ebdndnaqalNJbbbZJbbb:;aqJbbbb9GEMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkavad87ebdndnawalNJbbbZJbbb:;awJbbbb9GEMgw:lJbbb9p9DTmbaw:Ohdxekcjjjj94hdkabad87ebabcwfhbaecufgembkkk:4ioiue99dud99dud99dnaeTmbcbhiabhlindndnal8Uebgv:YgoJ:ji:1Salcof8UebgrciVgw:Y:vgDNJbbbZJbbb:;avcu9kEMgq:lJbbb9p9DTmbaq:Ohkxekcjjjj94hkkalclf8Uebhvalcdf8UebhxalarcefciGcetfak87ebdndnax:YgqaDNJbbbZJbbb:;axcu9kEMgm:lJbbb9p9DTmbam:Ohxxekcjjjj94hxkabaiarciGgkfcd7cetfax87ebdndnav:YgmaDNJbbbZJbbb:;avcu9kEMgP:lJbbb9p9DTmbaP:Ohvxekcjjjj94hvkalarcufciGcetfav87ebdndnawaw2:ZgPaPMaoaoN:taqaqN:tamamN:tgoJbbbbaoJbbbb9GE:raDNJbbbZMgD:lJbbb9p9DTmbaD:Ohrxekcjjjj94hrkalakcetfar87ebalcwfhlaiclfhiaecufgembkkk9mbdnadcd4ae2gdTmbinababydbgecwtcw91:Yaece91cjjj98Gcjjj;8if::NUdbabclfhbadcufgdmbkkk:Tvirud99eudndnadcl9hmbaeTmeindndnabRbbgiabcefgl8Sbbgvabcdfgo8Sbbgrf9R:YJbbuJabcifgwRbbgdce4adVgDcd4aDVgDcl4aDVgD:Z:vgqNJbbbZMgk:lJbbb9p9DTmbak:Ohxxekcjjjj94hxkaoax86bbdndnaraif:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohoxekcjjjj94hokalao86bbdndnavaifar9R:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohixekcjjjj94hikabai86bbdndnaDadcetGadceGV:ZaqNJbbbZMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkawad86bbabclfhbaecufgembxdkkaeTmbindndnab8Vebgiabcdfgl8Uebgvabclfgo8Uebgrf9R:YJbFu9habcofgw8Vebgdce4adVgDcd4aDVgDcl4aDVgDcw4aDVgD:Z:vgqNJbbbZMgk:lJbbb9p9DTmbak:Ohxxekcjjjj94hxkaoax87ebdndnaraif:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohoxekcjjjj94hokalao87ebdndnavaifar9R:YaqNJbbbZMgk:lJbbb9p9DTmbak:Ohixekcjjjj94hikabai87ebdndnaDadcetGadceGV:ZaqNJbbbZMgq:lJbbb9p9DTmbaq:Ohdxekcjjjj94hdkawad87ebabcwfhbaecufgembkkk9teiucbcbyd:K:G:cjbgeabcifc98GfgbBd:K:G:cjbdndnabZbcztgd9nmbcuhiabad9RcFFifcz4nbcuSmekaehikaik;LeeeudndnaeabVciGTmbabhixekdndnadcz9pmbabhixekabhiinaiaeydbBdbaiclfaeclfydbBdbaicwfaecwfydbBdbaicxfaecxfydbBdbaeczfheaiczfhiadc9Wfgdcs0mbkkadcl6mbinaiaeydbBdbaeclfheaiclfhiadc98fgdci0mbkkdnadTmbinaiaeRbb86bbaicefhiaecefheadcufgdmbkkabk;aeedudndnabciGTmbabhixekaecFeGc:b:c:ew2hldndnadcz9pmbabhixekabhiinaialBdbaicxfalBdbaicwfalBdbaiclfalBdbaiczfhiadc9Wfgdcs0mbkkadcl6mbinaialBdbaiclfhiadc98fgdci0mbkkdnadTmbinaiae86bbaicefhiadcufgdmbkkabkk83dbcj:Gdk8Kbbbbdbbblbbbwbbbbbbbebbbdbbblbbbwbbbbc:K:Gdkl8W:qbb";
14698
+ var wasm_simd = "b9H79TebbbeKl9Gbb9Gvuuuuueu9Giuuub9Geueuixkbbebeeddddilve9Weeeviebeoweuecj:Gdkr;Neqo9TW9T9VV95dbH9F9F939H79T9F9J9H229F9Jt9VV7bb8A9TW79O9V9Wt9F9KW9J9V9KW9wWVtW949c919M9MWVbdY9TW79O9V9Wt9F9KW9J9V9KW69U9KW949c919M9MWVblE9TW79O9V9Wt9F9KW9J9V9KW69U9KW949tWG91W9U9JWbvL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9p9JtboK9TW79O9V9Wt9F9KW9J9V9KWS9P2tWV9r919HtbrL9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVT949WbwY9TW79O9V9Wt9F9KW9J9V9KWS9P2tWVJ9V29VVbDl79IV9Rbqq:W9Dklbzik94evu8Jjjjjbcz9Rhbcbheincbhdcbhiinabcwfadfaicjuaead4ceGglE86bbaialfhiadcefgdcw9hmbkaeai86b:q:W:cjbaecitab8Piw83i:q:G:cjbaecefgecjd9hmbkk:JBl8Aud97dur978Jjjjjbcj;kb9Rgv8Kjjjjbc9:hodnalTmbcuhoaiRbbgrc;WeGc:Ge9hmbarcsGgwce0mbc9:hoalcufadcd4cbawEgDadfgrcKcaawEgqaraq0Egk6mbaialfgxar9RhodnadTgmmbavaoad;8qbbkaicefhPcj;abad9Uc;WFbGcjdadca0EhsdndndnadTmbaoadfhzcbhHinaeaH9nmdaxaP9RaD6miabaHad2fhOaPaDfhAasaeaH9RaHasfae6EgCcsfgocl4cifcd4hXavcj;cbfaoc9WGgQcetfhLavcj;cbfaQci2fhKavcj;cbfaQfhYcbh8Aaoc;ab6hEincbh3dnawTmbaPa8Acd4fRbbh3kcbh5avcj;cbfh8Eindndndndna3a5cet4ciGgoc9:fPdebdkaxaA9RaQ6mwdnaQTmbavcj;cbfa5aQ2faAaQ;8qbbkaAaCfhAxdkaQTmeavcj;cbfa5aQ2fcbaQ;8kbxekaxaA9RaX6moaoclVcbawEhraAaXfhocbhidnaEmbaxao9Rc;Gb6mbcbhlina8EalfhidndndndndndnaAalco4fRbbgqciGarfPDbedibledibkaipxbbbbbbbbbbbbbbbbpklbxlkaiaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaoclffagRb:q:W:cjbfhoxikaiaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaocwffagRb:q:W:cjbfhoxdkaiaopbbbpklbaoczfhoxekaiaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbahaocdffagRb:q:W:cjbfhokdndndndndndnaqcd4ciGarfPDbedibledibkaiczfpxbbbbbbbbbbbbbbbbpklbxlkaiczfaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaoclffagRb:q:W:cjbfhoxikaiczfaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaocwffagRb:q:W:cjbfhoxdkaiczfaopbbbpklbaoczfhoxekaiczfaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbahaocdffagRb:q:W:cjbfhokdndndndndndnaqcl4ciGarfPDbedibledibkaicafpxbbbbbbbbbbbbbbbbpklbxlkaicafaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaoclffagRb:q:W:cjbfhoxikaicafaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbahaocwffagRb:q:W:cjbfhoxdkaicafaopbbbpklbaoczfhoxekaicafaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbahaocdffagRb:q:W:cjbfhokdndndndndndnaqco4arfPDbedibledibkaic8Wfpxbbbbbbbbbbbbbbbbpklbxlkaic8Wfaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Ngicitpbi:q:G:cjbaiRb:q:W:cjbgipsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Ngqcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbaiaoclffaqRb:q:W:cjbfhoxikaic8Wfaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Ngicitpbi:q:G:cjbaiRb:q:W:cjbgipsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Ngqcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spklbaiaocwffaqRb:q:W:cjbfhoxdkaic8Wfaopbbbpklbaoczfhoxekaic8WfaopbbdaoRbbgicitpbi:q:G:cjbaiRb:q:W:cjbgipsaoRbegqcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpklbaiaocdffaqRb:q:W:cjbfhokalc;abfhialcjefaQ0meaihlaxao9Rc;Fb0mbkkdnaiaQ9pmbaici4hlinaxao9RcK6mwa8EaifhqdndndndndndnaAaico4fRbbalcoG4ciGarfPDbedibledibkaqpxbbbbbbbbbbbbbbbbpkbbxlkaqaopbblaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLg8Fcdp:mea8FpmbzeHdOiAlCvXoQrLpxiiiiiiiiiiiiiiiip9ogapxiiiiiiiiiiiiiiiip8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spkbbahaoclffagRb:q:W:cjbfhoxikaqaopbbwaopbbbg8Fclp:mea8FpmbzeHdOiAlCvXoQrLpxssssssssssssssssp9ogapxssssssssssssssssp8Jg8Fp5b9cjF;8;4;W;G;ab9:9cU1:Nghcitpbi:q:G:cjbahRb:q:W:cjbghpsa8Fp5e9cjF;8;4;W;G;ab9:9cU1:Nggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPaaa8Fp9spkbbahaocwffagRb:q:W:cjbfhoxdkaqaopbbbpkbbaoczfhoxekaqaopbbdaoRbbghcitpbi:q:G:cjbahRb:q:W:cjbghpsaoRbeggcitpbi:q:G:cjbp9UpmbedilvorzHOACXQLpPpkbbahaocdffagRb:q:W:cjbfhokalcdfhlaiczfgiaQ6mbkkaohAaoTmoka8EaQfh8Ea5cefg5cl9hmbkdndndndnawTmbaza8Acd4fRbbglciGPlbedwbkaQTmdavcjdfa8Afhlava8Afpbdbh8Jcbhoinalavcj;cbfaofpblbg8KaYaofpblbg8LpmbzeHdOiAlCvXoQrLg8MaLaofpblbg8NaKaofpblbgypmbzeHdOiAlCvXoQrLg8PpmbezHdiOAlvCXorQLg8Fcep9Ta8Fpxeeeeeeeeeeeeeeeegap9op9Hp9rg8Fa8Jp9Ug8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9Abbbaladfgla8Ja8Ma8PpmwDKYqk8AExm35Ps8E8Fg8Fcep9Ta8Faap9op9Hp9rg8Fp9Ug8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9Abbbaladfgla8Ja8Ka8LpmwKDYq8AkEx3m5P8Es8Fg8Ka8NaypmwKDYq8AkEx3m5P8Es8Fg8LpmbezHdiOAlvCXorQLg8Fcep9Ta8Faap9op9Hp9rg8Fp9Ug8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp9Ug8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9Abbbaladfgla8Ja8Ka8LpmwDKYqk8AExm35Ps8E8Fg8Fcep9Ta8Faap9op9Hp9rg8Fp9Ugap9Abbbaladfglaaa8Fa8Fpmlvorlvorlvorlvorp9Ugap9Abbbaladfglaaa8Fa8FpmwDqkwDqkwDqkwDqkp9Ugap9Abbbaladfglaaa8Fa8FpmxmPsxmPsxmPsxmPsp9Ug8Jp9AbbbaladfhlaoczfgoaQ6mbxikkaQTmeavcjdfa8Afhlava8Afpbdbh8Jcbhoinalavcj;cbfaofpblbg8KaYaofpblbg8LpmbzeHdOiAlCvXoQrLg8MaLaofpblbg8NaKaofpblbgypmbzeHdOiAlCvXoQrLg8PpmbezHdiOAlvCXorQLg8Fcep:nea8Fpxebebebebebebebebgap9op:bep9rg8Fa8Jp:oeg8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9Abbbaladfgla8Ja8Ma8PpmwDKYqk8AExm35Ps8E8Fg8Fcep:nea8Faap9op:bep9rg8Fp:oeg8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9Abbbaladfgla8Ja8Ka8LpmwKDYq8AkEx3m5P8Es8Fg8Ka8NaypmwKDYq8AkEx3m5P8Es8Fg8LpmbezHdiOAlvCXorQLg8Fcep:nea8Faap9op:bep9rg8Fp:oeg8Jp9Abbbaladfgla8Ja8Fa8Fpmlvorlvorlvorlvorp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmwDqkwDqkwDqkwDqkp:oeg8Jp9Abbbaladfgla8Ja8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9Abbbaladfgla8Ja8Ka8LpmwDKYqk8AExm35Ps8E8Fg8Fcep:nea8Faap9op:bep9rg8Fp:oegap9Abbbaladfglaaa8Fa8Fpmlvorlvorlvorlvorp:oegap9Abbbaladfglaaa8Fa8FpmwDqkwDqkwDqkwDqkp:oegap9Abbbaladfglaaa8Fa8FpmxmPsxmPsxmPsxmPsp:oeg8Jp9AbbbaladfhlaoczfgoaQ6mbxdkkaQTmbcbhocbalcl4gl9Rc8FGhiavcjdfa8Afhrava8Afpbdbhainaravcj;cbfaofpblbg8JaYaofpblbg8KpmbzeHdOiAlCvXoQrLg8LaLaofpblbg8MaKaofpblbg8NpmbzeHdOiAlCvXoQrLgypmbezHdiOAlvCXorQLg8Faip:Rea8Falp:Tep9qg8Faap9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9Abbbaradfgraaa8LaypmwDKYqk8AExm35Ps8E8Fg8Faip:Rea8Falp:Tep9qg8Fp9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9Abbbaradfgraaa8Ja8KpmwKDYq8AkEx3m5P8Es8Fg8Ja8Ma8NpmwKDYq8AkEx3m5P8Es8Fg8KpmbezHdiOAlvCXorQLg8Faip:Rea8Falp:Tep9qg8Fp9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9Abbbaradfgraaa8Ja8KpmwDKYqk8AExm35Ps8E8Fg8Faip:Rea8Falp:Tep9qg8Fp9rgap9Abbbaradfgraaa8Fa8Fpmlvorlvorlvorlvorp9rgap9Abbbaradfgraaa8Fa8FpmwDqkwDqkwDqkwDqkp9rgap9Abbbaradfgraaa8Fa8FpmxmPsxmPsxmPsxmPsp9rgap9AbbbaradfhraoczfgoaQ6mbkka8Aclfg8Aad6mbkdnaCad2goTmbaOavcjdfao;8qbbkdnammbavavcjdfaCcufad2fad;8qbbkaCaHfhHc9:hoaAhPaAmbxlkkaeTmbaDalfhrcbhocuhlinaralaD9RglfaD6mdasaeao9Raoasfae6Eaofgoae6mbkaial9RhPkcbc99axaP9RakSEhoxekc9:hokavcj;kbf8Kjjjjbaokwbz:bjjjbkNsezu8Jjjjjbc;ae9Rgv8Kjjjjbc9:hodnalaeci9UgrcHf6mbcuhoaiRbbgwc;WeGc;Ge9hmbawcsGgDce0mbavc;abfcFecje;8kbav9cu83iUav9cu83i8Wav9cu83iyav9cu83iaav9cu83iKav9cu83izav9cu83iwav9cu83ibaialfc9WfhqaicefgwarfhldnaeTmbcmcsaDceSEhkcbhxcbhmcbhrcbhicbhoindnalaq9nmbc9:hoxikdndnawRbbgDc;Ve0mbavc;abfaoaDcu7gPcl4fcsGcitfgsydlhzasydbhHdndnaDcsGgsak9pmbavaiaPfcsGcdtfydbaxasEhDaxasTgOfhxxekdndnascsSmbcehOasc987asamffcefhDxekalcefhDal8SbbgscFeGhPdndnascu9mmbaDhlxekalcvfhlaPcFbGhPcrhsdninaD8SbbgOcFbGastaPVhPaOcu9kmeaDcefhDascrfgsc8J9hmbxdkkaDcefhlkcehOaPce4cbaPceG9R7amfhDkaDhmkavc;abfaocitfgsaDBdbasazBdlavaicdtfaDBdbavc;abfaocefcsGcitfgsaHBdbasaDBdlaocdfhoaOaifhidnadcd9hmbabarcetfgsaH87ebasclfaD87ebascdfaz87ebxdkabarcdtfgsaHBdbascwfaDBdbasclfazBdbxekdnaDcpe0mbavaiaqaDcsGfRbbgscl4gP9RcsGcdtfydbaxcefgOaPEhDavaias9RcsGcdtfydbaOaPTgzfgOascsGgPEhsaPThPdndnadcd9hmbabarcetfgHax87ebaHclfas87ebaHcdfaD87ebxekabarcdtfgHaxBdbaHcwfasBdbaHclfaDBdbkavaicdtfaxBdbavc;abfaocitfgHaDBdbaHaxBdlavaicefgicsGcdtfaDBdbavc;abfaocefcsGcitfgHasBdbaHaDBdlavaiazfgicsGcdtfasBdbavc;abfaocdfcsGcitfgDaxBdbaDasBdlaocifhoaiaPfhiaOaPfhxxekaxcbalRbbgsEgHaDc;:eSgDfhOascsGhAdndnascl4gCmbaOcefhzxekaOhzavaiaC9RcsGcdtfydbhOkdndnaAmbazcefhxxekazhxavaias9RcsGcdtfydbhzkdndnaDTmbalcefhDxekalcdfhDal8SbegPcFeGhsdnaPcu9kmbalcofhHascFbGhscrhldninaD8SbbgPcFbGaltasVhsaPcu9kmeaDcefhDalcrfglc8J9hmbkaHhDxekaDcefhDkasce4cbasceG9R7amfgmhHkdndnaCcsSmbaDhsxekaDcefhsaD8SbbglcFeGhPdnalcu9kmbaDcvfhOaPcFbGhPcrhldninas8SbbgDcFbGaltaPVhPaDcu9kmeascefhsalcrfglc8J9hmbkaOhsxekascefhskaPce4cbaPceG9R7amfgmhOkdndnaAcsSmbashlxekascefhlas8SbbgDcFeGhPdnaDcu9kmbascvfhzaPcFbGhPcrhDdninal8SbbgscFbGaDtaPVhPascu9kmealcefhlaDcrfgDc8J9hmbkazhlxekalcefhlkaPce4cbaPceG9R7amfgmhzkdndnadcd9hmbabarcetfgDaH87ebaDclfaz87ebaDcdfaO87ebxekabarcdtfgDaHBdbaDcwfazBdbaDclfaOBdbkavc;abfaocitfgDaOBdbaDaHBdlavaicdtfaHBdbavc;abfaocefcsGcitfgDazBdbaDaOBdlavaicefgicsGcdtfaOBdbavc;abfaocdfcsGcitfgDaHBdbaDazBdlavaiaCTaCcsSVfgicsGcdtfazBdbaiaATaAcsSVfhiaocifhokawcefhwaocsGhoaicsGhiarcifgrae6mbkkcbc99alaqSEhokavc;aef8Kjjjjbaok:clevu8Jjjjjbcz9Rhvdnalaecvf9pmbc9:skdnaiRbbc;:eGc;qeSmbcuskav9cb83iwaicefhoaialfc98fhrdnaeTmbdnadcdSmbcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcdtfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgiBdbalaiBdbawcefgwae9hmbxdkkcbhwindnaoar6mbc9:skaocefhlao8SbbgicFeGhddndnaicu9mmbalhoxekaocvfhoadcFbGhdcrhidninal8SbbgDcFbGaitadVhdaDcu9kmealcefhlaicrfgic8J9hmbxdkkalcefhokabawcetfadc8Etc8F91adcd47avcwfadceGcdtVglydbfgi87ebalaiBdbawcefgwae9hmbkkcbc99aoarSEk;Toio97eue97aec98Ghedndnadcl9hmbaeTmecbhdinababpbbbgicKp:RecKp:Sep;6eglaicwp:RecKp:Sep;6ealp;Geaiczp:RecKp:Sep;6egvp;Gep;Kep;Legopxbbbbbbbbbbbbbbbbp:2egralpxbbbjbbbjbbbjbbbjgwp9op9rp;Keglpxbb;:9cbb;:9cbb;:9cbb;:9calalp;Meaoaop;Meavaravawp9op9rp;Keglalp;Mep;Kep;Kep;Jep;Negvp;Mepxbbn0bbn0bbn0bbn0grp;KepxFbbbFbbbFbbbFbbbp9oaipxbbbFbbbFbbbFbbbFp9op9qalavp;Mearp;Kecwp:RepxbFbbbFbbbFbbbFbbp9op9qaoavp;Mearp;Keczp:RepxbbFbbbFbbbFbbbFbp9op9qpkbbabczfhbadclfgdae6mbxdkkaeTmbcbhdinabczfgDaDpbbbgipxbbbbbbFFbbbbbbFFgwp9oabpbbbgoaipmbediwDqkzHOAKY8AEgvczp:Reczp:Sep;6eglaoaipmlvorxmPsCXQL358E8FpxFubbFubbFubbFubbp9op;6eavczp:Sep;6egvp;Gealp;Gep;Kep;Legipxbbbbbbbbbbbbbbbbp:2egralpxbbbjbbbjbbbjbbbjgqp9op9rp;Keglpxb;:FSb;:FSb;:FSb;:FSalalp;Meaiaip;Meavaravaqp9op9rp;Keglalp;Mep;Kep;Kep;Jep;Negvp;Mepxbbn0bbn0bbn0bbn0grp;KepxFFbbFFbbFFbbFFbbp9oaiavp;Mearp;Keczp:Rep9qgialavp;Mearp;KepxFFbbFFbbFFbbFFbbp9oglpmwDKYqk8AExm35Ps8E8Fp9qpkbbabaoawp9oaialpmbezHdiOAlvCXorQLp9qpkbbabcafhbadclfgdae6mbkkk;2ileue97euo97dnaec98GgiTmbcbheinabcKfpx:ji:1S:ji:1S:ji:1S:ji:1SabpbbbglabczfgvpbbbgopmlvorxmPsCXQL358E8Fgrczp:Segwpxibbbibbbibbbibbbp9qp;6egDp;NegqaDaDp;MegDaDp;KealaopmbediwDqkzHOAKY8AEgDczp:Reczp:Sep;6eglalp;MeaDczp:Sep;6egoaop;Mearczp:Reczp:Sep;6egrarp;Mep;Kep;Kep;Lepxbbbbbbbbbbbbbbbbp:4ep;Jep;Mepxbbn0bbn0bbn0bbn0gDp;KepxFFbbFFbbFFbbFFbbgkp9oaqaop;MeaDp;Keczp:Rep9qgoaqalp;MeaDp;Keakp9oaqarp;MeaDp;Keczp:Rep9qgDpmwDKYqk8AExm35Ps8E8Fglp5eawclp:RegqpEi:T:j83ibavalp5baqpEd:T:j83ibabcwfaoaDpmbezHdiOAlvCXorQLgDp5eaqpEe:T:j83ibabaDp5baqpEb:T:j83ibabcafhbaeclfgeai6mbkkkuee97dnadcd4ae2c98GgeTmbcbhdinababpbbbgicwp:Recwp:Sep;6eaicep:SepxbbjFbbjFbbjFbbjFp9opxbbjZbbjZbbjZbbjZp:Uep;Mepkbbabczfhbadclfgdae6mbkkk:Sodw97euaec98Ghedndnadcl9hmbaeTmecbhdinabpxbbuJbbuJbbuJbbuJabpbbbgicKp:TeglaicYp:Tep9qgvcdp:Teavp9qgvclp:Teavp9qgop;6ep;Negvaicwp:RecKp:SegraipxFbbbFbbbFbbbFbbbgwp9ogDp:Uep;6ep;Mepxbbn0bbn0bbn0bbn0gqp;Kecwp:RepxbFbbbFbbbFbbbFbbp9oavaDarp:Xeaiczp:RecKp:Segip:Uep;6ep;Meaqp;Keawp9op9qavaDaraip:Uep:Xep;6ep;Meaqp;Keczp:RepxbbFbbbFbbbFbbbFbp9op9qavaoalcep:Rep9oalpxebbbebbbebbbebbbp9op9qp;6ep;Meaqp;KecKp:Rep9qpkbbabczfhbadclfgdae6mbxdkkaeTmbcbhdinabczfgkpxbFu9hbFu9hbFu9hbFu9habpbbbglakpbbbgrpmlvorxmPsCXQL358E8Fgvczp:TegqavcHp:Tep9qgicdp:Teaip9qgiclp:Teaip9qgicwp:Teaip9qgop;6ep;NegialarpmbediwDqkzHOAKY8AEgDpxFFbbFFbbFFbbFFbbglp9ograDczp:Segwp:Ueavczp:Reczp:SegDp:Xep;6ep;Mepxbbn0bbn0bbn0bbn0gvp;Kealp9oaiarawaDp:Uep:Xep;6ep;Meavp;Keczp:Rep9qgwaiaoaqcep:Rep9oaqpxebbbebbbebbbebbbp9op9qp;6ep;Meavp;Keczp:ReaiaDarp:Uep;6ep;Meavp;Kealp9op9qgipmwDKYqk8AExm35Ps8E8FpkbbabawaipmbezHdiOAlvCXorQLpkbbabcafhbadclfgdae6mbkkk9teiucbcbydj:G:cjbgeabcifc98GfgbBdj:G:cjbdndnabZbcztgd9nmbcuhiabad9RcFFifcz4nbcuSmekaehikaikkxebcj:Gdklz:zbb";
14699
+ var detector = new Uint8Array([
14700
+ 0,
14701
+ 97,
14702
+ 115,
14703
+ 109,
14704
+ 1,
14705
+ 0,
14706
+ 0,
14707
+ 0,
14708
+ 1,
14709
+ 4,
14710
+ 1,
14711
+ 96,
14712
+ 0,
14713
+ 0,
14714
+ 3,
14715
+ 3,
14716
+ 2,
14717
+ 0,
14718
+ 0,
14719
+ 5,
14720
+ 3,
14721
+ 1,
14722
+ 0,
14723
+ 1,
14724
+ 12,
14725
+ 1,
14726
+ 0,
14727
+ 10,
14728
+ 22,
14729
+ 2,
14730
+ 12,
14731
+ 0,
14732
+ 65,
14733
+ 0,
14734
+ 65,
14735
+ 0,
14736
+ 65,
14737
+ 0,
14738
+ 252,
14739
+ 10,
14740
+ 0,
14741
+ 0,
14742
+ 11,
14743
+ 7,
14744
+ 0,
14745
+ 65,
14746
+ 0,
14747
+ 253,
14748
+ 15,
14749
+ 26,
14750
+ 11
14751
+ ]);
14752
+ var wasmpack = new Uint8Array([
14753
+ 32,
14754
+ 0,
14755
+ 65,
14756
+ 2,
14757
+ 1,
14758
+ 106,
14759
+ 34,
14760
+ 33,
14761
+ 3,
14762
+ 128,
14763
+ 11,
14764
+ 4,
14765
+ 13,
14766
+ 64,
14767
+ 6,
14768
+ 253,
14769
+ 10,
14770
+ 7,
14771
+ 15,
14772
+ 116,
14773
+ 127,
14774
+ 5,
14775
+ 8,
14776
+ 12,
14777
+ 40,
14778
+ 16,
14779
+ 19,
14780
+ 54,
14781
+ 20,
14782
+ 9,
14783
+ 27,
14784
+ 255,
14785
+ 113,
14786
+ 17,
14787
+ 42,
14788
+ 67,
14789
+ 24,
14790
+ 23,
14791
+ 146,
14792
+ 148,
14793
+ 18,
14794
+ 14,
14795
+ 22,
14796
+ 45,
14797
+ 70,
14798
+ 69,
14799
+ 56,
14800
+ 114,
14801
+ 101,
14802
+ 21,
14803
+ 25,
14804
+ 63,
14805
+ 75,
14806
+ 136,
14807
+ 108,
14808
+ 28,
14809
+ 118,
14810
+ 29,
14811
+ 73,
14812
+ 115
14813
+ ]);
14814
+ if (typeof WebAssembly !== "object") {
14815
+ return {
14816
+ supported: false
14817
+ };
14818
+ }
14819
+ var wasm = WebAssembly.validate(detector) ? unpack(wasm_simd) : unpack(wasm_base);
14820
+ var instance;
14821
+ var ready = WebAssembly.instantiate(wasm, {}).then(function(result) {
14822
+ instance = result.instance;
14823
+ instance.exports.__wasm_call_ctors();
14824
+ });
14825
+ function unpack(data) {
14826
+ var result = new Uint8Array(data.length);
14827
+ for (var i2 = 0; i2 < data.length; ++i2) {
14828
+ var ch = data.charCodeAt(i2);
14829
+ result[i2] = ch > 96 ? ch - 97 : ch > 64 ? ch - 39 : ch + 4;
14830
+ }
14831
+ var write = 0;
14832
+ for (var i2 = 0; i2 < data.length; ++i2) {
14833
+ result[write++] = result[i2] < 60 ? wasmpack[result[i2]] : (result[i2] - 60) * 64 + result[++i2];
14834
+ }
14835
+ return result.buffer.slice(0, write);
14836
+ }
14837
+ function decode(instance2, fun, target, count, size, source, filter) {
14838
+ var sbrk = instance2.exports.sbrk;
14839
+ var count4 = count + 3 & -4;
14840
+ var tp = sbrk(count4 * size);
14841
+ var sp = sbrk(source.length);
14842
+ var heap = new Uint8Array(instance2.exports.memory.buffer);
14843
+ heap.set(source, sp);
14844
+ var res = fun(tp, count, size, sp, source.length);
14845
+ if (res == 0 && filter) {
14846
+ filter(tp, count4, size);
14847
+ }
14848
+ target.set(heap.subarray(tp, tp + count * size));
14849
+ sbrk(tp - sbrk(0));
14850
+ if (res != 0) {
14851
+ throw new Error("Malformed buffer data: " + res);
14852
+ }
14853
+ }
14854
+ var filters = {
14855
+ NONE: "",
14856
+ OCTAHEDRAL: "meshopt_decodeFilterOct",
14857
+ QUATERNION: "meshopt_decodeFilterQuat",
14858
+ EXPONENTIAL: "meshopt_decodeFilterExp",
14859
+ COLOR: "meshopt_decodeFilterColor"
14860
+ };
14861
+ var decoders = {
14862
+ ATTRIBUTES: "meshopt_decodeVertexBuffer",
14863
+ TRIANGLES: "meshopt_decodeIndexBuffer",
14864
+ INDICES: "meshopt_decodeIndexSequence"
14865
+ };
14866
+ var workers = [];
14867
+ var requestId = 0;
14868
+ function createWorker(url) {
14869
+ var worker = {
14870
+ object: new Worker(url),
14871
+ pending: 0,
14872
+ requests: {}
14873
+ };
14874
+ worker.object.onmessage = function(event) {
14875
+ var data = event.data;
14876
+ worker.pending -= data.count;
14877
+ worker.requests[data.id][data.action](data.value);
14878
+ delete worker.requests[data.id];
14879
+ };
14880
+ return worker;
14881
+ }
14882
+ function initWorkers(count) {
14883
+ var source = "self.ready = WebAssembly.instantiate(new Uint8Array([" + new Uint8Array(wasm) + "]), {}).then(function(result) { result.instance.exports.__wasm_call_ctors(); return result.instance; });self.onmessage = " + workerProcess.name + ";" + decode.toString() + workerProcess.toString();
14884
+ var blob = new Blob([source], { type: "text/javascript" });
14885
+ var url = URL.createObjectURL(blob);
14886
+ for (var i2 = workers.length; i2 < count; ++i2) {
14887
+ workers[i2] = createWorker(url);
14888
+ }
14889
+ for (var i2 = count; i2 < workers.length; ++i2) {
14890
+ workers[i2].object.postMessage({});
14891
+ }
14892
+ workers.length = count;
14893
+ URL.revokeObjectURL(url);
14894
+ }
14895
+ function decodeWorker(count, size, source, mode, filter) {
14896
+ var worker = workers[0];
14897
+ for (var i2 = 1; i2 < workers.length; ++i2) {
14898
+ if (workers[i2].pending < worker.pending) {
14899
+ worker = workers[i2];
14900
+ }
14901
+ }
14902
+ return new Promise(function(resolve, reject) {
14903
+ var data = new Uint8Array(source);
14904
+ var id = ++requestId;
14905
+ worker.pending += count;
14906
+ worker.requests[id] = { resolve, reject };
14907
+ worker.object.postMessage({ id, count, size, source: data, mode, filter }, [data.buffer]);
14908
+ });
14909
+ }
14910
+ function workerProcess(event) {
14911
+ var data = event.data;
14912
+ self.ready.then(function(instance2) {
14913
+ if (!data.id) {
14914
+ return self.close();
14915
+ }
14916
+ try {
14917
+ var target = new Uint8Array(data.count * data.size);
14918
+ decode(instance2, instance2.exports[data.mode], target, data.count, data.size, data.source, instance2.exports[data.filter]);
14919
+ self.postMessage({ id: data.id, count: data.count, action: "resolve", value: target }, [target.buffer]);
14920
+ } catch (error) {
14921
+ self.postMessage({ id: data.id, count: data.count, action: "reject", value: error });
14922
+ }
14923
+ });
14924
+ }
14925
+ return {
14926
+ ready,
14927
+ supported: true,
14928
+ useWorkers: function(count) {
14929
+ initWorkers(count);
14930
+ },
14931
+ decodeVertexBuffer: function(target, count, size, source, filter) {
14932
+ decode(instance, instance.exports.meshopt_decodeVertexBuffer, target, count, size, source, instance.exports[filters[filter]]);
14933
+ },
14934
+ decodeIndexBuffer: function(target, count, size, source) {
14935
+ decode(instance, instance.exports.meshopt_decodeIndexBuffer, target, count, size, source);
14936
+ },
14937
+ decodeIndexSequence: function(target, count, size, source) {
14938
+ decode(instance, instance.exports.meshopt_decodeIndexSequence, target, count, size, source);
14939
+ },
14940
+ decodeGltfBuffer: function(target, count, size, source, mode, filter) {
14941
+ decode(instance, instance.exports[decoders[mode]], target, count, size, source, instance.exports[filters[filter]]);
14942
+ },
14943
+ decodeGltfBufferAsync: function(count, size, source, mode, filter) {
14944
+ if (workers.length > 0) {
14945
+ return decodeWorker(count, size, source, decoders[mode], filters[filter]);
14946
+ }
14947
+ return ready.then(function() {
14948
+ var target = new Uint8Array(count * size);
14949
+ decode(instance, instance.exports[decoders[mode]], target, count, size, source, instance.exports[filters[filter]]);
14950
+ return target;
14951
+ });
14952
+ }
14953
+ };
14954
+ }();
14727
14955
  var patch_default = "uniform vec3 diffuse;uniform float opacity;\n#ifndef FLAT_SHADED\nvarying vec3 vNormal;\n#endif\n#include <common>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main(){vec4 diffuseColor=vec4(diffuse,opacity);\n#include <clipping_planes_fragment>\n#include <logdepthbuf_fragment>\n#include <map_fragment>\n#include <alphamap_fragment>\n#include <alphatest_fragment>\n#include <alphahash_fragment>\n#include <specularmap_fragment>\nReflectedLight reflectedLight=ReflectedLight(vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0));\n#ifdef USE_LIGHTMAP\nvec4 lightMapTexel=texture2D(lightMap,vLightMapUv);reflectedLight.indirectDiffuse+=lightMapTexel.rgb*lightMapIntensity*RECIPROCAL_PI;\n#else\nreflectedLight.indirectDiffuse+=vec3(1.0);\n#endif\n#include <aomap_fragment>\nreflectedLight.indirectDiffuse*=diffuseColor.rgb;vec3 outgoingLight=reflectedLight.indirectDiffuse;\n#include <envmap_fragment>\n#include <opaque_fragment>\n#include <tonemapping_fragment>\n#include <colorspace_fragment>\n#include <fog_fragment>\n#include <premultiplied_alpha_fragment>\n#include <dithering_fragment>\n#ifdef USE_COLOR\ngl_FragColor.a=vColor.r;\n#endif\n}";
14956
+ const GAUSSIAN_FORMATS = /* @__PURE__ */ new Set(["ply", "sog", "spz", "rad"]);
14957
+ function requireNonEmptyString(value, field) {
14958
+ if (typeof value !== "string" || value.trim().length === 0) {
14959
+ throw new Error(`${field} must be a non-empty string.`);
14960
+ }
14961
+ }
14962
+ function validateGaussianSplatLayerDefinition(definition) {
14963
+ var _a3, _b2, _c, _d;
14964
+ if (!definition || typeof definition !== "object") {
14965
+ throw new Error("Gaussian splat layer definition must be an object.");
14966
+ }
14967
+ requireNonEmptyString(definition.id, "id");
14968
+ requireNonEmptyString(definition.name, "name");
14969
+ if (definition.type !== "gaussian-splat") {
14970
+ throw new Error('type must be "gaussian-splat".');
14971
+ }
14972
+ if (!Array.isArray(definition.labels) || !definition.labels.every((label) => typeof label === "string")) {
14973
+ throw new Error("labels must be an array of strings.");
14974
+ }
14975
+ requireNonEmptyString((_a3 = definition.data) == null ? void 0 : _a3.url, "data.url");
14976
+ if (!GAUSSIAN_FORMATS.has((_b2 = definition.data) == null ? void 0 : _b2.format)) {
14977
+ throw new Error('data.format must be "ply", "sog", "spz", or "rad".');
14978
+ }
14979
+ const renderOrder = ((_c = definition.options) == null ? void 0 : _c.renderOrder) ?? 0;
14980
+ if (renderOrder !== 0) {
14981
+ throw new Error("Gaussian splat layers only support renderOrder 0.");
14982
+ }
14983
+ const transform = (_d = definition.options) == null ? void 0 : _d.transform;
14984
+ const anchor = transform == null ? void 0 : transform.anchor;
14985
+ if (!anchor) {
14986
+ throw new Error("options.transform.anchor must contain finite longitude, latitude, and altitude values.");
14987
+ }
14988
+ validateGeodeticPosition(anchor.longitude, anchor.latitude, anchor.altitude, "Gaussian anchor");
14989
+ const translationENU = transform.translationENU ?? [0, 0, 0];
14990
+ const rotationDegrees = transform.rotationDegrees ?? [0, 0, 0];
14991
+ const scale = transform.scale ?? 1;
14992
+ validateTuple(translationENU, "translationENU");
14993
+ validateTuple(rotationDegrees, "rotationDegrees");
14994
+ validateScale(scale, "scale");
14995
+ return {
14996
+ ...definition,
14997
+ labels: [...definition.labels],
14998
+ data: { ...definition.data },
14999
+ options: {
15000
+ ...definition.options,
15001
+ renderOrder: 0,
15002
+ transform: {
15003
+ anchor: { ...anchor },
15004
+ translationENU: [...translationENU],
15005
+ rotationDegrees: [...rotationDegrees],
15006
+ scale
15007
+ }
15008
+ }
15009
+ };
15010
+ }
15011
+ function makeGaussianSplatTransform(transform) {
15012
+ const { anchor, translationENU, rotationDegrees, scale } = transform;
15013
+ const basis = getEnuBasis(anchor.longitude, anchor.latitude);
15014
+ const east = new Vector3().fromArray(basis.east);
15015
+ const north = new Vector3().fromArray(basis.north);
15016
+ const up = new Vector3().fromArray(basis.up);
15017
+ const enuFrame = new Matrix4().makeBasis(east, north, up);
15018
+ enuFrame.setPosition(new Vector3().fromArray(geodeticToEcefPosition(anchor.longitude, anchor.latitude, anchor.altitude)));
15019
+ const rotation = new Quaternion().setFromEuler(new Euler(rotationDegrees[0] * Math.PI / 180, rotationDegrees[1] * Math.PI / 180, rotationDegrees[2] * Math.PI / 180, "XYZ"));
15020
+ const localTransform = new Matrix4().compose(new Vector3().fromArray(translationENU), rotation, new Vector3(scale, scale, scale));
15021
+ return enuFrame.multiply(localTransform);
15022
+ }
15023
+ function normalizeLocalMeshTransform(transform) {
15024
+ if (!transform || typeof transform !== "object") {
15025
+ throw new Error("Mesh transform must be an object.");
15026
+ }
15027
+ const anchor = transform.anchor;
15028
+ if (!anchor) {
15029
+ throw new Error("Mesh transform anchor is required.");
15030
+ }
15031
+ validateGeodeticPosition(anchor.longitude, anchor.latitude, anchor.altitude, "Mesh transform anchor");
15032
+ const translationENU = transform.translationENU ?? [0, 0, 0];
15033
+ const rotationDegrees = transform.rotationDegrees ?? [90, 0, 0];
15034
+ const scale = transform.scale ?? 1;
15035
+ validateTuple(translationENU, "Mesh translationENU");
15036
+ validateTuple(rotationDegrees, "Mesh rotationDegrees");
15037
+ validateScale(scale, "Mesh scale");
15038
+ return { anchor: { ...anchor }, translationENU: [...translationENU], rotationDegrees: [...rotationDegrees], scale };
15039
+ }
14728
15040
  class MeshLayer extends ContentLayer {
14729
15041
  constructor(url, options = {}, callbacks = {}) {
14730
15042
  super();
14731
15043
  this.type = "MeshLayer";
14732
15044
  this.isMeshLayer = true;
14733
15045
  const renderOrder = (options == null ? void 0 : options.renderOrder) ?? 0;
14734
- const offset = normalizeLayerOffset(options.offset, "Mesh offset");
15046
+ if (options.transform !== void 0 && (options.offset !== void 0 || options.offsetOrigin !== void 0)) {
15047
+ throw new Error("Mesh transform and legacy offset/offsetOrigin are mutually exclusive.");
15048
+ }
15049
+ const transform = options.transform === void 0 ? null : normalizeLocalMeshTransform(options.transform);
15050
+ const renderLayer = transform ? 0 : 6;
15051
+ this.xrRenderLayer = renderLayer;
15052
+ const offset = transform ? { lon: 0, lat: 0, alt: 0 } : normalizeLayerOffset(options.offset, "Mesh offset");
14735
15053
  const hasOffset = offset.lon !== 0 || offset.lat !== 0 || offset.alt !== 0;
15054
+ if (transform) {
15055
+ this.matrix.copy(makeGaussianSplatTransform(transform));
15056
+ this.matrix.decompose(this.position, this.quaternion, this.scale);
15057
+ }
15058
+ this.abortController = new AbortController();
15059
+ this.disposed = false;
14736
15060
  const loader = new GLTFLoader();
14737
- loader.load(
14738
- url,
14739
- (gltf) => {
14740
- var _a3;
14741
- const root = gltf.scene;
14742
- root.traverse((child) => {
14743
- child.layers.set(6);
15061
+ loader.setMeshoptDecoder(MeshoptDecoder);
15062
+ if (options.decoders) {
15063
+ loader.setDRACOLoader(options.decoders.getDraco());
15064
+ loader.setKTX2Loader(options.decoders.getKTX2());
15065
+ }
15066
+ const reportError = (error) => {
15067
+ var _a3;
15068
+ if (!this.disposed && (error == null ? void 0 : error.name) !== "AbortError") {
15069
+ (_a3 = callbacks.onError) == null ? void 0 : _a3.call(callbacks, error instanceof Error ? error : new Error(String(error)));
15070
+ }
15071
+ };
15072
+ const onLoad = (gltf) => {
15073
+ var _a3;
15074
+ const root = gltf.scene;
15075
+ if (this.disposed) {
15076
+ this.disposeObjectResources(root);
15077
+ return;
15078
+ }
15079
+ root.traverse((child) => {
15080
+ child.layers.set(renderLayer);
15081
+ if (!transform) {
14744
15082
  const materials = Array.isArray(child.material) ? child.material : child.material ? [child.material] : [];
14745
15083
  for (const material of materials) {
14746
15084
  material.transparent = true;
@@ -14748,28 +15086,32 @@ class MeshLayer extends ContentLayer {
14748
15086
  program.fragmentShader = patch_default;
14749
15087
  };
14750
15088
  }
14751
- });
14752
- this.mesh = root;
14753
- this.add(root);
14754
- if (hasOffset) {
14755
- try {
14756
- const origin = options.offsetOrigin;
14757
- const position = validateGeodeticPosition(origin == null ? void 0 : origin.longitude, origin == null ? void 0 : origin.latitude, origin == null ? void 0 : origin.altitude, "Mesh offset origin");
14758
- this.position.fromArray(geodeticOffsetToEcefDelta({ lon: position.longitude, lat: position.latitude, alt: position.altitude }, offset));
14759
- } catch (error) {
14760
- (_a3 = callbacks.onError) == null ? void 0 : _a3.call(callbacks, error instanceof Error ? error : new Error(String(error)));
14761
- }
14762
15089
  }
14763
- },
14764
- void 0,
14765
- (error) => {
14766
- var _a3;
14767
- return (_a3 = callbacks.onError) == null ? void 0 : _a3.call(callbacks, error instanceof Error ? error : new Error(String(error)));
15090
+ });
15091
+ this.mesh = root;
15092
+ this.add(root);
15093
+ if (hasOffset) {
15094
+ try {
15095
+ const origin = options.offsetOrigin;
15096
+ const position = validateGeodeticPosition(origin == null ? void 0 : origin.longitude, origin == null ? void 0 : origin.latitude, origin == null ? void 0 : origin.altitude, "Mesh offset origin");
15097
+ this.position.fromArray(geodeticOffsetToEcefDelta({ lon: position.longitude, lat: position.latitude, alt: position.altitude }, offset));
15098
+ } catch (error) {
15099
+ (_a3 = callbacks.onError) == null ? void 0 : _a3.call(callbacks, error instanceof Error ? error : new Error(String(error)));
15100
+ }
14768
15101
  }
14769
- );
14770
- this.layers.set(renderOrder);
15102
+ };
15103
+ void fetch(url, { signal: this.abortController.signal }).then(async (response) => {
15104
+ if (!response.ok) throw new Error(`Failed to load mesh: ${response.status} ${response.statusText}`);
15105
+ const bytes = await response.arrayBuffer();
15106
+ if (this.disposed) return;
15107
+ loader.parse(bytes, url.slice(0, url.lastIndexOf("/") + 1), onLoad, reportError);
15108
+ }).catch(reportError);
15109
+ this.layers.set(transform ? 0 : renderOrder);
14771
15110
  }
14772
15111
  dispose() {
15112
+ if (this.disposed) return;
15113
+ this.disposed = true;
15114
+ this.abortController.abort();
14773
15115
  super.dispose();
14774
15116
  if (this.mesh) {
14775
15117
  this.disposeObjectResources(this.mesh);
@@ -14778,19 +15120,24 @@ class MeshLayer extends ContentLayer {
14778
15120
  }
14779
15121
  }
14780
15122
  }
15123
+ function abortError() {
15124
+ return new DOMException("Worker job canceled", "AbortError");
15125
+ }
14781
15126
  class WorkerPool2 {
14782
15127
  constructor(limit = 4) {
14783
15128
  this.limit = limit;
14784
15129
  this.queue = [];
14785
15130
  this.workers = [];
14786
- this.workersResolve = [];
15131
+ this.active = [];
14787
15132
  this.workerStatus = 0;
14788
15133
  this.workerCreator = null;
15134
+ this.disposed = false;
14789
15135
  }
14790
15136
  initWorker(workerId) {
14791
15137
  if (!this.workers[workerId]) {
14792
15138
  const worker = this.workerCreator();
14793
- worker.addEventListener("message", this.onMessage.bind(this, workerId));
15139
+ worker.addEventListener("message", (event) => this.onMessage(workerId, worker, event));
15140
+ worker.addEventListener("error", (event) => this.onError(workerId, worker, event));
14794
15141
  this.workers[workerId] = worker;
14795
15142
  }
14796
15143
  }
@@ -14798,15 +15145,63 @@ class WorkerPool2 {
14798
15145
  for (let i2 = 0; i2 < this.limit; i2++) if (!(this.workerStatus & 1 << i2)) return i2;
14799
15146
  return -1;
14800
15147
  }
14801
- onMessage(workerId, msg) {
14802
- const resolve = this.workersResolve[workerId];
14803
- resolve && resolve(msg);
14804
- if (this.queue.length) {
14805
- const { resolve: resolve2, msg: msg2, transfer } = this.queue.shift();
14806
- this.workersResolve[workerId] = resolve2;
14807
- this.workers[workerId].postMessage(msg2, transfer);
14808
- } else {
14809
- this.workerStatus ^= 1 << workerId;
15148
+ finishJob(workerId, job) {
15149
+ var _a3;
15150
+ if (this.active[workerId] !== job) return false;
15151
+ (_a3 = job.signal) == null ? void 0 : _a3.removeEventListener("abort", job.onAbort);
15152
+ this.active[workerId] = null;
15153
+ this.workerStatus &= ~(1 << workerId);
15154
+ return true;
15155
+ }
15156
+ onMessage(workerId, worker, event) {
15157
+ if (this.workers[workerId] !== worker) return;
15158
+ const job = this.active[workerId];
15159
+ if (!job || !this.finishJob(workerId, job)) return;
15160
+ job.resolve(event);
15161
+ this.drain();
15162
+ }
15163
+ onError(workerId, worker, event) {
15164
+ if (this.workers[workerId] !== worker) return;
15165
+ const job = this.active[workerId];
15166
+ if (!job || !this.finishJob(workerId, job)) return;
15167
+ worker.terminate();
15168
+ this.workers[workerId] = null;
15169
+ job.reject((event == null ? void 0 : event.error) ?? new Error("Worker job failed"));
15170
+ this.drain();
15171
+ }
15172
+ cancelJob(job) {
15173
+ var _a3, _b2;
15174
+ if (job.workerId === null) {
15175
+ const index = this.queue.indexOf(job);
15176
+ if (index < 0) return;
15177
+ this.queue.splice(index, 1);
15178
+ (_a3 = job.signal) == null ? void 0 : _a3.removeEventListener("abort", job.onAbort);
15179
+ job.reject(abortError());
15180
+ return;
15181
+ }
15182
+ const workerId = job.workerId;
15183
+ if (!this.finishJob(workerId, job)) return;
15184
+ (_b2 = this.workers[workerId]) == null ? void 0 : _b2.terminate();
15185
+ this.workers[workerId] = null;
15186
+ job.reject(abortError());
15187
+ this.drain();
15188
+ }
15189
+ startJob(workerId, job) {
15190
+ this.initWorker(workerId);
15191
+ job.workerId = workerId;
15192
+ this.active[workerId] = job;
15193
+ this.workerStatus |= 1 << workerId;
15194
+ try {
15195
+ this.workers[workerId].postMessage(job.msg, job.transfer);
15196
+ } catch (error) {
15197
+ this.onError(workerId, this.workers[workerId], { error });
15198
+ }
15199
+ }
15200
+ drain() {
15201
+ while (this.queue.length) {
15202
+ const workerId = this.getIdleWorker();
15203
+ if (workerId < 0) return;
15204
+ this.startJob(workerId, this.queue.shift());
14810
15205
  }
14811
15206
  }
14812
15207
  setWorkerCreator(workerCreator) {
@@ -14814,25 +15209,35 @@ class WorkerPool2 {
14814
15209
  }
14815
15210
  setWorkerLimit(limit) {
14816
15211
  this.limit = limit;
15212
+ this.drain();
14817
15213
  }
14818
- postMessage(msg, transfer) {
14819
- return new Promise((resolve) => {
14820
- const workerId = this.getIdleWorker();
14821
- if (workerId !== -1) {
14822
- this.initWorker(workerId);
14823
- this.workerStatus |= 1 << workerId;
14824
- this.workersResolve[workerId] = resolve;
14825
- this.workers[workerId].postMessage(msg, transfer);
14826
- } else {
14827
- this.queue.push({ resolve, msg, transfer });
14828
- }
15214
+ postMessage(msg, transfer, { signal } = {}) {
15215
+ if (this.disposed || (signal == null ? void 0 : signal.aborted)) return Promise.reject(abortError());
15216
+ return new Promise((resolve, reject) => {
15217
+ const job = { msg, transfer, signal, resolve, reject, workerId: null, onAbort: null };
15218
+ job.onAbort = () => this.cancelJob(job);
15219
+ signal == null ? void 0 : signal.addEventListener("abort", job.onAbort, { once: true });
15220
+ this.queue.push(job);
15221
+ this.drain();
14829
15222
  });
14830
15223
  }
14831
15224
  dispose() {
14832
- this.workers.forEach((worker) => worker.terminate());
14833
- this.workersResolve.length = 0;
15225
+ var _a3;
15226
+ if (this.disposed) return;
15227
+ this.disposed = true;
15228
+ for (const job of this.queue.splice(0)) {
15229
+ (_a3 = job.signal) == null ? void 0 : _a3.removeEventListener("abort", job.onAbort);
15230
+ job.reject(abortError());
15231
+ }
15232
+ for (let i2 = 0; i2 < this.active.length; i2++) {
15233
+ const job = this.active[i2];
15234
+ if (job) {
15235
+ this.finishJob(i2, job);
15236
+ job.reject(abortError());
15237
+ }
15238
+ }
15239
+ this.workers.forEach((worker) => worker == null ? void 0 : worker.terminate());
14834
15240
  this.workers.length = 0;
14835
- this.queue.length = 0;
14836
15241
  this.workerStatus = 0;
14837
15242
  }
14838
15243
  }
@@ -14858,7 +15263,33 @@ function intersectsTimeFrame(timeFrameStart = /* @__PURE__ */ new Date(-864e13),
14858
15263
  }
14859
15264
  return true;
14860
15265
  }
14861
- class ArrowFeature extends Object3D {
15266
+ class WorkerFeature extends Object3D {
15267
+ constructor() {
15268
+ super();
15269
+ this.workerAbortController = new AbortController();
15270
+ this.disposed = false;
15271
+ }
15272
+ runWorker(message, callbacks, onSuccess) {
15273
+ geoWorkerPool.postMessage(message, void 0, { signal: this.workerAbortController.signal }).then((response) => {
15274
+ var _a3;
15275
+ if (this.disposed) return;
15276
+ const { success, error } = response.data;
15277
+ if (success) onSuccess(response.data);
15278
+ else (_a3 = callbacks.onError) == null ? void 0 : _a3.call(callbacks, new Error((error == null ? void 0 : error.message) ?? String(error)));
15279
+ }).catch((error) => {
15280
+ var _a3;
15281
+ if (!this.disposed && (error == null ? void 0 : error.name) !== "AbortError") {
15282
+ (_a3 = callbacks.onError) == null ? void 0 : _a3.call(callbacks, error instanceof Error ? error : new Error(String(error)));
15283
+ }
15284
+ });
15285
+ }
15286
+ dispose() {
15287
+ if (this.disposed) return;
15288
+ this.disposed = true;
15289
+ this.workerAbortController.abort();
15290
+ }
15291
+ }
15292
+ class ArrowFeature extends WorkerFeature {
14862
15293
  constructor(data, options = {}, callbacks = {}) {
14863
15294
  var _a3, _b2, _c;
14864
15295
  super();
@@ -14869,28 +15300,22 @@ class ArrowFeature extends Object3D {
14869
15300
  const type = "arrow";
14870
15301
  const renderOrder = (options == null ? void 0 : options.renderOrder) ?? 0;
14871
15302
  const color = ((_c = data == null ? void 0 : data.properties) == null ? void 0 : _c.color) ?? 15955057;
14872
- geoWorkerPool.postMessage({ type, data, color }).then((response) => {
14873
- var _a4;
14874
- const { success, error, origin, meshVertexBuffer } = response.data;
14875
- if (success) {
14876
- this.position.fromArray(origin);
14877
- const meshGeometry = new BufferGeometry();
14878
- meshGeometry.setAttribute("position", new BufferAttribute(meshVertexBuffer, 3));
14879
- const meshMaterial = new MeshBasicMaterial({ color, side: DoubleSide, depthTest: false, opacity: 0.75, transparent: true, toneMapped: false });
14880
- meshMaterial.onBeforeCompile = (program) => {
14881
- program.vertexShader = annotation_default$1;
14882
- program.fragmentShader = annotation_default;
14883
- };
14884
- const mesh = new Mesh(meshGeometry, meshMaterial);
14885
- mesh.layers.set(renderOrder);
14886
- this.add(mesh);
14887
- } else {
14888
- (_a4 = callbacks.onError) == null ? void 0 : _a4.call(callbacks, new Error((error == null ? void 0 : error.message) ?? String(error)));
14889
- }
15303
+ this.runWorker({ type, data, color }, callbacks, ({ origin, meshVertexBuffer }) => {
15304
+ this.position.fromArray(origin);
15305
+ const meshGeometry = new BufferGeometry();
15306
+ meshGeometry.setAttribute("position", new BufferAttribute(meshVertexBuffer, 3));
15307
+ const meshMaterial = new MeshBasicMaterial({ color, side: DoubleSide, depthTest: false, opacity: 0.75, transparent: true, toneMapped: false });
15308
+ meshMaterial.onBeforeCompile = (program) => {
15309
+ program.vertexShader = annotation_default$1;
15310
+ program.fragmentShader = annotation_default;
15311
+ };
15312
+ const mesh = new Mesh(meshGeometry, meshMaterial);
15313
+ mesh.layers.set(renderOrder);
15314
+ this.add(mesh);
14890
15315
  });
14891
15316
  }
14892
15317
  }
14893
- class PerimeterFeature extends Object3D {
15318
+ class PerimeterFeature extends WorkerFeature {
14894
15319
  constructor(data, options = {}, callbacks = {}) {
14895
15320
  var _a3, _b2, _c;
14896
15321
  super();
@@ -14901,32 +15326,26 @@ class PerimeterFeature extends Object3D {
14901
15326
  const type = "perimeter";
14902
15327
  const renderOrder = (options == null ? void 0 : options.renderOrder) ?? 0;
14903
15328
  const color = ((_c = data == null ? void 0 : data.properties) == null ? void 0 : _c.color) ?? 15955057;
14904
- geoWorkerPool.postMessage({ type, data, color }).then((response) => {
14905
- var _a4;
14906
- const { success, error, origin, lineVertexBuffer, lineVertexRGBBuffer, meshVertexBuffer, meshVertexRGBBuffer, meshIndexBuffer } = response.data;
14907
- if (success) {
14908
- this.position.fromArray(origin);
14909
- const meshGeometry = new BufferGeometry();
14910
- meshGeometry.setAttribute("position", new BufferAttribute(meshVertexBuffer, 3));
14911
- meshGeometry.setAttribute("color", new BufferAttribute(meshVertexRGBBuffer, 3, true));
14912
- meshGeometry.setIndex(new BufferAttribute(meshIndexBuffer, 1));
14913
- const meshMaterial = new MeshBasicMaterial({ opacity: 0.2, transparent: true, side: DoubleSide, depthTest: false, vertexColors: true, toneMapped: false });
14914
- meshMaterial.onBeforeCompile = (program) => {
14915
- program.vertexShader = annotation_default$1;
14916
- program.fragmentShader = annotation_default;
14917
- };
14918
- const mesh = new Mesh(meshGeometry, meshMaterial);
14919
- mesh.layers.set(renderOrder);
14920
- const lineGeometry = new BufferGeometry();
14921
- lineGeometry.setAttribute("position", new BufferAttribute(lineVertexBuffer, 3));
14922
- lineGeometry.setAttribute("color", new BufferAttribute(lineVertexRGBBuffer, 3, true));
14923
- const line = new LineSegments(lineGeometry, new LineBasicMaterial({ vertexColors: true, toneMapped: false }));
14924
- line.layers.set(renderOrder);
14925
- line.add(mesh);
14926
- this.add(line);
14927
- } else {
14928
- (_a4 = callbacks.onError) == null ? void 0 : _a4.call(callbacks, new Error((error == null ? void 0 : error.message) ?? String(error)));
14929
- }
15329
+ this.runWorker({ type, data, color }, callbacks, ({ origin, lineVertexBuffer, lineVertexRGBBuffer, meshVertexBuffer, meshVertexRGBBuffer, meshIndexBuffer }) => {
15330
+ this.position.fromArray(origin);
15331
+ const meshGeometry = new BufferGeometry();
15332
+ meshGeometry.setAttribute("position", new BufferAttribute(meshVertexBuffer, 3));
15333
+ meshGeometry.setAttribute("color", new BufferAttribute(meshVertexRGBBuffer, 3, true));
15334
+ meshGeometry.setIndex(new BufferAttribute(meshIndexBuffer, 1));
15335
+ const meshMaterial = new MeshBasicMaterial({ opacity: 0.2, transparent: true, side: DoubleSide, depthTest: false, vertexColors: true, toneMapped: false });
15336
+ meshMaterial.onBeforeCompile = (program) => {
15337
+ program.vertexShader = annotation_default$1;
15338
+ program.fragmentShader = annotation_default;
15339
+ };
15340
+ const mesh = new Mesh(meshGeometry, meshMaterial);
15341
+ mesh.layers.set(renderOrder);
15342
+ const lineGeometry = new BufferGeometry();
15343
+ lineGeometry.setAttribute("position", new BufferAttribute(lineVertexBuffer, 3));
15344
+ lineGeometry.setAttribute("color", new BufferAttribute(lineVertexRGBBuffer, 3, true));
15345
+ const line = new LineSegments(lineGeometry, new LineBasicMaterial({ vertexColors: true, toneMapped: false }));
15346
+ line.layers.set(renderOrder);
15347
+ line.add(mesh);
15348
+ this.add(line);
14930
15349
  });
14931
15350
  }
14932
15351
  }
@@ -14936,6 +15355,7 @@ class GeojsonLayer extends ContentLayer {
14936
15355
  super();
14937
15356
  this.type = "GeojsonLayer";
14938
15357
  this.isGeojsonLayer = true;
15358
+ this.disposed = false;
14939
15359
  for (let i2 = 0; i2 < ((_a3 = data.features) == null ? void 0 : _a3.length); i2++) {
14940
15360
  const feature = data.features[i2];
14941
15361
  switch (feature.properties.type) {
@@ -14965,78 +15385,13 @@ class GeojsonLayer extends ContentLayer {
14965
15385
  }
14966
15386
  }
14967
15387
  dispose() {
15388
+ if (this.disposed) return;
15389
+ this.disposed = true;
14968
15390
  super.dispose();
14969
15391
  this.disposeObjectResources();
14970
15392
  this.clear();
14971
15393
  }
14972
15394
  }
14973
- const GAUSSIAN_FORMATS = /* @__PURE__ */ new Set(["ply", "sog", "spz", "rad"]);
14974
- function requireNonEmptyString(value, field) {
14975
- if (typeof value !== "string" || value.trim().length === 0) {
14976
- throw new Error(`${field} must be a non-empty string.`);
14977
- }
14978
- }
14979
- function validateGaussianSplatLayerDefinition(definition) {
14980
- var _a3, _b2, _c, _d;
14981
- if (!definition || typeof definition !== "object") {
14982
- throw new Error("Gaussian splat layer definition must be an object.");
14983
- }
14984
- requireNonEmptyString(definition.id, "id");
14985
- requireNonEmptyString(definition.name, "name");
14986
- if (definition.type !== "gaussian-splat") {
14987
- throw new Error('type must be "gaussian-splat".');
14988
- }
14989
- if (!Array.isArray(definition.labels) || !definition.labels.every((label) => typeof label === "string")) {
14990
- throw new Error("labels must be an array of strings.");
14991
- }
14992
- requireNonEmptyString((_a3 = definition.data) == null ? void 0 : _a3.url, "data.url");
14993
- if (!GAUSSIAN_FORMATS.has((_b2 = definition.data) == null ? void 0 : _b2.format)) {
14994
- throw new Error('data.format must be "ply", "sog", "spz", or "rad".');
14995
- }
14996
- const renderOrder = ((_c = definition.options) == null ? void 0 : _c.renderOrder) ?? 0;
14997
- if (renderOrder !== 0) {
14998
- throw new Error("Gaussian splat layers only support renderOrder 0.");
14999
- }
15000
- const transform = (_d = definition.options) == null ? void 0 : _d.transform;
15001
- const anchor = transform == null ? void 0 : transform.anchor;
15002
- if (!anchor) {
15003
- throw new Error("options.transform.anchor must contain finite longitude, latitude, and altitude values.");
15004
- }
15005
- validateGeodeticPosition(anchor.longitude, anchor.latitude, anchor.altitude, "Gaussian anchor");
15006
- const translationENU = transform.translationENU ?? [0, 0, 0];
15007
- const rotationDegrees = transform.rotationDegrees ?? [0, 0, 0];
15008
- const scale = transform.scale ?? 1;
15009
- validateTuple(translationENU, "translationENU");
15010
- validateTuple(rotationDegrees, "rotationDegrees");
15011
- validateScale(scale, "scale");
15012
- return {
15013
- ...definition,
15014
- labels: [...definition.labels],
15015
- data: { ...definition.data },
15016
- options: {
15017
- ...definition.options,
15018
- renderOrder: 0,
15019
- transform: {
15020
- anchor: { ...anchor },
15021
- translationENU: [...translationENU],
15022
- rotationDegrees: [...rotationDegrees],
15023
- scale
15024
- }
15025
- }
15026
- };
15027
- }
15028
- function makeGaussianSplatTransform(transform) {
15029
- const { anchor, translationENU, rotationDegrees, scale } = transform;
15030
- const basis = getEnuBasis(anchor.longitude, anchor.latitude);
15031
- const east = new Vector3().fromArray(basis.east);
15032
- const north = new Vector3().fromArray(basis.north);
15033
- const up = new Vector3().fromArray(basis.up);
15034
- const enuFrame = new Matrix4().makeBasis(east, north, up);
15035
- enuFrame.setPosition(new Vector3().fromArray(geodeticToEcefPosition(anchor.longitude, anchor.latitude, anchor.altitude)));
15036
- const rotation = new Quaternion().setFromEuler(new Euler(rotationDegrees[0] * Math.PI / 180, rotationDegrees[1] * Math.PI / 180, rotationDegrees[2] * Math.PI / 180, "XYZ"));
15037
- const localTransform = new Matrix4().compose(new Vector3().fromArray(translationENU), rotation, new Vector3(scale, scale, scale));
15038
- return enuFrame.multiply(localTransform);
15039
- }
15040
15395
  function formatType(spark, format) {
15041
15396
  return { ply: spark.SplatFileType.PLY, sog: spark.SplatFileType.PCSOGSZIP, spz: spark.SplatFileType.SPZ, rad: spark.SplatFileType.RAD }[format];
15042
15397
  }
@@ -15062,6 +15417,10 @@ class GaussianSplatLayer extends ContentLayer {
15062
15417
  }
15063
15418
  setEnabled(enabled) {
15064
15419
  if (this.loadState === "disposed") return;
15420
+ if (!enabled) {
15421
+ this.dispose();
15422
+ return;
15423
+ }
15065
15424
  const wasEnabled = this.visible;
15066
15425
  this.visible = enabled;
15067
15426
  if (enabled && !wasEnabled && (this.loadState === "idle" || this.loadState === "error")) {
@@ -15149,9 +15508,11 @@ class GaussianSplatLayer extends ContentLayer {
15149
15508
  });
15150
15509
  }
15151
15510
  disposeMesh(mesh) {
15511
+ var _a3, _b2;
15152
15512
  if (!mesh || mesh.userData.gaussianSplatDisposed) return;
15153
15513
  mesh.userData.gaussianSplatDisposed = true;
15154
15514
  mesh.removeFromParent();
15515
+ if (mesh.paged) (_b2 = (_a3 = this.callbacks).releasePagedSplats) == null ? void 0 : _b2.call(_a3, mesh.paged);
15155
15516
  mesh.dispose();
15156
15517
  if (this.splatMesh === mesh) this.splatMesh = null;
15157
15518
  }
@@ -15167,6 +15528,197 @@ class GaussianSplatLayer extends ContentLayer {
15167
15528
  super.dispose();
15168
15529
  }
15169
15530
  }
15531
+ class ContentLayerRegistry {
15532
+ /** @param {{ group?: import('three').Group, createLayer?: (definition: any) => any, onError?: (definition: any, error: Error) => void, onAllSplatsDisabled?: () => void }} [options] */
15533
+ constructor({ group, createLayer, onError, onAllSplatsDisabled } = {}) {
15534
+ this.group = group;
15535
+ this.createLayer = createLayer;
15536
+ this.onError = onError;
15537
+ this.onAllSplatsDisabled = onAllSplatsDisabled;
15538
+ this.definitions = [];
15539
+ this.enabledLabels = /* @__PURE__ */ new Set();
15540
+ this.active = /* @__PURE__ */ new Map();
15541
+ this.timeFrameStart = null;
15542
+ this.timeFrameEnd = null;
15543
+ this.disposed = false;
15544
+ }
15545
+ setDefinitions(definitions = []) {
15546
+ if (this.disposed) return;
15547
+ this.clear();
15548
+ this.definitions = [...definitions];
15549
+ this.reconcile();
15550
+ }
15551
+ setEnabledLabels(labels = []) {
15552
+ if (this.disposed) return;
15553
+ this.enabledLabels = new Set(labels);
15554
+ this.reconcile();
15555
+ }
15556
+ setTimeFrame(start, end) {
15557
+ var _a3;
15558
+ if (this.disposed) return;
15559
+ this.timeFrameStart = start;
15560
+ this.timeFrameEnd = end;
15561
+ for (const layer of this.active.values()) (_a3 = layer.setTimeFrame) == null ? void 0 : _a3.call(layer, start, end);
15562
+ }
15563
+ getActiveLayer(id) {
15564
+ return this.active.get(id) ?? null;
15565
+ }
15566
+ createActiveLayer(definition) {
15567
+ var _a3, _b2, _c, _d, _e;
15568
+ const layer = this.createLayer(definition);
15569
+ try {
15570
+ layer.contentLayerId = definition.id;
15571
+ layer.setLabels(definition.labels);
15572
+ layer.contentRenderOrder = definition.type === "mesh" && ((_a3 = definition.options) == null ? void 0 : _a3.transform) ? 0 : ((_b2 = definition.options) == null ? void 0 : _b2.renderOrder) ?? 0;
15573
+ layer.xrRenderLayer = layer.xrRenderLayer ?? (definition.type === "mesh" && ((_c = definition.options) == null ? void 0 : _c.transform) ? 0 : definition.type === "mesh" ? 6 : layer.contentRenderOrder);
15574
+ (_d = layer.setTimeFrame) == null ? void 0 : _d.call(layer, this.timeFrameStart, this.timeFrameEnd);
15575
+ layer.setEnabled(true);
15576
+ return layer;
15577
+ } catch (error) {
15578
+ (_e = layer.dispose) == null ? void 0 : _e.call(layer);
15579
+ throw error;
15580
+ }
15581
+ }
15582
+ orderActiveLayers() {
15583
+ const order = new Map(this.definitions.map((definition, index) => [definition.id, index]));
15584
+ this.group.children.sort((a2, b2) => (order.get(a2.contentLayerId) ?? 0) - (order.get(b2.contentLayerId) ?? 0));
15585
+ }
15586
+ /** Replace one definition while preserving unrelated live layers. */
15587
+ replaceDefinition(id, definition) {
15588
+ var _a3, _b2, _c, _d;
15589
+ if (this.disposed) return false;
15590
+ const index = this.definitions.findIndex((item) => item.id === id);
15591
+ const wanted = ((_a3 = definition == null ? void 0 : definition.labels) == null ? void 0 : _a3.some((label) => this.enabledLabels.has(label))) ?? false;
15592
+ let incoming = null;
15593
+ if (wanted) {
15594
+ try {
15595
+ incoming = this.createActiveLayer(definition);
15596
+ } catch (error) {
15597
+ (_b2 = this.onError) == null ? void 0 : _b2.call(this, definition, error);
15598
+ return false;
15599
+ }
15600
+ }
15601
+ const outgoing = this.active.get(id);
15602
+ const hadSplats = [...this.active.values()].some((layer) => layer.isGaussianSplatLayer);
15603
+ if (outgoing) {
15604
+ (_c = outgoing.dispose) == null ? void 0 : _c.call(outgoing);
15605
+ this.group.remove(outgoing);
15606
+ this.active.delete(id);
15607
+ }
15608
+ if (definition) {
15609
+ if (index < 0) this.definitions.push(definition);
15610
+ else this.definitions[index] = definition;
15611
+ } else if (index >= 0) this.definitions.splice(index, 1);
15612
+ if (incoming) {
15613
+ this.group.add(incoming);
15614
+ this.active.set(id, incoming);
15615
+ }
15616
+ this.orderActiveLayers();
15617
+ if (hadSplats && ![...this.active.values()].some((layer) => layer.isGaussianSplatLayer)) (_d = this.onAllSplatsDisabled) == null ? void 0 : _d.call(this);
15618
+ return true;
15619
+ }
15620
+ reconcile() {
15621
+ var _a3, _b2, _c;
15622
+ const wanted = new Set(this.definitions.filter((definition) => {
15623
+ var _a4;
15624
+ return (_a4 = definition.labels) == null ? void 0 : _a4.some((label) => this.enabledLabels.has(label));
15625
+ }).map((definition) => definition.id));
15626
+ const hadSplats = [...this.active.values()].some((layer) => layer.isGaussianSplatLayer);
15627
+ for (const [id, layer] of this.active) {
15628
+ if (!wanted.has(id)) {
15629
+ (_a3 = layer.dispose) == null ? void 0 : _a3.call(layer);
15630
+ this.group.remove(layer);
15631
+ this.active.delete(id);
15632
+ }
15633
+ }
15634
+ for (const definition of this.definitions) {
15635
+ if (!wanted.has(definition.id) || this.active.has(definition.id)) continue;
15636
+ try {
15637
+ const layer = this.createActiveLayer(definition);
15638
+ this.group.add(layer);
15639
+ this.active.set(definition.id, layer);
15640
+ } catch (error) {
15641
+ (_b2 = this.onError) == null ? void 0 : _b2.call(this, definition, error);
15642
+ }
15643
+ }
15644
+ this.orderActiveLayers();
15645
+ if (hadSplats && ![...this.active.values()].some((layer) => layer.isGaussianSplatLayer)) (_c = this.onAllSplatsDisabled) == null ? void 0 : _c.call(this);
15646
+ }
15647
+ clear() {
15648
+ var _a3, _b2;
15649
+ for (const layer of this.active.values()) {
15650
+ (_a3 = layer.dispose) == null ? void 0 : _a3.call(layer);
15651
+ this.group.remove(layer);
15652
+ }
15653
+ const hadSplats = [...this.active.values()].some((layer) => layer.isGaussianSplatLayer);
15654
+ this.active.clear();
15655
+ this.definitions = [];
15656
+ if (hadSplats) (_b2 = this.onAllSplatsDisabled) == null ? void 0 : _b2.call(this);
15657
+ }
15658
+ dispose() {
15659
+ if (this.disposed) return;
15660
+ this.clear();
15661
+ this.disposed = true;
15662
+ }
15663
+ }
15664
+ class SplatDestinationPrefetch {
15665
+ constructor({ getRenderer, getLayer }) {
15666
+ this.getRenderer = getRenderer;
15667
+ this.getLayer = getLayer;
15668
+ this.target = null;
15669
+ this.boundRenderer = null;
15670
+ this.boundMeshes = [];
15671
+ this.timeoutId = null;
15672
+ this.disposed = false;
15673
+ }
15674
+ set(target) {
15675
+ if (this.disposed || !target || !Number.isFinite(target.timeoutMs) || target.timeoutMs <= 0 || target.timeoutMs > 3e4) return;
15676
+ this.clear("cancel");
15677
+ this.target = target;
15678
+ this.timeoutId = setTimeout(() => this.clear("timeout"), target.timeoutMs);
15679
+ this.sync();
15680
+ }
15681
+ sync() {
15682
+ var _a3;
15683
+ if (this.disposed || !this.target) return;
15684
+ const renderer = this.getRenderer();
15685
+ const meshes = [...new Set(this.target.layerIds)].map((id) => this.getLayer(id)).filter(
15686
+ (layer) => {
15687
+ var _a4, _b2, _c;
15688
+ return (layer == null ? void 0 : layer.isGaussianSplatLayer) && layer.visible && ((_b2 = (_a4 = layer.definition) == null ? void 0 : _a4.data) == null ? void 0 : _b2.format) === "rad" && ((_c = layer.splatMesh) == null ? void 0 : _c.paged) && layer.loadState !== "disposed";
15689
+ }
15690
+ ).map((layer) => layer.splatMesh);
15691
+ if (renderer === this.boundRenderer && meshes.length === this.boundMeshes.length && meshes.every((mesh, i2) => mesh === this.boundMeshes[i2])) return;
15692
+ (_a3 = this.boundRenderer) == null ? void 0 : _a3.clearDestinationHint(false);
15693
+ this.boundRenderer = null;
15694
+ this.boundMeshes = [];
15695
+ if (!renderer || meshes.length === 0) return;
15696
+ const target = this.target;
15697
+ renderer.setDestinationHint({
15698
+ position: new Vector3().fromArray(geodeticToEcefPosition(target.longitude, target.latitude, target.altitude)),
15699
+ quaternion: geodeticToEcefRotation(target.longitude, target.latitude, target.pitch, target.yaw, target.roll),
15700
+ meshes,
15701
+ maxChunks: target.mode === "xr" ? 1 : 4,
15702
+ maxSplats: 3e4
15703
+ });
15704
+ this.boundRenderer = renderer;
15705
+ this.boundMeshes = meshes;
15706
+ }
15707
+ clear(reason = "cancel") {
15708
+ var _a3;
15709
+ if (this.timeoutId !== null) clearTimeout(this.timeoutId);
15710
+ this.timeoutId = null;
15711
+ this.target = null;
15712
+ (_a3 = this.boundRenderer) == null ? void 0 : _a3.clearDestinationHint(reason === "arrival");
15713
+ this.boundRenderer = null;
15714
+ this.boundMeshes = [];
15715
+ }
15716
+ dispose() {
15717
+ if (this.disposed) return;
15718
+ this.clear("cancel");
15719
+ this.disposed = true;
15720
+ }
15721
+ }
15170
15722
  function applyKeyboardMovement(position, forward, right, up, state, deltaTime, movementSpeed, boostMultiplier) {
15171
15723
  const boost = state.BOOST ? boostMultiplier : 1;
15172
15724
  const distance = movementSpeed * deltaTime * boost;
@@ -15541,127 +16093,47 @@ class XRNavigationCue {
15541
16093
  this.scene.remove(this.group);
15542
16094
  }
15543
16095
  }
15544
- const cesiumLogoUrl = "data:image/png;base64,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";
15545
- const googleLogoUrl = "data:image/png;base64,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";
15546
- const HEAD_PANEL_OFFSET = new Matrix4().makeTranslation(-0.35, -0.55, -1.2);
15547
- function attributionText(tileAttributions = [], terrainCredits = []) {
15548
- const values = [
15549
- ...tileAttributions.filter(({ type, value }) => type === "string" && typeof value === "string").map(({ value }) => value),
15550
- ...terrainCredits.filter((value) => typeof value === "string")
15551
- ];
15552
- return [...new Set(values.map((value) => value.replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").trim()).filter(Boolean))];
15553
- }
15554
- function wrap(ctx, value, maxWidth) {
15555
- const lines = [];
15556
- let line = "";
15557
- const words = value.split(/\s+/).flatMap((word) => {
15558
- if (ctx.measureText(word).width <= maxWidth) return [word];
15559
- const pieces = [];
15560
- let piece = "";
15561
- for (const character of word) {
15562
- if (piece && ctx.measureText(piece + character).width > maxWidth) {
15563
- pieces.push(piece);
15564
- piece = "";
15565
- }
15566
- piece += character;
15567
- }
15568
- if (piece) pieces.push(piece);
15569
- return pieces;
15570
- });
15571
- for (const word of words) {
15572
- const candidate = line ? `${line} ${word}` : word;
15573
- if (line && ctx.measureText(candidate).width > maxWidth) {
15574
- lines.push(line);
15575
- line = word;
15576
- } else line = candidate;
15577
- }
15578
- if (line) lines.push(line);
15579
- return lines;
15580
- }
15581
- function layoutAttributionLines(ctx, values) {
15582
- const lines = values.flatMap((value) => wrap(ctx, value, 540));
15583
- const rows = Math.max(1, Math.ceil(lines.length / 2));
15584
- const lineHeight = Math.min(38, 230 / rows);
15585
- const fontSize = Math.min(32, lineHeight - 4);
15586
- return lines.map((text, index) => ({
15587
- text,
15588
- x: index < rows ? 30 : 630,
15589
- y: 120 + index % rows * lineHeight,
15590
- fontSize
15591
- }));
15592
- }
15593
- class XRAttributionPanel {
16096
+ const HEAD_OFFSET = new Matrix4().makeTranslation(0.35, -0.32, -0.9);
16097
+ class XRDiagnosticsPanel {
15594
16098
  constructor() {
15595
16099
  const canvas = document.createElement("canvas");
15596
- canvas.width = 1200;
15597
- canvas.height = 360;
15598
- this.canvas = canvas;
16100
+ canvas.width = 480;
16101
+ canvas.height = 220;
15599
16102
  this.context = canvas.getContext("2d");
15600
16103
  this.texture = new CanvasTexture(canvas);
15601
16104
  this.texture.colorSpace = SRGBColorSpace;
15602
- this.mesh = new Mesh(new PlaneGeometry(1.1, 0.33), new MeshBasicMaterial({ map: this.texture, transparent: true, depthTest: false, depthWrite: false, side: DoubleSide, toneMapped: false }));
15603
- this.mesh.name = "XRAttributionPanel";
16105
+ this.mesh = new Mesh(new PlaneGeometry(0.42, 0.1925), new MeshBasicMaterial({ map: this.texture, transparent: true, depthTest: false, depthWrite: false, side: DoubleSide, toneMapped: false }));
16106
+ this.mesh.name = "XRDiagnosticsPanel";
15604
16107
  this.mesh.matrixAutoUpdate = false;
15605
16108
  this.mesh.layers.set(31);
15606
- this.mesh.renderOrder = 10002;
15607
- this.terrainCredits = [];
15608
- this.lastText = "";
15609
- this.lastTileCheckMs = -Infinity;
15610
- this.tileCredits = [];
15611
- this.logos = [];
15612
- for (const url of [cesiumLogoUrl, googleLogoUrl]) {
15613
- const image = new Image();
15614
- image.onload = () => this.draw();
15615
- image.src = url;
15616
- this.logos.push(image);
15617
- }
15618
- this.draw();
15619
- }
15620
- setTerrainCredits(credits) {
15621
- this.terrainCredits = Array.isArray(credits) ? credits : [];
15622
- this.draw();
15623
- }
15624
- update(headMatrix, tiles, timeMs) {
15625
- var _a3;
15626
- this.mesh.matrix.copy(headMatrix).multiply(HEAD_PANEL_OFFSET);
15627
- this.mesh.matrixWorldNeedsUpdate = true;
15628
- if (timeMs - this.lastTileCheckMs >= 1e3) {
15629
- this.lastTileCheckMs = timeMs;
15630
- this.tileCredits = ((_a3 = tiles == null ? void 0 : tiles.getAttributions) == null ? void 0 : _a3.call(tiles)) ?? [];
15631
- this.draw();
15632
- }
16109
+ this.mesh.renderOrder = 10003;
16110
+ this.rows = [];
15633
16111
  }
15634
- draw() {
15635
- var _a3, _b2;
16112
+ setRows(rows) {
16113
+ this.rows = rows.slice(0, 5).map(String);
15636
16114
  const ctx = this.context;
15637
16115
  if (!ctx) return;
15638
- const lines = attributionText(this.tileCredits, this.terrainCredits);
15639
- const signature = lines.join("\n") + ":" + this.logos.map((logo) => logo.complete && logo.naturalWidth > 0).join(",");
15640
- if (signature === this.lastText) return;
15641
- this.lastText = signature;
15642
- ctx.clearRect(0, 0, 1200, 360);
15643
- ctx.fillStyle = "rgba(255,255,255,0.82)";
15644
- ctx.fillRect(0, 0, 1200, 360);
15645
- ctx.fillStyle = "#1b2430";
15646
- ctx.font = "bold 28px Arial, sans-serif";
15647
- if (((_a3 = this.logos[0]) == null ? void 0 : _a3.complete) && this.logos[0].naturalWidth > 0) ctx.drawImage(this.logos[0], 30, 22, 294, 50);
15648
- else ctx.fillText("Cesium ion", 30, 61);
15649
- if (((_b2 = this.logos[1]) == null ? void 0 : _b2.complete) && this.logos[1].naturalWidth > 0) ctx.drawImage(this.logos[1], 390, 28, 272, 50);
15650
- else ctx.fillText("Google Maps", 390, 61);
16116
+ ctx.clearRect(0, 0, 480, 220);
16117
+ ctx.fillStyle = "rgba(17,24,39,0.9)";
16118
+ ctx.beginPath();
16119
+ ctx.roundRect(0, 0, 480, 220, 24);
16120
+ ctx.fill();
16121
+ ctx.fillStyle = "#e5e7eb";
15651
16122
  ctx.font = "32px Arial, sans-serif";
15652
- for (const line of layoutAttributionLines(ctx, lines.length ? lines : ["Google Maps 3D Tiles"])) {
15653
- ctx.font = `${line.fontSize}px Arial, sans-serif`;
15654
- ctx.fillText(line.text, line.x, line.y);
15655
- }
16123
+ this.rows.forEach((row, index) => ctx.fillText(row, 24, 45 + index * 39, 432));
15656
16124
  this.texture.needsUpdate = true;
15657
16125
  }
16126
+ update(headMatrix) {
16127
+ this.mesh.matrix.copy(headMatrix).multiply(HEAD_OFFSET);
16128
+ this.mesh.matrixWorldNeedsUpdate = true;
16129
+ }
15658
16130
  dispose() {
15659
16131
  this.mesh.geometry.dispose();
15660
16132
  this.mesh.material.dispose();
15661
16133
  this.texture.dispose();
15662
16134
  }
15663
16135
  }
15664
- function createXRRenderBridge(viewer, initialHeadPose, { showAttribution = true } = {}) {
16136
+ function createXRRenderBridge(viewer, initialHeadPose, _options = {}) {
15665
16137
  const { camera, renderer } = viewer;
15666
16138
  if (camera.parent !== null) throw new Error("WebXR camera bridge requires an unparented viewer camera");
15667
16139
  camera.updateMatrixWorld(true);
@@ -15676,24 +16148,22 @@ function createXRRenderBridge(viewer, initialHeadPose, { showAttribution = true
15676
16148
  let currentHead = camera.matrix.clone().fromArray(initialHeadPose.matrix);
15677
16149
  let disposed = false;
15678
16150
  const comfort = new XRComfortOverlay();
15679
- const attribution = !showAttribution || typeof document === "undefined" || typeof Image === "undefined" ? null : new XRAttributionPanel();
16151
+ let diagnostics = null;
15680
16152
  const navigationCue = new XRNavigationCue(viewer.scene);
15681
16153
  anchor.add(comfort.mesh);
15682
- if (attribution) anchor.add(attribution.mesh);
15683
16154
  const restoreWorldPose = (world) => {
15684
16155
  if (camera.parent === anchor) anchor.remove(camera);
15685
16156
  world.decompose(camera.position, camera.quaternion, camera.scale);
15686
16157
  camera.updateMatrixWorld(true);
15687
16158
  };
15688
16159
  const renderBridge = (scene, renderCamera) => {
15689
- var _a3;
15690
16160
  if (disposed || renderCamera !== camera) return originalRender.call(renderer, scene, renderCamera);
15691
16161
  camera.updateMatrixWorld(true);
15692
16162
  const worldBeforeRender = camera.matrixWorld.clone();
15693
16163
  anchor.matrix.copy(rig.getVisualWorldFromLocalMatrix());
15694
16164
  anchor.updateMatrixWorld(true);
15695
16165
  comfort.update(currentHead);
15696
- attribution == null ? void 0 : attribution.update(currentHead, (_a3 = viewer.globeManager) == null ? void 0 : _a3.tiles, performance.now());
16166
+ diagnostics == null ? void 0 : diagnostics.update(currentHead);
15697
16167
  anchor.add(camera);
15698
16168
  currentHead.decompose(camera.position, camera.quaternion, camera.scale);
15699
16169
  camera.updateMatrixWorld(true);
@@ -15716,7 +16186,26 @@ function createXRRenderBridge(viewer, initialHeadPose, { showAttribution = true
15716
16186
  getHeadForward: () => rig.getVisualHeadForward(),
15717
16187
  getControllerWorldRay: (controller) => rig.getControllerWorldRay(controller),
15718
16188
  setNavigationCue: (cue) => navigationCue.set(cue),
15719
- setTerrainCredits: (credits) => attribution == null ? void 0 : attribution.setTerrainCredits(credits),
16189
+ setTerrainCredits: () => {
16190
+ },
16191
+ setDiagnosticsRows(rows) {
16192
+ if (disposed) return;
16193
+ if (!Array.isArray(rows) || rows.length === 0) {
16194
+ if (diagnostics) {
16195
+ anchor.remove(diagnostics.mesh);
16196
+ diagnostics.dispose();
16197
+ diagnostics = null;
16198
+ }
16199
+ return;
16200
+ }
16201
+ if (!diagnostics) {
16202
+ if (typeof document === "undefined") return;
16203
+ diagnostics = new XRDiagnosticsPanel();
16204
+ anchor.add(diagnostics.mesh);
16205
+ }
16206
+ diagnostics.setRows(rows);
16207
+ diagnostics.update(currentHead);
16208
+ },
15720
16209
  setPlayerPose(pose) {
15721
16210
  if (disposed) return;
15722
16211
  rig.setPose(pose);
@@ -15728,7 +16217,7 @@ function createXRRenderBridge(viewer, initialHeadPose, { showAttribution = true
15728
16217
  restoreWorldPose(rig.getVisualWorldHeadMatrix());
15729
16218
  },
15730
16219
  hasUi() {
15731
- return Boolean(attribution) || comfort.moving || comfort.navigationLimit !== null || comfort.fade > 0;
16220
+ return Boolean(diagnostics) || comfort.moving || comfort.navigationLimit !== null || comfort.fade > 0;
15732
16221
  },
15733
16222
  setNavigationLimit(reason) {
15734
16223
  if (comfort.setNavigationLimit(reason)) comfort.update(currentHead);
@@ -15756,9 +16245,10 @@ function createXRRenderBridge(viewer, initialHeadPose, { showAttribution = true
15756
16245
  if (camera.parent === anchor) restoreWorldPose(rig.getVisualWorldHeadMatrix());
15757
16246
  anchor.remove(comfort.mesh);
15758
16247
  comfort.dispose();
15759
- if (attribution) {
15760
- anchor.remove(attribution.mesh);
15761
- attribution.dispose();
16248
+ if (diagnostics) {
16249
+ anchor.remove(diagnostics.mesh);
16250
+ diagnostics.dispose();
16251
+ diagnostics = null;
15762
16252
  }
15763
16253
  navigationCue.dispose();
15764
16254
  viewer.scene.remove(anchor);
@@ -15771,7 +16261,6 @@ class WebXRSession {
15771
16261
  this.viewer = viewer;
15772
16262
  this.onFrame = options.onFrame;
15773
16263
  this.onStateChange = options.onStateChange;
15774
- this.showAttribution = options.showAttribution !== false;
15775
16264
  this.session = null;
15776
16265
  this.starting = null;
15777
16266
  this.bridge = null;
@@ -15881,8 +16370,10 @@ class WebXRSession {
15881
16370
  if (!referenceSpace) return;
15882
16371
  const pose = frame.getViewerPose(referenceSpace);
15883
16372
  if (!pose) return;
15884
- if (!this.bridge) this.bridge = createXRRenderBridge(this.viewer, pose.transform, { showAttribution: this.showAttribution });
15885
- else this.bridge.updateHeadPose(pose.transform);
16373
+ if (!this.bridge) {
16374
+ this.bridge = createXRRenderBridge(this.viewer, pose.transform, { showAttribution: false });
16375
+ this.bridge.setDiagnosticsRows(this.viewer.xrDiagnosticsRows);
16376
+ } else this.bridge.updateHeadPose(pose.transform);
15886
16377
  (_a3 = this.onFrame) == null ? void 0 : _a3.call(this, { time, frame, pose });
15887
16378
  this.applySparkProfile(this.viewer.sparkRenderer);
15888
16379
  this.viewer.animate(time);
@@ -15988,6 +16479,85 @@ function readXRFrameInput(frame, referenceSpace) {
15988
16479
  });
15989
16480
  return { head, controllers };
15990
16481
  }
16482
+ class DesktopLookControls {
16483
+ constructor(canvas, onLook) {
16484
+ var _a3;
16485
+ this.canvas = canvas;
16486
+ this.onLook = onLook;
16487
+ this.root = canvas.ownerDocument ?? canvas.getRootNode();
16488
+ this.window = this.root.defaultView ?? (typeof window === "undefined" ? null : window);
16489
+ this.pointer = null;
16490
+ this.down = (event) => {
16491
+ if (event.pointerType !== "mouse" || event.buttons !== 1 && event.buttons !== 2 || this.pointer) return;
16492
+ this.pointer = { id: event.pointerId, startX: event.clientX, startY: event.clientY, x: event.clientX, y: event.clientY, dragging: false };
16493
+ };
16494
+ this.move = (event) => {
16495
+ var _a4, _b2;
16496
+ const pointer = this.pointer;
16497
+ if (!pointer || event.pointerId !== pointer.id) return;
16498
+ if (!pointer.dragging) {
16499
+ if (Math.hypot(event.clientX - pointer.startX, event.clientY - pointer.startY) < 4) return;
16500
+ pointer.dragging = true;
16501
+ (_b2 = (_a4 = this.canvas).setPointerCapture) == null ? void 0 : _b2.call(_a4, pointer.id);
16502
+ }
16503
+ const dx = event.clientX - pointer.x;
16504
+ const dy = event.clientY - pointer.y;
16505
+ pointer.x = event.clientX;
16506
+ pointer.y = event.clientY;
16507
+ if (dx || dy) this.onLook(dx, dy);
16508
+ event.preventDefault();
16509
+ };
16510
+ this.end = (event) => {
16511
+ var _a4, _b2;
16512
+ if (!this.pointer || event.pointerId !== void 0 && event.pointerId !== this.pointer.id) return;
16513
+ if (this.pointer.dragging) {
16514
+ try {
16515
+ (_b2 = (_a4 = this.canvas).releasePointerCapture) == null ? void 0 : _b2.call(_a4, this.pointer.id);
16516
+ } catch {
16517
+ }
16518
+ }
16519
+ this.pointer = null;
16520
+ };
16521
+ this.wheel = (event) => event.preventDefault();
16522
+ this.contextMenu = (event) => event.preventDefault();
16523
+ canvas.addEventListener("pointerdown", this.down);
16524
+ canvas.addEventListener("wheel", this.wheel, { passive: false });
16525
+ canvas.addEventListener("contextmenu", this.contextMenu);
16526
+ this.root.addEventListener("pointermove", this.move);
16527
+ this.root.addEventListener("pointerup", this.end);
16528
+ this.root.addEventListener("pointercancel", this.end);
16529
+ this.root.addEventListener("lostpointercapture", this.end);
16530
+ (_a3 = this.window) == null ? void 0 : _a3.addEventListener("blur", this.end);
16531
+ }
16532
+ handleKeyDown(event) {
16533
+ if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return false;
16534
+ const target = event.target;
16535
+ if ((target == null ? void 0 : target.isContentEditable) || ["INPUT", "TEXTAREA", "SELECT"].includes(target == null ? void 0 : target.tagName)) return false;
16536
+ if (target !== this.canvas && target !== this.root.body && target !== this.root.documentElement) return false;
16537
+ const delta = {
16538
+ ArrowLeft: [-16, 0],
16539
+ ArrowRight: [16, 0],
16540
+ ArrowUp: [0, -16],
16541
+ ArrowDown: [0, 16]
16542
+ }[event.key];
16543
+ if (!delta) return false;
16544
+ event.preventDefault();
16545
+ this.onLook(delta[0], delta[1]);
16546
+ return true;
16547
+ }
16548
+ dispose() {
16549
+ var _a3;
16550
+ this.end({});
16551
+ this.canvas.removeEventListener("pointerdown", this.down);
16552
+ this.canvas.removeEventListener("wheel", this.wheel);
16553
+ this.canvas.removeEventListener("contextmenu", this.contextMenu);
16554
+ this.root.removeEventListener("pointermove", this.move);
16555
+ this.root.removeEventListener("pointerup", this.end);
16556
+ this.root.removeEventListener("pointercancel", this.end);
16557
+ this.root.removeEventListener("lostpointercapture", this.end);
16558
+ (_a3 = this.window) == null ? void 0 : _a3.removeEventListener("blur", this.end);
16559
+ }
16560
+ }
15991
16561
  const shadowCanvas = document.createElement("canvas");
15992
16562
  shadowCanvas.width = 128;
15993
16563
  shadowCanvas.height = 128;
@@ -16028,6 +16598,11 @@ class Viewer extends EventDispatcher {
16028
16598
  this.type = "viewer";
16029
16599
  this.disposed = false;
16030
16600
  this.xrSession = null;
16601
+ this.xrDiagnosticsRows = null;
16602
+ this.loadingDiagnosticsEnabled = false;
16603
+ this.diagnosticFps = null;
16604
+ this.diagnosticWindowStart = null;
16605
+ this.diagnosticFrameCount = 0;
16031
16606
  this.sparkRenderer = null;
16032
16607
  this.transparentBackground = options.transparentBackground ?? true;
16033
16608
  Tokens.CESIUM = options.cesiumToken || void 0;
@@ -16066,14 +16641,33 @@ class Viewer extends EventDispatcher {
16066
16641
  this.scene = new Scene();
16067
16642
  this.content = new Group$1();
16068
16643
  this.scene.add(this.content);
16644
+ this.contentLayerRegistry = new ContentLayerRegistry({
16645
+ group: this.content,
16646
+ createLayer: (definition) => this.createContentLayer(definition),
16647
+ onError: (definition, error) => this.dispatchEvent({
16648
+ type: ViewerEvents.CONTENT_LAYER_ERROR,
16649
+ layerId: typeof (definition == null ? void 0 : definition.id) === "string" && definition.id.length > 0 ? definition.id : null,
16650
+ error: error instanceof Error ? error : new Error(String(error))
16651
+ }),
16652
+ onAllSplatsDisabled: () => this.disposeSparkRenderer()
16653
+ });
16654
+ this.splatDestinationPrefetch = new SplatDestinationPrefetch({
16655
+ getRenderer: () => this.sparkRenderer,
16656
+ getLayer: (id) => this.contentLayerRegistry.getActiveLayer(id)
16657
+ });
16069
16658
  this.topLayerIndex = 0;
16070
16659
  this.enabledLayers = [];
16071
16660
  this.labels = /* @__PURE__ */ new Map();
16072
16661
  this.features = /* @__PURE__ */ new Map();
16073
16662
  this.globeControls = new GlobeControls(this.scene, this.camera, this.renderer.domElement);
16074
16663
  this.globeControls.enableDamping = true;
16664
+ this._viewModeState = { version: 0, confirmedRotationSpeed: this.globeControls.rotationSpeed };
16665
+ this.desktopNavigationController = null;
16666
+ this._desktopControlsBefore = null;
16667
+ this.desktopLookControls = null;
16075
16668
  this._onGlobeControlsStart = () => {
16076
16669
  this.transitionControls.cancel();
16670
+ this.globeControls.adjustHeight = true;
16077
16671
  };
16078
16672
  this.globeControls.addEventListener("start", this._onGlobeControlsStart);
16079
16673
  this.transitionControls = new TransitionControls(this.camera);
@@ -16112,6 +16706,8 @@ class Viewer extends EventDispatcher {
16112
16706
  DOWN: false
16113
16707
  };
16114
16708
  this._onKeyDown = (event) => {
16709
+ var _a3;
16710
+ if (this.desktopNavigationController && this.keysEnabled && ((_a3 = this.desktopLookControls) == null ? void 0 : _a3.handleKeyDown(event))) return;
16115
16711
  if (event.repeat && !this.keysDown[event.code]) return;
16116
16712
  this.keysDown[event.code] = true;
16117
16713
  this.updateMovementState();
@@ -16123,9 +16719,15 @@ class Viewer extends EventDispatcher {
16123
16719
  this._onResize = (event) => {
16124
16720
  this.resizeView(event);
16125
16721
  };
16722
+ this._onWindowBlur = () => this._clearHeldKeys();
16723
+ this._onVisibilityChange = () => {
16724
+ if (document.hidden) this._clearHeldKeys();
16725
+ };
16126
16726
  window.addEventListener("keydown", this._onKeyDown);
16127
16727
  window.addEventListener("keyup", this._onKeyUp);
16128
16728
  window.addEventListener("resize", this._onResize);
16729
+ window.addEventListener("blur", this._onWindowBlur);
16730
+ document.addEventListener("visibilitychange", this._onVisibilityChange);
16129
16731
  if (container) this.appendTo(container);
16130
16732
  this.sky = new Sky();
16131
16733
  this.sky.scale.setScalar(2e7);
@@ -16167,12 +16769,13 @@ class Viewer extends EventDispatcher {
16167
16769
  }
16168
16770
  /** @param {{ onFrame?: (input: { time: number, frame: XRFrame, pose: XRViewerPose }) => void, onStateChange?: (active: boolean) => void, showAttribution?: boolean }} [options] */
16169
16771
  startXRSession(options = {}) {
16772
+ var _a3, _b2;
16170
16773
  if (this.disposed) return Promise.reject(new Error("Viewer has been disposed"));
16774
+ (_b2 = (_a3 = this.desktopNavigationController) == null ? void 0 : _a3.interrupt) == null ? void 0 : _b2.call(_a3, "XR session started");
16171
16775
  if (!this.xrSession) this.xrSession = new WebXRSession(this, options);
16172
16776
  else {
16173
16777
  this.xrSession.onFrame = options.onFrame;
16174
16778
  this.xrSession.onStateChange = options.onStateChange;
16175
- this.xrSession.showAttribution = options.showAttribution !== false;
16176
16779
  }
16177
16780
  return this.xrSession.start();
16178
16781
  }
@@ -16242,6 +16845,9 @@ class Viewer extends EventDispatcher {
16242
16845
  set keysEnabled(enabled) {
16243
16846
  if (enabled === this._keysEnabled) return;
16244
16847
  this._keysEnabled = enabled;
16848
+ this._clearHeldKeys();
16849
+ }
16850
+ _clearHeldKeys() {
16245
16851
  this.keysDown = {};
16246
16852
  for (const key in this.movementState) this.movementState[key] = false;
16247
16853
  }
@@ -16257,6 +16863,82 @@ class Viewer extends EventDispatcher {
16257
16863
  }
16258
16864
  }
16259
16865
  }
16866
+ if (this.movementState.FRONT || this.movementState.BACK || this.movementState.LEFT || this.movementState.RIGHT || this.movementState.UP || this.movementState.DOWN) {
16867
+ if (this.globeControls) this.globeControls.adjustHeight = true;
16868
+ }
16869
+ }
16870
+ /** Install one desktop camera owner. A null controller restores the prior globe controls. @param {DesktopNavigationController | null} controller */
16871
+ setDesktopNavigationController(controller) {
16872
+ var _a3, _b2, _c, _d;
16873
+ if (this.desktopNavigationController === controller) return;
16874
+ if (controller && this.xrActive) return;
16875
+ this._clearHeldKeys();
16876
+ if (controller) {
16877
+ this.transitionControls.cancel();
16878
+ if (this.desktopNavigationController) (_b2 = (_a3 = this.desktopNavigationController).interrupt) == null ? void 0 : _b2.call(_a3, "replaced");
16879
+ if (!this._desktopControlsBefore) {
16880
+ this._desktopControlsBefore = { enabled: this.globeControls.enabled, rotationSpeed: this.globeControls.rotationSpeed };
16881
+ }
16882
+ this.globeControls.enabled = false;
16883
+ this.globeControls.adjustHeight = false;
16884
+ this.globeControls.globeInertiaFactor = 0;
16885
+ this.globeControls.globeInertia.identity();
16886
+ this.desktopNavigationController = controller;
16887
+ const canvas = (_c = this.renderer) == null ? void 0 : _c.domElement;
16888
+ if ((canvas == null ? void 0 : canvas.getRootNode) && !this.desktopLookControls) this.desktopLookControls = new DesktopLookControls(canvas, (dx, dy) => this._applyDesktopLook(dx, dy));
16889
+ if (canvas == null ? void 0 : canvas.focus) {
16890
+ if (canvas.tabIndex < 0) canvas.tabIndex = 0;
16891
+ canvas.focus({ preventScroll: true });
16892
+ }
16893
+ } else {
16894
+ this.desktopNavigationController = null;
16895
+ (_d = this.desktopLookControls) == null ? void 0 : _d.dispose();
16896
+ this.desktopLookControls = null;
16897
+ if (this._desktopControlsBefore) {
16898
+ this.globeControls.adjustHeight = true;
16899
+ this.globeControls.rotationSpeed = this._desktopControlsBefore.rotationSpeed;
16900
+ this.globeControls.enabled = this._desktopControlsBefore.enabled;
16901
+ this._desktopControlsBefore = null;
16902
+ }
16903
+ }
16904
+ }
16905
+ /** Rotate Walk view around its eye; pointer delta is CSS pixels. */
16906
+ _applyDesktopLook(dx, dy) {
16907
+ var _a3, _b2;
16908
+ if (!this.desktopNavigationController || ((_b2 = (_a3 = this.desktopNavigationController).canLook) == null ? void 0 : _b2.call(_a3)) === false || this.xrActive) return;
16909
+ const { lon, lat, pitch, yaw, roll } = this.cameraCoords;
16910
+ if (![lon, lat, pitch, yaw, roll, dx, dy].every(Number.isFinite)) return;
16911
+ const nextPitch = MathUtils.clamp(pitch - dy * 0.15, -80, 80);
16912
+ const nextYaw = yaw - dx * 0.15;
16913
+ this.camera.quaternion.copy(geodeticToEcefRotation(lon, lat, nextPitch, nextYaw, roll));
16914
+ this.camera.updateMatrixWorld(true);
16915
+ this.cameraCoords = ecefToGeodeticRotation(this.camera.position.x, this.camera.position.y, this.camera.position.z, this.camera.quaternion);
16916
+ }
16917
+ /** Turn Walk toward north without moving its eye or leaving Walk. */
16918
+ turnDesktopNorth() {
16919
+ if (!this.desktopNavigationController || this.xrActive) return false;
16920
+ const { lon, lat, pitch, roll } = this.cameraCoords;
16921
+ if (![lon, lat, pitch, roll].every(Number.isFinite)) return false;
16922
+ this.camera.quaternion.copy(geodeticToEcefRotation(lon, lat, pitch, 0, roll));
16923
+ this.camera.updateMatrixWorld(true);
16924
+ this.cameraCoords = ecefToGeodeticRotation(this.camera.position.x, this.camera.position.y, this.camera.position.z, this.camera.quaternion);
16925
+ return true;
16926
+ }
16927
+ /** Invoke the desktop controller after globe input and before tiles/content/render. Invalid results leave the previous eye intact. */
16928
+ _applyDesktopNavigationFrame(timeMs, deltaSeconds) {
16929
+ const controller = this.desktopNavigationController;
16930
+ if (!controller || this.xrActive) return;
16931
+ const pose = this.cameraCoords;
16932
+ const result = controller.update({ timeMs, deltaSeconds, pose: { lon: pose.lon, lat: pose.lat, alt: pose.alt, pitch: pose.pitch, yaw: pose.yaw, roll: pose.roll }, movement: { ...this.movementState } });
16933
+ const { longitude, latitude, altitude } = (result == null ? void 0 : result.position) ?? {};
16934
+ if (![longitude, latitude, altitude].every(Number.isFinite) || longitude < -180 || longitude > 180 || latitude < -90 || latitude > 90 || result.pitch !== void 0 && !Number.isFinite(result.pitch)) {
16935
+ console.error("Desktop navigation returned an invalid camera pose");
16936
+ return;
16937
+ }
16938
+ this.camera.position.fromArray(geodeticToEcefPosition(longitude, latitude, altitude));
16939
+ if (result.pitch !== void 0) this.camera.quaternion.copy(geodeticToEcefRotation(longitude, latitude, result.pitch, pose.yaw, pose.roll));
16940
+ this.camera.updateMatrixWorld(true);
16941
+ this.cameraCoords = ecefToGeodeticRotation(this.camera.position.x, this.camera.position.y, this.camera.position.z, this.camera.quaternion);
16260
16942
  }
16261
16943
  /**
16262
16944
  * @param {number} lon
@@ -16335,11 +17017,8 @@ class Viewer extends EventDispatcher {
16335
17017
  }
16336
17018
  clearContentLayers() {
16337
17019
  var _a3;
16338
- while (this.content.children.length > 0) {
16339
- const layer = this.content.children[0];
16340
- (_a3 = layer.dispose) == null ? void 0 : _a3.call(layer);
16341
- this.content.remove(layer);
16342
- }
17020
+ (_a3 = this.splatDestinationPrefetch) == null ? void 0 : _a3.clear("cancel");
17021
+ this.contentLayerRegistry.clear();
16343
17022
  this.disposeSparkRenderer();
16344
17023
  }
16345
17024
  async ensureSparkRenderer() {
@@ -16355,110 +17034,111 @@ class Viewer extends EventDispatcher {
16355
17034
  return this.sparkRenderer;
16356
17035
  }
16357
17036
  disposeSparkRenderer() {
16358
- var _a3, _b2;
17037
+ var _a3, _b2, _c;
16359
17038
  this.sparkRendererGeneration = (this.sparkRendererGeneration ?? 0) + 1;
16360
17039
  if (!this.sparkRenderer) return;
17040
+ (_a3 = this.splatDestinationPrefetch) == null ? void 0 : _a3.sync();
16361
17041
  this.sparkRenderer.removeFromParent();
16362
- (_b2 = (_a3 = this.sparkRenderer).clearSplats) == null ? void 0 : _b2.call(_a3);
17042
+ (_c = (_b2 = this.sparkRenderer).clearSplats) == null ? void 0 : _c.call(_b2);
16363
17043
  this.sparkRenderer.dispose();
16364
17044
  this.sparkRenderer = null;
16365
17045
  }
17046
+ /** @private @param {import('../main.js').ContentLayerDefinition} sourceLayerData */
17047
+ prepareContentLayerDefinition(sourceLayerData) {
17048
+ const layerData = (sourceLayerData == null ? void 0 : sourceLayerData.type) === "gaussian-splat" ? validateGaussianSplatLayerDefinition(sourceLayerData) : sourceLayerData;
17049
+ if (typeof (layerData == null ? void 0 : layerData.id) !== "string" || !layerData.id || !Array.isArray(layerData.labels)) {
17050
+ throw new Error("Content layer requires a non-empty id and labels array.");
17051
+ }
17052
+ if (!["3d-tiles", "mesh", "geojson", "gaussian-splat"].includes(layerData.type)) {
17053
+ throw new Error(`Unsupported content layer type: ${String(layerData.type)}`);
17054
+ }
17055
+ return layerData;
17056
+ }
17057
+ /** @private @param {import('../main.js').ContentLayerDefinition} layerData */
17058
+ createContentLayer(layerData) {
17059
+ const onError = (error) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_ERROR, layerId: layerData.id, error });
17060
+ switch (layerData.type) {
17061
+ case "3d-tiles":
17062
+ return new TilesLayer(layerData.data.url, { camera: this.camera, renderer: this.renderer, ...layerData.options, decoders: this.tileDecoders }, { onError });
17063
+ case "mesh":
17064
+ return new MeshLayer(layerData.data.url, { ...layerData.options, decoders: this.tileDecoders }, { onError });
17065
+ case "geojson":
17066
+ return new GeojsonLayer(layerData.data, layerData.options, { onError });
17067
+ case "gaussian-splat":
17068
+ return new GaussianSplatLayer(layerData, {
17069
+ ensureRenderer: () => this.ensureSparkRenderer(),
17070
+ releasePagedSplats: (paged) => {
17071
+ var _a3, _b2;
17072
+ return (_b2 = (_a3 = this.sparkRenderer) == null ? void 0 : _a3.releasePagedSplats) == null ? void 0 : _b2.call(_a3, paged);
17073
+ },
17074
+ onStart: (layer) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_LOAD_START, layerId: layer.contentLayerId }),
17075
+ onProgress: (layer, progress) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_LOAD_PROGRESS, layerId: layer.contentLayerId, progress }),
17076
+ onReady: (layer) => {
17077
+ this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_READY, layerId: layer.contentLayerId });
17078
+ this.splatDestinationPrefetch.sync();
17079
+ },
17080
+ onError: (layer, error) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_ERROR, layerId: layer.contentLayerId, error })
17081
+ });
17082
+ default:
17083
+ throw new Error(`Unsupported content layer type: ${String(layerData.type)}`);
17084
+ }
17085
+ }
17086
+ /** @private */
17087
+ reportContentLayerError(sourceLayerData, error) {
17088
+ this.dispatchEvent({
17089
+ type: ViewerEvents.CONTENT_LAYER_ERROR,
17090
+ layerId: typeof (sourceLayerData == null ? void 0 : sourceLayerData.id) === "string" && sourceLayerData.id.length > 0 ? sourceLayerData.id : null,
17091
+ error: error instanceof Error ? error : new Error(String(error))
17092
+ });
17093
+ }
17094
+ /** @private */
17095
+ refreshContentLayerDepth() {
17096
+ this.topLayerIndex = Math.max(0, ...this.content.children.map((layer) => Math.max(layer.contentRenderOrder ?? 0, layer.xrRenderLayer ?? 0)));
17097
+ this.globeControls.pivotMesh.layers.set(this.topLayerIndex);
17098
+ }
16366
17099
  /** @param {import('../main.js').ContentLayerDefinition[]} content */
16367
17100
  defineContentLayers(content = []) {
16368
- var _a3, _b2, _c, _d;
16369
- this.topLayerIndex = 0;
16370
- this.clearContentLayers();
16371
- for (let i2 = 0; i2 < content.length; i2++) {
16372
- const sourceLayerData = content[i2];
17101
+ const definitions = [];
17102
+ for (const sourceLayerData of content) {
16373
17103
  try {
16374
- const layerData = (sourceLayerData == null ? void 0 : sourceLayerData.type) === "gaussian-splat" ? validateGaussianSplatLayerDefinition(sourceLayerData) : sourceLayerData;
16375
- this.topLayerIndex = Math.max(this.topLayerIndex, ((_a3 = layerData == null ? void 0 : layerData.options) == null ? void 0 : _a3.renderOrder) ?? 0);
16376
- switch (layerData == null ? void 0 : layerData.type) {
16377
- case "3d-tiles": {
16378
- const tilesLayer = new TilesLayer(
16379
- layerData.data.url,
16380
- { camera: this.camera, renderer: this.renderer, ...layerData.options, decoders: this.tileDecoders },
16381
- { onError: (error) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_ERROR, layerId: layerData.id, error }) }
16382
- );
16383
- tilesLayer.contentLayerId = layerData.id;
16384
- tilesLayer.setLabels(layerData.labels);
16385
- (_b2 = tilesLayer.setTimeFrame) == null ? void 0 : _b2.call(tilesLayer, this.timeFrameStart, this.timeFrameEnd);
16386
- tilesLayer.setEnabled(false);
16387
- this.content.add(tilesLayer);
16388
- break;
16389
- }
16390
- case "mesh": {
16391
- const meshLayer = new MeshLayer(
16392
- layerData.data.url,
16393
- { ...layerData.options },
16394
- { onError: (error) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_ERROR, layerId: layerData.id, error }) }
16395
- );
16396
- meshLayer.contentLayerId = layerData.id;
16397
- meshLayer.setLabels(layerData.labels);
16398
- meshLayer.setEnabled(false);
16399
- this.content.add(meshLayer);
16400
- break;
16401
- }
16402
- case "geojson": {
16403
- const geojsonLayer = new GeojsonLayer(layerData.data, layerData.options, {
16404
- onError: (error) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_ERROR, layerId: layerData.id, error })
16405
- });
16406
- geojsonLayer.contentLayerId = layerData.id;
16407
- geojsonLayer.setLabels(layerData.labels);
16408
- (_c = geojsonLayer.setTimeFrame) == null ? void 0 : _c.call(geojsonLayer, this.timeFrameStart, this.timeFrameEnd);
16409
- geojsonLayer.setEnabled(false);
16410
- this.content.add(geojsonLayer);
16411
- break;
16412
- }
16413
- case "gaussian-splat": {
16414
- const gaussianLayer = new GaussianSplatLayer(layerData, {
16415
- ensureRenderer: () => this.ensureSparkRenderer(),
16416
- onStart: (layer) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_LOAD_START, layerId: layer.contentLayerId }),
16417
- onProgress: (layer, progress) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_LOAD_PROGRESS, layerId: layer.contentLayerId, progress }),
16418
- onReady: (layer) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_READY, layerId: layer.contentLayerId }),
16419
- onError: (layer, error) => this.dispatchEvent({ type: ViewerEvents.CONTENT_LAYER_ERROR, layerId: layer.contentLayerId, error })
16420
- });
16421
- this.content.add(gaussianLayer);
16422
- break;
16423
- }
16424
- default:
16425
- throw new Error(`Unsupported content layer type: ${String(layerData == null ? void 0 : layerData.type)}`);
16426
- }
16427
- this.content.children.at(-1).xrRenderLayer = layerData.type === "mesh" ? 6 : ((_d = layerData.options) == null ? void 0 : _d.renderOrder) ?? 0;
17104
+ definitions.push(this.prepareContentLayerDefinition(sourceLayerData));
16428
17105
  } catch (error) {
16429
- this.dispatchEvent({
16430
- type: ViewerEvents.CONTENT_LAYER_ERROR,
16431
- layerId: typeof (sourceLayerData == null ? void 0 : sourceLayerData.id) === "string" && sourceLayerData.id.length > 0 ? sourceLayerData.id : null,
16432
- error: error instanceof Error ? error : new Error(String(error))
16433
- });
17106
+ this.reportContentLayerError(sourceLayerData, error);
16434
17107
  }
16435
17108
  }
16436
- this.globeControls.pivotMesh.layers.set(this.topLayerIndex);
16437
- this.enableContentLayers(this.enabledLayers);
17109
+ this.contentLayerRegistry.setDefinitions(definitions);
17110
+ this.splatDestinationPrefetch.sync();
17111
+ this.refreshContentLayerDepth();
16438
17112
  }
16439
- enableContentLayers(labels = []) {
16440
- this.enabledLayers = labels;
16441
- for (let i2 = 0; i2 < this.content.children.length; i2++) {
16442
- const contentLayer = this.content.children[i2];
16443
- let enabled = false;
16444
- for (let j2 = 0; j2 < labels.length; j2++) {
16445
- const label = labels[j2];
16446
- if (contentLayer.labels.has(label)) {
16447
- enabled = true;
16448
- break;
16449
- }
17113
+ /** Replace, add, or remove one content layer without rebuilding its siblings. */
17114
+ /** @param {string} id @param {import('../main.js').ContentLayerDefinition | null} definition */
17115
+ replaceContentLayer(id, definition) {
17116
+ if (this.disposed) return;
17117
+ if (typeof id !== "string" || id.length === 0) throw new Error("Content layer id must be a non-empty string.");
17118
+ if (definition && definition.id !== id) throw new Error("Replacement layer id must match the requested id.");
17119
+ let prepared = null;
17120
+ if (definition) {
17121
+ try {
17122
+ prepared = this.prepareContentLayerDefinition(definition);
17123
+ } catch (error) {
17124
+ this.reportContentLayerError(definition, error);
17125
+ return;
16450
17126
  }
16451
- contentLayer.setEnabled(enabled);
16452
17127
  }
17128
+ if (!this.contentLayerRegistry.replaceDefinition(id, prepared)) return;
17129
+ this.splatDestinationPrefetch.sync();
17130
+ this.refreshContentLayerDepth();
17131
+ }
17132
+ enableContentLayers(labels = []) {
17133
+ this.enabledLayers = labels;
17134
+ this.contentLayerRegistry.setEnabledLabels(labels);
17135
+ this.splatDestinationPrefetch.sync();
17136
+ this.refreshContentLayerDepth();
16453
17137
  }
16454
17138
  setTimeFrame(start, end) {
16455
- var _a3;
16456
17139
  this.timeFrameStart = start ? new Date(start) : null;
16457
17140
  this.timeFrameEnd = end ? new Date(end) : null;
16458
- for (let i2 = 0; i2 < this.content.children.length; i2++) {
16459
- const contentLayer = this.content.children[i2];
16460
- (_a3 = contentLayer.setTimeFrame) == null ? void 0 : _a3.call(contentLayer, this.timeFrameStart, this.timeFrameEnd);
16461
- }
17141
+ this.contentLayerRegistry.setTimeFrame(this.timeFrameStart, this.timeFrameEnd);
16462
17142
  }
16463
17143
  /**
16464
17144
  * @param {string} id
@@ -16518,7 +17198,16 @@ class Viewer extends EventDispatcher {
16518
17198
  }
16519
17199
  animate(time) {
16520
17200
  var _a3, _b2, _c;
16521
- if (this.stats) this.stats.begin();
17201
+ if (this.loadingDiagnosticsEnabled) {
17202
+ if (this.diagnosticWindowStart === null) this.diagnosticWindowStart = time;
17203
+ this.diagnosticFrameCount++;
17204
+ const elapsed = time - this.diagnosticWindowStart;
17205
+ if (elapsed >= 1e3) {
17206
+ this.diagnosticFps = Math.round(this.diagnosticFrameCount * 1e3 / elapsed);
17207
+ this.diagnosticWindowStart = time;
17208
+ this.diagnosticFrameCount = 0;
17209
+ }
17210
+ }
16522
17211
  this.transitionControls.update(time);
16523
17212
  this.state.get("time").value = time;
16524
17213
  const deltaTime = this.clock.getDelta();
@@ -16527,7 +17216,10 @@ class Viewer extends EventDispatcher {
16527
17216
  this.updateCameraDirections();
16528
17217
  this.cameraCoords = ecefToGeodeticRotation(this.camera.position.x, this.camera.position.y, this.camera.position.z, this.camera.quaternion);
16529
17218
  this.sky.setSurfaceUp(geodeticToEcefUpVector(this.cameraCoords.lon, this.cameraCoords.lat));
16530
- if (this.keysEnabled) {
17219
+ if (this.desktopNavigationController) {
17220
+ this._applyDesktopNavigationFrame(time, deltaTime);
17221
+ this.updateCameraDirections();
17222
+ } else if (this.keysEnabled) {
16531
17223
  applyKeyboardMovement(this.camera.position, this.cameraForward, this.cameraRight, this.cameraUp, this.movementState, deltaTime, this.movementSpeed, this.boostMultiplier);
16532
17224
  }
16533
17225
  this.globeManager.update();
@@ -16574,9 +17266,9 @@ class Viewer extends EventDispatcher {
16574
17266
  }
16575
17267
  this.cameraCoords = ecefToGeodeticRotation(this.camera.position.x, this.camera.position.y, this.camera.position.z, this.camera.quaternion);
16576
17268
  this.dispatchEvent({ type: ViewerEvents.ANIMATED, time, deltaTime });
16577
- if (this.stats) this.stats.end();
16578
17269
  }
16579
17270
  render() {
17271
+ this.splatDestinationPrefetch.sync();
16580
17272
  this.renderer.render(this.scene, this.camera);
16581
17273
  this.css2DRenderer.render(this.scene, this.camera);
16582
17274
  if (this.state.get("added") === true && this.state.get("ready") === false) {
@@ -16640,15 +17332,20 @@ class Viewer extends EventDispatcher {
16640
17332
  * @param {number} [yaw]
16641
17333
  * @param {number} [roll]
16642
17334
  * @param {number} [duration]
17335
+ * @returns {Promise<boolean>} Whether the requested transition completed.
16643
17336
  */
16644
17337
  async alignCamera(lon, lat, alt, pitch, yaw, roll, duration) {
17338
+ var _a3, _b2;
17339
+ (_b2 = (_a3 = this.desktopNavigationController) == null ? void 0 : _a3.interrupt) == null ? void 0 : _b2.call(_a3, "alignCamera");
16645
17340
  const position = validateGeodeticPosition(lon, lat, alt, "Camera position");
16646
17341
  const rotation = validateRotation(pitch ?? this.cameraCoords.pitch, yaw ?? this.cameraCoords.yaw, roll ?? this.cameraCoords.roll, "Camera rotation");
16647
17342
  const resolvedDuration = requireFiniteNumber(duration ?? TweenSettings.DURATION, "Camera transition duration");
16648
17343
  const targetPosition = new Vector3().fromArray(geodeticToEcefPosition(position.longitude, position.latitude, position.altitude));
16649
17344
  const targetQuaternion = geodeticToEcefRotation(position.longitude, position.latitude, rotation.pitch, rotation.yaw, rotation.roll);
17345
+ if (this.globeControls) this.globeControls.adjustHeight = false;
16650
17346
  return this.transitionControls.animateTo(targetPosition, targetQuaternion, resolvedDuration).then((completed) => {
16651
17347
  if (completed) this.dispatchEvent({ type: ViewerEvents.TRANSITIONED, lon, lat, alt, ...rotation });
17348
+ return completed;
16652
17349
  });
16653
17350
  }
16654
17351
  /**
@@ -16658,8 +17355,11 @@ class Viewer extends EventDispatcher {
16658
17355
  * @param {number} [distance]
16659
17356
  * @param {number} [pitch]
16660
17357
  * @param {number} [duration]
17358
+ * @returns {Promise<boolean>} Whether the requested transition completed.
16661
17359
  */
16662
17360
  async focusCamera(lon, lat, alt, distance, pitch, duration) {
17361
+ var _a3, _b2;
17362
+ (_b2 = (_a3 = this.desktopNavigationController) == null ? void 0 : _a3.interrupt) == null ? void 0 : _b2.call(_a3, "focusCamera");
16663
17363
  const position = validateGeodeticPosition(lon, lat, alt, "Camera focus");
16664
17364
  const resolvedDistance = requireFiniteNumber(distance ?? 2e3, "Camera focus distance");
16665
17365
  const rotation = validateRotation(pitch ?? this.cameraCoords.pitch, 0, 0, "Camera rotation");
@@ -16668,25 +17368,72 @@ class Viewer extends EventDispatcher {
16668
17368
  const targetQuaternion = geodeticToEcefRotation(position.longitude, position.latitude, rotation.pitch, rotation.yaw, rotation.roll);
16669
17369
  const direction = new Vector3(0, 0, 1).applyQuaternion(targetQuaternion);
16670
17370
  targetPosition.addScaledVector(direction, resolvedDistance);
17371
+ if (this.globeControls) this.globeControls.adjustHeight = true;
16671
17372
  return this.transitionControls.animateTo(targetPosition, targetQuaternion, resolvedDuration).then((completed) => {
16672
17373
  if (completed) this.dispatchEvent({ type: ViewerEvents.TRANSITIONED, lon, lat, alt });
17374
+ return completed;
16673
17375
  });
16674
17376
  }
16675
17377
  /**
16676
17378
  * Temporarily load Google 3D tiles around a WGS84 destination without changing the live view.
16677
17379
  * Position and altitude use degrees and WGS84 ellipsoidal metres; rotation uses degrees.
16678
- * @param {{longitude:number, latitude:number, altitude:number, pitch:number, yaw:number, roll:number, resolutionPixels:128|256} | null} target
17380
+ * @param {{longitude:number, latitude:number, altitude:number, pitch:number, yaw:number, roll:number, resolutionPixels:128|256, timeoutMs?:number} | null} target
16679
17381
  */
16680
17382
  setDestinationTilePrefetch(target) {
16681
17383
  if (this.disposed) return;
16682
17384
  this.globeManager.setDestinationTilePrefetch(target);
16683
17385
  }
17386
+ /**
17387
+ * Warm only active RAD layers at a WGS84 destination. This never changes the live camera.
17388
+ * @param {{longitude:number, latitude:number, altitude:number, pitch:number, yaw:number, roll:number, layerIds:string[], mode:'desktop'|'xr', timeoutMs:number} | null} target
17389
+ * @param {'cancel'|'arrival'} [clearReason]
17390
+ */
17391
+ setDestinationSplatPrefetch(target, clearReason = "cancel") {
17392
+ if (this.disposed) return;
17393
+ if (!target) {
17394
+ this.splatDestinationPrefetch.clear(clearReason);
17395
+ return;
17396
+ }
17397
+ try {
17398
+ validateGeodeticPosition(target.longitude, target.latitude, target.altitude, "Destination splat prefetch");
17399
+ validateRotation(target.pitch, target.yaw, target.roll, "Destination splat prefetch rotation");
17400
+ } catch {
17401
+ return;
17402
+ }
17403
+ if (!Array.isArray(target.layerIds) || !target.layerIds.every((id) => typeof id === "string") || !["desktop", "xr"].includes(target.mode)) return;
17404
+ this.splatDestinationPrefetch.set(target);
17405
+ }
17406
+ /** Optional viewer-owned counters, sampled by the app only while its stats setting is on.
17407
+ * @param {boolean} enabled
17408
+ */
17409
+ setLoadingDiagnosticsEnabled(enabled) {
17410
+ this.loadingDiagnosticsEnabled = Boolean(enabled);
17411
+ if (!enabled) {
17412
+ this.diagnosticFps = null;
17413
+ this.diagnosticWindowStart = null;
17414
+ this.diagnosticFrameCount = 0;
17415
+ }
17416
+ }
17417
+ getLoadingDiagnostics() {
17418
+ return readLoadingDiagnostics(this);
17419
+ }
17420
+ /** Short numeric rows in a small lower-right panel for both XR eyes.
17421
+ * @param {string[] | null} rows
17422
+ */
17423
+ setXRDiagnosticsPanel(rows) {
17424
+ var _a3, _b2;
17425
+ this.xrDiagnosticsRows = Array.isArray(rows) && rows.length > 0 ? rows.slice(0, 5).map(String) : null;
17426
+ (_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.setDiagnosticsRows(this.xrDiagnosticsRows);
17427
+ }
16684
17428
  /**
16685
17429
  * @param {number} [pitch]
16686
17430
  * @param {number} [yaw]
16687
17431
  * @param {number} [duration]
17432
+ * @returns {Promise<boolean>} Whether the requested transition completed.
16688
17433
  */
16689
17434
  async angleCamera(pitch, yaw, duration) {
17435
+ var _a3, _b2;
17436
+ (_b2 = (_a3 = this.desktopNavigationController) == null ? void 0 : _a3.interrupt) == null ? void 0 : _b2.call(_a3, "angleCamera");
16690
17437
  const rotation = validateRotation(pitch ?? this.cameraCoords.pitch, yaw ?? this.cameraCoords.yaw, 0, "Camera rotation");
16691
17438
  const resolvedDuration = requireFiniteNumber(duration ?? TweenSettings.DURATION, "Camera transition duration");
16692
17439
  const pivot = this.getCameraPivot();
@@ -16698,15 +17445,27 @@ class Viewer extends EventDispatcher {
16698
17445
  targetPosition.addScaledVector(direction, distance);
16699
17446
  return this.transitionControls.animateTo(targetPosition, targetQuaternion, resolvedDuration).then((completed) => {
16700
17447
  if (completed) this.dispatchEvent({ type: ViewerEvents.TRANSITIONED, lon, lat, alt });
17448
+ return completed;
16701
17449
  });
16702
17450
  }
16703
- setViewMode(mode = "3d") {
16704
- if (mode == "3d") {
16705
- this.globeControls.rotationSpeed = 1;
16706
- return this.angleCamera(-60, void 0, 500);
16707
- } else {
16708
- this.globeControls.rotationSpeed = 0;
16709
- return this.angleCamera(-90, 0, 500);
17451
+ /** @param {'2d' | '3d'} [mode] @returns {Promise<boolean>} Whether the camera orientation transition completed. */
17452
+ async setViewMode(mode = "3d") {
17453
+ var _a3, _b2;
17454
+ (_b2 = (_a3 = this.desktopNavigationController) == null ? void 0 : _a3.interrupt) == null ? void 0 : _b2.call(_a3, "setViewMode");
17455
+ const request = this._viewModeState ?? (this._viewModeState = { version: 0, confirmedRotationSpeed: this.globeControls.rotationSpeed });
17456
+ const version = ++request.version;
17457
+ const targetRotationSpeed = mode === "3d" ? 1 : 0;
17458
+ this.globeControls.rotationSpeed = targetRotationSpeed;
17459
+ try {
17460
+ const completed = mode === "3d" ? await this.angleCamera(-60, void 0, 500) : await this.angleCamera(-90, 0, 500);
17461
+ if (request.version === version && !this.disposed) {
17462
+ if (completed) request.confirmedRotationSpeed = targetRotationSpeed;
17463
+ else this.globeControls.rotationSpeed = request.confirmedRotationSpeed;
17464
+ }
17465
+ return completed;
17466
+ } catch (error) {
17467
+ if (request.version === version && !this.disposed) this.globeControls.rotationSpeed = request.confirmedRotationSpeed;
17468
+ throw error;
16710
17469
  }
16711
17470
  }
16712
17471
  exportScene() {
@@ -16731,9 +17490,12 @@ class Viewer extends EventDispatcher {
16731
17490
  link.click();
16732
17491
  }
16733
17492
  dispose() {
16734
- var _a3, _b2, _c, _d, _e, _f, _g, _h, _i2, _j, _k, _l, _m, _n2, _o;
17493
+ var _a3, _b2, _c, _d, _e, _f, _g, _h, _i2, _j, _k, _l, _m, _n2, _o, _p;
16735
17494
  if (this.disposed) return;
16736
17495
  this.disposed = true;
17496
+ (_a3 = this.splatDestinationPrefetch) == null ? void 0 : _a3.dispose();
17497
+ (_c = (_b2 = this.desktopNavigationController) == null ? void 0 : _b2.interrupt) == null ? void 0 : _c.call(_b2, "viewer disposed");
17498
+ if (this.desktopNavigationController || this.desktopLookControls) this.setDesktopNavigationController(null);
16737
17499
  if (this.xrSession) {
16738
17500
  void this.xrSession.dispose().catch((error) => console.error("WebXR session end failed during viewer disposal", error));
16739
17501
  this.xrSession = null;
@@ -16744,6 +17506,8 @@ class Viewer extends EventDispatcher {
16744
17506
  window.removeEventListener("keydown", this._onKeyDown);
16745
17507
  window.removeEventListener("keyup", this._onKeyUp);
16746
17508
  window.removeEventListener("resize", this._onResize);
17509
+ window.removeEventListener("blur", this._onWindowBlur);
17510
+ document.removeEventListener("visibilitychange", this._onVisibilityChange);
16747
17511
  window.removeEventListener("pointermove", this._onPointerMove);
16748
17512
  this.globeControls.removeEventListener("start", this._onGlobeControlsStart);
16749
17513
  this.globeControls.dispose();
@@ -16766,17 +17530,16 @@ class Viewer extends EventDispatcher {
16766
17530
  }
16767
17531
  this.labels.clear();
16768
17532
  this.features.clear();
16769
- (_b2 = (_a3 = this.scene.environment) == null ? void 0 : _a3.dispose) == null ? void 0 : _b2.call(_a3);
17533
+ (_e = (_d = this.scene.environment) == null ? void 0 : _d.dispose) == null ? void 0 : _e.call(_d);
16770
17534
  this.scene.environment = null;
16771
- (_e = (_d = (_c = this.sky) == null ? void 0 : _c.geometry) == null ? void 0 : _d.dispose) == null ? void 0 : _e.call(_d);
16772
- (_h = (_g = (_f = this.sky) == null ? void 0 : _f.material) == null ? void 0 : _g.dispose) == null ? void 0 : _h.call(_g);
16773
- (_i2 = this.sky) == null ? void 0 : _i2.removeFromParent();
16774
- (_k = (_j = this.stats) == null ? void 0 : _j.dom) == null ? void 0 : _k.remove();
17535
+ (_h = (_g = (_f = this.sky) == null ? void 0 : _f.geometry) == null ? void 0 : _g.dispose) == null ? void 0 : _h.call(_g);
17536
+ (_k = (_j = (_i2 = this.sky) == null ? void 0 : _i2.material) == null ? void 0 : _j.dispose) == null ? void 0 : _k.call(_j);
17537
+ (_l = this.sky) == null ? void 0 : _l.removeFromParent();
16775
17538
  this.css2DRenderer.domElement.remove();
16776
17539
  this.renderer.domElement.remove();
16777
- (_m = (_l = this.renderer.renderLists) == null ? void 0 : _l.dispose) == null ? void 0 : _m.call(_l);
17540
+ (_n2 = (_m = this.renderer.renderLists) == null ? void 0 : _m.dispose) == null ? void 0 : _n2.call(_m);
16778
17541
  this.renderer.dispose();
16779
- (_o = (_n2 = this.renderer).forceContextLoss) == null ? void 0 : _o.call(_n2);
17542
+ (_p = (_o = this.renderer).forceContextLoss) == null ? void 0 : _p.call(_o);
16780
17543
  this.container = null;
16781
17544
  this.listeners = {};
16782
17545
  }