@loaders.gl/3d-tiles 4.3.0-alpha.6 → 4.3.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/3d-tiles-archive-loader.js +1 -1
- package/dist/cesium-ion-loader.d.ts +1 -1
- package/dist/dist.dev.js +11624 -12546
- package/dist/dist.min.js +1 -1
- package/dist/index.cjs +71 -71
- package/dist/index.cjs.map +2 -2
- package/dist/lib/classes/helpers/tile-3d-accessor-utils.d.ts +1 -1
- package/dist/lib/classes/tile-3d-batch-table.d.ts +1 -1
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.d.ts +35 -19
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.d.ts.map +1 -1
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.js +108 -85
- package/dist/lib/utils/version.js +1 -1
- package/package.json +14 -14
- package/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts +130 -116
|
@@ -79,47 +79,52 @@ function getChildS2VolumeBox(
|
|
|
79
79
|
* Recursively parse implicit tiles tree
|
|
80
80
|
* Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling
|
|
81
81
|
* TODO Check out do we able to use Tile3D class as return type here.
|
|
82
|
-
*
|
|
83
|
-
* @param
|
|
84
|
-
*
|
|
85
|
-
* @param
|
|
86
|
-
* @param
|
|
87
|
-
*
|
|
88
|
-
*
|
|
82
|
+
*
|
|
83
|
+
* @param subtree - the current subtree. Subtrees contain availability data for <implicitOptions.subtreeLevels>.
|
|
84
|
+
* Once we go deeper than that many levels, we will need load a child subtree to get further availability data.
|
|
85
|
+
* @param subtreeData - the coordinates of the current subtree, relative to the root of this implicit tiles tree.
|
|
86
|
+
* @param parentData - the coordinates of the parent tile, relative to the current subtree.
|
|
87
|
+
* The overall coordinates of the current tile can be found by combining the coordinates of the current subtree, the parent tile,
|
|
88
|
+
* and tje single-bit coordinates that can be calculated from the childIndex.
|
|
89
|
+
* @param childIndex - which child the current tile is of its parent. In the range 0-7 for OCTREE, 0-3 for QUADTREE.
|
|
90
|
+
* @param implicitOptions - options specified at the root of this implicit tile tree - numbers of levels, URL templates.
|
|
91
|
+
* @param loaderOptions - see Tiles3DLoaderOptions.
|
|
89
92
|
*/
|
|
90
93
|
// eslint-disable-next-line max-statements
|
|
91
94
|
export async function parseImplicitTiles(params: {
|
|
92
95
|
subtree: Subtree;
|
|
93
|
-
|
|
94
|
-
parentData?: {
|
|
96
|
+
subtreeData?: {level: number; x: number; y: number; z: number};
|
|
97
|
+
parentData?: {
|
|
98
|
+
mortonIndex: number;
|
|
99
|
+
localLevel: number;
|
|
100
|
+
localX: number;
|
|
101
|
+
localY: number;
|
|
102
|
+
localZ: number;
|
|
103
|
+
};
|
|
95
104
|
childIndex?: number;
|
|
96
|
-
|
|
97
|
-
globalData?: {level: number; mortonIndex: number; x: number; y: number; z: number};
|
|
98
|
-
s2VolumeBox?: S2VolumeBox;
|
|
105
|
+
implicitOptions: ImplicitOptions;
|
|
99
106
|
loaderOptions: Tiles3DLoaderOptions;
|
|
107
|
+
s2VolumeBox?: S2VolumeBox;
|
|
100
108
|
}) {
|
|
101
109
|
const {
|
|
102
|
-
implicitOptions,
|
|
103
|
-
parentData = {
|
|
104
|
-
mortonIndex: 0,
|
|
105
|
-
x: 0,
|
|
106
|
-
y: 0,
|
|
107
|
-
z: 0
|
|
108
|
-
},
|
|
109
|
-
childIndex = 0,
|
|
110
|
-
s2VolumeBox,
|
|
111
|
-
loaderOptions
|
|
112
|
-
} = params;
|
|
113
|
-
let {
|
|
114
110
|
subtree,
|
|
115
|
-
|
|
116
|
-
globalData = {
|
|
111
|
+
subtreeData = {
|
|
117
112
|
level: 0,
|
|
118
|
-
mortonIndex: 0,
|
|
119
113
|
x: 0,
|
|
120
114
|
y: 0,
|
|
121
115
|
z: 0
|
|
122
|
-
}
|
|
116
|
+
},
|
|
117
|
+
parentData = {
|
|
118
|
+
mortonIndex: 0,
|
|
119
|
+
localLevel: -1,
|
|
120
|
+
localX: 0,
|
|
121
|
+
localY: 0,
|
|
122
|
+
localZ: 0
|
|
123
|
+
},
|
|
124
|
+
childIndex = 0,
|
|
125
|
+
implicitOptions,
|
|
126
|
+
loaderOptions,
|
|
127
|
+
s2VolumeBox
|
|
123
128
|
} = params;
|
|
124
129
|
const {
|
|
125
130
|
subdivisionScheme,
|
|
@@ -138,82 +143,88 @@ export async function parseImplicitTiles(params: {
|
|
|
138
143
|
return tile;
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
// Local tile level - relative to the current subtree.
|
|
147
|
+
const localLevel = parentData.localLevel + 1;
|
|
148
|
+
// Global tile level - relative to the root tile of this implicit subdivision scheme.
|
|
149
|
+
const level = subtreeData.level + localLevel;
|
|
150
|
+
|
|
151
|
+
if (level > maximumLevel) {
|
|
143
152
|
return tile;
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
const childrenPerTile = SUBDIVISION_COUNT_MAP[subdivisionScheme];
|
|
147
156
|
const bitsPerTile = Math.log2(childrenPerTile);
|
|
148
157
|
|
|
149
|
-
// childIndex is in range
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
const
|
|
158
|
+
// childIndex is in range 0...3 for quadtrees and 0...7 for octrees
|
|
159
|
+
const lastBitX = childIndex & 0b01; // Get first bit for X
|
|
160
|
+
const lastBitY = (childIndex >> 1) & 0b01; // Get second bit for Y
|
|
161
|
+
const lastBitZ = (childIndex >> 2) & 0b01; // Get third bit for Z
|
|
153
162
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
// Local tile coordinates - relative to the current subtree root.
|
|
164
|
+
const localX = concatBits(parentData.localX, lastBitX, 1);
|
|
165
|
+
const localY = concatBits(parentData.localY, lastBitY, 1);
|
|
166
|
+
const localZ = concatBits(parentData.localZ, lastBitZ, 1);
|
|
157
167
|
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
168
|
+
// Global tile coordinates - relative to the implicit-tile-tree root.
|
|
169
|
+
// Found by combining the local coordinates which are relative to the current subtree, with the subtree coordinates.
|
|
170
|
+
const x = concatBits(subtreeData.x, localX, localLevel);
|
|
171
|
+
const y = concatBits(subtreeData.y, localY, localLevel);
|
|
172
|
+
const z = concatBits(subtreeData.z, localZ, localLevel);
|
|
162
173
|
|
|
163
|
-
|
|
174
|
+
const mortonIndex = concatBits(parentData.mortonIndex, childIndex, bitsPerTile);
|
|
164
175
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
childTileMortonIndex
|
|
169
|
-
);
|
|
170
|
-
}
|
|
176
|
+
const isChildSubtreeAvailable =
|
|
177
|
+
localLevel === subtreeLevels &&
|
|
178
|
+
getAvailabilityResult(subtree.childSubtreeAvailability, mortonIndex);
|
|
171
179
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
180
|
+
// Context to provide the next recursive call.
|
|
181
|
+
// This context is set up differently depending on whether its time to start a new subtree or not.
|
|
182
|
+
let nextSubtree;
|
|
183
|
+
let nextSubtreeData;
|
|
184
|
+
let nextParentData;
|
|
185
|
+
let tileAvailabilityIndex;
|
|
175
186
|
|
|
176
187
|
if (isChildSubtreeAvailable) {
|
|
177
188
|
const subtreePath = `${basePath}/${subtreesUriTemplate}`;
|
|
178
|
-
const childSubtreeUrl = replaceContentUrlTemplate(subtreePath,
|
|
189
|
+
const childSubtreeUrl = replaceContentUrlTemplate(subtreePath, level, x, y, z);
|
|
179
190
|
const childSubtree = await load(childSubtreeUrl, Tile3DSubtreeLoader, loaderOptions);
|
|
180
191
|
|
|
181
|
-
subtree
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
mortonIndex: childTileMortonIndex,
|
|
185
|
-
x: childTileX,
|
|
186
|
-
y: childTileY,
|
|
187
|
-
z: childTileZ,
|
|
188
|
-
level
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
childTileMortonIndex = 0;
|
|
192
|
+
// The next subtree is the newly-loaded child subtree.
|
|
193
|
+
nextSubtree = childSubtree;
|
|
194
|
+
// The current tile is actually the root tile in the next subtree, so it has a tileAvailabilityIndex of 0.
|
|
192
195
|
tileAvailabilityIndex = 0;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
// The next subtree starts HERE - at the current tile.
|
|
197
|
+
nextSubtreeData = {level, x, y, z};
|
|
198
|
+
// The next parent is also the current tile - so it has local coordinates of 0 relative to the next subtree.
|
|
199
|
+
nextParentData = {mortonIndex: 0, localLevel: 0, localX: 0, localY: 0, localZ: 0};
|
|
200
|
+
} else {
|
|
201
|
+
// Continue on with the same subtree as we're using currently.
|
|
202
|
+
nextSubtree = subtree;
|
|
203
|
+
// Calculate a tileAvailabilityIndex for the current tile within the current subtree.
|
|
204
|
+
const levelOffset = (childrenPerTile ** localLevel - 1) / (childrenPerTile - 1);
|
|
205
|
+
tileAvailabilityIndex = levelOffset + mortonIndex;
|
|
206
|
+
// The next subtree is the same as the current subtree.
|
|
207
|
+
nextSubtreeData = subtreeData;
|
|
208
|
+
// The next parent is the current tile: it has the local coordinates we already calculated.
|
|
209
|
+
nextParentData = {mortonIndex, localLevel, localX, localY, localZ};
|
|
197
210
|
}
|
|
198
211
|
|
|
199
|
-
const isTileAvailable = getAvailabilityResult(
|
|
200
|
-
|
|
212
|
+
const isTileAvailable = getAvailabilityResult(
|
|
213
|
+
nextSubtree.tileAvailability,
|
|
214
|
+
tileAvailabilityIndex
|
|
215
|
+
);
|
|
201
216
|
if (!isTileAvailable) {
|
|
202
217
|
return tile;
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
const isContentAvailable = getAvailabilityResult(
|
|
206
|
-
|
|
221
|
+
nextSubtree.contentAvailability,
|
|
207
222
|
tileAvailabilityIndex
|
|
208
223
|
);
|
|
209
|
-
|
|
210
224
|
if (isContentAvailable) {
|
|
211
|
-
tile.contentUrl = replaceContentUrlTemplate(contentUrlTemplate,
|
|
225
|
+
tile.contentUrl = replaceContentUrlTemplate(contentUrlTemplate, level, x, y, z);
|
|
212
226
|
}
|
|
213
227
|
|
|
214
|
-
const childTileLevel = level + 1;
|
|
215
|
-
const pData = {mortonIndex: childTileMortonIndex, x: childTileX, y: childTileY, z: childTileZ};
|
|
216
|
-
|
|
217
228
|
for (let index = 0; index < childrenPerTile; index++) {
|
|
218
229
|
const childS2VolumeBox: S2VolumeBox | undefined = getChildS2VolumeBox(
|
|
219
230
|
s2VolumeBox,
|
|
@@ -222,32 +233,28 @@ export async function parseImplicitTiles(params: {
|
|
|
222
233
|
);
|
|
223
234
|
|
|
224
235
|
// Recursive calling...
|
|
225
|
-
const
|
|
226
|
-
subtree,
|
|
236
|
+
const childTile = await parseImplicitTiles({
|
|
237
|
+
subtree: nextSubtree,
|
|
238
|
+
subtreeData: nextSubtreeData,
|
|
239
|
+
parentData: nextParentData,
|
|
240
|
+
childIndex: index,
|
|
227
241
|
implicitOptions,
|
|
228
242
|
loaderOptions,
|
|
229
|
-
parentData: pData,
|
|
230
|
-
childIndex: index,
|
|
231
|
-
level: childTileLevel,
|
|
232
|
-
globalData: {...globalData},
|
|
233
243
|
s2VolumeBox: childS2VolumeBox
|
|
234
244
|
});
|
|
235
245
|
|
|
236
|
-
if (
|
|
237
|
-
const globalLevel = lev + 1;
|
|
238
|
-
const childCoordinates = {childTileX, childTileY, childTileZ};
|
|
239
|
-
const formattedTile = formatTileData(
|
|
240
|
-
childTileParsed,
|
|
241
|
-
globalLevel,
|
|
242
|
-
childCoordinates,
|
|
243
|
-
implicitOptions,
|
|
244
|
-
s2VolumeBox
|
|
245
|
-
);
|
|
246
|
+
if (childTile.contentUrl || childTile.children.length) {
|
|
246
247
|
// @ts-ignore
|
|
247
|
-
tile.children.push(
|
|
248
|
+
tile.children.push(childTile);
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
|
|
252
|
+
if (tile.contentUrl || tile.children.length) {
|
|
253
|
+
const coordinates = {level, x, y, z};
|
|
254
|
+
const formattedTile = formatTileData(tile, coordinates, implicitOptions, s2VolumeBox);
|
|
255
|
+
return formattedTile;
|
|
256
|
+
}
|
|
257
|
+
|
|
251
258
|
return tile;
|
|
252
259
|
}
|
|
253
260
|
|
|
@@ -290,15 +297,16 @@ function getAvailabilityResult(
|
|
|
290
297
|
/**
|
|
291
298
|
* Do formatting of implicit tile data.
|
|
292
299
|
* TODO Check out do we able to use Tile3D class as type here.
|
|
293
|
-
*
|
|
294
|
-
* @param
|
|
295
|
-
* @param
|
|
300
|
+
*
|
|
301
|
+
* @param tile - tile data to format.
|
|
302
|
+
* @param coordinates - global tile coordinates (relative to the root of the implicit tile tree).
|
|
303
|
+
* @param options - options specified at the root of this implicit tile tree - numbers of levels, URL templates.
|
|
304
|
+
* @param s2VolumeBox - the S2VolumeBox for this particular child, if available.
|
|
296
305
|
* @returns
|
|
297
306
|
*/
|
|
298
307
|
function formatTileData(
|
|
299
308
|
tile,
|
|
300
|
-
level: number,
|
|
301
|
-
childCoordinates: {childTileX: number; childTileY: number; childTileZ: number},
|
|
309
|
+
coordinates: {level: number; x: number; y: number; z: number},
|
|
302
310
|
options: ImplicitOptions,
|
|
303
311
|
s2VolumeBox?: S2VolumeBox
|
|
304
312
|
) {
|
|
@@ -312,16 +320,16 @@ function formatTileData(
|
|
|
312
320
|
rootBoundingVolume
|
|
313
321
|
} = options;
|
|
314
322
|
const uri = tile.contentUrl && tile.contentUrl.replace(`${basePath}/`, '');
|
|
315
|
-
const lodMetricValue = rootLodMetricValue / 2 ** level;
|
|
323
|
+
const lodMetricValue = rootLodMetricValue / 2 ** coordinates.level;
|
|
316
324
|
|
|
317
325
|
const boundingVolume: Tile3DBoundingVolume = s2VolumeBox?.box
|
|
318
326
|
? {box: s2VolumeBox.box}
|
|
319
327
|
: rootBoundingVolume;
|
|
320
328
|
|
|
321
329
|
const boundingVolumeForChildTile = calculateBoundingVolumeForChildTile(
|
|
322
|
-
level,
|
|
323
330
|
boundingVolume,
|
|
324
|
-
|
|
331
|
+
coordinates,
|
|
332
|
+
options.subdivisionScheme
|
|
325
333
|
);
|
|
326
334
|
|
|
327
335
|
return {
|
|
@@ -342,37 +350,43 @@ function formatTileData(
|
|
|
342
350
|
/**
|
|
343
351
|
* Calculate child bounding volume.
|
|
344
352
|
* Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling#subdivision-rules
|
|
345
|
-
* @param level
|
|
346
353
|
* @param rootBoundingVolume
|
|
347
|
-
* @param
|
|
354
|
+
* @param coordinates
|
|
355
|
+
* @param subdivisionScheme
|
|
348
356
|
*/
|
|
349
357
|
function calculateBoundingVolumeForChildTile(
|
|
350
|
-
level: number,
|
|
351
358
|
rootBoundingVolume: Tile3DBoundingVolume,
|
|
352
|
-
|
|
359
|
+
coordinates: {level: number; x: number; y: number; z: number},
|
|
360
|
+
subdivisionScheme: string
|
|
353
361
|
): Tile3DBoundingVolume {
|
|
354
362
|
if (rootBoundingVolume.region) {
|
|
355
|
-
const {
|
|
363
|
+
const {level, x, y, z} = coordinates;
|
|
356
364
|
const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
|
|
357
365
|
const boundingVolumesCount = 2 ** level;
|
|
358
366
|
|
|
359
367
|
const sizeX = (east - west) / boundingVolumesCount;
|
|
360
|
-
const
|
|
368
|
+
const [childWest, childEast] = [west + sizeX * x, west + sizeX * (x + 1)];
|
|
361
369
|
|
|
362
|
-
|
|
370
|
+
const sizeY = (north - south) / boundingVolumesCount;
|
|
371
|
+
const [childSouth, childNorth] = [south + sizeY * y, south + sizeY * (y + 1)];
|
|
363
372
|
|
|
364
373
|
// In case of QUADTREE the sizeZ should NOT be changed!
|
|
365
374
|
// https://portal.ogc.org/files/102132
|
|
366
|
-
// A quadtree divides space only on the x and y dimensions.
|
|
367
|
-
|
|
368
|
-
|
|
375
|
+
// A quadtree divides space only on the x and y dimensions.
|
|
376
|
+
// It divides each tile into 4 smaller tiles where the x and y dimensions are halved.
|
|
377
|
+
// The quadtree z minimum and maximum remain unchanged.
|
|
369
378
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
379
|
+
let childMinimumHeight: number;
|
|
380
|
+
let childMaximumHeight: number;
|
|
381
|
+
if (subdivisionScheme === 'OCTREE') {
|
|
382
|
+
const sizeZ = (maximumHeight - minimumHeight) / boundingVolumesCount;
|
|
383
|
+
[childMinimumHeight, childMaximumHeight] = [
|
|
384
|
+
minimumHeight + sizeZ * z,
|
|
385
|
+
minimumHeight + sizeZ * (z + 1)
|
|
386
|
+
];
|
|
387
|
+
} else {
|
|
388
|
+
[childMinimumHeight, childMaximumHeight] = [minimumHeight, maximumHeight];
|
|
389
|
+
}
|
|
376
390
|
|
|
377
391
|
return {
|
|
378
392
|
region: [childWest, childSouth, childEast, childNorth, childMinimumHeight, childMaximumHeight]
|