@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.
Files changed (205) hide show
  1. package/LICENSE +14 -3
  2. package/README.md +26 -0
  3. package/dist/dist.dev.js +20341 -5081
  4. package/dist/dist.min.js +3 -1
  5. package/dist/index.cjs +7904 -2071
  6. package/dist/index.cjs.map +3 -4
  7. package/dist/index.d.ts +25 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +11 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
  12. package/dist/point-cloud/point-cloud-tile.js +3 -0
  13. package/dist/point-cloud/point-cloud-tile.js.map +1 -1
  14. package/dist/point-cloud/point-cloud-tileset.d.ts +4 -0
  15. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
  16. package/dist/point-cloud/point-cloud-tileset.js +54 -12
  17. package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
  18. package/dist/point-cloud/types.d.ts +17 -0
  19. package/dist/point-cloud/types.d.ts.map +1 -1
  20. package/dist/point-cloud/types.js +3 -0
  21. package/dist/point-cloud/types.js.map +1 -1
  22. package/dist/raster-set.d.ts +48 -31
  23. package/dist/raster-set.d.ts.map +1 -1
  24. package/dist/raster-set.js +104 -24
  25. package/dist/raster-set.js.map +1 -1
  26. package/dist/spatial/format-spatial-reference.d.ts +68 -0
  27. package/dist/spatial/format-spatial-reference.d.ts.map +1 -0
  28. package/dist/spatial/format-spatial-reference.js +291 -0
  29. package/dist/spatial/format-spatial-reference.js.map +1 -0
  30. package/dist/spatial/get-spatial-coordinate-frame.d.ts +20 -0
  31. package/dist/spatial/get-spatial-coordinate-frame.d.ts.map +1 -0
  32. package/dist/spatial/get-spatial-coordinate-frame.js +139 -0
  33. package/dist/spatial/get-spatial-coordinate-frame.js.map +1 -0
  34. package/dist/spatial/i3s-elevation.d.ts +20 -0
  35. package/dist/spatial/i3s-elevation.d.ts.map +1 -0
  36. package/dist/spatial/i3s-elevation.js +90 -0
  37. package/dist/spatial/i3s-elevation.js.map +1 -0
  38. package/dist/spatial/i3s-spatial-transformer.d.ts +205 -0
  39. package/dist/spatial/i3s-spatial-transformer.d.ts.map +1 -0
  40. package/dist/spatial/i3s-spatial-transformer.js +731 -0
  41. package/dist/spatial/i3s-spatial-transformer.js.map +1 -0
  42. package/dist/spatial/normalize-crs-identifier.d.ts +15 -0
  43. package/dist/spatial/normalize-crs-identifier.d.ts.map +1 -0
  44. package/dist/spatial/normalize-crs-identifier.js +22 -0
  45. package/dist/spatial/normalize-crs-identifier.js.map +1 -0
  46. package/dist/spatial/spatial-coordinate-transformer.d.ts +52 -0
  47. package/dist/spatial/spatial-coordinate-transformer.d.ts.map +1 -0
  48. package/dist/spatial/spatial-coordinate-transformer.js +295 -0
  49. package/dist/spatial/spatial-coordinate-transformer.js.map +1 -0
  50. package/dist/spatial/spatial-resource-registry.d.ts +47 -0
  51. package/dist/spatial/spatial-resource-registry.d.ts.map +1 -0
  52. package/dist/spatial/spatial-resource-registry.js +76 -0
  53. package/dist/spatial/spatial-resource-registry.js.map +1 -0
  54. package/dist/spatial/spatial-types.d.ts +190 -0
  55. package/dist/spatial/spatial-types.d.ts.map +1 -0
  56. package/dist/spatial/spatial-types.js +192 -0
  57. package/dist/spatial/spatial-types.js.map +1 -0
  58. package/dist/spatial/tiles-3d-spatial-transformer.d.ts +32 -0
  59. package/dist/spatial/tiles-3d-spatial-transformer.d.ts.map +1 -0
  60. package/dist/spatial/tiles-3d-spatial-transformer.js +163 -0
  61. package/dist/spatial/tiles-3d-spatial-transformer.js.map +1 -0
  62. package/dist/tileset-2d/adapter.d.ts +2 -3
  63. package/dist/tileset-2d/adapter.d.ts.map +1 -1
  64. package/dist/tileset-2d/tileset-2d.d.ts +2 -3
  65. package/dist/tileset-2d/tileset-2d.d.ts.map +1 -1
  66. package/dist/tileset-2d/tileset-2d.js.map +1 -1
  67. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts +3 -3
  68. package/dist/tileset-3d/common/indexed-archive-tileset-source.d.ts.map +1 -1
  69. package/dist/tileset-3d/common/indexed-archive-tileset-source.js +9 -4
  70. package/dist/tileset-3d/common/indexed-archive-tileset-source.js.map +1 -1
  71. package/dist/tileset-3d/common/tile-3d-contracts.d.ts +63 -0
  72. package/dist/tileset-3d/common/tile-3d-contracts.d.ts.map +1 -0
  73. package/dist/tileset-3d/common/tile-3d-contracts.js +51 -0
  74. package/dist/tileset-3d/common/tile-3d-contracts.js.map +1 -0
  75. package/dist/tileset-3d/common/tile-3d.d.ts +176 -5
  76. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  77. package/dist/tileset-3d/common/tile-3d.js +523 -89
  78. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  79. package/dist/tileset-3d/common/tileset-3d.d.ts +199 -9
  80. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  81. package/dist/tileset-3d/common/tileset-3d.js +345 -32
  82. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  83. package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
  84. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
  85. package/dist/tileset-3d/common/tileset-cache.js +11 -3
  86. package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
  87. package/dist/tileset-3d/common/tileset-content-traversal.d.ts +37 -0
  88. package/dist/tileset-3d/common/tileset-content-traversal.d.ts.map +1 -0
  89. package/dist/tileset-3d/common/tileset-content-traversal.js +80 -0
  90. package/dist/tileset-3d/common/tileset-content-traversal.js.map +1 -0
  91. package/dist/tileset-3d/common/tileset-observability.d.ts +60 -0
  92. package/dist/tileset-3d/common/tileset-observability.d.ts.map +1 -0
  93. package/dist/tileset-3d/common/tileset-observability.js +39 -0
  94. package/dist/tileset-3d/common/tileset-observability.js.map +1 -0
  95. package/dist/tileset-3d/common/tileset-source.d.ts +51 -9
  96. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  97. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  98. package/dist/tileset-3d/common/tileset-traverser.d.ts +33 -1
  99. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  100. package/dist/tileset-3d/common/tileset-traverser.js +61 -14
  101. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  102. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +182 -0
  103. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  104. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +681 -0
  105. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  106. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +127 -6
  107. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  108. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +485 -27
  109. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  110. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +104 -0
  111. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  112. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +286 -0
  113. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  114. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
  115. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
  116. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
  117. package/dist/tileset-3d/format-i3s/i3s-source.d.ts +23 -6
  118. package/dist/tileset-3d/format-i3s/i3s-source.d.ts.map +1 -1
  119. package/dist/tileset-3d/format-i3s/i3s-source.js +176 -25
  120. package/dist/tileset-3d/format-i3s/i3s-source.js.map +1 -1
  121. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  122. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
  123. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
  124. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  125. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +4 -1
  126. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  127. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
  128. package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
  129. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
  130. package/dist/tileset-3d/helpers/bounding-volume.d.ts +7 -3
  131. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  132. package/dist/tileset-3d/helpers/bounding-volume.js +20 -91
  133. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  134. package/dist/tileset-3d/helpers/frame-state.d.ts +67 -2
  135. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  136. package/dist/tileset-3d/helpers/frame-state.js +92 -10
  137. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  138. package/dist/tileset-3d/helpers/i3s-lod.d.ts.map +1 -1
  139. package/dist/tileset-3d/helpers/i3s-lod.js +20 -8
  140. package/dist/tileset-3d/helpers/i3s-lod.js.map +1 -1
  141. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts +17 -0
  142. package/dist/tileset-3d/helpers/s2-bounding-volume.d.ts.map +1 -0
  143. package/dist/tileset-3d/helpers/s2-bounding-volume.js +20 -0
  144. package/dist/tileset-3d/helpers/s2-bounding-volume.js.map +1 -0
  145. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
  146. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
  147. package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
  148. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
  149. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  150. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  151. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  152. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  153. package/dist/types.d.ts +7 -0
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +3 -0
  156. package/dist/types.js.map +1 -1
  157. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  158. package/dist/utils/doubly-linked-list.js +1 -0
  159. package/dist/utils/doubly-linked-list.js.map +1 -1
  160. package/dist/utils/managed-array.d.ts.map +1 -1
  161. package/dist/utils/managed-array.js +1 -0
  162. package/dist/utils/managed-array.js.map +1 -1
  163. package/package.json +12 -10
  164. package/src/index.ts +79 -1
  165. package/src/point-cloud/point-cloud-tile.ts +4 -0
  166. package/src/point-cloud/point-cloud-tileset.ts +70 -12
  167. package/src/point-cloud/types.ts +21 -0
  168. package/src/raster-set.ts +197 -67
  169. package/src/spatial/format-spatial-reference.ts +385 -0
  170. package/src/spatial/get-spatial-coordinate-frame.ts +169 -0
  171. package/src/spatial/i3s-elevation.ts +96 -0
  172. package/src/spatial/i3s-spatial-transformer.ts +1021 -0
  173. package/src/spatial/normalize-crs-identifier.ts +26 -0
  174. package/src/spatial/spatial-coordinate-transformer.ts +377 -0
  175. package/src/spatial/spatial-resource-registry.ts +90 -0
  176. package/src/spatial/spatial-types.ts +422 -0
  177. package/src/spatial/tiles-3d-spatial-transformer.ts +194 -0
  178. package/src/tileset-2d/adapter.ts +2 -3
  179. package/src/tileset-2d/tileset-2d.ts +2 -3
  180. package/src/tileset-3d/common/indexed-archive-tileset-source.ts +13 -13
  181. package/src/tileset-3d/common/tile-3d-contracts.ts +115 -0
  182. package/src/tileset-3d/common/tile-3d.ts +629 -98
  183. package/src/tileset-3d/common/tileset-3d.ts +512 -40
  184. package/src/tileset-3d/common/tileset-cache.ts +15 -7
  185. package/src/tileset-3d/common/tileset-content-traversal.ts +114 -0
  186. package/src/tileset-3d/common/tileset-observability.ts +100 -0
  187. package/src/tileset-3d/common/tileset-source.ts +63 -13
  188. package/src/tileset-3d/common/tileset-traverser.ts +73 -15
  189. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +1059 -0
  190. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +674 -28
  191. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +328 -0
  192. package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
  193. package/src/tileset-3d/format-i3s/i3s-source.ts +220 -29
  194. package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
  195. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +6 -1
  196. package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
  197. package/src/tileset-3d/helpers/bounding-volume.ts +31 -114
  198. package/src/tileset-3d/helpers/frame-state.ts +152 -13
  199. package/src/tileset-3d/helpers/i3s-lod.ts +22 -8
  200. package/src/tileset-3d/helpers/s2-bounding-volume.ts +35 -0
  201. package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
  202. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  203. package/src/types.ts +11 -0
  204. package/src/utils/doubly-linked-list.ts +2 -0
  205. package/src/utils/managed-array.ts +2 -0
@@ -8,16 +8,63 @@ import { Stats } from '@probe.gl/stats';
8
8
  import { RequestScheduler } from '@loaders.gl/loader-utils';
9
9
  import { TilesetCache } from "./tileset-cache.js";
10
10
  import { calculateTransformProps } from "../helpers/transform-utils.js";
11
- import { getFrameState, limitSelectedTiles } from "../helpers/frame-state.js";
11
+ import { getFrameState, limitSelectedTiles, updateCameraMotionState } from "../helpers/frame-state.js";
12
+ import { calculateDynamicScreenSpaceErrorDensity, updateRootTransformForDynamicScreenSpaceError } from "../helpers/tiles-3d-lod.js";
13
+ import { interpolateLinearly } from "../helpers/tiles-3d-request-priority.js";
14
+ import { TILESET_TYPE } from "../../constants.js";
15
+ import { applyTilesetSpatialOptions, createTilesetSpatialReference } from "../../spatial/spatial-types.js";
12
16
  import { isTileset3DSource } from "./tileset-source.js";
17
+ const BYTES_PER_MEBIBYTE = 1024 * 1024;
18
+ const DEFAULT_CACHE_BYTES = 512 * BYTES_PER_MEBIBYTE;
19
+ const DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES = 512 * BYTES_PER_MEBIBYTE;
20
+ const DEFAULT_I3S_CACHE_BYTES = 32 * BYTES_PER_MEBIBYTE;
21
+ const DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES = BYTES_PER_MEBIBYTE;
22
+ /**
23
+ * Validates a byte-based tile-cache budget.
24
+ *
25
+ * Fractions are accepted because JavaScript memory estimates are numbers, but negative, infinite,
26
+ * and `NaN` values cannot define a stable cache threshold.
27
+ *
28
+ * @param value - Proposed cache budget in bytes.
29
+ * @param optionName - Public option name used in an actionable error message.
30
+ * @returns The validated byte budget.
31
+ */
32
+ function validateCacheByteLength(value, optionName) {
33
+ if (!Number.isFinite(value) || value < 0) {
34
+ throw new Error(`${optionName} must be a finite number greater than or equal to 0`);
35
+ }
36
+ return value;
37
+ }
38
+ /**
39
+ * Resolves the base cache target from the byte-native option or its fallback.
40
+ *
41
+ * @param options - Options that may contain a byte-native cache value.
42
+ * @param fallbackCacheBytes - Existing or default cache target in bytes.
43
+ * @returns The resolved and validated cache target in bytes.
44
+ */
45
+ function resolveCacheBytes(options, fallbackCacheBytes) {
46
+ const cacheBytes = options.cacheBytes ?? fallbackCacheBytes;
47
+ return validateCacheByteLength(cacheBytes, 'cacheBytes');
48
+ }
49
+ /**
50
+ * Resolves cache overflow headroom from the byte-native option or its fallback.
51
+ *
52
+ * @param options - Options that may contain a byte-native overflow value.
53
+ * @param fallbackOverflowBytes - Existing or default overflow headroom in bytes.
54
+ * @returns The resolved and validated overflow headroom in bytes.
55
+ */
56
+ function resolveMaximumCacheOverflowBytes(options, fallbackOverflowBytes) {
57
+ const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? fallbackOverflowBytes;
58
+ return validateCacheByteLength(maximumCacheOverflowBytes, 'maximumCacheOverflowBytes');
59
+ }
13
60
  const DEFAULT_PROPS = {
14
61
  description: '',
15
62
  ellipsoid: Ellipsoid.WGS84,
16
63
  modelMatrix: new Matrix4(),
17
64
  throttleRequests: true,
18
65
  maxRequests: 64,
19
- maximumMemoryUsage: 32,
20
- memoryCacheOverflow: 1,
66
+ cacheBytes: DEFAULT_CACHE_BYTES,
67
+ maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
21
68
  maximumTilesSelected: 0,
22
69
  debounceTime: 0,
23
70
  onTileLoad: () => { },
@@ -29,15 +76,31 @@ const DEFAULT_PROPS = {
29
76
  onUpdate: () => { },
30
77
  contentLoader: undefined,
31
78
  viewDistanceScale: 1.0,
79
+ progressiveResolutionHeightFraction: 0.3,
80
+ foveatedScreenSpaceError: true,
81
+ foveatedConeSize: 0.1,
82
+ foveatedMinimumScreenSpaceErrorRelaxation: 0,
83
+ foveatedInterpolationCallback: interpolateLinearly,
84
+ foveatedTimeDelay: 0.2,
32
85
  maximumScreenSpaceError: 8,
33
- memoryAdjustedScreenSpaceError: false,
86
+ skipLevelOfDetail: false,
87
+ baseScreenSpaceError: 1024,
88
+ skipScreenSpaceErrorFactor: 16,
89
+ skipLevels: 1,
90
+ immediatelyLoadDesiredLevelOfDetail: false,
91
+ dynamicScreenSpaceError: true,
92
+ dynamicScreenSpaceErrorDensity: 2.0e-4,
93
+ dynamicScreenSpaceErrorFactor: 24,
94
+ dynamicScreenSpaceErrorHeightFalloff: 0.25,
95
+ memoryAdjustedScreenSpaceError: true,
34
96
  loadTiles: true,
35
97
  updateTransforms: true,
36
98
  viewportTraversersMap: null,
37
99
  loadOptions: { fetch: {} },
38
100
  attributions: [],
39
101
  basePath: '',
40
- i3s: {}
102
+ i3s: {},
103
+ spatial: {}
41
104
  };
42
105
  const TILES_TOTAL = 'Tiles In Tileset(s)';
43
106
  const TILES_IN_MEMORY = 'Tiles In Memory';
@@ -73,6 +136,18 @@ export class Tileset3D {
73
136
  root = null;
74
137
  roots = {};
75
138
  asset = {};
139
+ /** Inline metadata schema declared by the source tileset, when present. */
140
+ schema = null;
141
+ /** External metadata schema URI declared by the source tileset, when present. */
142
+ schemaUri = null;
143
+ /** Metadata groups declared by the source tileset, in source order. */
144
+ groups = [];
145
+ /** Tileset-wide metadata entity, preserved without property-table decoding. */
146
+ metadata = null;
147
+ /** Tileset statistics metadata, preserved for application-level inspection. */
148
+ statistics = null;
149
+ /** Normalized source and target coordinate reference system metadata. */
150
+ spatialReference = createTilesetSpatialReference({});
76
151
  description = '';
77
152
  properties;
78
153
  extras = null;
@@ -84,12 +159,14 @@ export class Tileset3D {
84
159
  cartesianCenter = null;
85
160
  zoom = 1;
86
161
  boundingVolume = null;
87
- dynamicScreenSpaceErrorComputedDensity = 0.0;
88
- maximumMemoryUsage = 32;
162
+ /** Effective dynamic SSE density calculated for the most recently traversed viewport. */
163
+ dynamicScreenSpaceErrorComputedDensity = 0;
164
+ /** Estimated bytes currently retained by loaded tile content. */
89
165
  gpuMemoryUsageInBytes = 0;
166
+ /** Active SSE threshold after optional cache-pressure adjustment, in logical/CSS pixels. */
90
167
  memoryAdjustedScreenSpaceError = 0.0;
91
- _cacheBytes = 0;
92
- _cacheOverflowBytes = 0;
168
+ _cacheBytes = DEFAULT_CACHE_BYTES;
169
+ _maximumCacheOverflowBytes = DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES;
93
170
  _frameNumber = 0;
94
171
  _tiles = {};
95
172
  _pendingCount = 0;
@@ -100,9 +177,14 @@ export class Tileset3D {
100
177
  _requestedTiles = [];
101
178
  _emptyTiles = [];
102
179
  frameStateData = {};
180
+ /** Last camera pose retained independently for each viewport. */
181
+ _cameraMotionStates = {};
182
+ /** Pending traversal that releases requests after camera motion settles. */
183
+ _deferredTraversalTimeout = null;
103
184
  _traverser;
104
185
  _cache = new TilesetCache();
105
186
  _requestScheduler;
187
+ /** Drawn fallback instances retained independently for each viewport tree. */
106
188
  _heldTiles = new Set();
107
189
  updatePromise = null;
108
190
  tilesetInitializationPromise;
@@ -112,11 +194,28 @@ export class Tileset3D {
112
194
  * @param options Traversal and runtime options.
113
195
  */
114
196
  constructor(source, options) {
115
- this.options = { ...DEFAULT_PROPS, ...options };
116
- this.loadOptions = this.options.loadOptions || {};
117
197
  if (!isTileset3DSource(source)) {
118
198
  throw new Error('Tileset3D requires a Tileset3DSource instance');
119
199
  }
200
+ const suppliedOptions = options || {};
201
+ const usesTiles3DCacheDefaults = source.type === TILESET_TYPE.TILES3D;
202
+ const defaultCacheBytes = usesTiles3DCacheDefaults
203
+ ? DEFAULT_PROPS.cacheBytes
204
+ : DEFAULT_I3S_CACHE_BYTES;
205
+ const defaultMaximumCacheOverflowBytes = usesTiles3DCacheDefaults
206
+ ? DEFAULT_PROPS.maximumCacheOverflowBytes
207
+ : DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES;
208
+ const cacheBytes = resolveCacheBytes(suppliedOptions, defaultCacheBytes);
209
+ const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(suppliedOptions, defaultMaximumCacheOverflowBytes);
210
+ this.options = {
211
+ ...DEFAULT_PROPS,
212
+ ...suppliedOptions,
213
+ cacheBytes,
214
+ maximumCacheOverflowBytes,
215
+ skipLevelOfDetail: suppliedOptions.skipLevelOfDetail ?? source.type === TILESET_TYPE.TILES3D,
216
+ memoryAdjustedScreenSpaceError: suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
217
+ };
218
+ this.loadOptions = this.options.loadOptions || {};
120
219
  this.source = source;
121
220
  this.tileset = null;
122
221
  this.loader = this.source.loader;
@@ -135,13 +234,19 @@ export class Tileset3D {
135
234
  maxRequests: this.options.maxRequests
136
235
  });
137
236
  this.memoryAdjustedScreenSpaceError = this.options.maximumScreenSpaceError;
138
- this._cacheBytes = this.options.maximumMemoryUsage * 1024 * 1024;
139
- this._cacheOverflowBytes = this.options.memoryCacheOverflow * 1024 * 1024;
237
+ this._cacheBytes = cacheBytes;
238
+ this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
239
+ const metadata = this._getSourceMetadata();
240
+ if (metadata) {
241
+ this.spatialReference = applyTilesetSpatialOptions(metadata.spatialReference, this.options.spatial);
242
+ }
140
243
  this.stats = new Stats({ id: this.url });
141
244
  this._initializeStats();
142
245
  this.tilesetInitializationPromise = this._initializeTileSet();
143
246
  }
144
247
  destroy() {
248
+ this._clearDeferredTraversal();
249
+ this.source.destroy?.();
145
250
  this._destroy();
146
251
  }
147
252
  isLoaded() {
@@ -153,13 +258,74 @@ export class Tileset3D {
153
258
  get frameNumber() {
154
259
  return this._frameNumber;
155
260
  }
261
+ /**
262
+ * Returns the tiles requested during the most recent completed traversal.
263
+ *
264
+ * A defensive copy makes request ordering deterministic for diagnostics and prevents callers
265
+ * from mutating the scheduler's working set.
266
+ */
267
+ get requestedTiles() {
268
+ return this._requestedTiles.slice();
269
+ }
270
+ /**
271
+ * Returns empty tiles considered during the most recent completed traversal.
272
+ *
273
+ * Empty tiles can still be useful hierarchy placeholders, but their IDs are included in
274
+ * observability snapshots so applications can distinguish traversal work from render content.
275
+ */
276
+ get emptyTiles() {
277
+ return this._emptyTiles.slice();
278
+ }
279
+ /**
280
+ * Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
281
+ *
282
+ * Current-frame tiles remain protected even when their estimated memory exceeds this target.
283
+ * Reducing the value makes unused least-recently-used tiles eligible for eviction on the next
284
+ * traversal.
285
+ * @default 536870912 for 3D Tiles; 33554432 for I3S
286
+ */
287
+ get cacheBytes() {
288
+ return this._cacheBytes;
289
+ }
290
+ set cacheBytes(value) {
291
+ this._cacheBytes = validateCacheByteLength(value, 'cacheBytes');
292
+ this.options.cacheBytes = this._cacheBytes;
293
+ }
294
+ /**
295
+ * Gets or sets the additional current-frame headroom, in bytes, before memory-adjusted SSE rises.
296
+ *
297
+ * The pressure ceiling is `cacheBytes + maximumCacheOverflowBytes`. This value does not change
298
+ * the base target used to evict tiles that were not touched in the current frame.
299
+ * @default 536870912 for 3D Tiles; 1048576 for I3S
300
+ */
301
+ get maximumCacheOverflowBytes() {
302
+ return this._maximumCacheOverflowBytes;
303
+ }
304
+ set maximumCacheOverflowBytes(value) {
305
+ this._maximumCacheOverflowBytes = validateCacheByteLength(value, 'maximumCacheOverflowBytes');
306
+ this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
307
+ }
156
308
  get queryParams() {
157
309
  const rootUrl = this.source.getTileUrl(this.url);
158
310
  const queryIndex = rootUrl.indexOf('?');
159
311
  return queryIndex >= 0 ? rootUrl.slice(queryIndex + 1) : '';
160
312
  }
313
+ /**
314
+ * Updates traversal and runtime options while retaining unspecified byte budgets.
315
+ *
316
+ * @param props - Partial runtime options to apply.
317
+ */
161
318
  setProps(props) {
162
- this.options = { ...this.options, ...props };
319
+ const cacheBytes = resolveCacheBytes(props, this.cacheBytes);
320
+ const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(props, this.maximumCacheOverflowBytes);
321
+ this.options = {
322
+ ...this.options,
323
+ ...props,
324
+ cacheBytes,
325
+ maximumCacheOverflowBytes
326
+ };
327
+ this._cacheBytes = cacheBytes;
328
+ this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
163
329
  }
164
330
  getTileUrl(tilePath) {
165
331
  return this.source.getTileUrl(tilePath);
@@ -198,11 +364,18 @@ export class Tileset3D {
198
364
  }
199
365
  return this.updatePromise;
200
366
  }
367
+ /**
368
+ * Moves the active LOD threshold toward the configured SSE or away from cache pressure.
369
+ *
370
+ * Usage below {@link Tileset3D.cacheBytes} restores quality in two-percent steps. Usage above
371
+ * `cacheBytes + maximumCacheOverflowBytes` reduces refinement in two-percent steps. Values in
372
+ * the overflow window leave the threshold unchanged, preventing rapid load/evict oscillation.
373
+ */
201
374
  adjustScreenSpaceError() {
202
- if (this.gpuMemoryUsageInBytes < this._cacheBytes) {
375
+ if (this.gpuMemoryUsageInBytes < this.cacheBytes) {
203
376
  this.memoryAdjustedScreenSpaceError = Math.max(this.memoryAdjustedScreenSpaceError / 1.02, this.options.maximumScreenSpaceError);
204
377
  }
205
- else if (this.gpuMemoryUsageInBytes > this._cacheBytes + this._cacheOverflowBytes) {
378
+ else if (this.gpuMemoryUsageInBytes > this.cacheBytes + this.maximumCacheOverflowBytes) {
206
379
  this.memoryAdjustedScreenSpaceError *= 1.02;
207
380
  }
208
381
  }
@@ -214,9 +387,19 @@ export class Tileset3D {
214
387
  return;
215
388
  }
216
389
  const preparedViewports = viewports instanceof Array ? viewports : [viewports];
390
+ const activeViewportIds = new Set(preparedViewports.map(viewport => viewport.id));
391
+ // A viewport may be removed between frames. Discard its completed state before aggregating
392
+ // requested and selected tiles so observability reflects only the current traversal inputs.
393
+ for (const frameStateId of Object.keys(this.frameStateData)) {
394
+ if (!activeViewportIds.has(frameStateId)) {
395
+ delete this.frameStateData[frameStateId];
396
+ }
397
+ }
217
398
  this._cache.reset();
218
399
  this._frameNumber++;
219
400
  this.traverseCounter = preparedViewports.length;
401
+ const currentTime = Date.now();
402
+ let minimumDeferredTraversalDelay = Number.POSITIVE_INFINITY;
220
403
  const viewportsToTraverse = [];
221
404
  for (const viewport of preparedViewports) {
222
405
  const id = viewport.id;
@@ -236,7 +419,57 @@ export class Tileset3D {
236
419
  continue;
237
420
  }
238
421
  const frameState = getFrameState(viewport, this._frameNumber);
239
- this._traverser.traverse(this.roots[id], frameState, this.options);
422
+ const root = this.roots[id];
423
+ const cameraMotionUpdate = updateCameraMotionState(this._cameraMotionStates[id], frameState.camera.position, frameState.camera.direction, currentTime);
424
+ this._cameraMotionStates[id] = cameraMotionUpdate.state;
425
+ frameState.camera.timeSinceMovement = cameraMotionUpdate.timeSinceMovement;
426
+ // Dynamic SSE reads the root's inverse computed transform before traversal updates tile
427
+ // visibility. Refresh it from the current tileset model matrix here so animated local
428
+ // box/sphere tilesets never calculate their height falloff one transform behind the frame.
429
+ updateRootTransformForDynamicScreenSpaceError(root, this.modelMatrix);
430
+ frameState.dynamicScreenSpaceErrorDensity =
431
+ this.type === TILESET_TYPE.TILES3D &&
432
+ this.options.dynamicScreenSpaceError &&
433
+ !frameState.viewport.orthographic
434
+ ? calculateDynamicScreenSpaceErrorDensity(root, frameState, this.options)
435
+ : 0;
436
+ // Keep the legacy diagnostic field, but traversal reads the per-viewport frame value above.
437
+ this.dynamicScreenSpaceErrorComputedDensity = frameState.dynamicScreenSpaceErrorDensity;
438
+ this._traverser.traverse(root, frameState, this.options);
439
+ if (Object.keys(this._traverser.deferredTiles).length > 0) {
440
+ minimumDeferredTraversalDelay = Math.min(minimumDeferredTraversalDelay, Math.max(this.options.foveatedTimeDelay - cameraMotionUpdate.timeSinceMovement, 0));
441
+ }
442
+ }
443
+ if (Number.isFinite(minimumDeferredTraversalDelay)) {
444
+ this._scheduleDeferredTraversal(minimumDeferredTraversalDelay);
445
+ }
446
+ else {
447
+ this._clearDeferredTraversal();
448
+ }
449
+ }
450
+ /**
451
+ * Schedules one follow-up traversal when the moving-camera deferral window expires.
452
+ *
453
+ * Replacing an existing timer ensures continuing camera movement pushes the retry window forward
454
+ * instead of creating a queue of redundant traversals.
455
+ *
456
+ * @param delayInSeconds - Remaining stationary delay before deferred requests become eligible.
457
+ */
458
+ _scheduleDeferredTraversal(delayInSeconds) {
459
+ this._clearDeferredTraversal();
460
+ this._deferredTraversalTimeout = setTimeout(() => {
461
+ this._deferredTraversalTimeout = null;
462
+ this.selectTiles().catch((error) => {
463
+ const traversalError = error instanceof Error ? error : new Error(String(error));
464
+ this._onTilesetError(traversalError);
465
+ });
466
+ }, Math.max(delayInSeconds * 1000, 0));
467
+ }
468
+ /** Clears a pending moving-camera follow-up traversal. */
469
+ _clearDeferredTraversal() {
470
+ if (this._deferredTraversalTimeout !== null) {
471
+ clearTimeout(this._deferredTraversalTimeout);
472
+ this._deferredTraversalTimeout = null;
240
473
  }
241
474
  }
242
475
  _needTraverse(viewportId) {
@@ -278,29 +511,47 @@ export class Tileset3D {
278
511
  this._emptyTiles = this._emptyTiles.concat(frameStateDataValue._emptyTiles);
279
512
  }
280
513
  this.selectedTiles = this.options.onTraversalComplete(this.selectedTiles);
281
- const selectedIds = new Set(this.selectedTiles.map(tile => tile.id));
514
+ const selectedTiles = new Set(this.selectedTiles);
282
515
  const hasUndrawnTiles = this.selectedTiles.some(tile => !tile.tileDrawn);
283
516
  let heldBackCount = 0;
284
517
  if (hasUndrawnTiles) {
285
- for (const tileId of selectedIds) {
286
- this._heldTiles.add(tileId);
518
+ const activeRoots = new Set(Object.keys(this.frameStateData).map(viewportId => this.roots[viewportId]));
519
+ const descendantReadiness = new Map();
520
+ for (const tile of this.selectedTiles) {
521
+ for (let ancestor = tile.parent; ancestor; ancestor = ancestor.parent) {
522
+ descendantReadiness.set(ancestor, (descendantReadiness.get(ancestor) ?? true) && tile.tileDrawn);
523
+ }
524
+ }
525
+ // Drawing can finish between traversals. Promote only previously selected
526
+ // geometry that actually drew; pending tiles cannot provide visible fallback.
527
+ for (const tile of previousSelectedTiles) {
528
+ if (tile.tileDrawn)
529
+ this._heldTiles.add(tile);
287
530
  }
288
- for (const tileId of this._heldTiles) {
289
- if (selectedIds.has(tileId))
531
+ for (const tile of this._heldTiles) {
532
+ let root = tile;
533
+ while (root.parent)
534
+ root = root.parent;
535
+ // Removed viewport trees cannot contribute fallback to the remaining views.
536
+ if (!activeRoots.has(root)) {
537
+ this._heldTiles.delete(tile);
290
538
  continue;
291
- const tile = this._tiles[tileId];
292
- if (tile && tile.contentAvailable) {
539
+ }
540
+ if (selectedTiles.has(tile))
541
+ continue;
542
+ if (tile.contentAvailable &&
543
+ !this._hasDrawnReplacement(tile, selectedTiles, descendantReadiness)) {
293
544
  tile._selectedFrame = this._frameNumber;
294
545
  this.selectedTiles.push(tile);
295
546
  heldBackCount++;
296
547
  }
297
548
  else {
298
- this._heldTiles.delete(tileId);
549
+ this._heldTiles.delete(tile);
299
550
  }
300
551
  }
301
552
  }
302
553
  else {
303
- this._heldTiles = selectedIds;
554
+ this._heldTiles = selectedTiles;
304
555
  }
305
556
  if (heldBackCount > 0) {
306
557
  setTimeout(() => {
@@ -317,6 +568,19 @@ export class Tileset3D {
317
568
  this.options.onUpdate();
318
569
  }
319
570
  }
571
+ /**
572
+ * Releases fallback geometry once its own selected replacement has drawn.
573
+ * Unrelated pending branches must not delay this region's handoff. When no related
574
+ * replacement is selected, retain the fallback until the complete selection has drawn.
575
+ */
576
+ _hasDrawnReplacement(tile, selectedTiles, descendantReadiness) {
577
+ for (let ancestor = tile.parent; ancestor; ancestor = ancestor.parent) {
578
+ if (selectedTiles.has(ancestor)) {
579
+ return ancestor.tileDrawn;
580
+ }
581
+ }
582
+ return descendantReadiness.get(tile) === true;
583
+ }
320
584
  _tilesChanged(oldSelectedTiles, selectedTiles) {
321
585
  if (oldSelectedTiles.length !== selectedTiles.length) {
322
586
  return true;
@@ -337,6 +601,9 @@ export class Tileset3D {
337
601
  }
338
602
  _unloadTiles() {
339
603
  this._cache.unloadTiles(this, (tileset, tile) => tileset._unloadTile(tile));
604
+ if (this.options.memoryAdjustedScreenSpaceError) {
605
+ this.adjustScreenSpaceError();
606
+ }
340
607
  }
341
608
  _updateStats() {
342
609
  let tilesRenderable = 0;
@@ -344,11 +611,14 @@ export class Tileset3D {
344
611
  for (const tile of this.selectedTiles) {
345
612
  if (tile.contentAvailable && tile.content) {
346
613
  tilesRenderable++;
347
- if (tile.content.pointCount) {
348
- pointsRenderable += tile.content.pointCount;
349
- }
350
- else {
351
- pointsRenderable += tile.content.vertexCount;
614
+ const contents = tile.contents.length ? tile.contents : [tile.content];
615
+ for (const content of contents) {
616
+ if (content.pointCount) {
617
+ pointsRenderable += content.pointCount;
618
+ }
619
+ else {
620
+ pointsRenderable += content.vertexCount || 0;
621
+ }
352
622
  }
353
623
  }
354
624
  }
@@ -365,13 +635,15 @@ export class Tileset3D {
365
635
  this._syncSourceState(initialMetadata);
366
636
  }
367
637
  if (initialMetadata?.tileset?.root &&
368
- typeof initialMetadata.tileset.root.then !== 'function') {
638
+ typeof initialMetadata.tileset.root.then !== 'function' &&
639
+ (!this.source.prepareTileset || this.spatialReference?.status === 'native')) {
369
640
  this.root = this._initializeTileHeaders(initialMetadata.tileset, null);
370
641
  this._applyViewState();
371
642
  }
372
643
  await initializePromise;
373
644
  const metadata = this.source.getMetadata();
374
645
  this._syncSourceState(metadata);
646
+ await this.source.prepareTileset?.(this);
375
647
  if (!this.root) {
376
648
  this.root = this._initializeTileHeaders(metadata.tileset, null);
377
649
  }
@@ -425,6 +697,29 @@ export class Tileset3D {
425
697
  this._onTileLoad(tile, loadResult);
426
698
  }
427
699
  }
700
+ /**
701
+ * Loads a source-managed lazy child-header group and reports failures through tile callbacks.
702
+ *
703
+ * Subtree metadata shares the normal loading counters but is not reported as render content and
704
+ * is not added to the GPU cache. Traversal is notified separately after the promise settles.
705
+ *
706
+ * @param tile - Runtime tile that owns the lazy subtree reference.
707
+ * @param frameState - View state that made the subtree eligible for refinement.
708
+ */
709
+ async _loadTileChildren(tile, frameState) {
710
+ try {
711
+ this._onStartTileLoading();
712
+ await tile.loadChildren(frameState);
713
+ }
714
+ catch (error) {
715
+ const tileError = error instanceof Error ? error : new Error('subtree load failed');
716
+ this._onSourceError(tileError, tile);
717
+ this._onTileLoadError(tile, tileError);
718
+ }
719
+ finally {
720
+ this._onEndTileLoading();
721
+ }
722
+ }
428
723
  _onTileLoadError(tile, error) {
429
724
  this.stats.get(TILES_LOAD_FAILED).incrementCount();
430
725
  const message = error.message || error.toString();
@@ -527,6 +822,14 @@ export class Tileset3D {
527
822
  this.refine = metadata.refine;
528
823
  this.contentFormats = this.source.contentFormats;
529
824
  this.asset = this.source.asset || this.asset;
825
+ const tileset = metadata.tileset;
826
+ this.schema = tileset.schema || null;
827
+ this.schemaUri = tileset.schemaUri || null;
828
+ this.groups = tileset.groups || [];
829
+ this.metadata = tileset.metadata || null;
830
+ this.statistics = tileset.statistics ?? null;
831
+ const spatialReference = applyTilesetSpatialOptions(metadata.spatialReference, this.options.spatial);
832
+ this.spatialReference = preserveTransformedSpatialReference(this.spatialReference, spatialReference);
530
833
  this.properties = this.source.properties ?? this.properties;
531
834
  this.extras = this.source.extras ?? this.extras;
532
835
  if (this.options.attributions?.length) {
@@ -563,4 +866,14 @@ export class Tileset3D {
563
866
  }
564
867
  }
565
868
  }
869
+ /** Preserve a completed runtime operation when asynchronous source metadata describes the same request. */
870
+ function preserveTransformedSpatialReference(current, next) {
871
+ return current?.status === 'transformed' &&
872
+ current.sourceCrs === next.sourceCrs &&
873
+ current.targetCrs === next.targetCrs &&
874
+ current.targetHeightReference === next.targetHeightReference &&
875
+ current.outputCoordinates === next.outputCoordinates
876
+ ? current
877
+ : next;
878
+ }
566
879
  //# sourceMappingURL=tileset-3d.js.map