@loaders.gl/tiles 5.0.0-alpha.6 → 5.0.0-alpha.8
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.
- package/README.md +26 -0
- package/dist/dist.dev.js +10015 -8172
- package/dist/dist.min.js +3 -4
- package/dist/index.cjs +1676 -477
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/spatial/format-spatial-reference.d.ts +1 -0
- package/dist/spatial/format-spatial-reference.d.ts.map +1 -1
- package/dist/spatial/format-spatial-reference.js +104 -46
- package/dist/spatial/format-spatial-reference.js.map +1 -1
- package/dist/spatial/get-spatial-coordinate-frame.d.ts +12 -1
- package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -1
- package/dist/spatial/get-spatial-coordinate-frame.js +105 -24
- package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -1
- package/dist/spatial/normalize-crs-identifier.d.ts +15 -0
- package/dist/spatial/normalize-crs-identifier.d.ts.map +1 -0
- package/dist/spatial/normalize-crs-identifier.js +22 -0
- package/dist/spatial/normalize-crs-identifier.js.map +1 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts +4 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -1
- package/dist/spatial/spatial-coordinate-transformer.js +102 -18
- package/dist/spatial/spatial-coordinate-transformer.js.map +1 -1
- package/dist/spatial/spatial-resource-registry.js +3 -3
- package/dist/spatial/spatial-resource-registry.js.map +1 -1
- package/dist/spatial/tiles-3d-spatial-transformer.d.ts +32 -0
- package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -0
- package/dist/spatial/tiles-3d-spatial-transformer.js +175 -0
- package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
- package/dist/tileset-3d/common/tile-3d-contracts.d.ts +63 -0
- package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
- package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
- package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
- package/dist/tileset-3d/common/tile-3d.d.ts +33 -5
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +179 -29
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +15 -25
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +35 -42
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-content-traversal.d.ts +37 -0
- package/dist/tileset-3d/common/tileset-content-traversal.d.ts.map +1 -0
- package/dist/tileset-3d/common/tileset-content-traversal.js +80 -0
- package/dist/tileset-3d/common/tileset-content-traversal.js.map +1 -0
- package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
- package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
- package/dist/tileset-3d/common/tileset-observability.js +39 -0
- package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
- package/dist/tileset-3d/common/tileset-source.d.ts +11 -1
- package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-source.js.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.js +4 -3
- package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +47 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +327 -30
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +34 -0
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +278 -14
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +5 -3
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +7 -4
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts +4 -2
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.js +60 -9
- package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
- package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/bounding-volume.js +19 -61
- package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts +5 -0
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +11 -5
- package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
- package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
- package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
- package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
- package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -12
- package/src/index.ts +18 -1
- package/src/spatial/format-spatial-reference.ts +127 -53
- package/src/spatial/get-spatial-coordinate-frame.ts +126 -31
- package/src/spatial/normalize-crs-identifier.ts +26 -0
- package/src/spatial/spatial-coordinate-transformer.ts +124 -24
- package/src/spatial/spatial-resource-registry.ts +3 -3
- package/src/spatial/tiles-3d-spatial-transformer.ts +206 -0
- package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
- package/src/tileset-3d/common/tile-3d.ts +208 -32
- package/src/tileset-3d/common/tileset-3d.ts +37 -61
- package/src/tileset-3d/common/tileset-content-traversal.ts +114 -0
- package/src/tileset-3d/common/tileset-observability.ts +100 -0
- package/src/tileset-3d/common/tileset-source.ts +19 -1
- package/src/tileset-3d/common/tileset-traverser.ts +4 -3
- package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +481 -34
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +405 -14
- package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +7 -4
- package/src/tileset-3d/format-i3s/i3s-source.ts +84 -13
- package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +2 -1
- package/src/tileset-3d/helpers/bounding-volume.ts +29 -62
- package/src/tileset-3d/helpers/frame-state.ts +15 -7
- package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
- package/src/types.ts +2 -0
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import {Vector3, Matrix4} from '@math.gl/core';
|
|
9
9
|
import {CullingVolume} from '@math.gl/culling';
|
|
10
10
|
import type {CullingResult} from '@math.gl/culling';
|
|
11
|
+
import {getTile3DFeatureIdSets} from './tile-3d-contracts';
|
|
12
|
+
import type {Tile3DContent, Tile3DFeatureIdSet, Tile3DMetadataContext} from './tile-3d-contracts';
|
|
11
13
|
|
|
12
14
|
// Note: circular dependency
|
|
13
15
|
import type {Tileset3D} from './tileset-3d';
|
|
@@ -85,6 +87,14 @@ export function getContentVisibility(
|
|
|
85
87
|
return intersectingVolume ? 'intersecting' : 'inside';
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
function isNestedTilesetPayload(payload: unknown): boolean {
|
|
91
|
+
if (!payload || typeof payload !== 'object') {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const record = payload as Record<string, any>;
|
|
95
|
+
return record.shape === 'tileset3d' || record.type === 'tileset3d' || Boolean(record.tileset);
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
function defined(x) {
|
|
89
99
|
return x !== undefined && x !== null;
|
|
90
100
|
}
|
|
@@ -137,6 +147,9 @@ export class Tile3D {
|
|
|
137
147
|
*/
|
|
138
148
|
private _unscaledLodMetricValue: number = 0;
|
|
139
149
|
|
|
150
|
+
/** Whether the source format defines geometric error in transform-local units. */
|
|
151
|
+
private _scaleGeometricError: boolean = true;
|
|
152
|
+
|
|
140
153
|
/** @todo math.gl is not exporting BoundingVolume base type? */
|
|
141
154
|
boundingVolume: any = null;
|
|
142
155
|
|
|
@@ -147,6 +160,14 @@ export class Tile3D {
|
|
|
147
160
|
content: any = null;
|
|
148
161
|
/** Loaded payloads in the same order as {@link contentUrls}; `content` remains the primary payload. */
|
|
149
162
|
contents: any[] = [];
|
|
163
|
+
/** Ordered renderer-neutral descriptors for each source content entry. */
|
|
164
|
+
contentEntries: Tile3DContent[] = [];
|
|
165
|
+
/** Raw metadata references inherited by this tile and its content. */
|
|
166
|
+
metadataContext: Tile3DMetadataContext = {};
|
|
167
|
+
/** Feature-id declarations normalized from supported content extensions. */
|
|
168
|
+
featureIdSets: Tile3DFeatureIdSet[] = [];
|
|
169
|
+
/** Decoded 3D Tiles vector topology for the primary loaded content, when present. */
|
|
170
|
+
vectorContent: unknown = null;
|
|
150
171
|
/**
|
|
151
172
|
* Metadata entity attached to this tile in the source tileset, if present.
|
|
152
173
|
*
|
|
@@ -228,6 +249,7 @@ export class Tile3D {
|
|
|
228
249
|
return this._viewerRequestVolume;
|
|
229
250
|
}
|
|
230
251
|
|
|
252
|
+
/** Accumulated tile transforms from the source JSON, excluding the tileset model matrix. */
|
|
231
253
|
_initialTransform: Matrix4 = new Matrix4();
|
|
232
254
|
|
|
233
255
|
// Used by traverser, cannot be marked private
|
|
@@ -283,11 +305,7 @@ export class Tile3D {
|
|
|
283
305
|
this.type = header.type;
|
|
284
306
|
this.contentUrl = header.contentUrl;
|
|
285
307
|
this.contentUrls = header.contentUrls || (header.contentUrl ? [header.contentUrl] : []);
|
|
286
|
-
this.
|
|
287
|
-
const contentHeaders = Array.isArray(header.content) ? header.content : [header.content];
|
|
288
|
-
this.contentMetadata = contentHeaders
|
|
289
|
-
.filter(Boolean)
|
|
290
|
-
.map(contentHeader => contentHeader.metadata || null);
|
|
308
|
+
this._initializeMetadata(header);
|
|
291
309
|
this.childrenState = header.implicitSubtree ? 'unloaded' : 'ready';
|
|
292
310
|
|
|
293
311
|
this._initializeLodMetric(header);
|
|
@@ -295,6 +313,7 @@ export class Tile3D {
|
|
|
295
313
|
this._updateLodMetricScale();
|
|
296
314
|
this._initializeBoundingVolumes(header);
|
|
297
315
|
this._initializeContent(header);
|
|
316
|
+
this._initializeContentEntries(header);
|
|
298
317
|
this._initializeRenderingState(header);
|
|
299
318
|
|
|
300
319
|
Object.seal(this);
|
|
@@ -407,12 +426,16 @@ export class Tile3D {
|
|
|
407
426
|
}
|
|
408
427
|
|
|
409
428
|
/**
|
|
410
|
-
* Get bounding
|
|
429
|
+
* Get the current world-space bounding volume in cartographic coordinates.
|
|
411
430
|
* @returns [min, max] each in [longitude, latitude, altitude]
|
|
412
431
|
*/
|
|
413
432
|
get boundingBox(): CartographicBounds {
|
|
414
433
|
if (!this._boundingBox) {
|
|
415
|
-
this._boundingBox = getCartographicBounds(
|
|
434
|
+
this._boundingBox = getCartographicBounds(
|
|
435
|
+
this.header.boundingVolume,
|
|
436
|
+
this.boundingVolume,
|
|
437
|
+
!this.computedTransform.equals(this._initialTransform)
|
|
438
|
+
);
|
|
416
439
|
}
|
|
417
440
|
return this._boundingBox;
|
|
418
441
|
}
|
|
@@ -579,20 +602,7 @@ export class Tile3D {
|
|
|
579
602
|
}
|
|
580
603
|
|
|
581
604
|
try {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (loadResult.contents) {
|
|
585
|
-
this.contents = loadResult.contents;
|
|
586
|
-
this.content = this.contents[0] || null;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
if (this.tileset.options.contentLoader) {
|
|
590
|
-
await this.tileset.options.contentLoader(this);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
this.contentState = TILE_CONTENT_STATE.READY;
|
|
594
|
-
this._onContentLoaded();
|
|
595
|
-
return loadResult;
|
|
605
|
+
return await this._loadContentFromSource();
|
|
596
606
|
} catch (error) {
|
|
597
607
|
// Tile is unloaded before the content finishes loading
|
|
598
608
|
this.contentState = TILE_CONTENT_STATE.FAILED;
|
|
@@ -602,6 +612,44 @@ export class Tile3D {
|
|
|
602
612
|
}
|
|
603
613
|
}
|
|
604
614
|
|
|
615
|
+
/** Loads content for complete dataset operations without viewport-based request priority. */
|
|
616
|
+
async loadContentForTraversal(): Promise<TileContentLoadResult> {
|
|
617
|
+
if (this.hasEmptyContent) {
|
|
618
|
+
return {loaded: false};
|
|
619
|
+
}
|
|
620
|
+
if (this.content) {
|
|
621
|
+
return {loaded: true, contents: this.contents};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
this.contentState = TILE_CONTENT_STATE.LOADING;
|
|
625
|
+
try {
|
|
626
|
+
return await this._loadContentFromSource();
|
|
627
|
+
} catch (error) {
|
|
628
|
+
this.contentState = TILE_CONTENT_STATE.FAILED;
|
|
629
|
+
throw error;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/** Loads source content and updates the tile's renderer-neutral payload state. */
|
|
634
|
+
private async _loadContentFromSource(): Promise<TileContentLoadResult> {
|
|
635
|
+
const loadResult = await this.tileset.source.loadTileContent(this);
|
|
636
|
+
if (loadResult.contents) {
|
|
637
|
+
this.contents = loadResult.contents;
|
|
638
|
+
this.content = this.contents[0] || null;
|
|
639
|
+
}
|
|
640
|
+
this.vectorContent =
|
|
641
|
+
this.contents.find(content => Boolean(content?.vectorContent))?.vectorContent || null;
|
|
642
|
+
|
|
643
|
+
if (this.tileset.options.contentLoader) {
|
|
644
|
+
await this.tileset.options.contentLoader(this);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
this.contentState = TILE_CONTENT_STATE.READY;
|
|
648
|
+
this._onContentLoaded();
|
|
649
|
+
this._updateContentEntriesAfterLoad(loadResult);
|
|
650
|
+
return loadResult;
|
|
651
|
+
}
|
|
652
|
+
|
|
605
653
|
/**
|
|
606
654
|
* Requests and installs this tile's lazy child-header group through its source.
|
|
607
655
|
*
|
|
@@ -665,34 +713,137 @@ export class Tile3D {
|
|
|
665
713
|
/**
|
|
666
714
|
* Replaces a contentless implicit placeholder with its materialized subtree-root header.
|
|
667
715
|
*
|
|
668
|
-
*
|
|
669
|
-
* transform
|
|
670
|
-
*
|
|
716
|
+
* The placeholder transform is preserved when the subtree omits a root transform. An explicit
|
|
717
|
+
* subtree-root transform replaces it and is recomposed against the runtime parent. Content,
|
|
718
|
+
* metadata, bounds, and raw geometric error are reinitialized from the available header.
|
|
671
719
|
*
|
|
672
720
|
* @param materializedHeader - Available root header produced from one subtree resource.
|
|
673
721
|
*/
|
|
674
722
|
applyImplicitSubtreeHeader(materializedHeader: Record<string, any>): void {
|
|
675
723
|
const existingTransform = this.header.transform;
|
|
676
724
|
const existingTransformMatrix = this.header.transformMatrix;
|
|
725
|
+
const hasMaterializedTransform = materializedHeader.transform !== undefined;
|
|
677
726
|
this.header = {
|
|
678
727
|
...this.header,
|
|
679
728
|
...materializedHeader,
|
|
680
|
-
transform: existingTransform,
|
|
681
|
-
transformMatrix:
|
|
729
|
+
transform: hasMaterializedTransform ? materializedHeader.transform : existingTransform,
|
|
730
|
+
transformMatrix: hasMaterializedTransform
|
|
731
|
+
? materializedHeader.transformMatrix
|
|
732
|
+
: existingTransformMatrix,
|
|
682
733
|
implicitSubtree: undefined
|
|
683
734
|
};
|
|
735
|
+
if (hasMaterializedTransform) {
|
|
736
|
+
this._initializeTransforms(this.header);
|
|
737
|
+
}
|
|
684
738
|
this.refine = this._getRefine(materializedHeader.refine);
|
|
685
739
|
this.type = materializedHeader.type;
|
|
686
740
|
this.contentUrl = materializedHeader.contentUrl;
|
|
687
741
|
this.contentUrls =
|
|
688
742
|
materializedHeader.contentUrls ||
|
|
689
743
|
(materializedHeader.contentUrl ? [materializedHeader.contentUrl] : []);
|
|
744
|
+
this._initializeMetadata(this.header);
|
|
690
745
|
this._initializeLodMetric(materializedHeader);
|
|
691
746
|
this._updateLodMetricScale();
|
|
692
747
|
this._initializeBoundingVolumes(this.header);
|
|
748
|
+
this._boundingBox = undefined;
|
|
693
749
|
// Use the merged header so inherited viewer-request-volume metadata remains available when a
|
|
694
750
|
// materialized implicit tile has no render content of its own.
|
|
695
751
|
this._initializeContent(this.header);
|
|
752
|
+
this._initializeContentEntries(this.header);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/** Refreshes tile and content metadata from a source or materialized tile header. */
|
|
756
|
+
private _initializeMetadata(header: Record<string, any>): void {
|
|
757
|
+
this.metadata = header.metadata || null;
|
|
758
|
+
const contentHeaders = Array.isArray(header.content) ? header.content : [header.content];
|
|
759
|
+
this.contentMetadata = contentHeaders
|
|
760
|
+
.filter(Boolean)
|
|
761
|
+
.map(contentHeader => contentHeader.metadata || null);
|
|
762
|
+
const firstContentHeader = contentHeaders.find(Boolean) || {};
|
|
763
|
+
const groupReference = firstContentHeader.group;
|
|
764
|
+
const group =
|
|
765
|
+
typeof groupReference === 'number'
|
|
766
|
+
? this.tileset.groups?.[groupReference] || null
|
|
767
|
+
: groupReference || null;
|
|
768
|
+
const metadataContext: Tile3DMetadataContext = {
|
|
769
|
+
tileset: this.tileset.metadata || null,
|
|
770
|
+
group,
|
|
771
|
+
tile: this.metadata,
|
|
772
|
+
content: this.contentMetadata[0] || null,
|
|
773
|
+
subtree: header.implicitMetadata || null
|
|
774
|
+
};
|
|
775
|
+
if (this.tileset.groups) {
|
|
776
|
+
metadataContext.groups = this.tileset.groups;
|
|
777
|
+
}
|
|
778
|
+
this.metadataContext = metadataContext;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/** Updates payload and renderability while retaining immutable metadata descriptors. */
|
|
782
|
+
private _updateContentEntriesAfterLoad(loadResult: TileContentLoadResult): void {
|
|
783
|
+
if (loadResult.contentEntries) {
|
|
784
|
+
this.contentEntries = loadResult.contentEntries.map((entry, index) => ({
|
|
785
|
+
...entry,
|
|
786
|
+
index,
|
|
787
|
+
boundingVolume:
|
|
788
|
+
entry.boundingVolume || this._contentBoundingVolumes[index] || this.boundingVolume,
|
|
789
|
+
payload: entry.payload ?? this.contents[index] ?? null,
|
|
790
|
+
featureIds: entry.featureIds?.length
|
|
791
|
+
? entry.featureIds
|
|
792
|
+
: getTile3DFeatureIdSets(entry.payload ?? this.contents[index]),
|
|
793
|
+
renderable: Boolean(
|
|
794
|
+
entry.renderable !== false &&
|
|
795
|
+
(entry.payload ?? this.contents[index]) &&
|
|
796
|
+
!isNestedTilesetPayload(entry.payload ?? this.contents[index])
|
|
797
|
+
)
|
|
798
|
+
}));
|
|
799
|
+
this.featureIdSets = this.contentEntries.flatMap(entry => entry.featureIds);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
this.contentEntries = this.contentEntries.map((entry, index) => ({
|
|
803
|
+
...entry,
|
|
804
|
+
payload: this.contents[index] ?? null,
|
|
805
|
+
featureIds: getTile3DFeatureIdSets(this.contents[index]),
|
|
806
|
+
renderable: Boolean(this.contents[index]) && !isNestedTilesetPayload(this.contents[index])
|
|
807
|
+
}));
|
|
808
|
+
this.featureIdSets = this.contentEntries.flatMap(entry => entry.featureIds);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/** Returns one ordered content descriptor, or null when the index is out of range. */
|
|
812
|
+
getContentEntry(index: number): Tile3DContent | null {
|
|
813
|
+
return this.contentEntries[index] || null;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/** Returns whether an ordered content entry currently has renderable payload. */
|
|
817
|
+
isContentRenderable(index: number): boolean {
|
|
818
|
+
return Boolean(this.contentEntries[index]?.renderable);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/** Initializes additive renderer-neutral descriptors from a normalized tile header. */
|
|
822
|
+
private _initializeContentEntries(tileHeader: Record<string, any>): void {
|
|
823
|
+
const headers = Array.isArray(tileHeader.content)
|
|
824
|
+
? tileHeader.content
|
|
825
|
+
: tileHeader.content
|
|
826
|
+
? [tileHeader.content]
|
|
827
|
+
: [];
|
|
828
|
+
this.contentEntries = headers.map((contentHeader, index) => {
|
|
829
|
+
const normalizedContentHeader = contentHeader || {};
|
|
830
|
+
return {
|
|
831
|
+
index,
|
|
832
|
+
uri: this.contentUrls[index] || normalizedContentHeader.uri || normalizedContentHeader.url,
|
|
833
|
+
group: normalizedContentHeader.group,
|
|
834
|
+
type: normalizedContentHeader.type,
|
|
835
|
+
payload: null,
|
|
836
|
+
metadata: normalizedContentHeader.metadata || null,
|
|
837
|
+
boundingVolume: this._contentBoundingVolumes[index] || this.boundingVolume,
|
|
838
|
+
featureIds: this._getFeatureIdSets(normalizedContentHeader),
|
|
839
|
+
renderable: false
|
|
840
|
+
};
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/** Extracts renderer-neutral feature-id declarations without decoding property values. */
|
|
845
|
+
private _getFeatureIdSets(contentHeader: Record<string, any>): Tile3DFeatureIdSet[] {
|
|
846
|
+
return getTile3DFeatureIdSets(contentHeader);
|
|
696
847
|
}
|
|
697
848
|
|
|
698
849
|
// Unloads the tile's content.
|
|
@@ -704,6 +855,13 @@ export class Tile3D {
|
|
|
704
855
|
}
|
|
705
856
|
this.contents = [];
|
|
706
857
|
this.content = null;
|
|
858
|
+
this.vectorContent = null;
|
|
859
|
+
this.contentEntries = this.contentEntries.map(entry => ({
|
|
860
|
+
...entry,
|
|
861
|
+
payload: null,
|
|
862
|
+
renderable: false
|
|
863
|
+
}));
|
|
864
|
+
this.featureIdSets = this.contentEntries.flatMap(entry => entry.featureIds);
|
|
707
865
|
if (this.header.content && this.header.content.destroy) {
|
|
708
866
|
this.header.content.destroy();
|
|
709
867
|
}
|
|
@@ -848,9 +1006,15 @@ export class Tile3D {
|
|
|
848
1006
|
* @param frameState Current camera, culling, and optional clipping-plane state.
|
|
849
1007
|
* @returns The content visibility classification.
|
|
850
1008
|
*/
|
|
851
|
-
contentVisibility(frameState: FrameState): CullingResult {
|
|
1009
|
+
contentVisibility(frameState: FrameState, contentIndex?: number): CullingResult {
|
|
1010
|
+
const contentVolumes =
|
|
1011
|
+
contentIndex === undefined
|
|
1012
|
+
? this._contentBoundingVolumes
|
|
1013
|
+
: this._contentBoundingVolumes[contentIndex]
|
|
1014
|
+
? [this._contentBoundingVolumes[contentIndex]]
|
|
1015
|
+
: [];
|
|
852
1016
|
return getContentVisibility(
|
|
853
|
-
|
|
1017
|
+
contentVolumes,
|
|
854
1018
|
this.boundingVolume,
|
|
855
1019
|
frameState.cullingVolume,
|
|
856
1020
|
this._visibilityPlaneMask,
|
|
@@ -913,6 +1077,10 @@ export class Tile3D {
|
|
|
913
1077
|
|
|
914
1078
|
/** Initializes the tile's LOD metric from its header or its nearest available ancestor. */
|
|
915
1079
|
_initializeLodMetric(header: {[key: string]: any}): void {
|
|
1080
|
+
this._scaleGeometricError =
|
|
1081
|
+
typeof header._scaleGeometricError === 'boolean'
|
|
1082
|
+
? header._scaleGeometricError
|
|
1083
|
+
: (this.parent?._scaleGeometricError ?? true);
|
|
916
1084
|
if ('lodMetricType' in header) {
|
|
917
1085
|
this.lodMetricType = header.lodMetricType;
|
|
918
1086
|
} else {
|
|
@@ -969,7 +1137,7 @@ export class Tile3D {
|
|
|
969
1137
|
* I3S `maxScreenThreshold` is already a screen-space metric and must not be transform-scaled.
|
|
970
1138
|
*/
|
|
971
1139
|
_updateLodMetricScale(): void {
|
|
972
|
-
if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR) {
|
|
1140
|
+
if (this.lodMetricType !== LOD_METRIC_TYPE.GEOMETRIC_ERROR || !this._scaleGeometricError) {
|
|
973
1141
|
this.lodMetricValue = this._unscaledLodMetricValue;
|
|
974
1142
|
return;
|
|
975
1143
|
}
|
|
@@ -1074,11 +1242,13 @@ export class Tile3D {
|
|
|
1074
1242
|
}
|
|
1075
1243
|
|
|
1076
1244
|
_updateBoundingVolume(header) {
|
|
1245
|
+
this._boundingBox = undefined;
|
|
1077
1246
|
// Update the bounding volumes
|
|
1078
1247
|
this.boundingVolume = createBoundingVolume(
|
|
1079
1248
|
header.boundingVolume,
|
|
1080
1249
|
this.computedTransform,
|
|
1081
|
-
this.boundingVolume
|
|
1250
|
+
this.boundingVolume,
|
|
1251
|
+
this._initialTransform
|
|
1082
1252
|
);
|
|
1083
1253
|
|
|
1084
1254
|
// Viewer request volumes constrain traversal, not just render content. A contentless implicit
|
|
@@ -1088,7 +1258,8 @@ export class Tile3D {
|
|
|
1088
1258
|
this._viewerRequestVolume = createBoundingVolume(
|
|
1089
1259
|
header.viewerRequestVolume,
|
|
1090
1260
|
this.computedTransform,
|
|
1091
|
-
this._viewerRequestVolume
|
|
1261
|
+
this._viewerRequestVolume,
|
|
1262
|
+
this._initialTransform
|
|
1092
1263
|
);
|
|
1093
1264
|
}
|
|
1094
1265
|
|
|
@@ -1106,7 +1277,12 @@ export class Tile3D {
|
|
|
1106
1277
|
this._contentBoundingVolumes = contentHeaders.length
|
|
1107
1278
|
? contentHeaders.map(headerEntry =>
|
|
1108
1279
|
headerEntry?.boundingVolume
|
|
1109
|
-
? createBoundingVolume(
|
|
1280
|
+
? createBoundingVolume(
|
|
1281
|
+
headerEntry.boundingVolume,
|
|
1282
|
+
this.computedTransform,
|
|
1283
|
+
undefined,
|
|
1284
|
+
this._initialTransform
|
|
1285
|
+
)
|
|
1110
1286
|
: this.boundingVolume
|
|
1111
1287
|
)
|
|
1112
1288
|
: [this.boundingVolume];
|
|
@@ -58,17 +58,6 @@ export type Tileset3DProps = {
|
|
|
58
58
|
* @default 536870912
|
|
59
59
|
*/
|
|
60
60
|
maximumCacheOverflowBytes?: number;
|
|
61
|
-
/**
|
|
62
|
-
* Soft cache target in mebibytes.
|
|
63
|
-
* @deprecated Use {@link Tileset3DProps.cacheBytes}; when both are supplied, `cacheBytes` wins.
|
|
64
|
-
*/
|
|
65
|
-
maximumMemoryUsage?: number;
|
|
66
|
-
/**
|
|
67
|
-
* Cache overflow headroom in mebibytes.
|
|
68
|
-
* @deprecated Use {@link Tileset3DProps.maximumCacheOverflowBytes}; when both are supplied, the
|
|
69
|
-
* byte-native option wins.
|
|
70
|
-
*/
|
|
71
|
-
memoryCacheOverflow?: number;
|
|
72
61
|
maximumTilesSelected?: number;
|
|
73
62
|
debounceTime?: number;
|
|
74
63
|
|
|
@@ -158,10 +147,6 @@ type Props = {
|
|
|
158
147
|
cacheBytes: number;
|
|
159
148
|
/** Current-frame headroom, in bytes, before memory-adjusted SSE rises. */
|
|
160
149
|
maximumCacheOverflowBytes: number;
|
|
161
|
-
/** @deprecated Byte-native code should use `cacheBytes`. */
|
|
162
|
-
maximumMemoryUsage: number;
|
|
163
|
-
/** @deprecated Byte-native code should use `maximumCacheOverflowBytes`. */
|
|
164
|
-
memoryCacheOverflow: number;
|
|
165
150
|
maximumTilesSelected: number;
|
|
166
151
|
debounceTime: number;
|
|
167
152
|
onTileLoad: (tile: Tile3D) => void;
|
|
@@ -241,31 +226,21 @@ function validateCacheByteLength(value: number, optionName: string): number {
|
|
|
241
226
|
}
|
|
242
227
|
|
|
243
228
|
/**
|
|
244
|
-
* Resolves the base cache target from byte-native or
|
|
229
|
+
* Resolves the base cache target from the byte-native option or its fallback.
|
|
245
230
|
*
|
|
246
|
-
*
|
|
247
|
-
* The fallback is already expressed in bytes and is used for both construction and runtime updates.
|
|
248
|
-
*
|
|
249
|
-
* @param options - Options that may contain byte-native or deprecated cache values.
|
|
231
|
+
* @param options - Options that may contain a byte-native cache value.
|
|
250
232
|
* @param fallbackCacheBytes - Existing or default cache target in bytes.
|
|
251
233
|
* @returns The resolved and validated cache target in bytes.
|
|
252
234
|
*/
|
|
253
235
|
function resolveCacheBytes(options: Tileset3DProps, fallbackCacheBytes: number): number {
|
|
254
|
-
const cacheBytes =
|
|
255
|
-
options.cacheBytes ??
|
|
256
|
-
(options.maximumMemoryUsage !== undefined
|
|
257
|
-
? options.maximumMemoryUsage * BYTES_PER_MEBIBYTE
|
|
258
|
-
: fallbackCacheBytes);
|
|
236
|
+
const cacheBytes = options.cacheBytes ?? fallbackCacheBytes;
|
|
259
237
|
return validateCacheByteLength(cacheBytes, 'cacheBytes');
|
|
260
238
|
}
|
|
261
239
|
|
|
262
240
|
/**
|
|
263
|
-
* Resolves cache overflow headroom from byte-native or
|
|
264
|
-
*
|
|
265
|
-
* The byte-native option takes precedence so callers can migrate the two cache controls
|
|
266
|
-
* independently without unit ambiguity.
|
|
241
|
+
* Resolves cache overflow headroom from the byte-native option or its fallback.
|
|
267
242
|
*
|
|
268
|
-
* @param options - Options that may contain byte-native
|
|
243
|
+
* @param options - Options that may contain a byte-native overflow value.
|
|
269
244
|
* @param fallbackOverflowBytes - Existing or default overflow headroom in bytes.
|
|
270
245
|
* @returns The resolved and validated overflow headroom in bytes.
|
|
271
246
|
*/
|
|
@@ -273,11 +248,7 @@ function resolveMaximumCacheOverflowBytes(
|
|
|
273
248
|
options: Tileset3DProps,
|
|
274
249
|
fallbackOverflowBytes: number
|
|
275
250
|
): number {
|
|
276
|
-
const maximumCacheOverflowBytes =
|
|
277
|
-
options.maximumCacheOverflowBytes ??
|
|
278
|
-
(options.memoryCacheOverflow !== undefined
|
|
279
|
-
? options.memoryCacheOverflow * BYTES_PER_MEBIBYTE
|
|
280
|
-
: fallbackOverflowBytes);
|
|
251
|
+
const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? fallbackOverflowBytes;
|
|
281
252
|
return validateCacheByteLength(maximumCacheOverflowBytes, 'maximumCacheOverflowBytes');
|
|
282
253
|
}
|
|
283
254
|
|
|
@@ -289,8 +260,6 @@ const DEFAULT_PROPS: Props = {
|
|
|
289
260
|
maxRequests: 64,
|
|
290
261
|
cacheBytes: DEFAULT_CACHE_BYTES,
|
|
291
262
|
maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
|
|
292
|
-
maximumMemoryUsage: DEFAULT_CACHE_BYTES / BYTES_PER_MEBIBYTE,
|
|
293
|
-
memoryCacheOverflow: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES / BYTES_PER_MEBIBYTE,
|
|
294
263
|
maximumTilesSelected: 0,
|
|
295
264
|
debounceTime: 0,
|
|
296
265
|
onTileLoad: () => {},
|
|
@@ -456,8 +425,6 @@ export class Tileset3D {
|
|
|
456
425
|
...suppliedOptions,
|
|
457
426
|
cacheBytes,
|
|
458
427
|
maximumCacheOverflowBytes,
|
|
459
|
-
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
460
|
-
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE,
|
|
461
428
|
memoryAdjustedScreenSpaceError:
|
|
462
429
|
suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
|
|
463
430
|
};
|
|
@@ -517,6 +484,26 @@ export class Tileset3D {
|
|
|
517
484
|
return this._frameNumber;
|
|
518
485
|
}
|
|
519
486
|
|
|
487
|
+
/**
|
|
488
|
+
* Returns the tiles requested during the most recent completed traversal.
|
|
489
|
+
*
|
|
490
|
+
* A defensive copy makes request ordering deterministic for diagnostics and prevents callers
|
|
491
|
+
* from mutating the scheduler's working set.
|
|
492
|
+
*/
|
|
493
|
+
get requestedTiles(): readonly Tile3D[] {
|
|
494
|
+
return this._requestedTiles.slice();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Returns empty tiles considered during the most recent completed traversal.
|
|
499
|
+
*
|
|
500
|
+
* Empty tiles can still be useful hierarchy placeholders, but their IDs are included in
|
|
501
|
+
* observability snapshots so applications can distinguish traversal work from render content.
|
|
502
|
+
*/
|
|
503
|
+
get emptyTiles(): readonly Tile3D[] {
|
|
504
|
+
return this._emptyTiles.slice();
|
|
505
|
+
}
|
|
506
|
+
|
|
520
507
|
/**
|
|
521
508
|
* Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
|
|
522
509
|
*
|
|
@@ -532,7 +519,6 @@ export class Tileset3D {
|
|
|
532
519
|
set cacheBytes(value: number) {
|
|
533
520
|
this._cacheBytes = validateCacheByteLength(value, 'cacheBytes');
|
|
534
521
|
this.options.cacheBytes = this._cacheBytes;
|
|
535
|
-
this.options.maximumMemoryUsage = this._cacheBytes / BYTES_PER_MEBIBYTE;
|
|
536
522
|
}
|
|
537
523
|
|
|
538
524
|
/**
|
|
@@ -549,19 +535,6 @@ export class Tileset3D {
|
|
|
549
535
|
set maximumCacheOverflowBytes(value: number) {
|
|
550
536
|
this._maximumCacheOverflowBytes = validateCacheByteLength(value, 'maximumCacheOverflowBytes');
|
|
551
537
|
this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
|
|
552
|
-
this.options.memoryCacheOverflow = this._maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* Gets or sets the soft cache target in mebibytes.
|
|
557
|
-
* @deprecated Use {@link Tileset3D.cacheBytes}. Assignments remain synchronized for compatibility.
|
|
558
|
-
*/
|
|
559
|
-
get maximumMemoryUsage(): number {
|
|
560
|
-
return this.cacheBytes / BYTES_PER_MEBIBYTE;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
set maximumMemoryUsage(value: number) {
|
|
564
|
-
this.cacheBytes = validateCacheByteLength(value * BYTES_PER_MEBIBYTE, 'maximumMemoryUsage');
|
|
565
538
|
}
|
|
566
539
|
|
|
567
540
|
get queryParams(): string {
|
|
@@ -571,10 +544,7 @@ export class Tileset3D {
|
|
|
571
544
|
}
|
|
572
545
|
|
|
573
546
|
/**
|
|
574
|
-
* Updates traversal and runtime options
|
|
575
|
-
*
|
|
576
|
-
* For each budget, the byte-native option takes precedence when both unit forms are supplied.
|
|
577
|
-
* Unspecified budgets retain their current byte values, avoiding repeated MiB conversion.
|
|
547
|
+
* Updates traversal and runtime options while retaining unspecified byte budgets.
|
|
578
548
|
*
|
|
579
549
|
* @param props - Partial runtime options to apply.
|
|
580
550
|
*/
|
|
@@ -588,9 +558,7 @@ export class Tileset3D {
|
|
|
588
558
|
...this.options,
|
|
589
559
|
...props,
|
|
590
560
|
cacheBytes,
|
|
591
|
-
maximumCacheOverflowBytes
|
|
592
|
-
maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
|
|
593
|
-
memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE
|
|
561
|
+
maximumCacheOverflowBytes
|
|
594
562
|
};
|
|
595
563
|
this._cacheBytes = cacheBytes;
|
|
596
564
|
this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
|
|
@@ -662,6 +630,14 @@ export class Tileset3D {
|
|
|
662
630
|
return;
|
|
663
631
|
}
|
|
664
632
|
const preparedViewports = viewports instanceof Array ? viewports : [viewports];
|
|
633
|
+
const activeViewportIds = new Set(preparedViewports.map(viewport => viewport.id));
|
|
634
|
+
// A viewport may be removed between frames. Discard its completed state before aggregating
|
|
635
|
+
// requested and selected tiles so observability reflects only the current traversal inputs.
|
|
636
|
+
for (const frameStateId of Object.keys(this.frameStateData)) {
|
|
637
|
+
if (!activeViewportIds.has(frameStateId)) {
|
|
638
|
+
delete this.frameStateData[frameStateId];
|
|
639
|
+
}
|
|
640
|
+
}
|
|
665
641
|
|
|
666
642
|
this._cache.reset();
|
|
667
643
|
this._frameNumber++;
|
|
@@ -909,7 +885,7 @@ export class Tileset3D {
|
|
|
909
885
|
if (
|
|
910
886
|
initialMetadata?.tileset?.root &&
|
|
911
887
|
typeof initialMetadata.tileset.root.then !== 'function' &&
|
|
912
|
-
!this.source.prepareTileset
|
|
888
|
+
(!this.source.prepareTileset || this.spatialReference?.status === 'native')
|
|
913
889
|
) {
|
|
914
890
|
this.root = this._initializeTileHeaders(initialMetadata.tileset, null);
|
|
915
891
|
this._applyViewState();
|