@loaders.gl/tiles 5.0.0-alpha.1 → 5.0.0-alpha.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.
Files changed (115) hide show
  1. package/LICENSE +14 -3
  2. package/dist/dist.dev.js +2396 -274
  3. package/dist/dist.min.js +1 -1
  4. package/dist/index.cjs +2763 -337
  5. package/dist/index.cjs.map +3 -4
  6. package/dist/index.d.ts +7 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
  11. package/dist/point-cloud/point-cloud-tile.js +3 -0
  12. package/dist/point-cloud/point-cloud-tile.js.map +1 -1
  13. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
  14. package/dist/point-cloud/point-cloud-tileset.js +3 -0
  15. package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
  16. package/dist/point-cloud/types.d.ts.map +1 -1
  17. package/dist/point-cloud/types.js +3 -0
  18. package/dist/point-cloud/types.js.map +1 -1
  19. package/dist/raster-set.d.ts +35 -29
  20. package/dist/raster-set.d.ts.map +1 -1
  21. package/dist/raster-set.js +7 -1
  22. package/dist/raster-set.js.map +1 -1
  23. package/dist/tileset-3d/common/tile-3d.d.ts +98 -3
  24. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  25. package/dist/tileset-3d/common/tile-3d.js +229 -15
  26. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  27. package/dist/tileset-3d/common/tileset-3d.d.ts +143 -3
  28. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  29. package/dist/tileset-3d/common/tileset-3d.js +257 -16
  30. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  31. package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
  32. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
  33. package/dist/tileset-3d/common/tileset-cache.js +11 -3
  34. package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
  35. package/dist/tileset-3d/common/tileset-source.d.ts +18 -0
  36. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  37. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  38. package/dist/tileset-3d/common/tileset-traverser.d.ts +13 -0
  39. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  40. package/dist/tileset-3d/common/tileset-traverser.js +28 -1
  41. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  42. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +111 -0
  43. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  44. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +340 -0
  45. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  46. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +92 -3
  47. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  48. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +211 -13
  49. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  50. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +36 -0
  51. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  52. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +66 -0
  53. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  54. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
  55. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
  56. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
  57. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  58. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
  59. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
  60. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  61. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +3 -0
  62. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  63. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
  64. package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
  65. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
  66. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  67. package/dist/tileset-3d/helpers/bounding-volume.js +1 -0
  68. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  69. package/dist/tileset-3d/helpers/frame-state.d.ts +59 -1
  70. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  71. package/dist/tileset-3d/helpers/frame-state.js +84 -9
  72. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  73. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
  74. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
  75. package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
  76. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
  77. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  78. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  79. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  80. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  81. package/dist/types.d.ts +2 -0
  82. package/dist/types.d.ts.map +1 -1
  83. package/dist/types.js +3 -0
  84. package/dist/types.js.map +1 -1
  85. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  86. package/dist/utils/doubly-linked-list.js +1 -0
  87. package/dist/utils/doubly-linked-list.js.map +1 -1
  88. package/dist/utils/managed-array.d.ts.map +1 -1
  89. package/dist/utils/managed-array.js +1 -0
  90. package/dist/utils/managed-array.js.map +1 -1
  91. package/package.json +5 -5
  92. package/src/index.ts +19 -0
  93. package/src/point-cloud/point-cloud-tile.ts +4 -0
  94. package/src/point-cloud/point-cloud-tileset.ts +4 -0
  95. package/src/point-cloud/types.ts +4 -0
  96. package/src/raster-set.ts +81 -43
  97. package/src/tileset-3d/common/tile-3d.ts +285 -21
  98. package/src/tileset-3d/common/tileset-3d.ts +368 -17
  99. package/src/tileset-3d/common/tileset-cache.ts +15 -7
  100. package/src/tileset-3d/common/tileset-source.ts +21 -0
  101. package/src/tileset-3d/common/tileset-traverser.ts +29 -1
  102. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +534 -0
  103. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +266 -13
  104. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +77 -0
  105. package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
  106. package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
  107. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +4 -0
  108. package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
  109. package/src/tileset-3d/helpers/bounding-volume.ts +2 -0
  110. package/src/tileset-3d/helpers/frame-state.ts +139 -11
  111. package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
  112. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  113. package/src/types.ts +6 -0
  114. package/src/utils/doubly-linked-list.ts +2 -0
  115. package/src/utils/managed-array.ts +2 -0
@@ -12,7 +12,7 @@ import {CullingVolume} from '@math.gl/culling';
12
12
  import type {Tileset3D} from './tileset-3d';
13
13
  import type {DoublyLinkedListNode} from '../../utils/doubly-linked-list-node';
14
14
  import {LOD_METRIC_TYPE, TILE_REFINEMENT, TILE_CONTENT_STATE, TILESET_TYPE} from '../../constants';
15
- import type {TileContentLoadResult} from './tileset-source';
15
+ import type {TileChildrenLoadResult, TileContentLoadResult} from './tileset-source';
16
16
 
17
17
  import {FrameState} from '../helpers/frame-state';
18
18
  import {
@@ -23,6 +23,13 @@ import {
23
23
  import {getTiles3DScreenSpaceError} from '../helpers/tiles-3d-lod';
24
24
  import {getProjectedRadius} from '../helpers/i3s-lod';
25
25
  import {TilesetTraverser} from './tileset-traverser';
26
+ import {
27
+ calculateFoveatedFactor,
28
+ calculateTileRequestPriority,
29
+ isFoveatedRequestDelayActive,
30
+ isFoveatedRequestDeferred,
31
+ isProgressiveResolutionPriority
32
+ } from '../helpers/tiles-3d-request-priority';
26
33
 
27
34
  const scratchVector = new Vector3();
28
35
 
@@ -44,6 +51,9 @@ export type Tile3DProps = {
44
51
  extendedId: string;
45
52
  };
46
53
 
54
+ /** Lifecycle of a source-managed lazy child-header group. */
55
+ export type TileChildrenState = 'unloaded' | 'loading' | 'ready' | 'failed';
56
+
47
57
  /**
48
58
  * A Tile3DHeader represents a tile as Tileset3D. When a tile is first created, its content is not loaded;
49
59
  * the content is loaded on-demand when needed based on the view.
@@ -86,6 +96,8 @@ export class Tile3D {
86
96
 
87
97
  /** The tile's children - an array of Tile3D objects. */
88
98
  children: Tile3D[] = [];
99
+ /** Current lifecycle state for a lazy implicit subtree rooted at this tile. */
100
+ childrenState: TileChildrenState = 'ready';
89
101
  depth: number = 0;
90
102
  viewportIds: any[] = [];
91
103
  transform = new Matrix4();
@@ -111,11 +123,22 @@ export class Tile3D {
111
123
  private _expireDate: any = null;
112
124
  private _expiredContent: any = null;
113
125
 
126
+ /** In-flight source request shared by repeated traversal frames. */
127
+ private _childrenPromise: Promise<TileChildrenLoadResult> | null = null;
128
+
114
129
  private _boundingBox?: CartographicBounds = undefined;
115
130
 
116
131
  /** updated every frame for tree traversal and rendering optimizations: */
117
132
  public _distanceToCamera: number = 0;
118
133
  _screenSpaceError: number = 0;
134
+ /** SSE calculated at the reduced viewport height used to prioritize coarse coverage. */
135
+ _screenSpaceErrorProgressiveResolution: number = 0;
136
+ /** Whether this tile should be promoted into the progressive-resolution request pass. */
137
+ _priorityProgressiveResolution: boolean = false;
138
+ /** Angular distance from the camera view axis used to prioritize center tiles. */
139
+ _foveatedFactor: number = 0;
140
+ /** Whether this peripheral request is currently inside the camera-motion deferral window. */
141
+ priorityDeferred: boolean = false;
119
142
  private _visibilityPlaneMask: any;
120
143
  private _visible: boolean | undefined = undefined;
121
144
 
@@ -176,6 +199,7 @@ export class Tile3D {
176
199
  this.refine = this._getRefine(header.refine);
177
200
  this.type = header.type;
178
201
  this.contentUrl = header.contentUrl;
202
+ this.childrenState = header.implicitSubtree ? 'unloaded' : 'ready';
179
203
 
180
204
  this._initializeLodMetric(header);
181
205
  this._initializeTransforms(header);
@@ -214,7 +238,24 @@ export class Tile3D {
214
238
 
215
239
  /** Returns true if tile has children */
216
240
  get hasChildren() {
217
- return this.children.length > 0 || (this.header.children && this.header.children.length > 0);
241
+ return (
242
+ this.children.length > 0 ||
243
+ (this.header.children && this.header.children.length > 0) ||
244
+ Boolean(this.header.implicitSubtree && this.childrenState !== 'ready')
245
+ );
246
+ }
247
+
248
+ /** Returns whether traversal may request an unresolved lazy child-header group. */
249
+ get hasUnloadedChildren(): boolean {
250
+ return Boolean(
251
+ this.header.implicitSubtree &&
252
+ (this.childrenState === 'unloaded' || this.childrenState === 'failed')
253
+ );
254
+ }
255
+
256
+ /** Returns whether a lazy child-header request is currently in flight. */
257
+ get childrenLoading(): boolean {
258
+ return this.childrenState === 'loading';
218
259
  }
219
260
 
220
261
  /**
@@ -287,13 +328,28 @@ export class Tile3D {
287
328
  return this._boundingBox;
288
329
  }
289
330
 
290
- /** Get the tile's screen space error. */
291
- getScreenSpaceError(frameState, useParentLodMetric) {
331
+ /**
332
+ * Gets the tile's screen-space error for normal refinement or a reduced-height priority pass.
333
+ *
334
+ * I3S already supplies a screen-oriented LOD metric and intentionally ignores the optional 3D
335
+ * Tiles progressive-resolution height fraction.
336
+ *
337
+ * @param frameState - Current camera and viewport measurements.
338
+ * @param useParentLodMetric - Whether to substitute the parent LOD metric.
339
+ * @param viewportHeightFraction - Fraction of viewport height represented by the priority pass.
340
+ * @returns Screen-space LOD error for the current tileset format.
341
+ */
342
+ getScreenSpaceError(frameState, useParentLodMetric, viewportHeightFraction = 1) {
292
343
  switch (this.tileset.type) {
293
344
  case TILESET_TYPE.I3S:
294
345
  return getProjectedRadius(this, frameState);
295
346
  case TILESET_TYPE.TILES3D:
296
- return getTiles3DScreenSpaceError(this, frameState, useParentLodMetric);
347
+ return getTiles3DScreenSpaceError(
348
+ this,
349
+ frameState,
350
+ useParentLodMetric,
351
+ viewportHeightFraction
352
+ );
297
353
  default:
298
354
  // eslint-disable-next-line
299
355
  throw new Error('Unsupported tileset type');
@@ -315,12 +371,36 @@ export class Tile3D {
315
371
  return this.content.gpuMemoryUsageInBytes || this.content.byteLength || 0;
316
372
  }
317
373
 
318
- /*
319
- * If skipLevelOfDetail is off try to load child tiles as soon as possible so that their parent can refine sooner.
320
- * Tiles are prioritized by screen space error.
374
+ /**
375
+ * Returns scheduler priority for render-content requests.
376
+ *
377
+ * @returns Numeric priority, or a negative value when the queued request should be cancelled.
378
+ */
379
+ _getPriority(): number {
380
+ return this._calculateRequestPriority(false);
381
+ }
382
+
383
+ /**
384
+ * Returns scheduler priority for lazy child-header requests.
385
+ *
386
+ * Contentless implicit placeholders intentionally retain `UNLOADED` content state, so metadata
387
+ * scheduling skips only that render-content cancellation check and preserves every visibility,
388
+ * recency, progressive, foveated, and motion-deferral rule.
389
+ *
390
+ * @returns Numeric priority, or a negative value when the queued request should be cancelled.
391
+ */
392
+ private _getChildrenPriority(): number {
393
+ return this._calculateRequestPriority(true);
394
+ }
395
+
396
+ /**
397
+ * Calculates the common scheduler priority for content or hierarchy metadata.
398
+ *
399
+ * @param allowUnloadedContent - Whether an empty placeholder may request child metadata.
400
+ * @returns Numeric priority, or a negative value when the queued request should be cancelled.
321
401
  */
322
402
  // eslint-disable-next-line complexity
323
- _getPriority() {
403
+ private _calculateRequestPriority(allowUnloadedContent: boolean): number {
324
404
  const traverser = this.tileset._traverser;
325
405
  const {skipLevelOfDetail} = traverser.options;
326
406
 
@@ -340,7 +420,13 @@ export class Tile3D {
340
420
  if (this.tileset._frameNumber - this._touchedFrame >= 1) {
341
421
  return -1;
342
422
  }
343
- if (this.contentState === TILE_CONTENT_STATE.UNLOADED) {
423
+ // RequestScheduler re-evaluates this callback before a queued request receives a slot. A
424
+ // negative priority cancels work that became deferred after it was queued; the scheduled
425
+ // follow-up traversal submits it again after the motion delay expires.
426
+ if (this.priorityDeferred) {
427
+ return -1;
428
+ }
429
+ if (!allowUnloadedContent && this.contentState === TILE_CONTENT_STATE.UNLOADED) {
344
430
  return -1;
345
431
  }
346
432
 
@@ -354,8 +440,15 @@ export class Tile3D {
354
440
 
355
441
  const rootScreenSpaceError = traverser.root ? traverser.root._screenSpaceError : 0.0;
356
442
 
357
- // Map higher SSE to lower values (e.g. root tile is highest priority)
358
- return Math.max(rootScreenSpaceError - screenSpaceError, 0);
443
+ // Map higher SSE to lower values (e.g. root tile is highest priority), then combine the
444
+ // existing order with independent progressive and foveated priority bands.
445
+ const reverseScreenSpaceError = Math.max(rootScreenSpaceError - screenSpaceError, 0);
446
+ return calculateTileRequestPriority({
447
+ priorityProgressiveResolution: this._priorityProgressiveResolution,
448
+ foveatedFactor: this._foveatedFactor,
449
+ reverseScreenSpaceError,
450
+ rootScreenSpaceError
451
+ });
359
452
  }
360
453
 
361
454
  /**
@@ -410,6 +503,96 @@ export class Tile3D {
410
503
  }
411
504
  }
412
505
 
506
+ /**
507
+ * Requests and installs this tile's lazy child-header group through its source.
508
+ *
509
+ * The same request scheduler and SSE/foveated priority value used for render content are reused
510
+ * for subtree metadata. Repeated traversal frames share one promise, and scheduler cancellation
511
+ * restores the unloaded state so a later frame can retry.
512
+ *
513
+ * @param frameState - View state that made the subtree eligible for refinement.
514
+ * @returns Source load result, or an unloaded result if scheduling was cancelled.
515
+ */
516
+ async loadChildren(frameState: FrameState): Promise<TileChildrenLoadResult> {
517
+ if (!this.header.implicitSubtree || this.childrenState === 'ready') {
518
+ return {loaded: false, tileCount: 0, childSubtreeCount: 0};
519
+ }
520
+ if (this._childrenPromise) {
521
+ return await this._childrenPromise;
522
+ }
523
+ if (!this.tileset.source.loadTileChildren) {
524
+ throw new Error('Tileset source does not support lazy tile children');
525
+ }
526
+
527
+ this.childrenState = 'loading';
528
+ this._childrenPromise = this._loadChildrenWithScheduler(frameState);
529
+ try {
530
+ return await this._childrenPromise;
531
+ } finally {
532
+ this._childrenPromise = null;
533
+ }
534
+ }
535
+
536
+ /**
537
+ * Runs one lazy child-header load after obtaining a scheduler token.
538
+ *
539
+ * @param frameState - View state forwarded to the source implementation.
540
+ * @returns Source load result.
541
+ */
542
+ private async _loadChildrenWithScheduler(
543
+ frameState: FrameState
544
+ ): Promise<TileChildrenLoadResult> {
545
+ const requestToken = await this.tileset._requestScheduler.scheduleRequest(
546
+ `${this.id}:implicit-subtree`,
547
+ this._getChildrenPriority.bind(this)
548
+ );
549
+ if (!requestToken) {
550
+ this.childrenState = 'unloaded';
551
+ return {loaded: false, tileCount: 0, childSubtreeCount: 0};
552
+ }
553
+
554
+ try {
555
+ const loadResult = await this.tileset.source.loadTileChildren!(this, frameState);
556
+ this.childrenState = loadResult.loaded ? 'ready' : 'unloaded';
557
+ return loadResult;
558
+ } catch (error) {
559
+ this.childrenState = 'failed';
560
+ throw error;
561
+ } finally {
562
+ requestToken.done();
563
+ }
564
+ }
565
+
566
+ /**
567
+ * Replaces a contentless implicit placeholder with its materialized subtree-root header.
568
+ *
569
+ * Transform state is intentionally preserved: the placeholder already owns the composed
570
+ * transform, and reapplying the root transform here would double-transform bounds and geometric
571
+ * error. Content and raw geometric error are reinitialized from the newly available header.
572
+ *
573
+ * @param materializedHeader - Available root header produced from one subtree resource.
574
+ */
575
+ applyImplicitSubtreeHeader(materializedHeader: Record<string, any>): void {
576
+ const existingTransform = this.header.transform;
577
+ const existingTransformMatrix = this.header.transformMatrix;
578
+ this.header = {
579
+ ...this.header,
580
+ ...materializedHeader,
581
+ transform: existingTransform,
582
+ transformMatrix: existingTransformMatrix,
583
+ implicitSubtree: undefined
584
+ };
585
+ this.refine = this._getRefine(materializedHeader.refine);
586
+ this.type = materializedHeader.type;
587
+ this.contentUrl = materializedHeader.contentUrl;
588
+ this._initializeLodMetric(materializedHeader);
589
+ this._updateLodMetricScale();
590
+ this._initializeBoundingVolumes(this.header);
591
+ // Use the merged header so inherited viewer-request-volume metadata remains available when a
592
+ // materialized implicit tile has no render content of its own.
593
+ this._initializeContent(this.header);
594
+ }
595
+
413
596
  // Unloads the tile's content.
414
597
  unloadContent() {
415
598
  if (this.content && this.content.destroy) {
@@ -450,6 +633,7 @@ export class Tile3D {
450
633
 
451
634
  this._distanceToCamera = this.distanceToTile(frameState);
452
635
  this._screenSpaceError = this.getScreenSpaceError(frameState, false);
636
+ this._updateRequestPriorityMetrics(frameState);
453
637
  this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask); // Use parent's plane mask to speed up visibility test
454
638
  this._visible = this._visibilityPlaneMask !== CullingVolume.MASK_OUTSIDE;
455
639
  this._inRequestVolume = this.insideViewerRequestVolume(frameState);
@@ -458,6 +642,71 @@ export class Tile3D {
458
642
  this.viewportIds = viewportIds;
459
643
  }
460
644
 
645
+ /**
646
+ * Updates progressive-resolution and foveated request metrics for a 3D Tiles traversal frame.
647
+ *
648
+ * I3S retains its format-specific screen-threshold ordering and is explicitly isolated from
649
+ * geometric-error scheduling. Orthographic viewports still receive progressive coverage, but
650
+ * angular foveation is disabled because an orthographic frustum has no perspective field of view.
651
+ *
652
+ * @param frameState - Current camera and viewport measurements.
653
+ */
654
+ _updateRequestPriorityMetrics(frameState: FrameState): void {
655
+ if (this.tileset.type !== TILESET_TYPE.TILES3D) {
656
+ this._screenSpaceErrorProgressiveResolution = this._screenSpaceError;
657
+ this._priorityProgressiveResolution = false;
658
+ this._foveatedFactor = 0;
659
+ this.priorityDeferred = false;
660
+ return;
661
+ }
662
+
663
+ const options = this.tileset.options;
664
+ const progressiveResolutionHeightFraction = options.progressiveResolutionHeightFraction;
665
+ this._screenSpaceErrorProgressiveResolution = this.getScreenSpaceError(
666
+ frameState,
667
+ false,
668
+ progressiveResolutionHeightFraction
669
+ );
670
+ this._priorityProgressiveResolution = isProgressiveResolutionPriority(
671
+ this._screenSpaceErrorProgressiveResolution,
672
+ this.parent?._screenSpaceErrorProgressiveResolution,
673
+ this.tileset.memoryAdjustedScreenSpaceError,
674
+ progressiveResolutionHeightFraction
675
+ );
676
+
677
+ if (
678
+ !options.foveatedScreenSpaceError ||
679
+ frameState.viewport?.orthographic ||
680
+ !frameState.camera?.position ||
681
+ !frameState.camera?.direction
682
+ ) {
683
+ this._foveatedFactor = 0;
684
+ this.priorityDeferred = false;
685
+ return;
686
+ }
687
+
688
+ this._foveatedFactor = calculateFoveatedFactor(this.boundingVolume, frameState.camera);
689
+ const deferralEligible = isFoveatedRequestDeferred({
690
+ refinement: this.refine,
691
+ skipLevelOfDetail: this.tileset._traverser.options.skipLevelOfDetail,
692
+ foveatedScreenSpaceError: options.foveatedScreenSpaceError,
693
+ foveatedConeSize: options.foveatedConeSize,
694
+ minimumScreenSpaceErrorRelaxation: options.foveatedMinimumScreenSpaceErrorRelaxation,
695
+ interpolationCallback: options.foveatedInterpolationCallback,
696
+ foveatedFactor: this._foveatedFactor,
697
+ verticalFieldOfView: frameState.camera.verticalFieldOfView,
698
+ screenSpaceError: this._screenSpaceError,
699
+ parentScreenSpaceError: this.parent?._screenSpaceError,
700
+ maximumScreenSpaceError: this.tileset.memoryAdjustedScreenSpaceError,
701
+ priorityProgressiveResolution: this._priorityProgressiveResolution
702
+ });
703
+ this.priorityDeferred = isFoveatedRequestDelayActive(
704
+ deferralEligible,
705
+ frameState.camera.timeSinceMovement,
706
+ options.foveatedTimeDelay
707
+ );
708
+ }
709
+
461
710
  // Determines whether the tile's bounding volume intersects the culling volume.
462
711
  // @param {FrameState} frameState The frame state.
463
712
  // @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check.
@@ -679,6 +928,10 @@ export class Tile3D {
679
928
  this._distanceToCamera = 0;
680
929
  this._centerZDepth = 0;
681
930
  this._screenSpaceError = 0;
931
+ this._screenSpaceErrorProgressiveResolution = 0;
932
+ this._priorityProgressiveResolution = false;
933
+ this._foveatedFactor = 0;
934
+ this.priorityDeferred = false;
682
935
  this._visibilityPlaneMask = CullingVolume.MASK_INDETERMINATE;
683
936
  this._visible = undefined;
684
937
  this._inRequestVolume = false;
@@ -700,8 +953,15 @@ export class Tile3D {
700
953
  return refine || (this.parent && this.parent.refine) || TILE_REFINEMENT.REPLACE;
701
954
  }
702
955
 
703
- _isTileset() {
704
- return this.contentUrl.indexOf('.json') !== -1;
956
+ /**
957
+ * Returns whether loaded content is an external tileset.
958
+ *
959
+ * Parsed shape is authoritative because valid external resources may be extensionless, signed,
960
+ * or use a misleading filename. This method is called only after source loading has populated
961
+ * `content`.
962
+ */
963
+ _isTileset(): boolean {
964
+ return this.content?.shape === 'tileset3d';
705
965
  }
706
966
 
707
967
  _onContentLoaded() {
@@ -731,6 +991,17 @@ export class Tile3D {
731
991
  this.boundingVolume
732
992
  );
733
993
 
994
+ // Viewer request volumes constrain traversal, not just render content. A contentless implicit
995
+ // connector can still own an inherited request volume, so initialize it before the
996
+ // content-specific early return below.
997
+ if (header.viewerRequestVolume) {
998
+ this._viewerRequestVolume = createBoundingVolume(
999
+ header.viewerRequestVolume,
1000
+ this.computedTransform,
1001
+ this._viewerRequestVolume
1002
+ );
1003
+ }
1004
+
734
1005
  const content = header.content;
735
1006
  if (!content) {
736
1007
  return;
@@ -749,13 +1020,6 @@ export class Tile3D {
749
1020
  this._contentBoundingVolume
750
1021
  );
751
1022
  }
752
- if (header.viewerRequestVolume) {
753
- this._viewerRequestVolume = createBoundingVolume(
754
- header.viewerRequestVolume,
755
- this.computedTransform,
756
- this._viewerRequestVolume
757
- );
758
- }
759
1023
  }
760
1024
 
761
1025
  /**