@loaders.gl/tiles 5.0.0-alpha.1 → 5.0.0-alpha.11
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/LICENSE +14 -3
- package/README.md +26 -0
- package/dist/dist.dev.js +20341 -5081
- package/dist/dist.min.js +3 -1
- package/dist/index.cjs +7904 -2071
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +25 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
- package/dist/point-cloud/point-cloud-tile.js +3 -0
- package/dist/point-cloud/point-cloud-tile.js.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
- package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
- package/dist/point-cloud/point-cloud-tileset.js +54 -12
- package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
- package/dist/point-cloud/types.d.ts +17 -0
- package/dist/point-cloud/types.d.ts.map +1 -1
- package/dist/point-cloud/types.js +3 -0
- package/dist/point-cloud/types.js.map +1 -1
- package/dist/raster-set.d.ts +48 -31
- package/dist/raster-set.d.ts.map +1 -1
- package/dist/raster-set.js +104 -24
- package/dist/raster-set.js.map +1 -1
- package/dist/spatial/format-spatial-reference.d.ts +68 -0
- package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
- package/dist/spatial/format-spatial-reference.js +291 -0
- package/dist/spatial/format-spatial-reference.js.map +1 -0
- package/dist/spatial/get-spatial-coordinate-frame.d.ts +20 -0
- package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
- package/dist/spatial/get-spatial-coordinate-frame.js +139 -0
- package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
- package/dist/spatial/i3s-elevation.d.ts +20 -0
- package/dist/spatial/i3s-elevation.d.ts.map +1 -0
- package/dist/spatial/i3s-elevation.js +90 -0
- package/dist/spatial/i3s-elevation.js.map +1 -0
- package/dist/spatial/i3s-spatial-transformer.d.ts +205 -0
- package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
- package/dist/spatial/i3s-spatial-transformer.js +731 -0
- package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
- 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 +52 -0
- package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
- package/dist/spatial/spatial-coordinate-transformer.js +295 -0
- package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
- package/dist/spatial/spatial-resource-registry.d.ts +47 -0
- package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
- package/dist/spatial/spatial-resource-registry.js +76 -0
- package/dist/spatial/spatial-resource-registry.js.map +1 -0
- package/dist/spatial/spatial-types.d.ts +190 -0
- package/dist/spatial/spatial-types.d.ts.map +1 -0
- package/dist/spatial/spatial-types.js +192 -0
- package/dist/spatial/spatial-types.js.map +1 -0
- 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 +163 -0
- package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
- package/dist/tileset-2d/adapter.d.ts +2 -3
- package/dist/tileset-2d/adapter.d.ts.map +1 -1
- package/dist/tileset-2d/tileset-2d.d.ts +2 -3
- package/dist/tileset-2d/tileset-2d.d.ts.map +1 -1
- package/dist/tileset-2d/tileset-2d.js.map +1 -1
- package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +3 -3
- package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -1
- package/dist/tileset-3d/common/indexed-archive-tileset-source.js +9 -4
- package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -1
- 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 +176 -5
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +523 -89
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +199 -9
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +345 -32
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
- package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-cache.js +11 -3
- package/dist/tileset-3d/common/tileset-cache.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 +51 -9
- 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 +33 -1
- package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.js +61 -14
- package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +182 -0
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +681 -0
- package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
- package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +127 -6
- 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 +485 -27
- 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 +104 -0
- 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 +286 -0
- package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
- package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts +23 -6
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.js +176 -25
- package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
- package/dist/tileset-3d/format-i3s/i3s-tile-manager.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 +4 -1
- package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
- package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
- package/dist/tileset-3d/helpers/3d-tiles-options.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 +20 -91
- package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.d.ts +67 -2
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +92 -10
- package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
- package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
- package/dist/tileset-3d/helpers/i3s-lod.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/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
- package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
- package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
- package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/doubly-linked-list.d.ts.map +1 -1
- package/dist/utils/doubly-linked-list.js +1 -0
- package/dist/utils/doubly-linked-list.js.map +1 -1
- package/dist/utils/managed-array.d.ts.map +1 -1
- package/dist/utils/managed-array.js +1 -0
- package/dist/utils/managed-array.js.map +1 -1
- package/package.json +12 -10
- package/src/index.ts +79 -1
- package/src/point-cloud/point-cloud-tile.ts +4 -0
- package/src/point-cloud/point-cloud-tileset.ts +70 -12
- package/src/point-cloud/types.ts +21 -0
- package/src/raster-set.ts +197 -67
- package/src/spatial/format-spatial-reference.ts +385 -0
- package/src/spatial/get-spatial-coordinate-frame.ts +169 -0
- package/src/spatial/i3s-elevation.ts +96 -0
- package/src/spatial/i3s-spatial-transformer.ts +1021 -0
- package/src/spatial/normalize-crs-identifier.ts +26 -0
- package/src/spatial/spatial-coordinate-transformer.ts +377 -0
- package/src/spatial/spatial-resource-registry.ts +90 -0
- package/src/spatial/spatial-types.ts +422 -0
- package/src/spatial/tiles-3d-spatial-transformer.ts +194 -0
- package/src/tileset-2d/adapter.ts +2 -3
- package/src/tileset-2d/tileset-2d.ts +2 -3
- package/src/tileset-3d/common/indexed-archive-tileset-source.ts +13 -13
- package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
- package/src/tileset-3d/common/tile-3d.ts +629 -98
- package/src/tileset-3d/common/tileset-3d.ts +512 -40
- package/src/tileset-3d/common/tileset-cache.ts +15 -7
- 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 +63 -13
- package/src/tileset-3d/common/tileset-traverser.ts +73 -15
- package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +1059 -0
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +674 -28
- package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +328 -0
- package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
- package/src/tileset-3d/format-i3s/i3s-source.ts +220 -29
- package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
- package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +6 -1
- package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
- package/src/tileset-3d/helpers/bounding-volume.ts +31 -114
- package/src/tileset-3d/helpers/frame-state.ts +152 -13
- package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
- package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
- package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
- package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
- package/src/types.ts +11 -0
- package/src/utils/doubly-linked-list.ts +2 -0
- package/src/utils/managed-array.ts +2 -0
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {path} from '@loaders.gl/loader-utils';
|
|
5
|
+
import {path, RequestCache, sliceArrayBuffer} from '@loaders.gl/loader-utils';
|
|
6
6
|
import {Ellipsoid} from '@math.gl/geospatial';
|
|
7
|
-
import {Vector3} from '@math.gl/core';
|
|
8
|
-
import type {CoreAPI,
|
|
7
|
+
import {Matrix4, Vector3} from '@math.gl/core';
|
|
8
|
+
import type {CoreAPI, Loader, LoaderContext, LoaderOptions} from '@loaders.gl/loader-utils';
|
|
9
9
|
import type {Tile3D} from '../common/tile-3d';
|
|
10
10
|
import {Tileset3DTraverser} from './tileset-3d-traverser';
|
|
11
11
|
import type {Tileset3D} from '../common/tileset-3d';
|
|
12
12
|
import type {
|
|
13
|
+
TileChildrenLoadResult,
|
|
13
14
|
TileContentLoadResult,
|
|
14
15
|
TilesetContentFormats,
|
|
15
16
|
TilesetJSON,
|
|
@@ -24,6 +25,19 @@ import {Tile3D as Tile3DNode} from '../common/tile-3d';
|
|
|
24
25
|
import {getZoomFromBoundingVolume} from '../helpers/zoom';
|
|
25
26
|
import {TILESET_TYPE} from '../../constants';
|
|
26
27
|
import type {TilesetTraverser, TilesetTraverserProps} from '../common/tileset-traverser';
|
|
28
|
+
import type {FrameState} from '../helpers/frame-state';
|
|
29
|
+
import {get3DTilesSpatialReference} from '../../spatial/format-spatial-reference';
|
|
30
|
+
import {Tiles3DSpatialTransformer} from '../../spatial/tiles-3d-spatial-transformer';
|
|
31
|
+
import {
|
|
32
|
+
applyTilesetSpatialOptions,
|
|
33
|
+
markTilesetSpatialReferenceTransformed
|
|
34
|
+
} from '../../spatial/spatial-types';
|
|
35
|
+
import type {TilesetSpatialReference} from '../../spatial/spatial-types';
|
|
36
|
+
import {
|
|
37
|
+
materializeImplicitSubtree,
|
|
38
|
+
type ImplicitSubtreeReference,
|
|
39
|
+
type ParsedImplicitSubtree
|
|
40
|
+
} from './implicit-tiling';
|
|
27
41
|
|
|
28
42
|
const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
|
|
29
43
|
draco: false,
|
|
@@ -32,6 +46,42 @@ const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
|
|
|
32
46
|
ktx2: false
|
|
33
47
|
};
|
|
34
48
|
|
|
49
|
+
const DEFAULT_MAXIMUM_CACHED_SUBTREES = 32;
|
|
50
|
+
|
|
51
|
+
type PackageFile = {
|
|
52
|
+
name?: string;
|
|
53
|
+
mimeType: string;
|
|
54
|
+
uri?: string;
|
|
55
|
+
originalUri?: string;
|
|
56
|
+
data?: ArrayBuffer;
|
|
57
|
+
byteOffset: number;
|
|
58
|
+
byteLength: number;
|
|
59
|
+
bufferUri?: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type PackageResource = {fileIndex: number; files: PackageFile[]};
|
|
63
|
+
|
|
64
|
+
type Tiles3DPackageLoaderContext = LoaderContext & {
|
|
65
|
+
/** Parent package records available while parsing an embedded tileset or subtree. */
|
|
66
|
+
_tiles3dPackageFiles: PackageFile[];
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** Diagnostics for source-managed implicit subtree loading. */
|
|
70
|
+
export type ImplicitTilingStats = {
|
|
71
|
+
/** Subtree resources requested from the resolver or core API. */
|
|
72
|
+
requestedSubtrees: number;
|
|
73
|
+
/** Successful subtree materializations, including parsed-cache hits. */
|
|
74
|
+
loadedSubtrees: number;
|
|
75
|
+
/** Requests served by the source's parsed-subtree cache. */
|
|
76
|
+
cacheHits: number;
|
|
77
|
+
/** Parsed subtree resources currently retained for reuse. */
|
|
78
|
+
cachedSubtrees: number;
|
|
79
|
+
/** Subtree resource requests currently in flight. */
|
|
80
|
+
pendingSubtrees: number;
|
|
81
|
+
/** Runtime headers created below already-existing subtree-root placeholders. */
|
|
82
|
+
materializedTiles: number;
|
|
83
|
+
};
|
|
84
|
+
|
|
35
85
|
/**
|
|
36
86
|
* {@link Tileset3DSource} implementation for 3D Tiles datasets.
|
|
37
87
|
*/
|
|
@@ -39,7 +89,7 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
39
89
|
/** 3D Tiles format discriminator. */
|
|
40
90
|
readonly type = TILESET_TYPE.TILES3D;
|
|
41
91
|
/** Loader used for tile content requests. */
|
|
42
|
-
readonly loader:
|
|
92
|
+
readonly loader: Loader;
|
|
43
93
|
/** Root tileset URL. */
|
|
44
94
|
readonly url: string;
|
|
45
95
|
/** Base path used for relative tile resource resolution. */
|
|
@@ -65,9 +115,37 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
65
115
|
metadata?: TilesetSourceMetadata;
|
|
66
116
|
|
|
67
117
|
private readonly queryParams: Record<string, string> = {};
|
|
118
|
+
/** Final request URLs cached by unmodified tile content URL. */
|
|
119
|
+
private readonly tileUrlCache: Map<string, string> = new Map();
|
|
120
|
+
/** Original content descriptors retained across {@link Tile3D.unloadContent} calls. */
|
|
121
|
+
private readonly tileContentHeaders = new WeakMap<Tile3D, Record<string, any>[]>();
|
|
122
|
+
/** Installed nested roots by parent placement and ordered nested-content slot, across reloads. */
|
|
123
|
+
private readonly nestedTilesetRoots = new WeakMap<Tile3D, Tile3D[]>();
|
|
124
|
+
/** Source-local namespaces assigned to distinct embedded glTF package file collections. */
|
|
125
|
+
private readonly packageNamespaces = new WeakMap<PackageFile[], number>();
|
|
126
|
+
/** Next source-local package namespace. */
|
|
127
|
+
private nextPackageNamespace = 0;
|
|
128
|
+
/** Parsed subtree requests keyed by final source URL for deduplication and LRU reuse. */
|
|
129
|
+
private readonly implicitSubtreeCache: RequestCache<ParsedImplicitSubtree>;
|
|
130
|
+
/** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
|
|
131
|
+
private readonly implicitTilingStats: Omit<
|
|
132
|
+
ImplicitTilingStats,
|
|
133
|
+
'cachedSubtrees' | 'pendingSubtrees'
|
|
134
|
+
> = {
|
|
135
|
+
requestedSubtrees: 0,
|
|
136
|
+
loadedSubtrees: 0,
|
|
137
|
+
cacheHits: 0,
|
|
138
|
+
materializedTiles: 0
|
|
139
|
+
};
|
|
140
|
+
/** Whether the owning tileset has released this source. */
|
|
141
|
+
private destroyed = false;
|
|
68
142
|
private readonly extensionsUsed: string[] = [];
|
|
69
143
|
private readonly resolver?: TilesetSourceResolver;
|
|
70
144
|
private rootTileset: TilesetJSON;
|
|
145
|
+
/** CRS transformer shared by this source's headers and decoded glTF content. */
|
|
146
|
+
private spatialTransformer?: Tiles3DSpatialTransformer;
|
|
147
|
+
/** Root header after composing source-frame transforms and rebuilding target bounds. */
|
|
148
|
+
private preparedRootHeader?: Record<string, any>;
|
|
71
149
|
|
|
72
150
|
/**
|
|
73
151
|
* Creates a 3D Tiles source.
|
|
@@ -84,6 +162,25 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
84
162
|
this.resolver = request.resolver;
|
|
85
163
|
this.coreApi = request.coreApi;
|
|
86
164
|
this.loadOptions = loadOptions;
|
|
165
|
+
const maximumCachedSubtrees = Number(
|
|
166
|
+
(loadOptions['3d-tiles'] as Record<string, unknown> | undefined)?.maximumCachedSubtrees
|
|
167
|
+
);
|
|
168
|
+
const maximumCacheEntries = Number.isFinite(maximumCachedSubtrees)
|
|
169
|
+
? Math.max(0, Math.floor(maximumCachedSubtrees))
|
|
170
|
+
: DEFAULT_MAXIMUM_CACHED_SUBTREES;
|
|
171
|
+
this.implicitSubtreeCache = new RequestCache({maxEntries: maximumCacheEntries});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Releases URL and parsed-subtree caches and blocks late subtree installation.
|
|
176
|
+
*
|
|
177
|
+
* The injected resolver or core API remains responsible for transport-level abort signals. A
|
|
178
|
+
* request that cannot be aborted may finish parsing, but it will not mutate destroyed tiles.
|
|
179
|
+
*/
|
|
180
|
+
destroy(): void {
|
|
181
|
+
this.destroyed = true;
|
|
182
|
+
this.tileUrlCache.clear();
|
|
183
|
+
this.implicitSubtreeCache.clear();
|
|
87
184
|
}
|
|
88
185
|
|
|
89
186
|
/**
|
|
@@ -104,7 +201,9 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
104
201
|
|
|
105
202
|
if (this.rootTileset.queryString) {
|
|
106
203
|
const searchParams = new URLSearchParams(this.rootTileset.queryString);
|
|
107
|
-
|
|
204
|
+
for (const [parameterName, parameterValue] of searchParams.entries()) {
|
|
205
|
+
this.setQueryParameter(parameterName, parameterValue);
|
|
206
|
+
}
|
|
108
207
|
}
|
|
109
208
|
|
|
110
209
|
this.asset = this.rootTileset.asset;
|
|
@@ -112,15 +211,16 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
112
211
|
throw new Error('Tileset must have an asset property.');
|
|
113
212
|
}
|
|
114
213
|
if (
|
|
214
|
+
this.rootTileset.formatVersion !== '2.0-draft' &&
|
|
115
215
|
this.asset.version !== '0.0' &&
|
|
116
216
|
this.asset.version !== '1.0' &&
|
|
117
217
|
this.asset.version !== '1.1'
|
|
118
218
|
) {
|
|
119
|
-
throw new Error('The tileset must be 3D Tiles version
|
|
219
|
+
throw new Error('The tileset must be 3D Tiles version 0.0, 1.0, 1.1, or 2.0-draft.');
|
|
120
220
|
}
|
|
121
221
|
|
|
122
222
|
if ('tilesetVersion' in this.asset) {
|
|
123
|
-
this.
|
|
223
|
+
this.setQueryParameter('v', this.asset.tilesetVersion);
|
|
124
224
|
}
|
|
125
225
|
|
|
126
226
|
this.properties = this.rootTileset.properties;
|
|
@@ -139,7 +239,8 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
139
239
|
tileset: this.rootTileset,
|
|
140
240
|
lodMetricType: this.rootTileset.lodMetricType,
|
|
141
241
|
lodMetricValue: this.rootTileset.lodMetricValue,
|
|
142
|
-
refine: this.rootTileset.root?.refine
|
|
242
|
+
refine: this.rootTileset.root?.refine,
|
|
243
|
+
spatialReference: get3DTilesSpatialReference(this.rootTileset)
|
|
143
244
|
};
|
|
144
245
|
}
|
|
145
246
|
|
|
@@ -160,15 +261,39 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
160
261
|
return this.getMetadata().tileset;
|
|
161
262
|
}
|
|
162
263
|
|
|
264
|
+
/** Prepare nonlinear CRS state before runtime headers are materialized. */
|
|
265
|
+
async prepareTileset(tileset: Tileset3D): Promise<void> {
|
|
266
|
+
const spatialReference = tileset.spatialReference;
|
|
267
|
+
if (spatialReference.status === 'unresolved') {
|
|
268
|
+
throw new Error(
|
|
269
|
+
spatialReference.warnings[0] ||
|
|
270
|
+
'3D Tiles spatial operations cannot be resolved from the supplied metadata and options'
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
if (spatialReference.status !== 'transformable' && spatialReference.status !== 'transformed') {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
this.spatialTransformer ||= new Tiles3DSpatialTransformer(
|
|
277
|
+
spatialReference,
|
|
278
|
+
tileset.options.spatial
|
|
279
|
+
);
|
|
280
|
+
tileset.spatialReference = markTilesetSpatialReferenceTransformed(spatialReference);
|
|
281
|
+
this.preparedRootHeader = this.transformTileHeader(
|
|
282
|
+
this.getMetadata().tileset.root,
|
|
283
|
+
new Matrix4()
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
163
287
|
/**
|
|
164
|
-
* Builds
|
|
288
|
+
* Builds explicit runtime headers while leaving implicit subtree references lazy.
|
|
165
289
|
*/
|
|
166
290
|
initializeTileHeaders(
|
|
167
291
|
tileset: Tileset3D,
|
|
168
292
|
tilesetJson: TilesetJSON,
|
|
169
293
|
parentTile?: Tile3D | null
|
|
170
294
|
): Tile3D {
|
|
171
|
-
const
|
|
295
|
+
const rootHeader = parentTile ? tilesetJson.root : this.preparedRootHeader || tilesetJson.root;
|
|
296
|
+
const rootTile = new Tile3DNode(tileset, rootHeader, parentTile || undefined);
|
|
172
297
|
|
|
173
298
|
if (parentTile) {
|
|
174
299
|
parentTile.children.push(rootTile);
|
|
@@ -186,7 +311,7 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
186
311
|
const url = new URL(childTile.contentUrl);
|
|
187
312
|
const session = url.searchParams.get('session');
|
|
188
313
|
if (session) {
|
|
189
|
-
this.
|
|
314
|
+
this.setQueryParameter('session', session);
|
|
190
315
|
}
|
|
191
316
|
}
|
|
192
317
|
tile.children.push(childTile);
|
|
@@ -209,36 +334,241 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
209
334
|
* Loads binary content or nested tileset JSON for a runtime tile.
|
|
210
335
|
*/
|
|
211
336
|
async loadTileContent(tile: Tile3D): Promise<TileContentLoadResult> {
|
|
212
|
-
const
|
|
337
|
+
const contentUrls = (tile.contentUrls || [tile.contentUrl]).filter(Boolean);
|
|
338
|
+
const cachedContentHeaders = this.tileContentHeaders.get(tile);
|
|
339
|
+
const contentHeaders: Record<string, any>[] =
|
|
340
|
+
cachedContentHeaders ||
|
|
341
|
+
(Array.isArray(tile.header?.content)
|
|
342
|
+
? tile.header.content
|
|
343
|
+
: tile.header?.content
|
|
344
|
+
? [tile.header.content]
|
|
345
|
+
: []);
|
|
346
|
+
if (!cachedContentHeaders) {
|
|
347
|
+
this.tileContentHeaders.set(tile, contentHeaders);
|
|
348
|
+
}
|
|
213
349
|
const tilesetLoaderOptions =
|
|
214
350
|
(this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
|
|
215
351
|
const options = {
|
|
216
352
|
...this.loadOptions,
|
|
217
353
|
[this.loader.id]: {
|
|
218
354
|
...tilesetLoaderOptions,
|
|
219
|
-
|
|
220
|
-
|
|
355
|
+
// Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
|
|
356
|
+
// payloads. This is required for signed and extensionless resources.
|
|
357
|
+
isTileset: 'auto',
|
|
358
|
+
assetGltfUpAxis: (this.asset && this.asset.gltfUpAxis) || 'Y',
|
|
359
|
+
_tilesetOptions: {
|
|
360
|
+
spatialReference: tile.header?._spatialReference || tile.tileset?.spatialReference,
|
|
361
|
+
spatialOptions: tile.tileset?.options.spatial,
|
|
362
|
+
spatialTransform: tile.header?._spatialTransform
|
|
363
|
+
}
|
|
221
364
|
}
|
|
222
365
|
};
|
|
223
366
|
|
|
224
|
-
const
|
|
225
|
-
|
|
367
|
+
const contents = await Promise.all(
|
|
368
|
+
contentUrls.map((contentUrl, contentIndex) => {
|
|
369
|
+
const contentHeader = contentHeaders[contentIndex] || {};
|
|
370
|
+
const vectorExtension = contentHeader.extensions?.['3DTILES_content_gltf_vector'];
|
|
371
|
+
const vectorContent =
|
|
372
|
+
contentHeader._vectorContent ||
|
|
373
|
+
(vectorExtension?.vector === true ? {clip: vectorExtension.clip === true} : undefined);
|
|
374
|
+
const contentOptions = vectorContent
|
|
375
|
+
? {
|
|
376
|
+
...options,
|
|
377
|
+
[this.loader.id]: {
|
|
378
|
+
...(options[this.loader.id] as Record<string, unknown>),
|
|
379
|
+
vectorContent
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
: options;
|
|
383
|
+
return this.loadContentResource(
|
|
384
|
+
this.getTileUrl(contentUrl),
|
|
385
|
+
contentHeader._resource,
|
|
386
|
+
contentOptions
|
|
387
|
+
);
|
|
388
|
+
})
|
|
389
|
+
);
|
|
390
|
+
tile.contents = contents;
|
|
391
|
+
tile.content = contents[0] || null;
|
|
392
|
+
const nestedTilesets = contents.filter(content => content?.shape === 'tileset3d');
|
|
226
393
|
|
|
227
394
|
return {
|
|
228
395
|
loaded: true,
|
|
229
|
-
|
|
396
|
+
contents,
|
|
397
|
+
nestedTileset: nestedTilesets[0],
|
|
398
|
+
nestedTilesets
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Transform a tile header and its eagerly declared descendants. */
|
|
403
|
+
private transformTileHeader(
|
|
404
|
+
header: Record<string, any>,
|
|
405
|
+
parentTransform: Matrix4 = new Matrix4()
|
|
406
|
+
): Record<string, any> {
|
|
407
|
+
if (!this.spatialTransformer) {
|
|
408
|
+
return header;
|
|
409
|
+
}
|
|
410
|
+
return this.transformTileHeaderWithTransformer(
|
|
411
|
+
header,
|
|
412
|
+
this.spatialTransformer,
|
|
413
|
+
parentTransform
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** Rebuilds one header subtree using an explicitly selected CRS transformer. */
|
|
418
|
+
private transformTileHeaderWithTransformer(
|
|
419
|
+
header: Record<string, any>,
|
|
420
|
+
transformer: Tiles3DSpatialTransformer,
|
|
421
|
+
parentTransform: Matrix4,
|
|
422
|
+
spatialReference?: TilesetSpatialReference
|
|
423
|
+
): Record<string, any> {
|
|
424
|
+
const localTransform = header.transform ? new Matrix4(header.transform) : new Matrix4();
|
|
425
|
+
const composedTransform = new Matrix4(parentTransform).multiplyRight(localTransform);
|
|
426
|
+
const content = Array.isArray(header.content)
|
|
427
|
+
? header.content.map((entry: Record<string, any>) =>
|
|
428
|
+
this.transformContentHeader(entry, composedTransform, transformer)
|
|
429
|
+
)
|
|
430
|
+
: header.content
|
|
431
|
+
? this.transformContentHeader(header.content, composedTransform, transformer)
|
|
432
|
+
: header.content;
|
|
433
|
+
return {
|
|
434
|
+
...header,
|
|
435
|
+
transform: undefined,
|
|
436
|
+
boundingVolume: header.boundingVolume
|
|
437
|
+
? transformer.transformBoundingVolume(header.boundingVolume, composedTransform)
|
|
438
|
+
: header.boundingVolume,
|
|
439
|
+
_spatialReference: spatialReference,
|
|
440
|
+
_spatialTransform: composedTransform.toArray(),
|
|
441
|
+
content,
|
|
442
|
+
children: Array.isArray(header.children)
|
|
443
|
+
? header.children.map((child: Record<string, any>) =>
|
|
444
|
+
this.transformTileHeaderWithTransformer(
|
|
445
|
+
child,
|
|
446
|
+
transformer,
|
|
447
|
+
composedTransform,
|
|
448
|
+
spatialReference
|
|
449
|
+
)
|
|
450
|
+
)
|
|
451
|
+
: header.children
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
private transformContentHeader(
|
|
456
|
+
content: Record<string, any>,
|
|
457
|
+
composedTransform: Matrix4,
|
|
458
|
+
transformer: Tiles3DSpatialTransformer
|
|
459
|
+
): Record<string, any> {
|
|
460
|
+
return {
|
|
461
|
+
...content,
|
|
462
|
+
boundingVolume: content.boundingVolume
|
|
463
|
+
? transformer.transformBoundingVolume(content.boundingVolume, composedTransform)
|
|
464
|
+
: content.boundingVolume
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Loads, materializes, and installs exactly one implicit subtree.
|
|
470
|
+
*
|
|
471
|
+
* Final URLs pass through the same query inheritance and archive/custom resolver path as render
|
|
472
|
+
* content. Parsed resources are deduplicated by final URL, while the pure materializer creates
|
|
473
|
+
* lazy placeholders for every available child subtree instead of recursively fetching them.
|
|
474
|
+
*
|
|
475
|
+
* @param tile - Existing contentless subtree-root placeholder.
|
|
476
|
+
* @param frameState - View state that made this request eligible; priority is consumed earlier.
|
|
477
|
+
* @returns Counts describing the installed subtree.
|
|
478
|
+
*/
|
|
479
|
+
async loadTileChildren(tile: Tile3D, frameState: FrameState): Promise<TileChildrenLoadResult> {
|
|
480
|
+
void frameState;
|
|
481
|
+
const reference = tile.header.implicitSubtree as ImplicitSubtreeReference | undefined;
|
|
482
|
+
if (!reference) {
|
|
483
|
+
return {loaded: false, tileCount: 0, childSubtreeCount: 0};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
|
|
487
|
+
const subtree = await this.loadImplicitSubtreeResource(subtreeUrl, reference.resource);
|
|
488
|
+
if (this.destroyed || tile.isDestroyed()) {
|
|
489
|
+
return {loaded: false, tileCount: 0, childSubtreeCount: 0};
|
|
490
|
+
}
|
|
491
|
+
const materializedSubtree = materializeImplicitSubtree(subtree, {
|
|
492
|
+
...reference,
|
|
493
|
+
subtreeUrl
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
const nestedSpatialReference = tile.header._spatialReference as
|
|
497
|
+
| TilesetSpatialReference
|
|
498
|
+
| undefined;
|
|
499
|
+
const subtreeTransformer = nestedSpatialReference
|
|
500
|
+
? new Tiles3DSpatialTransformer(nestedSpatialReference, tile.tileset.options.spatial)
|
|
501
|
+
: this.spatialTransformer;
|
|
502
|
+
const materializedRoot = subtreeTransformer
|
|
503
|
+
? this.transformTileHeaderWithTransformer(
|
|
504
|
+
materializedSubtree.root,
|
|
505
|
+
subtreeTransformer,
|
|
506
|
+
new Matrix4(),
|
|
507
|
+
nestedSpatialReference
|
|
508
|
+
)
|
|
509
|
+
: materializedSubtree.root;
|
|
510
|
+
tile.applyImplicitSubtreeHeader(materializedRoot);
|
|
511
|
+
const materializedTileCount = this.initializeMaterializedChildren(
|
|
512
|
+
tile.tileset,
|
|
513
|
+
tile,
|
|
514
|
+
materializedRoot.children
|
|
515
|
+
);
|
|
516
|
+
this.implicitTilingStats.loadedSubtrees++;
|
|
517
|
+
this.implicitTilingStats.materializedTiles += materializedTileCount;
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
loaded: true,
|
|
521
|
+
tileCount: materializedSubtree.tileCount,
|
|
522
|
+
childSubtreeCount: materializedSubtree.childSubtreeCount
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/** Loads an implicit subtree for dataset traversal without camera state. */
|
|
527
|
+
async loadTileChildrenForTraversal(
|
|
528
|
+
tile: Tile3D,
|
|
529
|
+
signal?: AbortSignal
|
|
530
|
+
): Promise<TileChildrenLoadResult> {
|
|
531
|
+
throwIfTraversalAborted(signal);
|
|
532
|
+
const result = await this.loadTileChildren(tile, {} as FrameState);
|
|
533
|
+
throwIfTraversalAborted(signal);
|
|
534
|
+
return result;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Returns a snapshot of implicit-subtree request, cache, and materialization counters.
|
|
539
|
+
*
|
|
540
|
+
* @returns Immutable-by-convention diagnostic values for runtime inspection.
|
|
541
|
+
*/
|
|
542
|
+
getImplicitTilingStats(): ImplicitTilingStats {
|
|
543
|
+
return {
|
|
544
|
+
...this.implicitTilingStats,
|
|
545
|
+
cachedSubtrees: this.implicitSubtreeCache.size - this.implicitSubtreeCache.pendingSize,
|
|
546
|
+
pendingSubtrees: this.implicitSubtreeCache.pendingSize
|
|
230
547
|
};
|
|
231
548
|
}
|
|
232
549
|
|
|
233
550
|
/**
|
|
234
551
|
* Resolves a tile content URL with source-managed query parameters.
|
|
552
|
+
*
|
|
553
|
+
* Existing per-resource parameters take precedence over inherited root, version, and session
|
|
554
|
+
* values. Completed URLs are cached by the original tile path; {@link setQueryParameter}
|
|
555
|
+
* invalidates the cache before changed source state can be observed.
|
|
556
|
+
*
|
|
557
|
+
* @param tilePath - Unmodified absolute content URL or data URL from the tile header.
|
|
558
|
+
* @returns Content URL with any missing source parameters appended.
|
|
235
559
|
*/
|
|
236
560
|
getTileUrl(tilePath: string): string {
|
|
237
561
|
if (tilePath.startsWith('data:')) {
|
|
238
562
|
return tilePath;
|
|
239
563
|
}
|
|
240
564
|
|
|
565
|
+
const cachedTileUrl = this.tileUrlCache.get(tilePath);
|
|
566
|
+
if (cachedTileUrl) {
|
|
567
|
+
return cachedTileUrl;
|
|
568
|
+
}
|
|
569
|
+
|
|
241
570
|
if (!Object.keys(this.queryParams).length) {
|
|
571
|
+
this.tileUrlCache.set(tilePath, tilePath);
|
|
242
572
|
return tilePath;
|
|
243
573
|
}
|
|
244
574
|
|
|
@@ -252,7 +582,27 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
252
582
|
}
|
|
253
583
|
|
|
254
584
|
const queryParams = mergedQueryParams.toString();
|
|
255
|
-
|
|
585
|
+
const tileUrl = queryParams ? `${pathWithoutQuery}?${queryParams}` : pathWithoutQuery;
|
|
586
|
+
this.tileUrlCache.set(tilePath, tileUrl);
|
|
587
|
+
return tileUrl;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Updates an inherited source query parameter and invalidates derived request URLs.
|
|
592
|
+
*
|
|
593
|
+
* Root tokens and tileset versions normally settle during initialization. Some providers expose
|
|
594
|
+
* a session parameter on a child URL, so invalidation is required to prevent URLs cached earlier
|
|
595
|
+
* in header construction from retaining stale authentication state.
|
|
596
|
+
*
|
|
597
|
+
* @param parameterName - Query parameter name.
|
|
598
|
+
* @param parameterValue - Query parameter value.
|
|
599
|
+
*/
|
|
600
|
+
private setQueryParameter(parameterName: string, parameterValue: string): void {
|
|
601
|
+
if (this.queryParams[parameterName] === parameterValue) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
this.queryParams[parameterName] = parameterValue;
|
|
605
|
+
this.tileUrlCache.clear();
|
|
256
606
|
}
|
|
257
607
|
|
|
258
608
|
/**
|
|
@@ -297,10 +647,13 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
297
647
|
}
|
|
298
648
|
|
|
299
649
|
/**
|
|
300
|
-
* Updates content-format flags and installs nested tileset subtrees.
|
|
650
|
+
* Updates content-format flags and installs nested tileset subtrees once per content slot.
|
|
651
|
+
* Reloading payloads retains attached roots and their descendants, including duplicate URLs
|
|
652
|
+
* declared in separate slots or parent placements. Detached roots are reinstalled in declaration order.
|
|
301
653
|
*/
|
|
302
654
|
onTileLoaded(tileset: Tileset3D, tile: Tile3D, loadResult: TileContentLoadResult): void {
|
|
303
|
-
const
|
|
655
|
+
const contents = tile.contents.length ? tile.contents : [tile.content];
|
|
656
|
+
const extensionsRemoved = contents.flatMap(content => content?.gltf?.extensionsRemoved || []);
|
|
304
657
|
if (extensionsRemoved.includes('KHR_draco_mesh_compression')) {
|
|
305
658
|
this.contentFormats.draco = true;
|
|
306
659
|
}
|
|
@@ -311,20 +664,133 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
311
664
|
this.contentFormats.ktx2 = true;
|
|
312
665
|
}
|
|
313
666
|
|
|
314
|
-
|
|
315
|
-
|
|
667
|
+
const nestedTilesets =
|
|
668
|
+
loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
|
|
669
|
+
const installedRoots = this.nestedTilesetRoots.get(tile) || [];
|
|
670
|
+
for (const [nestedIndex, nestedTileset] of nestedTilesets.entries()) {
|
|
671
|
+
const installedRoot = installedRoots[nestedIndex];
|
|
672
|
+
if (installedRoot && tile.children.includes(installedRoot)) {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
const nextRoot = installedRoots
|
|
676
|
+
.slice(nestedIndex + 1)
|
|
677
|
+
.find(root => tile.children.includes(root));
|
|
678
|
+
const nestedRoot = tileset._initializeTileHeaders(
|
|
679
|
+
this.prepareNestedTileset(tileset, nestedTileset),
|
|
680
|
+
tile
|
|
681
|
+
);
|
|
682
|
+
// Header installation appends roots; restore declaration order before any later retained slot.
|
|
683
|
+
if (nextRoot) {
|
|
684
|
+
tile.children.splice(tile.children.indexOf(nestedRoot), 1);
|
|
685
|
+
tile.children.splice(tile.children.indexOf(nextRoot), 0, nestedRoot);
|
|
686
|
+
}
|
|
687
|
+
installedRoots[nestedIndex] = nestedRoot;
|
|
688
|
+
this.nestedTilesetRoots.set(tile, installedRoots);
|
|
316
689
|
}
|
|
317
690
|
}
|
|
318
691
|
|
|
692
|
+
/**
|
|
693
|
+
* Resolves a nested tileset's CRS independently and adapts it to the owning tileset output frame.
|
|
694
|
+
*
|
|
695
|
+
* Nested resources may use a different source CRS from their parent. Their headers and glTF
|
|
696
|
+
* payloads must therefore use a transformer built from the nested descriptor, while the parent
|
|
697
|
+
* runtime continues to expose one requested output frame to traversal and rendering.
|
|
698
|
+
*/
|
|
699
|
+
private prepareNestedTileset(tileset: Tileset3D, nestedTileset: TilesetJSON): TilesetJSON {
|
|
700
|
+
const nestedSpatialReference = nestedTileset.spatialMetadata;
|
|
701
|
+
const parentSpatialReference = tileset.spatialReference;
|
|
702
|
+
|
|
703
|
+
if (
|
|
704
|
+
nestedSpatialReference?.status === 'unresolved' ||
|
|
705
|
+
nestedSpatialReference?.crs.state === 'unknown'
|
|
706
|
+
) {
|
|
707
|
+
throw new Error(
|
|
708
|
+
nestedSpatialReference.warnings[0] ||
|
|
709
|
+
'Nested 3D Tiles CRS is explicitly unresolved and cannot be placed in the parent frame'
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (!nestedSpatialReference || !nestedSpatialReference.sourceCrs) {
|
|
714
|
+
if (
|
|
715
|
+
this.spatialTransformer &&
|
|
716
|
+
(parentSpatialReference.status === 'transformable' ||
|
|
717
|
+
parentSpatialReference.status === 'transformed')
|
|
718
|
+
) {
|
|
719
|
+
return {
|
|
720
|
+
...nestedTileset,
|
|
721
|
+
spatialMetadata: parentSpatialReference,
|
|
722
|
+
root: this.transformTileHeaderWithTransformer(
|
|
723
|
+
nestedTileset.root,
|
|
724
|
+
this.spatialTransformer,
|
|
725
|
+
new Matrix4(),
|
|
726
|
+
parentSpatialReference
|
|
727
|
+
)
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
return addNestedSpatialReference(nestedTileset, parentSpatialReference);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const parentCoordinateEpoch = parentSpatialReference.coordinateEpoch;
|
|
734
|
+
const nestedCoordinateEpoch = nestedSpatialReference.coordinateEpoch;
|
|
735
|
+
if (
|
|
736
|
+
parentCoordinateEpoch !== undefined &&
|
|
737
|
+
nestedCoordinateEpoch !== undefined &&
|
|
738
|
+
parentCoordinateEpoch !== nestedCoordinateEpoch
|
|
739
|
+
) {
|
|
740
|
+
throw new Error(
|
|
741
|
+
`Nested 3D Tiles coordinate epoch ${nestedCoordinateEpoch} differs from parent epoch ${parentCoordinateEpoch}; epoch-aware transformation is unavailable`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const outputCrs = parentSpatialReference.targetCrs || parentSpatialReference.sourceCrs;
|
|
746
|
+
if (!outputCrs) {
|
|
747
|
+
return addNestedSpatialReference(nestedTileset, nestedSpatialReference);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const nestedOutputReference = applyTilesetSpatialOptions(nestedSpatialReference, {
|
|
751
|
+
...tileset.options.spatial,
|
|
752
|
+
outputCoordinates: 'target-crs',
|
|
753
|
+
targetCrs: outputCrs
|
|
754
|
+
});
|
|
755
|
+
if (nestedOutputReference.status === 'unresolved') {
|
|
756
|
+
throw new Error(
|
|
757
|
+
nestedOutputReference.warnings[0] ||
|
|
758
|
+
'Nested 3D Tiles CRS cannot be resolved in the parent output frame'
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
if (nestedOutputReference.status === 'native') {
|
|
762
|
+
return addNestedSpatialReference(nestedTileset, nestedOutputReference);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const nestedTransformer = new Tiles3DSpatialTransformer(
|
|
766
|
+
nestedOutputReference,
|
|
767
|
+
tileset.options.spatial
|
|
768
|
+
);
|
|
769
|
+
return {
|
|
770
|
+
...nestedTileset,
|
|
771
|
+
spatialMetadata: nestedOutputReference,
|
|
772
|
+
root: this.transformTileHeaderWithTransformer(
|
|
773
|
+
nestedTileset.root,
|
|
774
|
+
nestedTransformer,
|
|
775
|
+
new Matrix4(),
|
|
776
|
+
nestedOutputReference
|
|
777
|
+
)
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
319
781
|
/**
|
|
320
782
|
* Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
|
|
321
783
|
*/
|
|
322
|
-
private async loadWithCoreApi(
|
|
784
|
+
private async loadWithCoreApi(
|
|
785
|
+
url: string,
|
|
786
|
+
options: LoaderOptions,
|
|
787
|
+
loader: Loader = this.loader
|
|
788
|
+
): Promise<any> {
|
|
323
789
|
if (!this.coreApi) {
|
|
324
790
|
throw new Error('Tiles3DSource requires an injected coreApi to load tileset data');
|
|
325
791
|
}
|
|
326
792
|
|
|
327
|
-
return await this.coreApi.load(url,
|
|
793
|
+
return await this.coreApi.load(url, loader, options);
|
|
328
794
|
}
|
|
329
795
|
|
|
330
796
|
/**
|
|
@@ -339,14 +805,177 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
339
805
|
}
|
|
340
806
|
|
|
341
807
|
/**
|
|
342
|
-
* Loads
|
|
808
|
+
* Loads an arbitrary source resource through an injected resolver or core API.
|
|
343
809
|
*/
|
|
344
|
-
private async loadResourceData(
|
|
810
|
+
private async loadResourceData(
|
|
811
|
+
url: string,
|
|
812
|
+
options: LoaderOptions,
|
|
813
|
+
loader: Loader = this.loader
|
|
814
|
+
): Promise<any> {
|
|
345
815
|
if (this.resolver) {
|
|
346
|
-
return await this.resolver.loadResource(url,
|
|
816
|
+
return await this.resolver.loadResource(url, loader, options);
|
|
347
817
|
}
|
|
348
818
|
|
|
349
|
-
return await this.loadWithCoreApi(url, options);
|
|
819
|
+
return await this.loadWithCoreApi(url, options, loader);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/** Loads a URL content reference or parses a buffer-view file retained from a glTF package. */
|
|
823
|
+
private async loadContentResource(
|
|
824
|
+
url: string,
|
|
825
|
+
packageResource: PackageResource | undefined,
|
|
826
|
+
options: LoaderOptions
|
|
827
|
+
): Promise<any> {
|
|
828
|
+
if (!packageResource) {
|
|
829
|
+
return await this.loadResourceData(url, options);
|
|
830
|
+
}
|
|
831
|
+
const file = packageResource.files[packageResource.fileIndex];
|
|
832
|
+
if (!file) {
|
|
833
|
+
throw new Error(`3D Tiles package references missing file ${packageResource.fileIndex}`);
|
|
834
|
+
}
|
|
835
|
+
if (file.uri) {
|
|
836
|
+
return await this.loadResourceData(this.getTileUrl(file.uri), options);
|
|
837
|
+
}
|
|
838
|
+
if (!this.coreApi) {
|
|
839
|
+
throw new Error('Tiles3DSource requires an injected coreApi to parse embedded package data');
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
const data = await this.getPackageFileData(file);
|
|
843
|
+
const packageNamespace = this.getPackageNamespace(packageResource.files);
|
|
844
|
+
const packageRootUrl = `gltf-package://${packageNamespace}/${packageResource.fileIndex}`;
|
|
845
|
+
const fileName = file.name || 'content';
|
|
846
|
+
const directorySeparatorIndex = fileName.lastIndexOf('/');
|
|
847
|
+
const packageBaseUrl =
|
|
848
|
+
directorySeparatorIndex >= 0
|
|
849
|
+
? `${packageRootUrl}/${fileName.slice(0, directorySeparatorIndex)}`
|
|
850
|
+
: packageRootUrl;
|
|
851
|
+
return await this.coreApi.parse(data, this.loader, options, {
|
|
852
|
+
url: `${packageRootUrl}/${fileName}`,
|
|
853
|
+
filename: fileName,
|
|
854
|
+
baseUrl: packageBaseUrl,
|
|
855
|
+
loaders: [this.loader],
|
|
856
|
+
coreApi: this.coreApi,
|
|
857
|
+
_parse: this.coreApi.parse,
|
|
858
|
+
_tiles3dPackageFiles: packageResource.files,
|
|
859
|
+
fetch: async (resource: string, init?: RequestInit) => {
|
|
860
|
+
const reference = resource.startsWith(`${packageRootUrl}/`)
|
|
861
|
+
? resource.slice(packageRootUrl.length + 1)
|
|
862
|
+
: resource;
|
|
863
|
+
const referencedFile = packageResource.files.find(
|
|
864
|
+
packageFile => packageFile.name === reference || packageFile.originalUri === reference
|
|
865
|
+
);
|
|
866
|
+
if (!referencedFile) {
|
|
867
|
+
throw new Error(`3D Tiles glTF package does not contain file ${reference}`);
|
|
868
|
+
}
|
|
869
|
+
if (referencedFile.uri) {
|
|
870
|
+
return await this.coreApi!.fetchFile(this.getTileUrl(referencedFile.uri), init);
|
|
871
|
+
}
|
|
872
|
+
const referencedData = await this.getPackageFileData(referencedFile);
|
|
873
|
+
return new Response(referencedData, {
|
|
874
|
+
headers: {'content-type': referencedFile.mimeType}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
} as Tiles3DPackageLoaderContext);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/** Returns a stable source-local namespace for one embedded glTF package file collection. */
|
|
881
|
+
private getPackageNamespace(files: PackageFile[]): number {
|
|
882
|
+
const existingNamespace = this.packageNamespaces.get(files);
|
|
883
|
+
if (existingNamespace !== undefined) {
|
|
884
|
+
return existingNamespace;
|
|
885
|
+
}
|
|
886
|
+
const packageNamespace = this.nextPackageNamespace++;
|
|
887
|
+
this.packageNamespaces.set(files, packageNamespace);
|
|
888
|
+
return packageNamespace;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/** Resolves a retained package-file byte range without eagerly copying other package files. */
|
|
892
|
+
private async getPackageFileData(file: PackageFile): Promise<ArrayBuffer> {
|
|
893
|
+
if (file.data) {
|
|
894
|
+
return sliceArrayBuffer(file.data, file.byteOffset, file.byteLength);
|
|
895
|
+
}
|
|
896
|
+
if (file.bufferUri && this.coreApi) {
|
|
897
|
+
const response = await this.coreApi.fetchFile(this.getTileUrl(file.bufferUri));
|
|
898
|
+
if (!response.ok) {
|
|
899
|
+
throw new Error(`Failed to fetch 3D Tiles package buffer: HTTP ${response.status}`);
|
|
900
|
+
}
|
|
901
|
+
const buffer = await response.arrayBuffer();
|
|
902
|
+
return sliceArrayBuffer(buffer, file.byteOffset, file.byteLength);
|
|
903
|
+
}
|
|
904
|
+
throw new Error('3D Tiles package file has no available byte source');
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Returns a parsed subtree from the LRU cache or starts one source-managed request.
|
|
909
|
+
*
|
|
910
|
+
* @param subtreeUrl - Final subtree URL after query inheritance.
|
|
911
|
+
* @param packageResource - Optional glTF package file satisfying the subtree reference.
|
|
912
|
+
* @returns Parsed subtree availability data.
|
|
913
|
+
*/
|
|
914
|
+
private async loadImplicitSubtreeResource(
|
|
915
|
+
subtreeUrl: string,
|
|
916
|
+
packageResource?: PackageResource
|
|
917
|
+
): Promise<ParsedImplicitSubtree> {
|
|
918
|
+
const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
|
|
919
|
+
if (cachedSubtree) {
|
|
920
|
+
this.implicitTilingStats.cacheHits++;
|
|
921
|
+
return await cachedSubtree;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
this.implicitTilingStats.requestedSubtrees++;
|
|
925
|
+
const loaderOptions = (this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
|
|
926
|
+
return await this.implicitSubtreeCache.getOrLoad(subtreeUrl, async () => {
|
|
927
|
+
const options = {
|
|
928
|
+
...this.loadOptions,
|
|
929
|
+
[this.loader.id]: {
|
|
930
|
+
...loaderOptions,
|
|
931
|
+
isTileset: false,
|
|
932
|
+
isSubtree: true
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
return (await (packageResource
|
|
936
|
+
? this.loadContentResource(subtreeUrl, packageResource, options)
|
|
937
|
+
: this.loadResourceData(subtreeUrl, options))) as ParsedImplicitSubtree;
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Instantiates every header represented by one materialized subtree.
|
|
943
|
+
*
|
|
944
|
+
* @param tileset - Owning runtime tileset.
|
|
945
|
+
* @param parentTile - Existing materialized parent.
|
|
946
|
+
* @param childHeaders - Headers to install below the parent.
|
|
947
|
+
* @returns Number of newly allocated runtime tile nodes.
|
|
948
|
+
*/
|
|
949
|
+
private initializeMaterializedChildren(
|
|
950
|
+
tileset: Tileset3D,
|
|
951
|
+
parentTile: Tile3D,
|
|
952
|
+
childHeaders: Record<string, any>[]
|
|
953
|
+
): number {
|
|
954
|
+
let materializedTileCount = 0;
|
|
955
|
+
const stack: Array<{parentTile: Tile3D; childHeaders: Record<string, any>[]}> = [
|
|
956
|
+
{parentTile, childHeaders}
|
|
957
|
+
];
|
|
958
|
+
while (stack.length > 0) {
|
|
959
|
+
const entry = stack.pop()!;
|
|
960
|
+
for (const childHeader of entry.childHeaders) {
|
|
961
|
+
const childTile = new Tile3DNode(tileset, childHeader, entry.parentTile);
|
|
962
|
+
entry.parentTile.children.push(childTile);
|
|
963
|
+
childTile.depth = entry.parentTile.depth + 1;
|
|
964
|
+
tileset.stats.get('Tiles In Tileset(s)').incrementCount();
|
|
965
|
+
materializedTileCount++;
|
|
966
|
+
if (childHeader.children?.length) {
|
|
967
|
+
stack.push({parentTile: childTile, childHeaders: childHeader.children});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return materializedTileCount;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/** Throws a cancellation reason before or after source-managed traversal work. */
|
|
976
|
+
function throwIfTraversalAborted(signal?: AbortSignal): void {
|
|
977
|
+
if (signal?.aborted) {
|
|
978
|
+
throw signal.reason ?? new DOMException('The operation was aborted', 'AbortError');
|
|
350
979
|
}
|
|
351
980
|
}
|
|
352
981
|
|
|
@@ -377,3 +1006,20 @@ function normalizeTiles3DRequest(input: TilesetSourceInput): TilesetSourceReques
|
|
|
377
1006
|
coreApi: (input as TilesetSourceRequest).coreApi
|
|
378
1007
|
};
|
|
379
1008
|
}
|
|
1009
|
+
|
|
1010
|
+
/** Adds a nested CRS descriptor to each header without changing its native placement. */
|
|
1011
|
+
function addNestedSpatialReference(
|
|
1012
|
+
nestedTileset: TilesetJSON,
|
|
1013
|
+
spatialReference: TilesetSpatialReference
|
|
1014
|
+
): TilesetJSON {
|
|
1015
|
+
const addToHeader = (header: Record<string, any>): Record<string, any> => ({
|
|
1016
|
+
...header,
|
|
1017
|
+
_spatialReference: spatialReference,
|
|
1018
|
+
children: Array.isArray(header.children) ? header.children.map(addToHeader) : header.children
|
|
1019
|
+
});
|
|
1020
|
+
return {
|
|
1021
|
+
...nestedTileset,
|
|
1022
|
+
spatialMetadata: spatialReference,
|
|
1023
|
+
root: addToHeader(nestedTileset.root)
|
|
1024
|
+
};
|
|
1025
|
+
}
|