@splnlss/3d-story-viewer 0.8.1 → 0.8.2

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.
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  var _a2;
5
- import { S as ShaderChunk, M as Matrix4, V as Vector3, O as Object3D, a as Vector2, b as Mesh, P as PlaneGeometry, c as ShaderMaterial, R as Ray, E as EventDispatcher$1, d as Plane, e as Raycaster, C as Clock, Q as Quaternion, f as MathUtils, T as TrianglesDrawMode, g as TriangleFanDrawMode, h as TriangleStripDrawMode, L as Loader, i as LoaderUtils, F as FileLoader, j as MeshPhysicalMaterial, k as Color, l as LinearSRGBColorSpace, m as SRGBColorSpace, n as SpotLight, o as PointLight, D as DirectionalLight, I as InstancedMesh, p as InstancedBufferAttribute, B as BufferGeometry, q as TextureLoader, r as ImageBitmapLoader, s as BufferAttribute, t as InterleavedBuffer, u as LinearMipmapLinearFilter, N as NearestMipmapLinearFilter, v as LinearMipmapNearestFilter, w as NearestMipmapNearestFilter, x as LinearFilter, y as NearestFilter, z as RepeatWrapping, A as MirroredRepeatWrapping, G as ClampToEdgeWrapping, H as PointsMaterial, J as Material, K as LineBasicMaterial, U as MeshStandardMaterial, W as DoubleSide, X as MeshBasicMaterial, Y as PropertyBinding, Z as SkinnedMesh, _ as LineSegments, $ as Line, a0 as LineLoop, a1 as Points, a2 as Group$1, a3 as PerspectiveCamera, a4 as OrthographicCamera, a5 as Skeleton, a6 as AnimationClip, a7 as Bone, a8 as InterpolateDiscrete, a9 as InterpolateLinear, aa as InterleavedBufferAttribute, ab as Texture, ac as VectorKeyframeTrack, ad as NumberKeyframeTrack, ae as QuaternionKeyframeTrack, af as ColorManagement, ag as FrontSide, ah as Interpolant, ai as Box3, aj as Sphere, ak as Euler, al as Spherical, am as LoadingManager, an as Frustum, ao as DefaultLoadingManager, ap as Matrix3, aq as TextureUtils, ar as CompressedCubeTexture, as as CompressedArrayTexture, at as CompressedTexture, au as NoColorSpace, av as RGBA_PVRTC_2BPPV1_Format, aw as RGBA_PVRTC_4BPPV1_Format, ax as RGBA_BPTC_Format, ay as RED_GREEN_RGTC2_Format, az as SIGNED_RED_GREEN_RGTC2_Format, aA as RED_RGTC1_Format, aB as SIGNED_RED_RGTC1_Format, aC as RGBA_S3TC_DXT5_Format, aD as RGB_S3TC_DXT1_Format, aE as RGBA_S3TC_DXT1_Format, aF as RGBA_ASTC_6x6_Format, aG as RGBA_ASTC_4x4_Format, aH as SIGNED_RG11_EAC_Format, aI as RG11_EAC_Format, aJ as SIGNED_R11_EAC_Format, aK as R11_EAC_Format, aL as RGB_ETC2_Format, aM as RGBA_ETC2_EAC_Format, aN as RGBFormat, aO as RedFormat, aP as RGFormat, aQ as RGBAFormat, aR as UnsignedByteType, aS as HalfFloatType, aT as UnsignedInt101111Type, aU as UnsignedInt5999Type, aV as UnsignedShortType, aW as FloatType, aX as DataTexture, aY as Data3DTexture, aZ as RGB_PVRTC_4BPPV1_Format, a_ as RGB_ETC1_Format, a$ as RGB_BPTC_UNSIGNED_Format, b0 as Float32BufferAttribute, b1 as LinearMipMapLinearFilter, b2 as Triangle, b3 as CanvasTexture, b4 as WebGLRenderer, b5 as WebGLRenderTarget, b6 as OneFactor, b7 as ZeroFactor, b8 as CustomBlending, b9 as Box2, ba as Matrix2, bb as Vector4, bc as DataTextureLoader, bd as DataUtils, be as REVISION, bf as TextureSource, bg as ImageUtils, bh as Scene, bi as ecefToGeodeticPosition, bj as getEnuBasis, bk as BackSide, bl as BoxGeometry, bm as normalizeLayerOffset, bn as geodeticOffsetToEcefDelta, bo as validateGeodeticPosition, bp as validateTuple, bq as validateScale, br as geodeticToEcefPosition, bs as AgXToneMapping, bt as EquirectangularReflectionMapping, bu as validateRotation, bv as requireFiniteNumber } from "./index-DaqGSC4W.js";
5
+ import { S as ShaderChunk, M as Matrix4, V as Vector3, O as Object3D, a as Vector2, b as Mesh, P as PlaneGeometry, c as ShaderMaterial, R as Ray, E as EventDispatcher$1, d as Plane, e as Raycaster, C as Clock, Q as Quaternion, f as MathUtils, T as TrianglesDrawMode, g as TriangleFanDrawMode, h as TriangleStripDrawMode, L as Loader, i as LoaderUtils, F as FileLoader, j as MeshPhysicalMaterial, k as Color, l as LinearSRGBColorSpace, m as SRGBColorSpace, n as SpotLight, o as PointLight, D as DirectionalLight, I as InstancedMesh, p as InstancedBufferAttribute, B as BufferGeometry, q as TextureLoader, r as ImageBitmapLoader, s as BufferAttribute, t as InterleavedBuffer, u as LinearMipmapLinearFilter, N as NearestMipmapLinearFilter, v as LinearMipmapNearestFilter, w as NearestMipmapNearestFilter, x as LinearFilter, y as NearestFilter, z as RepeatWrapping, A as MirroredRepeatWrapping, G as ClampToEdgeWrapping, H as PointsMaterial, J as Material, K as LineBasicMaterial, U as MeshStandardMaterial, W as DoubleSide, X as MeshBasicMaterial, Y as PropertyBinding, Z as SkinnedMesh, _ as LineSegments, $ as Line, a0 as LineLoop, a1 as Points, a2 as Group$1, a3 as PerspectiveCamera, a4 as OrthographicCamera, a5 as Skeleton, a6 as AnimationClip, a7 as Bone, a8 as InterpolateDiscrete, a9 as InterpolateLinear, aa as InterleavedBufferAttribute, ab as Texture, ac as VectorKeyframeTrack, ad as NumberKeyframeTrack, ae as QuaternionKeyframeTrack, af as ColorManagement, ag as FrontSide, ah as Interpolant, ai as Box3, aj as Sphere, ak as Euler, al as Spherical, am as LoadingManager, an as Frustum, ao as DefaultLoadingManager, ap as Matrix3, aq as TextureUtils, ar as CompressedCubeTexture, as as CompressedArrayTexture, at as CompressedTexture, au as NoColorSpace, av as RGBA_PVRTC_2BPPV1_Format, aw as RGBA_PVRTC_4BPPV1_Format, ax as RGBA_BPTC_Format, ay as RED_GREEN_RGTC2_Format, az as SIGNED_RED_GREEN_RGTC2_Format, aA as RED_RGTC1_Format, aB as SIGNED_RED_RGTC1_Format, aC as RGBA_S3TC_DXT5_Format, aD as RGB_S3TC_DXT1_Format, aE as RGBA_S3TC_DXT1_Format, aF as RGBA_ASTC_6x6_Format, aG as RGBA_ASTC_4x4_Format, aH as SIGNED_RG11_EAC_Format, aI as RG11_EAC_Format, aJ as SIGNED_R11_EAC_Format, aK as R11_EAC_Format, aL as RGB_ETC2_Format, aM as RGBA_ETC2_EAC_Format, aN as RGBFormat, aO as RedFormat, aP as RGFormat, aQ as RGBAFormat, aR as UnsignedByteType, aS as HalfFloatType, aT as UnsignedInt101111Type, aU as UnsignedInt5999Type, aV as UnsignedShortType, aW as FloatType, aX as DataTexture, aY as Data3DTexture, aZ as RGB_PVRTC_4BPPV1_Format, a_ as RGB_ETC1_Format, a$ as RGB_BPTC_UNSIGNED_Format, b0 as Float32BufferAttribute, b1 as LinearMipMapLinearFilter, b2 as Triangle, b3 as CanvasTexture, b4 as WebGLRenderer, b5 as WebGLRenderTarget, b6 as OneFactor, b7 as ZeroFactor, b8 as CustomBlending, b9 as Box2, ba as Matrix2, bb as Vector4, bc as DataTextureLoader, bd as DataUtils, be as REVISION, bf as TextureSource, bg as ImageUtils, bh as Scene, bi as ecefToGeodeticPosition, bj as getEnuBasis, bk as BackSide, bl as BoxGeometry, bm as normalizeLayerOffset, bn as geodeticOffsetToEcefDelta, bo as validateGeodeticPosition, bp as validateTuple, bq as validateScale, br as geodeticToEcefPosition, bs as enuToEcefDelta, bt as RingGeometry, bu as AgXToneMapping, bv as EquirectangularReflectionMapping, bw as validateRotation, bx as requireFiniteNumber } from "./index-BgprA821.js";
6
6
  var bayer_dither_hash_pars_default$1 = "#ifdef USE_HASH\nuniform float hashOpacity;varying float vCameraDistance;float bayerDitherPattern(vec2 p){float bayerMatrix[16];bayerMatrix[0]=0.0;bayerMatrix[1]=8.0;bayerMatrix[2]=2.0;bayerMatrix[3]=10.0;bayerMatrix[4]=12.0;bayerMatrix[5]=4.0;bayerMatrix[6]=14.0;bayerMatrix[7]=6.0;bayerMatrix[8]=3.0;bayerMatrix[9]=11.0;bayerMatrix[10]=1.0;bayerMatrix[11]=9.0;bayerMatrix[12]=15.0;bayerMatrix[13]=7.0;bayerMatrix[14]=13.0;bayerMatrix[15]=5.0;int x=int(mod(p.x,4.0));int y=int(mod(p.y,4.0));int index=x+y*4;return bayerMatrix[index]/16.0;}\n#endif\n";
7
7
  var bayer_dither_hash_default$1 = "#ifdef USE_HASH\nfloat hashAmount=smoothstep(5e4,1e2,vCameraDistance)*0.75+0.1;float ditherValue=bayerDitherPattern(gl_FragCoord.xy);if(hashAmount<ditherValue){discard;}\n#endif\n";
8
8
  var bayer_dither_hash_pars_default = "#ifdef USE_HASH\nvarying float vCameraDistance;\n#endif\n";
@@ -156,7 +156,7 @@ class CSS2DRenderer {
156
156
  }
157
157
  let sparkPromise = null;
158
158
  function loadSpark() {
159
- sparkPromise ?? (sparkPromise = import("./spark.module-BGYmUUum.js").catch((error) => {
159
+ sparkPromise ?? (sparkPromise = import("./spark.module-T7c-X7OV.js").catch((error) => {
160
160
  sparkPromise = null;
161
161
  throw error;
162
162
  }));
@@ -7711,8 +7711,14 @@ class TileDecoders {
7711
7711
  this.disposed = false;
7712
7712
  }
7713
7713
  getDraco() {
7714
+ var _a3, _b2;
7714
7715
  if (this.disposed) throw new Error("Tile decoders disposed");
7715
- return this.draco ?? (this.draco = managedLoader(new DRACOLoader(), "_initDecoder", "decodeGeometry", "decoderPending"));
7716
+ if (!this.draco) {
7717
+ const loader = new DRACOLoader();
7718
+ if ((_b2 = (_a3 = globalThis.navigator) == null ? void 0 : _a3.userAgent) == null ? void 0 : _b2.includes("OculusBrowser/")) loader.decoderConfig.type = "js";
7719
+ this.draco = managedLoader(loader, "_initDecoder", "decodeGeometry", "decoderPending");
7720
+ }
7721
+ return this.draco;
7716
7722
  }
7717
7723
  getKTX2() {
7718
7724
  if (this.disposed) throw new Error("Tile decoders disposed");
@@ -7730,6 +7736,17 @@ class TileDecoders {
7730
7736
  (_b2 = this.ktx2) == null ? void 0 : _b2.dispose();
7731
7737
  }
7732
7738
  }
7739
+ function updateTilesCamera(tiles, camera, renderer) {
7740
+ var _a3;
7741
+ tiles.setCamera(camera);
7742
+ const eye = ((_a3 = renderer.xr) == null ? void 0 : _a3.isPresenting) ? renderer.xr.getCamera().cameras[0] : null;
7743
+ const viewport = eye == null ? void 0 : eye.viewport;
7744
+ if (viewport && viewport.width > 0 && viewport.height > 0) {
7745
+ tiles.setResolution(camera, viewport.width, viewport.height);
7746
+ } else {
7747
+ tiles.setResolutionFromRenderer(camera, renderer);
7748
+ }
7749
+ }
7733
7750
  var i = class {
7734
7751
  constructor(e = {}) {
7735
7752
  let { apiToken: t2, autoRefreshToken: n2 = false } = e;
@@ -11093,8 +11110,7 @@ class GlobeManager extends EventDispatcher {
11093
11110
  })
11094
11111
  );
11095
11112
  this.scene.add(this.tiles.group);
11096
- this.tiles.setResolutionFromRenderer(this.camera, this.renderer);
11097
- this.tiles.setCamera(this.camera);
11113
+ updateTilesCamera(this.tiles, this.camera, this.renderer);
11098
11114
  this.tiles.addEventListener("load-model", ({ scene }) => {
11099
11115
  scene.traverse((c3) => {
11100
11116
  if (Array.isArray(c3.material)) {
@@ -11108,8 +11124,7 @@ class GlobeManager extends EventDispatcher {
11108
11124
  }
11109
11125
  update() {
11110
11126
  if (!this.tiles) return;
11111
- this.tiles.setResolutionFromRenderer(this.camera, this.renderer);
11112
- this.tiles.setCamera(this.camera);
11127
+ updateTilesCamera(this.tiles, this.camera, this.renderer);
11113
11128
  this.tiles.errorTarget = 20;
11114
11129
  this.tiles.update();
11115
11130
  }
@@ -14578,8 +14593,7 @@ class TilesLayer extends ContentLayer {
14578
14593
  tiles.parseQueue = this.firstSet.parseQueue;
14579
14594
  tiles.processNodeQueue = this.firstSet.processNodeQueue;
14580
14595
  }
14581
- tiles.setCamera(this.camera);
14582
- tiles.setResolutionFromRenderer(this.camera, this.renderer);
14596
+ updateTilesCamera(tiles, this.camera, this.renderer);
14583
14597
  tiles.addEventListener("load-model", (event) => {
14584
14598
  var _a3;
14585
14599
  if (hasOffset && !offsetApplied) {
@@ -14629,7 +14643,10 @@ class TilesLayer extends ContentLayer {
14629
14643
  }
14630
14644
  update() {
14631
14645
  super.update();
14632
- if (this.visible) this.tiles.update();
14646
+ if (this.visible) {
14647
+ updateTilesCamera(this.tiles, this.camera, this.renderer);
14648
+ this.tiles.update();
14649
+ }
14633
14650
  }
14634
14651
  dispose() {
14635
14652
  super.dispose();
@@ -15167,8 +15184,9 @@ function raycastSurfaces(viewer, raycaster, options = {}) {
15167
15184
  return;
15168
15185
  }
15169
15186
  const hits = [];
15170
- (_b3 = object.raycast) == null ? void 0 : _b3.call(object, raycaster, hits);
15187
+ const traverseChildren = (_b3 = object.raycast) == null ? void 0 : _b3.call(object, raycaster, hits);
15171
15188
  for (const hit of hits) accept(hit, kind, layerId);
15189
+ if (traverseChildren === false) return;
15172
15190
  for (const child of object.children) visit(child, kind, layerId);
15173
15191
  };
15174
15192
  const previousMask = raycaster.layers.mask;
@@ -15192,6 +15210,653 @@ function raycastSurfaces(viewer, raycaster, options = {}) {
15192
15210
  }
15193
15211
  return nearest;
15194
15212
  }
15213
+ const normalizeDegrees = (degrees) => (degrees % 360 + 360) % 360;
15214
+ class XRPlayerRig {
15215
+ /** @param {Matrix4} initialCameraWorld @param {XRRigidTransform} initialHeadPose */
15216
+ constructor(initialCameraWorld, initialHeadPose) {
15217
+ this.headLocal = new Matrix4().fromArray(initialHeadPose.matrix);
15218
+ const cameraPosition = new Vector3().setFromMatrixPosition(initialCameraWorld);
15219
+ const geo = ecefToGeodeticPosition(...cameraPosition.toArray());
15220
+ const { east, north, up } = getEnuBasis(geo.lon, geo.lat);
15221
+ const eastAxis = new Vector3().fromArray(east);
15222
+ const northAxis = new Vector3().fromArray(north);
15223
+ const cameraForward = new Vector3(0, 0, -1).transformDirection(initialCameraWorld);
15224
+ const desktopHeading = Math.atan2(cameraForward.dot(eastAxis), cameraForward.dot(northAxis));
15225
+ const trackedForward = new Vector3(0, 0, -1).transformDirection(this.headLocal);
15226
+ const trackedHeading = Math.atan2(trackedForward.x, -trackedForward.z);
15227
+ this.worldFromLocal = new Matrix4().makeBasis(eastAxis, new Vector3().fromArray(up), northAxis.negate()).multiply(new Matrix4().makeRotationY(trackedHeading - desktopHeading));
15228
+ const headOffset = new Vector3().setFromMatrixPosition(this.headLocal).applyMatrix4(this.worldFromLocal);
15229
+ this.worldFromLocal.setPosition(cameraPosition.sub(headOffset));
15230
+ this.anchor = this.getHeadPose().geodetic;
15231
+ }
15232
+ /** @param {XRRigidTransform} pose */
15233
+ updateHeadPose(pose) {
15234
+ this.headLocal.fromArray(pose.matrix);
15235
+ }
15236
+ getWorldFromLocalMatrix() {
15237
+ return this.worldFromLocal.clone();
15238
+ }
15239
+ getWorldHeadMatrix() {
15240
+ return this.worldFromLocal.clone().multiply(this.headLocal);
15241
+ }
15242
+ /** @returns {XRHeadPose} WGS84 degrees, ellipsoidal altitude metres, ECEF metres, local-floor metres. */
15243
+ getHeadPose() {
15244
+ const world = this.getWorldHeadMatrix();
15245
+ const position = new Vector3().setFromMatrixPosition(world);
15246
+ const geo = ecefToGeodeticPosition(...position.toArray());
15247
+ const basis = getEnuBasis(geo.lon, geo.lat);
15248
+ const forward = new Vector3(0, 0, -1).transformDirection(world);
15249
+ const headingDegrees = normalizeDegrees(Math.atan2(forward.dot(new Vector3().fromArray(basis.east)), forward.dot(new Vector3().fromArray(basis.north))) * 180 / Math.PI);
15250
+ return {
15251
+ worldEcef: position.toArray(),
15252
+ geodetic: { longitude: geo.lon, latitude: geo.lat, altitude: geo.alt },
15253
+ headingDegrees,
15254
+ localHeadOffset: new Vector3().setFromMatrixPosition(this.headLocal).toArray()
15255
+ };
15256
+ }
15257
+ getNavigationFrame() {
15258
+ const { longitude, latitude, altitude } = this.anchor;
15259
+ const basis = getEnuBasis(longitude, latitude);
15260
+ const frame = new Matrix4().makeBasis(new Vector3().fromArray(basis.east), new Vector3().fromArray(basis.up), new Vector3().fromArray(basis.north).negate());
15261
+ frame.setPosition(new Vector3().fromArray(geodeticToEcefPosition(longitude, latitude, altitude)));
15262
+ return frame;
15263
+ }
15264
+ /** Convert the copied WebXR target-ray transform to ECEF metres. */
15265
+ getControllerWorldRay(controller) {
15266
+ var _a3, _b2;
15267
+ const ray = controller == null ? void 0 : controller.ray;
15268
+ if (!ray || ((_a3 = ray.position) == null ? void 0 : _a3.length) !== 3 || ((_b2 = ray.orientation) == null ? void 0 : _b2.length) !== 4 || ![...ray.position, ...ray.orientation].every(Number.isFinite)) return null;
15269
+ const orientation = new Quaternion(...ray.orientation);
15270
+ if (orientation.lengthSq() < 1e-12) return null;
15271
+ const world = this.getWorldFromLocalMatrix();
15272
+ const origin = new Vector3(...ray.position).applyMatrix4(world);
15273
+ const direction = new Vector3(0, 0, -1).applyQuaternion(orientation.normalize()).transformDirection(world);
15274
+ return { origin: origin.toArray(), direction: direction.toArray() };
15275
+ }
15276
+ /**
15277
+ * Places the tracked head at a WGS84 ellipsoidal position plus ENU metres.
15278
+ * Heading is clockwise degrees from true north. Physical room-scale motion remains tracked.
15279
+ */
15280
+ setPose({ anchor, east = 0, north = 0, up = 0, headingDegrees }) {
15281
+ const head = this.getHeadPose();
15282
+ const target = new Vector3().fromArray(geodeticToEcefPosition(anchor.longitude, anchor.latitude, anchor.altitude));
15283
+ target.add(new Vector3().fromArray(enuToEcefDelta([east, north, up], anchor.longitude, anchor.latitude)));
15284
+ const delta = target.clone().sub(new Vector3().fromArray(head.worldEcef));
15285
+ this.worldFromLocal.setPosition(new Vector3().setFromMatrixPosition(this.worldFromLocal).add(delta));
15286
+ if (Number.isFinite(headingDegrees)) {
15287
+ let turn = normalizeDegrees(headingDegrees - head.headingDegrees);
15288
+ if (turn > 180) turn -= 360;
15289
+ if (Math.abs(turn) > 1e-10) {
15290
+ const upAxis = new Vector3().fromArray(getEnuBasis(anchor.longitude, anchor.latitude).up);
15291
+ const rotation = new Matrix4().makeRotationFromQuaternion(new Quaternion().setFromAxisAngle(upAxis, -turn * Math.PI / 180));
15292
+ this.worldFromLocal.premultiply(new Matrix4().makeTranslation(-target.x, -target.y, -target.z));
15293
+ this.worldFromLocal.premultiply(rotation);
15294
+ this.worldFromLocal.premultiply(new Matrix4().makeTranslation(target.x, target.y, target.z));
15295
+ }
15296
+ }
15297
+ const anchorEcef = new Vector3().fromArray(geodeticToEcefPosition(this.anchor.longitude, this.anchor.latitude, this.anchor.altitude));
15298
+ if (target.distanceTo(anchorEcef) > 100) this.reanchor(headingDegrees);
15299
+ }
15300
+ reanchor(headingDegrees) {
15301
+ const head = this.getHeadPose();
15302
+ const { longitude, latitude } = head.geodetic;
15303
+ const { east, north, up } = getEnuBasis(longitude, latitude);
15304
+ const trackedForward = new Vector3(0, 0, -1).transformDirection(this.headLocal);
15305
+ const trackedHeading = Math.atan2(trackedForward.x, -trackedForward.z);
15306
+ const worldHeading = (Number.isFinite(headingDegrees) ? headingDegrees : head.headingDegrees) * Math.PI / 180;
15307
+ const frame = new Matrix4().makeBasis(new Vector3().fromArray(east), new Vector3().fromArray(up), new Vector3().fromArray(north).negate()).multiply(new Matrix4().makeRotationY(trackedHeading - worldHeading));
15308
+ const localOffset = new Vector3().setFromMatrixPosition(this.headLocal).applyMatrix4(frame);
15309
+ frame.setPosition(new Vector3(...head.worldEcef).sub(localOffset));
15310
+ this.worldFromLocal.copy(frame);
15311
+ this.anchor = head.geodetic;
15312
+ }
15313
+ }
15314
+ const HEAD_LOCAL_OFFSET = new Matrix4().makeTranslation(0, 0, -0.5);
15315
+ class XRComfortOverlay {
15316
+ constructor() {
15317
+ this.material = new ShaderMaterial({
15318
+ transparent: true,
15319
+ depthTest: false,
15320
+ depthWrite: false,
15321
+ side: DoubleSide,
15322
+ uniforms: { vignette: { value: 0 }, limit: { value: 0 }, fade: { value: 0 } },
15323
+ vertexShader: "varying vec2 uvOut; void main() { uvOut = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }",
15324
+ fragmentShader: "varying vec2 uvOut; uniform float vignette; uniform float limit; uniform float fade; void main() { float edge = smoothstep(0.48, 1.0, length((uvOut - 0.5) * 2.0)); vec3 color = limit > 2.5 ? vec3(0.2, 0.65, 1.0) : limit > 1.5 ? vec3(0.95, 0.65, 0.12) : vec3(0.95, 0.3, 0.08); float alpha = limit > 0.5 ? 0.48 : vignette; gl_FragColor = vec4(mix(limit > 0.5 ? color : vec3(0.0), vec3(0.0), fade), max(edge * alpha, fade)); }"
15325
+ });
15326
+ this.mesh = new Mesh(new PlaneGeometry(2.5, 2.5), this.material);
15327
+ this.mesh.matrixAutoUpdate = false;
15328
+ this.mesh.layers.set(31);
15329
+ this.mesh.renderOrder = 10001;
15330
+ this.mesh.visible = false;
15331
+ this.moving = false;
15332
+ this.navigationLimit = null;
15333
+ this.fade = 0;
15334
+ }
15335
+ setNavigationLimit(reason) {
15336
+ const next = reason === "floor" || reason === "unknown" || reason === "loading" ? reason : null;
15337
+ if (this.navigationLimit === next) return false;
15338
+ this.navigationLimit = next;
15339
+ return true;
15340
+ }
15341
+ setFade(opacity) {
15342
+ this.fade = Number.isFinite(opacity) ? Math.min(1, Math.max(0, opacity)) : 0;
15343
+ }
15344
+ update(headMatrix) {
15345
+ this.mesh.matrix.copy(headMatrix).multiply(HEAD_LOCAL_OFFSET);
15346
+ this.mesh.matrixWorldNeedsUpdate = true;
15347
+ this.material.uniforms.vignette.value = this.moving ? 0.65 : 0;
15348
+ this.material.uniforms.limit.value = this.navigationLimit === "floor" ? 1 : this.navigationLimit === "unknown" ? 2 : this.navigationLimit === "loading" ? 3 : 0;
15349
+ this.material.uniforms.fade.value = this.fade;
15350
+ this.mesh.visible = this.moving || this.navigationLimit !== null || this.fade > 0;
15351
+ }
15352
+ dispose() {
15353
+ this.mesh.geometry.dispose();
15354
+ this.material.dispose();
15355
+ }
15356
+ }
15357
+ const COLORS = { valid: 5242789, blocked: 16747366, unknown: 16765802 };
15358
+ class XRNavigationCue {
15359
+ constructor(scene) {
15360
+ this.scene = scene;
15361
+ this.group = new Group$1();
15362
+ this.group.name = "XRNavigationCue";
15363
+ this.group.matrixAutoUpdate = false;
15364
+ this.group.visible = false;
15365
+ this.scene.add(this.group);
15366
+ }
15367
+ set(cue) {
15368
+ this.clearChildren();
15369
+ if (!cue) {
15370
+ this.group.visible = false;
15371
+ return;
15372
+ }
15373
+ const { anchor, arc, marker, state } = cue;
15374
+ if (!anchor || ![anchor.longitude, anchor.latitude, anchor.altitude].every(Number.isFinite) || !Array.isArray(arc) || arc.length > 17 || !["valid", "blocked", "unknown"].includes(state)) {
15375
+ this.group.visible = false;
15376
+ return;
15377
+ }
15378
+ const basis = getEnuBasis(anchor.longitude, anchor.latitude);
15379
+ const east = new Vector3().fromArray(basis.east);
15380
+ const up = new Vector3().fromArray(basis.up);
15381
+ const south = new Vector3().fromArray(basis.north).negate();
15382
+ this.group.matrix.copy(new Matrix4().makeBasis(east, up, south));
15383
+ this.group.matrix.setPosition(...geodeticToEcefPosition(anchor.longitude, anchor.latitude, anchor.altitude));
15384
+ this.group.updateMatrixWorld(true);
15385
+ const color = COLORS[state];
15386
+ if (arc.length >= 2 && arc.every((point) => (point == null ? void 0 : point.length) === 3 && point.every(Number.isFinite))) {
15387
+ const geometry = new BufferGeometry();
15388
+ geometry.setAttribute("position", new Float32BufferAttribute(arc.flatMap(([e, n2, u2]) => [e, u2, -n2]), 3));
15389
+ const line = new Line(geometry, new LineBasicMaterial({ color, depthTest: true, depthWrite: false }));
15390
+ line.layers.set(0);
15391
+ this.group.add(line);
15392
+ }
15393
+ if ((marker == null ? void 0 : marker.length) === 3 && marker.every(Number.isFinite)) {
15394
+ const ring = new Mesh(new RingGeometry(0.13, 0.19, 24), new MeshBasicMaterial({ color, side: DoubleSide, depthTest: true, depthWrite: false }));
15395
+ ring.rotation.x = -Math.PI / 2;
15396
+ ring.position.set(marker[0], marker[2] + 0.03, -marker[1]);
15397
+ ring.layers.set(0);
15398
+ this.group.add(ring);
15399
+ const stem = new BufferGeometry().setFromPoints([new Vector3(marker[0], marker[2] + 0.03, -marker[1]), new Vector3(marker[0], marker[2] + 0.23, -marker[1])]);
15400
+ const whisker = new Line(stem, new LineBasicMaterial({ color, depthTest: true, depthWrite: false }));
15401
+ whisker.layers.set(0);
15402
+ this.group.add(whisker);
15403
+ }
15404
+ this.group.visible = this.group.children.length > 0;
15405
+ }
15406
+ clearChildren() {
15407
+ var _a3, _b2;
15408
+ for (const child of [...this.group.children]) {
15409
+ this.group.remove(child);
15410
+ (_a3 = child.geometry) == null ? void 0 : _a3.dispose();
15411
+ (_b2 = child.material) == null ? void 0 : _b2.dispose();
15412
+ }
15413
+ }
15414
+ dispose() {
15415
+ this.clearChildren();
15416
+ this.scene.remove(this.group);
15417
+ }
15418
+ }
15419
+ const cesiumLogoUrl = "data:image/png;base64,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";
15420
+ const googleLogoUrl = "data:image/png;base64,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";
15421
+ const HEAD_PANEL_OFFSET = new Matrix4().makeTranslation(-0.35, -0.55, -1.2);
15422
+ function attributionText(tileAttributions = [], terrainCredits = []) {
15423
+ const values = [
15424
+ ...tileAttributions.filter(({ type, value }) => type === "string" && typeof value === "string").map(({ value }) => value),
15425
+ ...terrainCredits.filter((value) => typeof value === "string")
15426
+ ];
15427
+ return [...new Set(values.map((value) => value.replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&nbsp;/g, " ").replace(/&lt;/g, "<").replace(/&gt;/g, ">").trim()).filter(Boolean))];
15428
+ }
15429
+ function wrap(ctx, value, maxWidth) {
15430
+ const lines = [];
15431
+ let line = "";
15432
+ const words = value.split(/\s+/).flatMap((word) => {
15433
+ if (ctx.measureText(word).width <= maxWidth) return [word];
15434
+ const pieces = [];
15435
+ let piece = "";
15436
+ for (const character of word) {
15437
+ if (piece && ctx.measureText(piece + character).width > maxWidth) {
15438
+ pieces.push(piece);
15439
+ piece = "";
15440
+ }
15441
+ piece += character;
15442
+ }
15443
+ if (piece) pieces.push(piece);
15444
+ return pieces;
15445
+ });
15446
+ for (const word of words) {
15447
+ const candidate = line ? `${line} ${word}` : word;
15448
+ if (line && ctx.measureText(candidate).width > maxWidth) {
15449
+ lines.push(line);
15450
+ line = word;
15451
+ } else line = candidate;
15452
+ }
15453
+ if (line) lines.push(line);
15454
+ return lines;
15455
+ }
15456
+ function layoutAttributionLines(ctx, values) {
15457
+ const lines = values.flatMap((value) => wrap(ctx, value, 540));
15458
+ const rows = Math.max(1, Math.ceil(lines.length / 2));
15459
+ const lineHeight = Math.min(38, 230 / rows);
15460
+ const fontSize = Math.min(32, lineHeight - 4);
15461
+ return lines.map((text, index) => ({
15462
+ text,
15463
+ x: index < rows ? 30 : 630,
15464
+ y: 120 + index % rows * lineHeight,
15465
+ fontSize
15466
+ }));
15467
+ }
15468
+ class XRAttributionPanel {
15469
+ constructor() {
15470
+ const canvas = document.createElement("canvas");
15471
+ canvas.width = 1200;
15472
+ canvas.height = 360;
15473
+ this.canvas = canvas;
15474
+ this.context = canvas.getContext("2d");
15475
+ this.texture = new CanvasTexture(canvas);
15476
+ this.texture.colorSpace = SRGBColorSpace;
15477
+ 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 }));
15478
+ this.mesh.name = "XRAttributionPanel";
15479
+ this.mesh.matrixAutoUpdate = false;
15480
+ this.mesh.layers.set(31);
15481
+ this.mesh.renderOrder = 10002;
15482
+ this.terrainCredits = [];
15483
+ this.lastText = "";
15484
+ this.lastTileCheckMs = -Infinity;
15485
+ this.tileCredits = [];
15486
+ this.logos = [];
15487
+ for (const url of [cesiumLogoUrl, googleLogoUrl]) {
15488
+ const image = new Image();
15489
+ image.onload = () => this.draw();
15490
+ image.src = url;
15491
+ this.logos.push(image);
15492
+ }
15493
+ this.draw();
15494
+ }
15495
+ setTerrainCredits(credits) {
15496
+ this.terrainCredits = Array.isArray(credits) ? credits : [];
15497
+ this.draw();
15498
+ }
15499
+ update(headMatrix, tiles, timeMs) {
15500
+ var _a3;
15501
+ this.mesh.matrix.copy(headMatrix).multiply(HEAD_PANEL_OFFSET);
15502
+ this.mesh.matrixWorldNeedsUpdate = true;
15503
+ if (timeMs - this.lastTileCheckMs >= 1e3) {
15504
+ this.lastTileCheckMs = timeMs;
15505
+ this.tileCredits = ((_a3 = tiles == null ? void 0 : tiles.getAttributions) == null ? void 0 : _a3.call(tiles)) ?? [];
15506
+ this.draw();
15507
+ }
15508
+ }
15509
+ draw() {
15510
+ var _a3, _b2;
15511
+ const ctx = this.context;
15512
+ if (!ctx) return;
15513
+ const lines = attributionText(this.tileCredits, this.terrainCredits);
15514
+ const signature = lines.join("\n") + ":" + this.logos.map((logo) => logo.complete && logo.naturalWidth > 0).join(",");
15515
+ if (signature === this.lastText) return;
15516
+ this.lastText = signature;
15517
+ ctx.clearRect(0, 0, 1200, 360);
15518
+ ctx.fillStyle = "rgba(255,255,255,0.82)";
15519
+ ctx.fillRect(0, 0, 1200, 360);
15520
+ ctx.fillStyle = "#1b2430";
15521
+ ctx.font = "bold 28px Arial, sans-serif";
15522
+ if (((_a3 = this.logos[0]) == null ? void 0 : _a3.complete) && this.logos[0].naturalWidth > 0) ctx.drawImage(this.logos[0], 30, 22, 294, 50);
15523
+ else ctx.fillText("Cesium ion", 30, 61);
15524
+ if (((_b2 = this.logos[1]) == null ? void 0 : _b2.complete) && this.logos[1].naturalWidth > 0) ctx.drawImage(this.logos[1], 390, 28, 272, 50);
15525
+ else ctx.fillText("Google Maps", 390, 61);
15526
+ ctx.font = "32px Arial, sans-serif";
15527
+ for (const line of layoutAttributionLines(ctx, lines.length ? lines : ["Google Maps 3D Tiles"])) {
15528
+ ctx.font = `${line.fontSize}px Arial, sans-serif`;
15529
+ ctx.fillText(line.text, line.x, line.y);
15530
+ }
15531
+ this.texture.needsUpdate = true;
15532
+ }
15533
+ dispose() {
15534
+ this.mesh.geometry.dispose();
15535
+ this.mesh.material.dispose();
15536
+ this.texture.dispose();
15537
+ }
15538
+ }
15539
+ function createXRRenderBridge(viewer, initialHeadPose, { showAttribution = true } = {}) {
15540
+ const { camera, renderer } = viewer;
15541
+ if (camera.parent !== null) throw new Error("WebXR camera bridge requires an unparented viewer camera");
15542
+ camera.updateMatrixWorld(true);
15543
+ const rig = new XRPlayerRig(camera.matrixWorld, initialHeadPose);
15544
+ const anchor = new Group$1();
15545
+ anchor.matrixAutoUpdate = false;
15546
+ anchor.matrix.copy(rig.getWorldFromLocalMatrix());
15547
+ anchor.updateMatrixWorld(true);
15548
+ viewer.scene.add(anchor);
15549
+ const originalRender = renderer.render;
15550
+ const originalCameraAutoUpdate = renderer.xr.cameraAutoUpdate;
15551
+ let currentHead = camera.matrix.clone().fromArray(initialHeadPose.matrix);
15552
+ let disposed = false;
15553
+ const comfort = new XRComfortOverlay();
15554
+ const attribution = !showAttribution || typeof document === "undefined" || typeof Image === "undefined" ? null : new XRAttributionPanel();
15555
+ const navigationCue = new XRNavigationCue(viewer.scene);
15556
+ anchor.add(comfort.mesh);
15557
+ if (attribution) anchor.add(attribution.mesh);
15558
+ const restoreWorldPose = (world) => {
15559
+ if (camera.parent === anchor) anchor.remove(camera);
15560
+ world.decompose(camera.position, camera.quaternion, camera.scale);
15561
+ camera.updateMatrixWorld(true);
15562
+ };
15563
+ const renderBridge = (scene, renderCamera) => {
15564
+ var _a3;
15565
+ if (disposed || renderCamera !== camera) return originalRender.call(renderer, scene, renderCamera);
15566
+ camera.updateMatrixWorld(true);
15567
+ const worldBeforeRender = camera.matrixWorld.clone();
15568
+ anchor.matrix.copy(rig.getWorldFromLocalMatrix());
15569
+ anchor.updateMatrixWorld(true);
15570
+ comfort.update(currentHead);
15571
+ attribution == null ? void 0 : attribution.update(currentHead, (_a3 = viewer.globeManager) == null ? void 0 : _a3.tiles, performance.now());
15572
+ anchor.add(camera);
15573
+ currentHead.decompose(camera.position, camera.quaternion, camera.scale);
15574
+ camera.updateMatrixWorld(true);
15575
+ try {
15576
+ if (renderer.xr.isPresenting) {
15577
+ renderer.xr.cameraAutoUpdate = false;
15578
+ renderer.xr.updateCamera(camera);
15579
+ const xrCamera = renderer.xr.getCamera();
15580
+ xrCamera.layers.mask = camera.layers.mask;
15581
+ for (const eye of xrCamera.cameras) eye.layers.mask = camera.layers.mask;
15582
+ }
15583
+ return originalRender.call(renderer, scene, renderCamera);
15584
+ } finally {
15585
+ restoreWorldPose(worldBeforeRender);
15586
+ }
15587
+ };
15588
+ renderer.render = renderBridge;
15589
+ return {
15590
+ getHeadPose: () => rig.getHeadPose(),
15591
+ getControllerWorldRay: (controller) => rig.getControllerWorldRay(controller),
15592
+ setNavigationCue: (cue) => navigationCue.set(cue),
15593
+ setTerrainCredits: (credits) => attribution == null ? void 0 : attribution.setTerrainCredits(credits),
15594
+ setPlayerPose(pose) {
15595
+ if (disposed) return;
15596
+ rig.setPose(pose);
15597
+ restoreWorldPose(rig.getWorldHeadMatrix());
15598
+ },
15599
+ hasUi() {
15600
+ return Boolean(attribution) || comfort.moving || comfort.navigationLimit !== null || comfort.fade > 0;
15601
+ },
15602
+ setNavigationLimit(reason) {
15603
+ if (comfort.setNavigationLimit(reason)) comfort.update(currentHead);
15604
+ },
15605
+ setComfortMoving(moving) {
15606
+ comfort.moving = Boolean(moving);
15607
+ comfort.update(currentHead);
15608
+ },
15609
+ setFade(opacity) {
15610
+ comfort.setFade(opacity);
15611
+ comfort.update(currentHead);
15612
+ },
15613
+ updateHeadPose(pose) {
15614
+ if (disposed) return;
15615
+ currentHead = camera.matrix.clone().fromArray(pose.matrix);
15616
+ rig.updateHeadPose(pose);
15617
+ comfort.update(currentHead);
15618
+ restoreWorldPose(rig.getWorldHeadMatrix());
15619
+ },
15620
+ dispose() {
15621
+ if (disposed) return;
15622
+ disposed = true;
15623
+ renderer.xr.cameraAutoUpdate = originalCameraAutoUpdate;
15624
+ if (renderer.render === renderBridge) renderer.render = originalRender;
15625
+ if (camera.parent === anchor) restoreWorldPose(rig.getWorldHeadMatrix());
15626
+ anchor.remove(comfort.mesh);
15627
+ comfort.dispose();
15628
+ if (attribution) {
15629
+ anchor.remove(attribution.mesh);
15630
+ attribution.dispose();
15631
+ }
15632
+ navigationCue.dispose();
15633
+ viewer.scene.remove(anchor);
15634
+ }
15635
+ };
15636
+ }
15637
+ class WebXRSession {
15638
+ /** @param {import('./index.js').default} viewer @param {{ onFrame?: (input: { time: number, frame: XRFrame, pose: XRViewerPose }) => void, onStateChange?: (active: boolean) => void, showAttribution?: boolean }} [options] */
15639
+ constructor(viewer, options = {}) {
15640
+ this.viewer = viewer;
15641
+ this.onFrame = options.onFrame;
15642
+ this.onStateChange = options.onStateChange;
15643
+ this.showAttribution = options.showAttribution !== false;
15644
+ this.session = null;
15645
+ this.starting = null;
15646
+ this.bridge = null;
15647
+ this.desktop = null;
15648
+ this.sparkProfiles = /* @__PURE__ */ new Map();
15649
+ this.disposed = false;
15650
+ this.stopping = false;
15651
+ this.endRequested = false;
15652
+ this.active = false;
15653
+ this.handleSessionEnd = () => this.cleanup();
15654
+ this.handleFrame = (time, frame) => this.frame(time, frame);
15655
+ }
15656
+ start() {
15657
+ var _a3;
15658
+ if (this.disposed) return Promise.reject(new Error("VR session has been disposed"));
15659
+ if (this.active) return Promise.resolve();
15660
+ if (this.starting) return this.starting;
15661
+ if (!((_a3 = globalThis.navigator) == null ? void 0 : _a3.xr)) return Promise.reject(new Error("Immersive VR is unavailable in this browser"));
15662
+ this.stopping = false;
15663
+ this.endRequested = false;
15664
+ let requested;
15665
+ try {
15666
+ requested = navigator.xr.requestSession("immersive-vr", {
15667
+ requiredFeatures: ["local-floor"],
15668
+ optionalFeatures: ["high-fixed-foveation-level"]
15669
+ });
15670
+ } catch (error) {
15671
+ return Promise.reject(error);
15672
+ }
15673
+ this.starting = this.activate(requested).finally(() => {
15674
+ this.starting = null;
15675
+ });
15676
+ return this.starting;
15677
+ }
15678
+ async activate(requested) {
15679
+ var _a3;
15680
+ const session = await requested;
15681
+ if (this.disposed || this.stopping) {
15682
+ await this.endSessionOnce(session);
15683
+ return;
15684
+ }
15685
+ this.session = session;
15686
+ const { viewer } = this;
15687
+ const { renderer, camera } = viewer;
15688
+ const xr2 = renderer.xr;
15689
+ camera.updateMatrixWorld(true);
15690
+ this.desktop = {
15691
+ cameraWorld: camera.matrixWorld.clone(),
15692
+ near: camera.near,
15693
+ far: camera.far,
15694
+ globeEnabled: viewer.globeControls.enabled,
15695
+ keysEnabled: viewer.keysEnabled,
15696
+ xrEnabled: xr2.enabled,
15697
+ foveation: xr2.getFoveation() ?? 1,
15698
+ referenceSpaceType: xr2.referenceSpaceType ?? "local-floor",
15699
+ framebufferScaleFactor: xr2.framebufferScaleFactor ?? 1,
15700
+ labelRender: viewer.css2DRenderer.render,
15701
+ ensureSparkRenderer: viewer.ensureSparkRenderer,
15702
+ transitionAnimateTo: viewer.transitionControls.animateTo,
15703
+ tilesSchedulerSession: i$1.session
15704
+ };
15705
+ try {
15706
+ viewer.setAnimate(false);
15707
+ viewer.transitionControls.cancel();
15708
+ viewer.transitionControls.animateTo = async () => false;
15709
+ viewer.globeControls.enabled = false;
15710
+ viewer.keysEnabled = false;
15711
+ viewer.css2DRenderer.render = () => void 0;
15712
+ camera.near = 0.25;
15713
+ camera.updateProjectionMatrix();
15714
+ xr2.enabled = true;
15715
+ xr2.setReferenceSpaceType("local-floor");
15716
+ xr2.setFramebufferScaleFactor(0.8);
15717
+ xr2.setFoveation(0.75);
15718
+ xr2.addEventListener("sessionend", this.handleSessionEnd);
15719
+ this.applySparkProfile(viewer.sparkRenderer);
15720
+ const ensureSparkRenderer = this.desktop.ensureSparkRenderer;
15721
+ viewer.ensureSparkRenderer = async () => {
15722
+ const spark = await ensureSparkRenderer.call(viewer);
15723
+ if (this.desktop) this.applySparkProfile(spark);
15724
+ return spark;
15725
+ };
15726
+ await xr2.setSession(session);
15727
+ if (this.disposed || this.stopping || this.session !== session) {
15728
+ await this.endSessionOnce(session);
15729
+ this.cleanup();
15730
+ return;
15731
+ }
15732
+ i$1.setXRSession(session);
15733
+ renderer.setAnimationLoop(this.handleFrame);
15734
+ this.active = true;
15735
+ (_a3 = this.onStateChange) == null ? void 0 : _a3.call(this, true);
15736
+ } catch (error) {
15737
+ try {
15738
+ await this.endSessionOnce(session);
15739
+ } finally {
15740
+ this.cleanup();
15741
+ }
15742
+ throw error;
15743
+ }
15744
+ }
15745
+ frame(time, frame) {
15746
+ var _a3;
15747
+ if (!frame || !this.session || this.stopping) return;
15748
+ try {
15749
+ const referenceSpace = this.viewer.renderer.xr.getReferenceSpace();
15750
+ if (!referenceSpace) return;
15751
+ const pose = frame.getViewerPose(referenceSpace);
15752
+ if (!pose) return;
15753
+ if (!this.bridge) this.bridge = createXRRenderBridge(this.viewer, pose.transform, { showAttribution: this.showAttribution });
15754
+ else this.bridge.updateHeadPose(pose.transform);
15755
+ (_a3 = this.onFrame) == null ? void 0 : _a3.call(this, { time, frame, pose });
15756
+ this.applySparkProfile(this.viewer.sparkRenderer);
15757
+ this.viewer.animate(time);
15758
+ const spark = this.viewer.sparkRenderer;
15759
+ if (spark) this.viewer.renderer.xr.getCamera().layers.mask = spark.layers.mask;
15760
+ } catch (error) {
15761
+ console.error("WebXR frame failed", error);
15762
+ void this.stop();
15763
+ }
15764
+ }
15765
+ applySparkProfile(spark) {
15766
+ if (!spark || typeof spark !== "object") return;
15767
+ if (!this.sparkProfiles.has(spark)) this.sparkProfiles.set(spark, { lodSplatCount: spark.lodSplatCount, lodRenderScale: spark.lodRenderScale });
15768
+ spark.lodSplatCount = 5e4;
15769
+ spark.lodRenderScale = 4;
15770
+ }
15771
+ cleanup() {
15772
+ var _a3, _b2;
15773
+ const desktop = this.desktop;
15774
+ if (!desktop) return;
15775
+ this.desktop = null;
15776
+ const { viewer } = this;
15777
+ const { renderer, camera } = viewer;
15778
+ const xr2 = renderer.xr;
15779
+ xr2.removeEventListener("sessionend", this.handleSessionEnd);
15780
+ renderer.setAnimationLoop(null);
15781
+ i$1.setXRSession(desktop.tilesSchedulerSession);
15782
+ (_a3 = this.bridge) == null ? void 0 : _a3.dispose();
15783
+ this.bridge = null;
15784
+ viewer.ensureSparkRenderer = desktop.ensureSparkRenderer;
15785
+ viewer.transitionControls.animateTo = desktop.transitionAnimateTo;
15786
+ for (const [spark, previous] of this.sparkProfiles) Object.assign(spark, previous);
15787
+ this.sparkProfiles.clear();
15788
+ desktop.cameraWorld.decompose(camera.position, camera.quaternion, camera.scale);
15789
+ camera.updateMatrixWorld(true);
15790
+ camera.near = desktop.near;
15791
+ camera.far = desktop.far;
15792
+ camera.updateProjectionMatrix();
15793
+ viewer.globeControls.enabled = desktop.globeEnabled;
15794
+ viewer.keysEnabled = desktop.keysEnabled;
15795
+ viewer.css2DRenderer.render = desktop.labelRender;
15796
+ xr2.setReferenceSpaceType(desktop.referenceSpaceType);
15797
+ xr2.setFramebufferScaleFactor(desktop.framebufferScaleFactor);
15798
+ xr2.setFoveation(desktop.foveation);
15799
+ xr2.enabled = desktop.xrEnabled;
15800
+ this.session = null;
15801
+ viewer.setAnimate(true);
15802
+ if (this.active) {
15803
+ this.active = false;
15804
+ (_b2 = this.onStateChange) == null ? void 0 : _b2.call(this, false);
15805
+ }
15806
+ }
15807
+ async endSessionOnce(session) {
15808
+ if (this.endRequested) return;
15809
+ this.endRequested = true;
15810
+ try {
15811
+ await session.end();
15812
+ } catch (error) {
15813
+ if (!(error instanceof DOMException && error.name === "InvalidStateError")) throw error;
15814
+ }
15815
+ }
15816
+ async stop() {
15817
+ this.stopping = true;
15818
+ const session = this.session;
15819
+ if (!session) return;
15820
+ try {
15821
+ await this.endSessionOnce(session);
15822
+ } finally {
15823
+ this.cleanup();
15824
+ }
15825
+ }
15826
+ async dispose() {
15827
+ this.disposed = true;
15828
+ this.stopping = true;
15829
+ const ending = this.session ? this.endSessionOnce(this.session) : Promise.resolve();
15830
+ this.cleanup();
15831
+ await ending;
15832
+ }
15833
+ }
15834
+ function copyTransform(transform) {
15835
+ if (!transform) return null;
15836
+ const { position, orientation } = transform;
15837
+ return {
15838
+ position: [position.x, position.y, position.z],
15839
+ orientation: [orientation.x, orientation.y, orientation.z, orientation.w]
15840
+ };
15841
+ }
15842
+ function readXRFrameInput(frame, referenceSpace) {
15843
+ var _a3;
15844
+ if (!referenceSpace) return { head: null, controllers: [] };
15845
+ const head = copyTransform((_a3 = frame.getViewerPose(referenceSpace)) == null ? void 0 : _a3.transform);
15846
+ const controllers = [...frame.session.inputSources].map((source) => {
15847
+ var _a4, _b2;
15848
+ const gamepad = source.gamepad;
15849
+ return {
15850
+ handedness: source.handedness,
15851
+ mapping: (gamepad == null ? void 0 : gamepad.mapping) ?? null,
15852
+ axes: gamepad ? [...gamepad.axes] : [],
15853
+ buttons: gamepad ? gamepad.buttons.map((button) => ({ pressed: button.pressed, touched: button.touched, value: button.value })) : [],
15854
+ ray: copyTransform((_a4 = frame.getPose(source.targetRaySpace, referenceSpace)) == null ? void 0 : _a4.transform),
15855
+ grip: source.gripSpace ? copyTransform((_b2 = frame.getPose(source.gripSpace, referenceSpace)) == null ? void 0 : _b2.transform) : null
15856
+ };
15857
+ });
15858
+ return { head, controllers };
15859
+ }
15195
15860
  const shadowCanvas = document.createElement("canvas");
15196
15861
  shadowCanvas.width = 128;
15197
15862
  shadowCanvas.height = 128;
@@ -15230,6 +15895,7 @@ class Viewer extends EventDispatcher {
15230
15895
  this.isViewer = true;
15231
15896
  this.type = "viewer";
15232
15897
  this.disposed = false;
15898
+ this.xrSession = null;
15233
15899
  this.sparkRenderer = null;
15234
15900
  this.transparentBackground = options.transparentBackground ?? true;
15235
15901
  Tokens.CESIUM = options.cesiumToken || void 0;
@@ -15361,6 +16027,70 @@ class Viewer extends EventDispatcher {
15361
16027
  window.addEventListener("pointermove", this._onPointerMove);
15362
16028
  this.setAnimate();
15363
16029
  }
16030
+ get xrActive() {
16031
+ var _a3;
16032
+ return ((_a3 = this.xrSession) == null ? void 0 : _a3.active) ?? false;
16033
+ }
16034
+ /** @param {{ onFrame?: (input: { time: number, frame: XRFrame, pose: XRViewerPose }) => void, onStateChange?: (active: boolean) => void, showAttribution?: boolean }} [options] */
16035
+ startXRSession(options = {}) {
16036
+ if (this.disposed) return Promise.reject(new Error("Viewer has been disposed"));
16037
+ if (!this.xrSession) this.xrSession = new WebXRSession(this, options);
16038
+ else {
16039
+ this.xrSession.onFrame = options.onFrame;
16040
+ this.xrSession.onStateChange = options.onStateChange;
16041
+ this.xrSession.showAttribution = options.showAttribution !== false;
16042
+ }
16043
+ return this.xrSession.start();
16044
+ }
16045
+ endXRSession() {
16046
+ var _a3;
16047
+ return ((_a3 = this.xrSession) == null ? void 0 : _a3.stop()) ?? Promise.resolve();
16048
+ }
16049
+ /** @param {XRFrame} frame */
16050
+ getXRFrameInput(frame) {
16051
+ return readXRFrameInput(frame, this.renderer.xr.getReferenceSpace());
16052
+ }
16053
+ /** @param {{ anchor: { longitude: number, latitude: number, altitude: number }, east?: number, north?: number, up?: number, headingDegrees: number }} pose */
16054
+ setXRPlayerPose(pose) {
16055
+ var _a3;
16056
+ if (!((_a3 = this.xrSession) == null ? void 0 : _a3.bridge)) throw new Error("No tracked XR head pose is available");
16057
+ this.xrSession.bridge.setPlayerPose(pose);
16058
+ }
16059
+ /** @returns {import('./XRPlayerRig.js').XRHeadPose | null} */
16060
+ getXRHeadPose() {
16061
+ var _a3, _b2;
16062
+ return ((_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.getHeadPose()) ?? null;
16063
+ }
16064
+ /** @param {{ray: {position: number[], orientation: number[]} | null}} controller @returns {{origin: number[], direction: number[]} | null} */
16065
+ getXRControllerWorldRay(controller) {
16066
+ var _a3, _b2;
16067
+ return ((_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.getControllerWorldRay(controller)) ?? null;
16068
+ }
16069
+ /** @param {boolean} moving */
16070
+ setXRComfortMoving(moving) {
16071
+ var _a3, _b2;
16072
+ (_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.setComfortMoving(moving);
16073
+ }
16074
+ /** @param {'floor' | 'unknown' | 'loading' | null} reason */
16075
+ setXRNavigationLimit(reason) {
16076
+ var _a3, _b2;
16077
+ (_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.setNavigationLimit(reason);
16078
+ }
16079
+ /** @param {{anchor: {longitude: number, latitude: number, altitude: number}, arc: readonly (readonly [number, number, number])[], marker: readonly [number, number, number] | null, state: 'valid' | 'blocked' | 'unknown'} | null} cue */
16080
+ setXRNavigationCue(cue) {
16081
+ var _a3, _b2;
16082
+ (_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.setNavigationCue(cue);
16083
+ }
16084
+ /** @param {readonly string[]} credits Current Cesium World Terrain asset credits. */
16085
+ setXRAttribution(credits) {
16086
+ var _a3, _b2;
16087
+ (_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.setTerrainCredits(credits);
16088
+ }
16089
+ /** @param {number} opacity Black headset fade, 0 to 1. */
16090
+ setXRNavigationFade(opacity) {
16091
+ var _a3, _b2;
16092
+ (_b2 = (_a3 = this.xrSession) == null ? void 0 : _a3.bridge) == null ? void 0 : _b2.setFade(opacity);
16093
+ }
15364
16094
  get keysEnabled() {
15365
16095
  return this._keysEnabled;
15366
16096
  }
@@ -15490,7 +16220,7 @@ class Viewer extends EventDispatcher {
15490
16220
  }
15491
16221
  /** @param {import('../main.js').ContentLayerDefinition[]} content */
15492
16222
  defineContentLayers(content = []) {
15493
- var _a3, _b2, _c;
16223
+ var _a3, _b2, _c, _d;
15494
16224
  this.topLayerIndex = 0;
15495
16225
  this.clearContentLayers();
15496
16226
  for (let i2 = 0; i2 < content.length; i2++) {
@@ -15549,6 +16279,7 @@ class Viewer extends EventDispatcher {
15549
16279
  default:
15550
16280
  throw new Error(`Unsupported content layer type: ${String(layerData == null ? void 0 : layerData.type)}`);
15551
16281
  }
16282
+ this.content.children.at(-1).xrRenderLayer = layerData.type === "mesh" ? 6 : ((_d = layerData.options) == null ? void 0 : _d.renderOrder) ?? 0;
15552
16283
  } catch (error) {
15553
16284
  this.dispatchEvent({
15554
16285
  type: ViewerEvents.CONTENT_LAYER_ERROR,
@@ -15641,6 +16372,7 @@ class Viewer extends EventDispatcher {
15641
16372
  return label.element;
15642
16373
  }
15643
16374
  animate(time) {
16375
+ var _a3, _b2, _c;
15644
16376
  if (this.stats) this.stats.begin();
15645
16377
  this.transitionControls.update(time);
15646
16378
  this.state.get("time").value = time;
@@ -15677,6 +16409,10 @@ class Viewer extends EventDispatcher {
15677
16409
  }
15678
16410
  }
15679
16411
  for (let i2 = 0; i2 < this.topLayerIndex + 1; i2++) {
16412
+ if (i2 > 0 && ((_a3 = this.xrSession) == null ? void 0 : _a3.active) && !this.content.children.some((layer) => {
16413
+ var _a4;
16414
+ return layer.visible && (layer.xrRenderLayer === i2 || ((_a4 = layer.layers) == null ? void 0 : _a4.isEnabled(i2)));
16415
+ })) continue;
15680
16416
  this.camera.layers.set(i2);
15681
16417
  if (i2 == 0) {
15682
16418
  this.renderer.clear();
@@ -15686,6 +16422,11 @@ class Viewer extends EventDispatcher {
15686
16422
  this.renderer.render(this.scene, this.camera);
15687
16423
  }
15688
16424
  }
16425
+ if (((_b2 = this.xrSession) == null ? void 0 : _b2.active) && ((_c = this.xrSession.bridge) == null ? void 0 : _c.hasUi())) {
16426
+ this.camera.layers.set(31);
16427
+ this.renderer.clearDepth();
16428
+ this.renderer.render(this.scene, this.camera);
16429
+ }
15689
16430
  this.cameraCoords = ecefToGeodeticRotation(this.camera.position.x, this.camera.position.y, this.camera.position.z, this.camera.quaternion);
15690
16431
  this.dispatchEvent({ type: ViewerEvents.ANIMATED, time, deltaTime });
15691
16432
  if (this.stats) this.stats.end();
@@ -15839,6 +16580,10 @@ class Viewer extends EventDispatcher {
15839
16580
  var _a3, _b2, _c, _d, _e, _f, _g, _h, _i2, _j, _k, _l, _m, _n2, _o;
15840
16581
  if (this.disposed) return;
15841
16582
  this.disposed = true;
16583
+ if (this.xrSession) {
16584
+ void this.xrSession.dispose().catch((error) => console.error("WebXR session end failed during viewer disposal", error));
16585
+ this.xrSession = null;
16586
+ }
15842
16587
  this.setAnimate(false);
15843
16588
  this.transitionControls.cancel();
15844
16589
  this.clearContentLayers();