@loaders.gl/tiles 5.0.0-alpha.5 → 5.0.0-alpha.7
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/dist/dist.dev.js +5122 -2641
- package/dist/dist.min.js +4 -2
- package/dist/index.cjs +1670 -403
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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 +89 -12
- package/dist/spatial/spatial-coordinate-transformer.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/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.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 +29 -5
- package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tile-3d.js +151 -20
- package/dist/tileset-3d/common/tile-3d.js.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.d.ts +44 -2
- package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-3d.js +31 -1
- package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
- 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 -8
- 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 +14 -0
- package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
- package/dist/tileset-3d/common/tileset-traverser.js +21 -5
- 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 -2
- 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 +265 -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 +70 -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 +216 -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 +5 -3
- package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
- package/dist/tileset-3d/format-i3s/i3s-source.js +14 -7
- package/dist/tileset-3d/format-i3s/i3s-source.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 +4 -0
- package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
- package/dist/tileset-3d/helpers/frame-state.js +6 -1
- 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 +14 -13
- package/src/index.ts +13 -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 +106 -15
- package/src/spatial/tiles-3d-spatial-transformer.ts +206 -0
- package/src/tileset-3d/common/indexed-archive-tileset-source.ts +4 -9
- package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
- package/src/tileset-3d/common/tile-3d.ts +178 -20
- package/src/tileset-3d/common/tileset-3d.ts +63 -3
- package/src/tileset-3d/common/tileset-observability.ts +100 -0
- package/src/tileset-3d/common/tileset-source.ts +11 -12
- package/src/tileset-3d/common/tileset-traverser.ts +32 -5
- package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +481 -34
- package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +390 -17
- package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +243 -4
- package/src/tileset-3d/format-i3s/i3s-source.ts +25 -9
- package/src/tileset-3d/helpers/bounding-volume.ts +29 -62
- package/src/tileset-3d/helpers/frame-state.ts +6 -1
- package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
- package/src/types.ts +2 -0
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {path, RequestCache} 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';
|
|
@@ -27,6 +27,12 @@ import {TILESET_TYPE} from '../../constants';
|
|
|
27
27
|
import type {TilesetTraverser, TilesetTraverserProps} from '../common/tileset-traverser';
|
|
28
28
|
import type {FrameState} from '../helpers/frame-state';
|
|
29
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';
|
|
30
36
|
import {
|
|
31
37
|
materializeImplicitSubtree,
|
|
32
38
|
type ImplicitSubtreeReference,
|
|
@@ -42,6 +48,24 @@ const EMPTY_CONTENT_FORMATS: TilesetContentFormats = {
|
|
|
42
48
|
|
|
43
49
|
const DEFAULT_MAXIMUM_CACHED_SUBTREES = 32;
|
|
44
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
|
+
|
|
45
69
|
/** Diagnostics for source-managed implicit subtree loading. */
|
|
46
70
|
export type ImplicitTilingStats = {
|
|
47
71
|
/** Subtree resources requested from the resolver or core API. */
|
|
@@ -65,7 +89,7 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
65
89
|
/** 3D Tiles format discriminator. */
|
|
66
90
|
readonly type = TILESET_TYPE.TILES3D;
|
|
67
91
|
/** Loader used for tile content requests. */
|
|
68
|
-
readonly loader:
|
|
92
|
+
readonly loader: Loader;
|
|
69
93
|
/** Root tileset URL. */
|
|
70
94
|
readonly url: string;
|
|
71
95
|
/** Base path used for relative tile resource resolution. */
|
|
@@ -93,6 +117,12 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
93
117
|
private readonly queryParams: Record<string, string> = {};
|
|
94
118
|
/** Final request URLs cached by unmodified tile content URL. */
|
|
95
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
|
+
/** Source-local namespaces assigned to distinct embedded glTF package file collections. */
|
|
123
|
+
private readonly packageNamespaces = new WeakMap<PackageFile[], number>();
|
|
124
|
+
/** Next source-local package namespace. */
|
|
125
|
+
private nextPackageNamespace = 0;
|
|
96
126
|
/** Parsed subtree requests keyed by final source URL for deduplication and LRU reuse. */
|
|
97
127
|
private readonly implicitSubtreeCache: RequestCache<ParsedImplicitSubtree>;
|
|
98
128
|
/** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
|
|
@@ -110,6 +140,10 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
110
140
|
private readonly extensionsUsed: string[] = [];
|
|
111
141
|
private readonly resolver?: TilesetSourceResolver;
|
|
112
142
|
private rootTileset: TilesetJSON;
|
|
143
|
+
/** CRS transformer shared by this source's headers and decoded glTF content. */
|
|
144
|
+
private spatialTransformer?: Tiles3DSpatialTransformer;
|
|
145
|
+
/** Root header after composing source-frame transforms and rebuilding target bounds. */
|
|
146
|
+
private preparedRootHeader?: Record<string, any>;
|
|
113
147
|
|
|
114
148
|
/**
|
|
115
149
|
* Creates a 3D Tiles source.
|
|
@@ -175,11 +209,12 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
175
209
|
throw new Error('Tileset must have an asset property.');
|
|
176
210
|
}
|
|
177
211
|
if (
|
|
212
|
+
this.rootTileset.formatVersion !== '2.0-draft' &&
|
|
178
213
|
this.asset.version !== '0.0' &&
|
|
179
214
|
this.asset.version !== '1.0' &&
|
|
180
215
|
this.asset.version !== '1.1'
|
|
181
216
|
) {
|
|
182
|
-
throw new Error('The tileset must be 3D Tiles version
|
|
217
|
+
throw new Error('The tileset must be 3D Tiles version 0.0, 1.0, 1.1, or 2.0-draft.');
|
|
183
218
|
}
|
|
184
219
|
|
|
185
220
|
if ('tilesetVersion' in this.asset) {
|
|
@@ -224,6 +259,29 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
224
259
|
return this.getMetadata().tileset;
|
|
225
260
|
}
|
|
226
261
|
|
|
262
|
+
/** Prepare nonlinear CRS state before runtime headers are materialized. */
|
|
263
|
+
async prepareTileset(tileset: Tileset3D): Promise<void> {
|
|
264
|
+
const spatialReference = tileset.spatialReference;
|
|
265
|
+
if (spatialReference.status === 'unresolved') {
|
|
266
|
+
throw new Error(
|
|
267
|
+
spatialReference.warnings[0] ||
|
|
268
|
+
'3D Tiles spatial operations cannot be resolved from the supplied metadata and options'
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
if (spatialReference.status !== 'transformable' && spatialReference.status !== 'transformed') {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
this.spatialTransformer ||= new Tiles3DSpatialTransformer(
|
|
275
|
+
spatialReference,
|
|
276
|
+
tileset.options.spatial
|
|
277
|
+
);
|
|
278
|
+
tileset.spatialReference = markTilesetSpatialReferenceTransformed(spatialReference);
|
|
279
|
+
this.preparedRootHeader = this.transformTileHeader(
|
|
280
|
+
this.getMetadata().tileset.root,
|
|
281
|
+
new Matrix4()
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
227
285
|
/**
|
|
228
286
|
* Builds explicit runtime headers while leaving implicit subtree references lazy.
|
|
229
287
|
*/
|
|
@@ -232,7 +290,8 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
232
290
|
tilesetJson: TilesetJSON,
|
|
233
291
|
parentTile?: Tile3D | null
|
|
234
292
|
): Tile3D {
|
|
235
|
-
const
|
|
293
|
+
const rootHeader = parentTile ? tilesetJson.root : this.preparedRootHeader || tilesetJson.root;
|
|
294
|
+
const rootTile = new Tile3DNode(tileset, rootHeader, parentTile || undefined);
|
|
236
295
|
|
|
237
296
|
if (parentTile) {
|
|
238
297
|
parentTile.children.push(rootTile);
|
|
@@ -274,6 +333,17 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
274
333
|
*/
|
|
275
334
|
async loadTileContent(tile: Tile3D): Promise<TileContentLoadResult> {
|
|
276
335
|
const contentUrls = (tile.contentUrls || [tile.contentUrl]).filter(Boolean);
|
|
336
|
+
const cachedContentHeaders = this.tileContentHeaders.get(tile);
|
|
337
|
+
const contentHeaders: Record<string, any>[] =
|
|
338
|
+
cachedContentHeaders ||
|
|
339
|
+
(Array.isArray(tile.header?.content)
|
|
340
|
+
? tile.header.content
|
|
341
|
+
: tile.header?.content
|
|
342
|
+
? [tile.header.content]
|
|
343
|
+
: []);
|
|
344
|
+
if (!cachedContentHeaders) {
|
|
345
|
+
this.tileContentHeaders.set(tile, contentHeaders);
|
|
346
|
+
}
|
|
277
347
|
const tilesetLoaderOptions =
|
|
278
348
|
(this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
|
|
279
349
|
const options = {
|
|
@@ -283,12 +353,37 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
283
353
|
// Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
|
|
284
354
|
// payloads. This is required for signed and extensionless resources.
|
|
285
355
|
isTileset: 'auto',
|
|
286
|
-
assetGltfUpAxis: (this.asset && this.asset.gltfUpAxis) || 'Y'
|
|
356
|
+
assetGltfUpAxis: (this.asset && this.asset.gltfUpAxis) || 'Y',
|
|
357
|
+
_tilesetOptions: {
|
|
358
|
+
spatialReference: tile.header?._spatialReference || tile.tileset?.spatialReference,
|
|
359
|
+
spatialOptions: tile.tileset?.options.spatial,
|
|
360
|
+
spatialTransform: tile.header?._spatialTransform
|
|
361
|
+
}
|
|
287
362
|
}
|
|
288
363
|
};
|
|
289
364
|
|
|
290
365
|
const contents = await Promise.all(
|
|
291
|
-
contentUrls.map(
|
|
366
|
+
contentUrls.map((contentUrl, contentIndex) => {
|
|
367
|
+
const contentHeader = contentHeaders[contentIndex] || {};
|
|
368
|
+
const vectorExtension = contentHeader.extensions?.['3DTILES_content_gltf_vector'];
|
|
369
|
+
const vectorContent =
|
|
370
|
+
contentHeader._vectorContent ||
|
|
371
|
+
(vectorExtension?.vector === true ? {clip: vectorExtension.clip === true} : undefined);
|
|
372
|
+
const contentOptions = vectorContent
|
|
373
|
+
? {
|
|
374
|
+
...options,
|
|
375
|
+
[this.loader.id]: {
|
|
376
|
+
...(options[this.loader.id] as Record<string, unknown>),
|
|
377
|
+
vectorContent
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
: options;
|
|
381
|
+
return this.loadContentResource(
|
|
382
|
+
this.getTileUrl(contentUrl),
|
|
383
|
+
contentHeader._resource,
|
|
384
|
+
contentOptions
|
|
385
|
+
);
|
|
386
|
+
})
|
|
292
387
|
);
|
|
293
388
|
tile.contents = contents;
|
|
294
389
|
tile.content = contents[0] || null;
|
|
@@ -302,6 +397,72 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
302
397
|
};
|
|
303
398
|
}
|
|
304
399
|
|
|
400
|
+
/** Transform a tile header and its eagerly declared descendants. */
|
|
401
|
+
private transformTileHeader(
|
|
402
|
+
header: Record<string, any>,
|
|
403
|
+
parentTransform: Matrix4 = new Matrix4()
|
|
404
|
+
): Record<string, any> {
|
|
405
|
+
if (!this.spatialTransformer) {
|
|
406
|
+
return header;
|
|
407
|
+
}
|
|
408
|
+
return this.transformTileHeaderWithTransformer(
|
|
409
|
+
header,
|
|
410
|
+
this.spatialTransformer,
|
|
411
|
+
parentTransform
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** Rebuilds one header subtree using an explicitly selected CRS transformer. */
|
|
416
|
+
private transformTileHeaderWithTransformer(
|
|
417
|
+
header: Record<string, any>,
|
|
418
|
+
transformer: Tiles3DSpatialTransformer,
|
|
419
|
+
parentTransform: Matrix4,
|
|
420
|
+
spatialReference?: TilesetSpatialReference
|
|
421
|
+
): Record<string, any> {
|
|
422
|
+
const localTransform = header.transform ? new Matrix4(header.transform) : new Matrix4();
|
|
423
|
+
const composedTransform = new Matrix4(parentTransform).multiplyRight(localTransform);
|
|
424
|
+
const content = Array.isArray(header.content)
|
|
425
|
+
? header.content.map((entry: Record<string, any>) =>
|
|
426
|
+
this.transformContentHeader(entry, composedTransform, transformer)
|
|
427
|
+
)
|
|
428
|
+
: header.content
|
|
429
|
+
? this.transformContentHeader(header.content, composedTransform, transformer)
|
|
430
|
+
: header.content;
|
|
431
|
+
return {
|
|
432
|
+
...header,
|
|
433
|
+
transform: undefined,
|
|
434
|
+
boundingVolume: header.boundingVolume
|
|
435
|
+
? transformer.transformBoundingVolume(header.boundingVolume, composedTransform)
|
|
436
|
+
: header.boundingVolume,
|
|
437
|
+
_spatialReference: spatialReference,
|
|
438
|
+
_spatialTransform: composedTransform.toArray(),
|
|
439
|
+
content,
|
|
440
|
+
children: Array.isArray(header.children)
|
|
441
|
+
? header.children.map((child: Record<string, any>) =>
|
|
442
|
+
this.transformTileHeaderWithTransformer(
|
|
443
|
+
child,
|
|
444
|
+
transformer,
|
|
445
|
+
composedTransform,
|
|
446
|
+
spatialReference
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
: header.children
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private transformContentHeader(
|
|
454
|
+
content: Record<string, any>,
|
|
455
|
+
composedTransform: Matrix4,
|
|
456
|
+
transformer: Tiles3DSpatialTransformer
|
|
457
|
+
): Record<string, any> {
|
|
458
|
+
return {
|
|
459
|
+
...content,
|
|
460
|
+
boundingVolume: content.boundingVolume
|
|
461
|
+
? transformer.transformBoundingVolume(content.boundingVolume, composedTransform)
|
|
462
|
+
: content.boundingVolume
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
305
466
|
/**
|
|
306
467
|
* Loads, materializes, and installs exactly one implicit subtree.
|
|
307
468
|
*
|
|
@@ -321,7 +482,7 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
321
482
|
}
|
|
322
483
|
|
|
323
484
|
const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
|
|
324
|
-
const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
|
|
485
|
+
const subtree = await this.loadImplicitSubtreeResource(subtreeUrl, reference.resource);
|
|
325
486
|
if (this.destroyed || tile.isDestroyed()) {
|
|
326
487
|
return {loaded: false, tileCount: 0, childSubtreeCount: 0};
|
|
327
488
|
}
|
|
@@ -330,11 +491,25 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
330
491
|
subtreeUrl
|
|
331
492
|
});
|
|
332
493
|
|
|
333
|
-
tile.
|
|
494
|
+
const nestedSpatialReference = tile.header._spatialReference as
|
|
495
|
+
| TilesetSpatialReference
|
|
496
|
+
| undefined;
|
|
497
|
+
const subtreeTransformer = nestedSpatialReference
|
|
498
|
+
? new Tiles3DSpatialTransformer(nestedSpatialReference, tile.tileset.options.spatial)
|
|
499
|
+
: this.spatialTransformer;
|
|
500
|
+
const materializedRoot = subtreeTransformer
|
|
501
|
+
? this.transformTileHeaderWithTransformer(
|
|
502
|
+
materializedSubtree.root,
|
|
503
|
+
subtreeTransformer,
|
|
504
|
+
new Matrix4(),
|
|
505
|
+
nestedSpatialReference
|
|
506
|
+
)
|
|
507
|
+
: materializedSubtree.root;
|
|
508
|
+
tile.applyImplicitSubtreeHeader(materializedRoot);
|
|
334
509
|
const materializedTileCount = this.initializeMaterializedChildren(
|
|
335
510
|
tile.tileset,
|
|
336
511
|
tile,
|
|
337
|
-
|
|
512
|
+
materializedRoot.children
|
|
338
513
|
);
|
|
339
514
|
this.implicitTilingStats.loadedSubtrees++;
|
|
340
515
|
this.implicitTilingStats.materializedTiles += materializedTileCount;
|
|
@@ -477,17 +652,106 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
477
652
|
const nestedTilesets =
|
|
478
653
|
loadResult.nestedTilesets || (loadResult.nestedTileset ? [loadResult.nestedTileset] : []);
|
|
479
654
|
for (const nestedTileset of nestedTilesets) {
|
|
480
|
-
tileset._initializeTileHeaders(nestedTileset, tile);
|
|
655
|
+
tileset._initializeTileHeaders(this.prepareNestedTileset(tileset, nestedTileset), tile);
|
|
481
656
|
}
|
|
482
657
|
}
|
|
483
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Resolves a nested tileset's CRS independently and adapts it to the owning tileset output frame.
|
|
661
|
+
*
|
|
662
|
+
* Nested resources may use a different source CRS from their parent. Their headers and glTF
|
|
663
|
+
* payloads must therefore use a transformer built from the nested descriptor, while the parent
|
|
664
|
+
* runtime continues to expose one requested output frame to traversal and rendering.
|
|
665
|
+
*/
|
|
666
|
+
private prepareNestedTileset(tileset: Tileset3D, nestedTileset: TilesetJSON): TilesetJSON {
|
|
667
|
+
const nestedSpatialReference = nestedTileset.spatialMetadata;
|
|
668
|
+
const parentSpatialReference = tileset.spatialReference;
|
|
669
|
+
|
|
670
|
+
if (
|
|
671
|
+
nestedSpatialReference?.status === 'unresolved' ||
|
|
672
|
+
nestedSpatialReference?.crs.state === 'unknown'
|
|
673
|
+
) {
|
|
674
|
+
throw new Error(
|
|
675
|
+
nestedSpatialReference.warnings[0] ||
|
|
676
|
+
'Nested 3D Tiles CRS is explicitly unresolved and cannot be placed in the parent frame'
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (!nestedSpatialReference || !nestedSpatialReference.sourceCrs) {
|
|
681
|
+
if (
|
|
682
|
+
this.spatialTransformer &&
|
|
683
|
+
(parentSpatialReference.status === 'transformable' ||
|
|
684
|
+
parentSpatialReference.status === 'transformed')
|
|
685
|
+
) {
|
|
686
|
+
return {
|
|
687
|
+
...nestedTileset,
|
|
688
|
+
spatialMetadata: parentSpatialReference,
|
|
689
|
+
root: this.transformTileHeaderWithTransformer(
|
|
690
|
+
nestedTileset.root,
|
|
691
|
+
this.spatialTransformer,
|
|
692
|
+
new Matrix4(),
|
|
693
|
+
parentSpatialReference
|
|
694
|
+
)
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
return addNestedSpatialReference(nestedTileset, parentSpatialReference);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const parentCoordinateEpoch = parentSpatialReference.coordinateEpoch;
|
|
701
|
+
const nestedCoordinateEpoch = nestedSpatialReference.coordinateEpoch;
|
|
702
|
+
if (
|
|
703
|
+
parentCoordinateEpoch !== undefined &&
|
|
704
|
+
nestedCoordinateEpoch !== undefined &&
|
|
705
|
+
parentCoordinateEpoch !== nestedCoordinateEpoch
|
|
706
|
+
) {
|
|
707
|
+
throw new Error(
|
|
708
|
+
`Nested 3D Tiles coordinate epoch ${nestedCoordinateEpoch} differs from parent epoch ${parentCoordinateEpoch}; epoch-aware transformation is unavailable`
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const outputCrs = parentSpatialReference.targetCrs || parentSpatialReference.sourceCrs;
|
|
713
|
+
if (!outputCrs) {
|
|
714
|
+
return addNestedSpatialReference(nestedTileset, nestedSpatialReference);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const nestedOutputReference = applyTilesetSpatialOptions(nestedSpatialReference, {
|
|
718
|
+
...tileset.options.spatial,
|
|
719
|
+
outputCoordinates: 'target-crs',
|
|
720
|
+
targetCrs: outputCrs
|
|
721
|
+
});
|
|
722
|
+
if (nestedOutputReference.status === 'unresolved') {
|
|
723
|
+
throw new Error(
|
|
724
|
+
nestedOutputReference.warnings[0] ||
|
|
725
|
+
'Nested 3D Tiles CRS cannot be resolved in the parent output frame'
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
if (nestedOutputReference.status === 'native') {
|
|
729
|
+
return addNestedSpatialReference(nestedTileset, nestedOutputReference);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const nestedTransformer = new Tiles3DSpatialTransformer(
|
|
733
|
+
nestedOutputReference,
|
|
734
|
+
tileset.options.spatial
|
|
735
|
+
);
|
|
736
|
+
return {
|
|
737
|
+
...nestedTileset,
|
|
738
|
+
spatialMetadata: nestedOutputReference,
|
|
739
|
+
root: this.transformTileHeaderWithTransformer(
|
|
740
|
+
nestedTileset.root,
|
|
741
|
+
nestedTransformer,
|
|
742
|
+
new Matrix4(),
|
|
743
|
+
nestedOutputReference
|
|
744
|
+
)
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
484
748
|
/**
|
|
485
749
|
* Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
|
|
486
750
|
*/
|
|
487
751
|
private async loadWithCoreApi(
|
|
488
752
|
url: string,
|
|
489
753
|
options: LoaderOptions,
|
|
490
|
-
loader:
|
|
754
|
+
loader: Loader = this.loader
|
|
491
755
|
): Promise<any> {
|
|
492
756
|
if (!this.coreApi) {
|
|
493
757
|
throw new Error('Tiles3DSource requires an injected coreApi to load tileset data');
|
|
@@ -513,7 +777,7 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
513
777
|
private async loadResourceData(
|
|
514
778
|
url: string,
|
|
515
779
|
options: LoaderOptions,
|
|
516
|
-
loader:
|
|
780
|
+
loader: Loader = this.loader
|
|
517
781
|
): Promise<any> {
|
|
518
782
|
if (this.resolver) {
|
|
519
783
|
return await this.resolver.loadResource(url, loader, options);
|
|
@@ -522,13 +786,102 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
522
786
|
return await this.loadWithCoreApi(url, options, loader);
|
|
523
787
|
}
|
|
524
788
|
|
|
789
|
+
/** Loads a URL content reference or parses a buffer-view file retained from a glTF package. */
|
|
790
|
+
private async loadContentResource(
|
|
791
|
+
url: string,
|
|
792
|
+
packageResource: PackageResource | undefined,
|
|
793
|
+
options: LoaderOptions
|
|
794
|
+
): Promise<any> {
|
|
795
|
+
if (!packageResource) {
|
|
796
|
+
return await this.loadResourceData(url, options);
|
|
797
|
+
}
|
|
798
|
+
const file = packageResource.files[packageResource.fileIndex];
|
|
799
|
+
if (!file) {
|
|
800
|
+
throw new Error(`3D Tiles package references missing file ${packageResource.fileIndex}`);
|
|
801
|
+
}
|
|
802
|
+
if (file.uri) {
|
|
803
|
+
return await this.loadResourceData(this.getTileUrl(file.uri), options);
|
|
804
|
+
}
|
|
805
|
+
if (!this.coreApi) {
|
|
806
|
+
throw new Error('Tiles3DSource requires an injected coreApi to parse embedded package data');
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const data = await this.getPackageFileData(file);
|
|
810
|
+
const packageNamespace = this.getPackageNamespace(packageResource.files);
|
|
811
|
+
const packageRootUrl = `gltf-package://${packageNamespace}/${packageResource.fileIndex}`;
|
|
812
|
+
const fileName = file.name || 'content';
|
|
813
|
+
const directorySeparatorIndex = fileName.lastIndexOf('/');
|
|
814
|
+
const packageBaseUrl =
|
|
815
|
+
directorySeparatorIndex >= 0
|
|
816
|
+
? `${packageRootUrl}/${fileName.slice(0, directorySeparatorIndex)}`
|
|
817
|
+
: packageRootUrl;
|
|
818
|
+
return await this.coreApi.parse(data, this.loader, options, {
|
|
819
|
+
url: `${packageRootUrl}/${fileName}`,
|
|
820
|
+
filename: fileName,
|
|
821
|
+
baseUrl: packageBaseUrl,
|
|
822
|
+
loaders: [this.loader],
|
|
823
|
+
coreApi: this.coreApi,
|
|
824
|
+
_parse: this.coreApi.parse,
|
|
825
|
+
_tiles3dPackageFiles: packageResource.files,
|
|
826
|
+
fetch: async (resource: string, init?: RequestInit) => {
|
|
827
|
+
const reference = resource.startsWith(`${packageRootUrl}/`)
|
|
828
|
+
? resource.slice(packageRootUrl.length + 1)
|
|
829
|
+
: resource;
|
|
830
|
+
const referencedFile = packageResource.files.find(
|
|
831
|
+
packageFile => packageFile.name === reference || packageFile.originalUri === reference
|
|
832
|
+
);
|
|
833
|
+
if (!referencedFile) {
|
|
834
|
+
throw new Error(`3D Tiles glTF package does not contain file ${reference}`);
|
|
835
|
+
}
|
|
836
|
+
if (referencedFile.uri) {
|
|
837
|
+
return await this.coreApi!.fetchFile(this.getTileUrl(referencedFile.uri), init);
|
|
838
|
+
}
|
|
839
|
+
const referencedData = await this.getPackageFileData(referencedFile);
|
|
840
|
+
return new Response(referencedData, {
|
|
841
|
+
headers: {'content-type': referencedFile.mimeType}
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
} as Tiles3DPackageLoaderContext);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/** Returns a stable source-local namespace for one embedded glTF package file collection. */
|
|
848
|
+
private getPackageNamespace(files: PackageFile[]): number {
|
|
849
|
+
const existingNamespace = this.packageNamespaces.get(files);
|
|
850
|
+
if (existingNamespace !== undefined) {
|
|
851
|
+
return existingNamespace;
|
|
852
|
+
}
|
|
853
|
+
const packageNamespace = this.nextPackageNamespace++;
|
|
854
|
+
this.packageNamespaces.set(files, packageNamespace);
|
|
855
|
+
return packageNamespace;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/** Resolves a retained package-file byte range without eagerly copying other package files. */
|
|
859
|
+
private async getPackageFileData(file: PackageFile): Promise<ArrayBuffer> {
|
|
860
|
+
if (file.data) {
|
|
861
|
+
return sliceArrayBuffer(file.data, file.byteOffset, file.byteLength);
|
|
862
|
+
}
|
|
863
|
+
if (file.bufferUri && this.coreApi) {
|
|
864
|
+
const response = await this.coreApi.fetchFile(this.getTileUrl(file.bufferUri));
|
|
865
|
+
if (!response.ok) {
|
|
866
|
+
throw new Error(`Failed to fetch 3D Tiles package buffer: HTTP ${response.status}`);
|
|
867
|
+
}
|
|
868
|
+
const buffer = await response.arrayBuffer();
|
|
869
|
+
return sliceArrayBuffer(buffer, file.byteOffset, file.byteLength);
|
|
870
|
+
}
|
|
871
|
+
throw new Error('3D Tiles package file has no available byte source');
|
|
872
|
+
}
|
|
873
|
+
|
|
525
874
|
/**
|
|
526
875
|
* Returns a parsed subtree from the LRU cache or starts one source-managed request.
|
|
527
876
|
*
|
|
528
877
|
* @param subtreeUrl - Final subtree URL after query inheritance.
|
|
878
|
+
* @param packageResource - Optional glTF package file satisfying the subtree reference.
|
|
529
879
|
* @returns Parsed subtree availability data.
|
|
530
880
|
*/
|
|
531
|
-
private async loadImplicitSubtreeResource(
|
|
881
|
+
private async loadImplicitSubtreeResource(
|
|
882
|
+
subtreeUrl: string,
|
|
883
|
+
packageResource?: PackageResource
|
|
884
|
+
): Promise<ParsedImplicitSubtree> {
|
|
532
885
|
const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
|
|
533
886
|
if (cachedSubtree) {
|
|
534
887
|
this.implicitTilingStats.cacheHits++;
|
|
@@ -538,14 +891,17 @@ export class Tiles3DSource implements Tileset3DSource {
|
|
|
538
891
|
this.implicitTilingStats.requestedSubtrees++;
|
|
539
892
|
const loaderOptions = (this.loadOptions[this.loader.id] as Record<string, unknown>) || {};
|
|
540
893
|
return await this.implicitSubtreeCache.getOrLoad(subtreeUrl, async () => {
|
|
541
|
-
|
|
894
|
+
const options = {
|
|
542
895
|
...this.loadOptions,
|
|
543
896
|
[this.loader.id]: {
|
|
544
897
|
...loaderOptions,
|
|
545
898
|
isTileset: false,
|
|
546
899
|
isSubtree: true
|
|
547
900
|
}
|
|
548
|
-
}
|
|
901
|
+
};
|
|
902
|
+
return (await (packageResource
|
|
903
|
+
? this.loadContentResource(subtreeUrl, packageResource, options)
|
|
904
|
+
: this.loadResourceData(subtreeUrl, options))) as ParsedImplicitSubtree;
|
|
549
905
|
});
|
|
550
906
|
}
|
|
551
907
|
|
|
@@ -610,3 +966,20 @@ function normalizeTiles3DRequest(input: TilesetSourceInput): TilesetSourceReques
|
|
|
610
966
|
coreApi: (input as TilesetSourceRequest).coreApi
|
|
611
967
|
};
|
|
612
968
|
}
|
|
969
|
+
|
|
970
|
+
/** Adds a nested CRS descriptor to each header without changing its native placement. */
|
|
971
|
+
function addNestedSpatialReference(
|
|
972
|
+
nestedTileset: TilesetJSON,
|
|
973
|
+
spatialReference: TilesetSpatialReference
|
|
974
|
+
): TilesetJSON {
|
|
975
|
+
const addToHeader = (header: Record<string, any>): Record<string, any> => ({
|
|
976
|
+
...header,
|
|
977
|
+
_spatialReference: spatialReference,
|
|
978
|
+
children: Array.isArray(header.children) ? header.children.map(addToHeader) : header.children
|
|
979
|
+
});
|
|
980
|
+
return {
|
|
981
|
+
...nestedTileset,
|
|
982
|
+
spatialMetadata: spatialReference,
|
|
983
|
+
root: addToHeader(nestedTileset.root)
|
|
984
|
+
};
|
|
985
|
+
}
|