t3d-3dtiles 0.1.0
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 +28 -0
- package/README.md +41 -0
- package/build/t3d.3dtiles.js +5418 -0
- package/build/t3d.3dtiles.min.js +2 -0
- package/build/t3d.3dtiles.module.js +6183 -0
- package/examples/jsm/CesiumIon.js +30 -0
- package/examples/jsm/Tiles3DDebugger.js +219 -0
- package/examples/jsm/Tiles3DUtils.js +32 -0
- package/examples/jsm/helpers/LineSegmentHelper.js +74 -0
- package/examples/jsm/helpers/OBBHelper.js +48 -0
- package/examples/jsm/helpers/Tiles3DHelper.js +135 -0
- package/examples/jsm/math/Intersects.js +100 -0
- package/examples/jsm/math/LineSegment.js +234 -0
- package/package.json +57 -0
- package/src/Tiles3D.js +402 -0
- package/src/libs/ImageBitmapLoader.js +56 -0
- package/src/libs/glTF/Constants.js +85 -0
- package/src/libs/glTF/GLTFLoader.js +169 -0
- package/src/libs/glTF/GLTFResource.js +28 -0
- package/src/libs/glTF/GLTFUtils.js +131 -0
- package/src/libs/glTF/extensions/EXT_meshopt_compression.js +35 -0
- package/src/libs/glTF/extensions/KHR_draco_mesh_compression.js +54 -0
- package/src/libs/glTF/extensions/KHR_lights_punctual.js +53 -0
- package/src/libs/glTF/extensions/KHR_materials_clearcoat.js +42 -0
- package/src/libs/glTF/extensions/KHR_materials_pbrSpecularGlossiness.js +53 -0
- package/src/libs/glTF/extensions/KHR_materials_unlit.js +13 -0
- package/src/libs/glTF/extensions/KHR_texture_basisu.js +14 -0
- package/src/libs/glTF/extensions/KHR_texture_transform.js +31 -0
- package/src/libs/glTF/parsers/AccessorParser.js +99 -0
- package/src/libs/glTF/parsers/AnimationParser.js +118 -0
- package/src/libs/glTF/parsers/BufferParser.js +35 -0
- package/src/libs/glTF/parsers/BufferViewParser.js +27 -0
- package/src/libs/glTF/parsers/ImageParser.js +97 -0
- package/src/libs/glTF/parsers/IndexParser.js +22 -0
- package/src/libs/glTF/parsers/MaterialParser.js +146 -0
- package/src/libs/glTF/parsers/NodeParser.js +145 -0
- package/src/libs/glTF/parsers/PrimitiveParser.js +289 -0
- package/src/libs/glTF/parsers/ReferenceParser.js +67 -0
- package/src/libs/glTF/parsers/SceneParser.js +41 -0
- package/src/libs/glTF/parsers/SkinParser.js +53 -0
- package/src/libs/glTF/parsers/TextureParser.js +71 -0
- package/src/libs/glTF/parsers/Validator.js +16 -0
- package/src/loaders/B3DMLoader.js +49 -0
- package/src/loaders/CMPTLoader.js +48 -0
- package/src/loaders/I3DMLoader.js +49 -0
- package/src/loaders/PNTSLoader.js +20 -0
- package/src/loaders/TileGLTFLoader.js +17 -0
- package/src/loaders/parsers/HeaderParser.js +104 -0
- package/src/loaders/parsers/LoadParser.js +22 -0
- package/src/loaders/parsers/TableParser.js +66 -0
- package/src/loaders/parsers/b3dm/B3DMParser.js +18 -0
- package/src/loaders/parsers/b3dm/B3DMRootParser.js +22 -0
- package/src/loaders/parsers/b3dm/MaterialParser.js +156 -0
- package/src/loaders/parsers/cmpt/CMPTParser.js +37 -0
- package/src/loaders/parsers/cmpt/CMPTRootParser.js +44 -0
- package/src/loaders/parsers/gltf/IndexParser.js +24 -0
- package/src/loaders/parsers/i3dm/I3DMParser.js +28 -0
- package/src/loaders/parsers/i3dm/I3DMRootParser.js +123 -0
- package/src/loaders/parsers/i3dm/MaterialParser.js +152 -0
- package/src/loaders/parsers/i3dm/PrimitiveParser.js +291 -0
- package/src/loaders/parsers/pnts/PNTSRootParser.js +69 -0
- package/src/main.js +14 -0
- package/src/materials/InstancedBasicMaterial.js +32 -0
- package/src/materials/InstancedPBRMaterial.js +32 -0
- package/src/materials/InstancedShaderChunks.js +23 -0
- package/src/math/Ellipsoid.js +41 -0
- package/src/math/EllipsoidRegion.js +160 -0
- package/src/math/FastFrustum.js +49 -0
- package/src/math/GeoUtils.js +31 -0
- package/src/math/OBB.js +395 -0
- package/src/math/TileBoundingVolume.js +172 -0
- package/src/math/TileOBB.js +103 -0
- package/src/utils/BatchTable.js +14 -0
- package/src/utils/CameraList.js +131 -0
- package/src/utils/FeatureTable.js +107 -0
- package/src/utils/IntersectionUtils.js +136 -0
- package/src/utils/LRUCache.js +159 -0
- package/src/utils/ModelLoader.js +150 -0
- package/src/utils/PriorityQueue.js +102 -0
- package/src/utils/RequestState.js +17 -0
- package/src/utils/TilesLoader.js +386 -0
- package/src/utils/TilesScheduler.js +375 -0
- package/src/utils/Utils.js +43 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import RequestState from './RequestState.js';
|
|
2
|
+
|
|
3
|
+
export const schedulingTiles = (tile, tiles3D) => {
|
|
4
|
+
determineFrustumSet(tile, tiles3D);
|
|
5
|
+
markUsedSetLeaves(tile, tiles3D);
|
|
6
|
+
skipTraversal(tile, tiles3D);
|
|
7
|
+
toggleTiles(tile, tiles3D);
|
|
8
|
+
|
|
9
|
+
tiles3D.$tilesLoader.lruCache.scheduleUnload();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const determineFrustumSet = (tile, tiles3D) => {
|
|
13
|
+
const stats = tiles3D.stats;
|
|
14
|
+
const frameCount = tiles3D.frameCount;
|
|
15
|
+
const errorTarget = tiles3D.errorTarget;
|
|
16
|
+
const maxDepth = tiles3D.maxDepth;
|
|
17
|
+
const loadSiblings = tiles3D.loadSiblings;
|
|
18
|
+
const lruCache = tiles3D.$tilesLoader.lruCache;
|
|
19
|
+
const stopAtEmptyTiles = tiles3D.stopAtEmptyTiles;
|
|
20
|
+
|
|
21
|
+
_resetFrameState(tile, frameCount);
|
|
22
|
+
|
|
23
|
+
// Early out if this tile is not within view.
|
|
24
|
+
const inFrustum = _tileInView(tile, tiles3D);
|
|
25
|
+
if (inFrustum === false) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
tile.__used = true;
|
|
30
|
+
lruCache.markUsed(tile);
|
|
31
|
+
|
|
32
|
+
tile.__inFrustum = true;
|
|
33
|
+
stats.inFrustum++;
|
|
34
|
+
|
|
35
|
+
// Early out if this tile has less error than we're targeting but don't stop
|
|
36
|
+
// at an external tile set.
|
|
37
|
+
if ((stopAtEmptyTiles || !tile.__contentEmpty) && !tile.__externalTileSet) {
|
|
38
|
+
// compute the _error and __distanceFromCamera fields
|
|
39
|
+
_calculateError(tile, tiles3D);
|
|
40
|
+
|
|
41
|
+
const error = tile.__error;
|
|
42
|
+
if (error <= errorTarget) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Early out if we've reached the maximum allowed depth.
|
|
47
|
+
if (maxDepth > 0 && tile.__depth + 1 >= maxDepth) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Traverse children and see if any children are in view.
|
|
53
|
+
let anyChildrenUsed = false;
|
|
54
|
+
const children = tile.children;
|
|
55
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
56
|
+
const c = children[i];
|
|
57
|
+
const r = determineFrustumSet(c, tiles3D);
|
|
58
|
+
anyChildrenUsed = anyChildrenUsed || r;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If there are children within view and we are loading siblings then mark
|
|
62
|
+
// all sibling tiles as used, as well.
|
|
63
|
+
if (anyChildrenUsed && loadSiblings) {
|
|
64
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
65
|
+
const c = children[i];
|
|
66
|
+
_recursivelyMarkUsed(c, frameCount, lruCache);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return true;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Traverse and mark the tiles that are at the leaf nodes of the "used" tree.
|
|
74
|
+
const markUsedSetLeaves = (tile, tiles3D) => {
|
|
75
|
+
const stats = tiles3D.stats;
|
|
76
|
+
const frameCount = tiles3D.frameCount;
|
|
77
|
+
|
|
78
|
+
if (!_isUsedThisFrame(tile, frameCount)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
stats.used++;
|
|
83
|
+
|
|
84
|
+
// This tile is a leaf if none of the children had been used.
|
|
85
|
+
const children = tile.children;
|
|
86
|
+
let anyChildrenUsed = false;
|
|
87
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
88
|
+
const c = children[i];
|
|
89
|
+
anyChildrenUsed = anyChildrenUsed || _isUsedThisFrame(c, frameCount);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!anyChildrenUsed) {
|
|
93
|
+
// TODO: This isn't necessarily right because it's possible that a parent tile is considered in the
|
|
94
|
+
// frustum while the child tiles are not, making them unused. If all children have loaded and were properly
|
|
95
|
+
// considered to be in the used set then we shouldn't set ourselves to a leaf here.
|
|
96
|
+
tile.__isLeaf = true;
|
|
97
|
+
} else {
|
|
98
|
+
let childrenWereVisible = false;
|
|
99
|
+
let allChildrenLoaded = true;
|
|
100
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
101
|
+
const c = children[i];
|
|
102
|
+
markUsedSetLeaves(c, tiles3D);
|
|
103
|
+
childrenWereVisible = childrenWereVisible || c.__wasSetVisible || c.__childrenWereVisible;
|
|
104
|
+
|
|
105
|
+
if (_isUsedThisFrame(c, frameCount)) {
|
|
106
|
+
const childLoaded =
|
|
107
|
+
c.__allChildrenLoaded ||
|
|
108
|
+
(!c.__contentEmpty && _isDownloadFinished(c.__loadingState)) ||
|
|
109
|
+
(c.__externalTileSet && c.__loadingState === RequestState.FAILED);
|
|
110
|
+
allChildrenLoaded = allChildrenLoaded && childLoaded;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
tile.__childrenWereVisible = childrenWereVisible;
|
|
114
|
+
tile.__allChildrenLoaded = allChildrenLoaded;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Skip past tiles we consider unrenderable because they are outside the error threshold.
|
|
119
|
+
const skipTraversal = (tile, tiles3D) => {
|
|
120
|
+
const stats = tiles3D.stats;
|
|
121
|
+
const frameCount = tiles3D.frameCount;
|
|
122
|
+
|
|
123
|
+
if (!_isUsedThisFrame(tile, frameCount)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const parent = tile.parent;
|
|
128
|
+
const parentDepthToParent = parent ? parent.__depthFromRenderedParent : -1;
|
|
129
|
+
tile.__depthFromRenderedParent = parentDepthToParent;
|
|
130
|
+
|
|
131
|
+
// Request the tile contents or mark it as visible if we've found a leaf.
|
|
132
|
+
const lruCache = tiles3D.$tilesLoader.lruCache;
|
|
133
|
+
if (tile.__isLeaf) {
|
|
134
|
+
tile.__depthFromRenderedParent++;
|
|
135
|
+
|
|
136
|
+
if (tile.__loadingState === RequestState.LOADED) {
|
|
137
|
+
if (tile.__inFrustum) {
|
|
138
|
+
tile.__visible = true;
|
|
139
|
+
stats.visible++;
|
|
140
|
+
}
|
|
141
|
+
tile.__active = true;
|
|
142
|
+
stats.active++;
|
|
143
|
+
} else if (!lruCache.isFull() && (!tile.__contentEmpty || tile.__externalTileSet)) {
|
|
144
|
+
tiles3D.$tilesLoader.requestTileContents(tile, tiles3D);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const errorRequirement = (tiles3D.errorTarget + 1) * tiles3D.errorThreshold;
|
|
151
|
+
const meetsSSE = tile.__error <= errorRequirement;
|
|
152
|
+
const includeTile = meetsSSE || tile.refine === 'ADD';
|
|
153
|
+
const hasModel = !tile.__contentEmpty;
|
|
154
|
+
const hasContent = hasModel || tile.__externalTileSet;
|
|
155
|
+
const loadedContent = _isDownloadFinished(tile.__loadingState) && hasContent;
|
|
156
|
+
const childrenWereVisible = tile.__childrenWereVisible;
|
|
157
|
+
const children = tile.children;
|
|
158
|
+
const allChildrenHaveContent = tile.__allChildrenLoaded;
|
|
159
|
+
|
|
160
|
+
// Increment the relative depth of the node to the nearest rendered parent if it has content
|
|
161
|
+
// and is being rendered.
|
|
162
|
+
if (includeTile && hasModel) {
|
|
163
|
+
tile.__depthFromRenderedParent++;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// If we've met the SSE requirements and we can load content then fire a fetch.
|
|
167
|
+
if (includeTile && !loadedContent && !lruCache.isFull() && hasContent) {
|
|
168
|
+
tiles3D.$tilesLoader.requestTileContents(tile, tiles3D);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Only mark this tile as visible if it meets the screen space error requirements, has loaded content, not
|
|
172
|
+
// all children have loaded yet, and if no children were visible last frame. We want to keep children visible
|
|
173
|
+
// that _were_ visible to avoid a pop in level of detail as the camera moves around and parent / sibling tiles
|
|
174
|
+
// load in.
|
|
175
|
+
|
|
176
|
+
// Skip the tile entirely if there's no content to load
|
|
177
|
+
if (
|
|
178
|
+
(meetsSSE && !allChildrenHaveContent && !childrenWereVisible && loadedContent)
|
|
179
|
+
|| (tile.refine === 'ADD' && loadedContent)
|
|
180
|
+
) {
|
|
181
|
+
if (tile.__inFrustum) {
|
|
182
|
+
tile.__visible = true;
|
|
183
|
+
stats.visible++;
|
|
184
|
+
}
|
|
185
|
+
tile.__active = true;
|
|
186
|
+
stats.active++;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// If we're additive then don't stop the traversal here because it doesn't matter whether the children load in
|
|
190
|
+
// at the same rate.
|
|
191
|
+
if (tile.refine !== 'ADD' && meetsSSE && !allChildrenHaveContent && loadedContent) {
|
|
192
|
+
// load the child content if we've found that we've been loaded so we can move down to the next tile
|
|
193
|
+
// layer when the data has loaded.
|
|
194
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
195
|
+
const c = children[i];
|
|
196
|
+
if (_isUsedThisFrame(c, frameCount) && !lruCache.isFull()) {
|
|
197
|
+
c.__depthFromRenderedParent = tile.__depthFromRenderedParent + 1;
|
|
198
|
+
_recursivelyLoadTiles(c, c.__depthFromRenderedParent, tiles3D);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
203
|
+
const c = children[i];
|
|
204
|
+
if (_isUsedThisFrame(c, frameCount)) {
|
|
205
|
+
skipTraversal(c, tiles3D);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Final traverse to toggle tile visibility.
|
|
212
|
+
const toggleTiles = (tile, tiles3D) => {
|
|
213
|
+
const frameCount = tiles3D.frameCount;
|
|
214
|
+
|
|
215
|
+
const isUsed = _isUsedThisFrame(tile, frameCount);
|
|
216
|
+
|
|
217
|
+
if (isUsed || tile.__usedLastFrame) {
|
|
218
|
+
let setActive = false;
|
|
219
|
+
let setVisible = false;
|
|
220
|
+
if (isUsed) {
|
|
221
|
+
// enable visibility if active due to shadows
|
|
222
|
+
setActive = tile.__active;
|
|
223
|
+
if (tiles3D.displayActiveTiles) {
|
|
224
|
+
setVisible = tile.__active || tile.__visible;
|
|
225
|
+
} else {
|
|
226
|
+
setVisible = tile.__visible;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// If the active or visible state changed then call the functions.
|
|
231
|
+
if (!tile.__contentEmpty && tile.__loadingState === RequestState.LOADED) {
|
|
232
|
+
if (tile.__wasSetActive !== setActive) {
|
|
233
|
+
tiles3D.$setTileActive(tile, setActive);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (tile.__wasSetVisible !== setVisible) {
|
|
237
|
+
tiles3D.$setTileVisible(tile, setVisible);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
tile.__wasSetActive = setActive;
|
|
241
|
+
tile.__wasSetVisible = setVisible;
|
|
242
|
+
tile.__usedLastFrame = isUsed;
|
|
243
|
+
|
|
244
|
+
const children = tile.children;
|
|
245
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
246
|
+
const c = children[i];
|
|
247
|
+
toggleTiles(c, tiles3D);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Resets the frame frame information for the given tile
|
|
253
|
+
const _resetFrameState = (tile, frameCount) => {
|
|
254
|
+
if (tile.__lastFrameVisited !== frameCount) {
|
|
255
|
+
tile.__lastFrameVisited = frameCount;
|
|
256
|
+
tile.__used = false;
|
|
257
|
+
tile.__inFrustum = false;
|
|
258
|
+
tile.__isLeaf = false;
|
|
259
|
+
tile.__visible = false;
|
|
260
|
+
tile.__active = false;
|
|
261
|
+
tile.__error = Infinity;
|
|
262
|
+
tile.__distanceFromCamera = Infinity;
|
|
263
|
+
tile.__childrenWereVisible = false;
|
|
264
|
+
tile.__allChildrenLoaded = false;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const _tileInView = (tile, tiles3D) => {
|
|
269
|
+
const cameraInfos = tiles3D.$cameras.getInfos();
|
|
270
|
+
|
|
271
|
+
const cached = tile.cached;
|
|
272
|
+
const boundingVolume = cached.boundingVolume;
|
|
273
|
+
const inFrustum = cached.inFrustum;
|
|
274
|
+
|
|
275
|
+
let inView = false;
|
|
276
|
+
|
|
277
|
+
for (let i = 0, l = cameraInfos.length; i < l; i++) {
|
|
278
|
+
// Track which camera frustums this tile is in so we can use it
|
|
279
|
+
// to ignore the error calculations for cameras that can't see it
|
|
280
|
+
const frustum = cameraInfos[i].frustum;
|
|
281
|
+
|
|
282
|
+
if (boundingVolume.intersectsFrustum(frustum)) {
|
|
283
|
+
inView = true;
|
|
284
|
+
inFrustum[i] = true;
|
|
285
|
+
} else {
|
|
286
|
+
inFrustum[i] = false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return inView;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const _calculateError = (tile, tiles3D) => {
|
|
294
|
+
const cameraInfos = tiles3D.$cameras.getInfos();
|
|
295
|
+
|
|
296
|
+
const cached = tile.cached;
|
|
297
|
+
const inFrustum = cached.inFrustum;
|
|
298
|
+
const boundingVolume = cached.boundingVolume;
|
|
299
|
+
|
|
300
|
+
let maxError = -Infinity;
|
|
301
|
+
let minDistance = Infinity;
|
|
302
|
+
|
|
303
|
+
for (let i = 0, l = cameraInfos.length; i < l; i++) {
|
|
304
|
+
if (!inFrustum[i]) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// transform camera position into local frame of the tile bounding box
|
|
309
|
+
const info = cameraInfos[i];
|
|
310
|
+
const invScale = info.invScale;
|
|
311
|
+
|
|
312
|
+
let error;
|
|
313
|
+
if (info.isOrthographic) {
|
|
314
|
+
const pixelSize = info.pixelSize;
|
|
315
|
+
error = cached.geometricError / (pixelSize * invScale);
|
|
316
|
+
} else {
|
|
317
|
+
const distance = boundingVolume.distanceToPoint(info.position);
|
|
318
|
+
const scaledDistance = distance * invScale;
|
|
319
|
+
const sseDenominator = info.sseDenominator;
|
|
320
|
+
error = cached.geometricError / (scaledDistance * sseDenominator);
|
|
321
|
+
|
|
322
|
+
minDistance = Math.min(minDistance, scaledDistance);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
maxError = Math.max(maxError, error);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
tile.__distanceFromCamera = minDistance;
|
|
329
|
+
tile.__error = maxError;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// Recursively mark tiles used down to the next tile with content
|
|
333
|
+
const _recursivelyMarkUsed = (tile, frameCount, lruCache) => {
|
|
334
|
+
_resetFrameState(tile, frameCount);
|
|
335
|
+
|
|
336
|
+
tile.__used = true;
|
|
337
|
+
lruCache.markUsed(tile);
|
|
338
|
+
if (tile.__contentEmpty) {
|
|
339
|
+
const children = tile.children;
|
|
340
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
341
|
+
_recursivelyMarkUsed(children[i], frameCount, lruCache);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// Checks whether this tile was last used on the given frame.
|
|
347
|
+
const _isUsedThisFrame = (tile, frameCount) => {
|
|
348
|
+
return tile.__lastFrameVisited === frameCount && tile.__used;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
function _isDownloadFinished(value) {
|
|
352
|
+
return value === RequestState.LOADED || value === RequestState.FAILED;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const _recursivelyLoadTiles = (tile, depthFromRenderedParent, tiles3D) => {
|
|
356
|
+
// Try to load any external tile set children if the external tile set has loaded.
|
|
357
|
+
const doTraverse =
|
|
358
|
+
tile.__contentEmpty && (
|
|
359
|
+
!tile.__externalTileSet ||
|
|
360
|
+
_isDownloadFinished(tile.__loadingState)
|
|
361
|
+
);
|
|
362
|
+
if (doTraverse) {
|
|
363
|
+
const children = tile.children;
|
|
364
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
365
|
+
// don't increment depth to rendered parent here because we should treat
|
|
366
|
+
// the next layer of rendered children as just a single depth away for the
|
|
367
|
+
// sake of sorting.
|
|
368
|
+
const child = children[i];
|
|
369
|
+
child.__depthFromRenderedParent = depthFromRenderedParent;
|
|
370
|
+
_recursivelyLoadTiles(child, depthFromRenderedParent, tiles3D);
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
tiles3D.$tilesLoader.requestTileContents(tile, tiles3D);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the file extension of the path component of a URL
|
|
3
|
+
* @param {string} url
|
|
4
|
+
* @returns {string} null if no extension found
|
|
5
|
+
*/
|
|
6
|
+
export const getUrlExtension = url => {
|
|
7
|
+
let parsedUrl;
|
|
8
|
+
try {
|
|
9
|
+
parsedUrl = new URL(url, 'http://fakehost.com/');
|
|
10
|
+
} catch (_) {
|
|
11
|
+
// Ignore invalid URLs
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const filename = parsedUrl.pathname.split('/').pop();
|
|
16
|
+
const dotIndex = filename.lastIndexOf('.');
|
|
17
|
+
if (dotIndex === -1 || dotIndex === filename.length - 1) {
|
|
18
|
+
// Has no extension or has trailing . character
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const extension = filename.substring(dotIndex + 1);
|
|
23
|
+
return extension;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const traverseSet = (tile, beforeCb = null, afterCb = null, parent = null, depth = 0) => {
|
|
27
|
+
if (beforeCb && beforeCb(tile, parent, depth)) {
|
|
28
|
+
if (afterCb) {
|
|
29
|
+
afterCb(tile, parent, depth);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const children = tile.children;
|
|
36
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
37
|
+
traverseSet(children[i], beforeCb, afterCb, tile, depth + 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (afterCb) {
|
|
41
|
+
afterCb(tile, parent, depth);
|
|
42
|
+
}
|
|
43
|
+
};
|