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.
Files changed (83) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +41 -0
  3. package/build/t3d.3dtiles.js +5418 -0
  4. package/build/t3d.3dtiles.min.js +2 -0
  5. package/build/t3d.3dtiles.module.js +6183 -0
  6. package/examples/jsm/CesiumIon.js +30 -0
  7. package/examples/jsm/Tiles3DDebugger.js +219 -0
  8. package/examples/jsm/Tiles3DUtils.js +32 -0
  9. package/examples/jsm/helpers/LineSegmentHelper.js +74 -0
  10. package/examples/jsm/helpers/OBBHelper.js +48 -0
  11. package/examples/jsm/helpers/Tiles3DHelper.js +135 -0
  12. package/examples/jsm/math/Intersects.js +100 -0
  13. package/examples/jsm/math/LineSegment.js +234 -0
  14. package/package.json +57 -0
  15. package/src/Tiles3D.js +402 -0
  16. package/src/libs/ImageBitmapLoader.js +56 -0
  17. package/src/libs/glTF/Constants.js +85 -0
  18. package/src/libs/glTF/GLTFLoader.js +169 -0
  19. package/src/libs/glTF/GLTFResource.js +28 -0
  20. package/src/libs/glTF/GLTFUtils.js +131 -0
  21. package/src/libs/glTF/extensions/EXT_meshopt_compression.js +35 -0
  22. package/src/libs/glTF/extensions/KHR_draco_mesh_compression.js +54 -0
  23. package/src/libs/glTF/extensions/KHR_lights_punctual.js +53 -0
  24. package/src/libs/glTF/extensions/KHR_materials_clearcoat.js +42 -0
  25. package/src/libs/glTF/extensions/KHR_materials_pbrSpecularGlossiness.js +53 -0
  26. package/src/libs/glTF/extensions/KHR_materials_unlit.js +13 -0
  27. package/src/libs/glTF/extensions/KHR_texture_basisu.js +14 -0
  28. package/src/libs/glTF/extensions/KHR_texture_transform.js +31 -0
  29. package/src/libs/glTF/parsers/AccessorParser.js +99 -0
  30. package/src/libs/glTF/parsers/AnimationParser.js +118 -0
  31. package/src/libs/glTF/parsers/BufferParser.js +35 -0
  32. package/src/libs/glTF/parsers/BufferViewParser.js +27 -0
  33. package/src/libs/glTF/parsers/ImageParser.js +97 -0
  34. package/src/libs/glTF/parsers/IndexParser.js +22 -0
  35. package/src/libs/glTF/parsers/MaterialParser.js +146 -0
  36. package/src/libs/glTF/parsers/NodeParser.js +145 -0
  37. package/src/libs/glTF/parsers/PrimitiveParser.js +289 -0
  38. package/src/libs/glTF/parsers/ReferenceParser.js +67 -0
  39. package/src/libs/glTF/parsers/SceneParser.js +41 -0
  40. package/src/libs/glTF/parsers/SkinParser.js +53 -0
  41. package/src/libs/glTF/parsers/TextureParser.js +71 -0
  42. package/src/libs/glTF/parsers/Validator.js +16 -0
  43. package/src/loaders/B3DMLoader.js +49 -0
  44. package/src/loaders/CMPTLoader.js +48 -0
  45. package/src/loaders/I3DMLoader.js +49 -0
  46. package/src/loaders/PNTSLoader.js +20 -0
  47. package/src/loaders/TileGLTFLoader.js +17 -0
  48. package/src/loaders/parsers/HeaderParser.js +104 -0
  49. package/src/loaders/parsers/LoadParser.js +22 -0
  50. package/src/loaders/parsers/TableParser.js +66 -0
  51. package/src/loaders/parsers/b3dm/B3DMParser.js +18 -0
  52. package/src/loaders/parsers/b3dm/B3DMRootParser.js +22 -0
  53. package/src/loaders/parsers/b3dm/MaterialParser.js +156 -0
  54. package/src/loaders/parsers/cmpt/CMPTParser.js +37 -0
  55. package/src/loaders/parsers/cmpt/CMPTRootParser.js +44 -0
  56. package/src/loaders/parsers/gltf/IndexParser.js +24 -0
  57. package/src/loaders/parsers/i3dm/I3DMParser.js +28 -0
  58. package/src/loaders/parsers/i3dm/I3DMRootParser.js +123 -0
  59. package/src/loaders/parsers/i3dm/MaterialParser.js +152 -0
  60. package/src/loaders/parsers/i3dm/PrimitiveParser.js +291 -0
  61. package/src/loaders/parsers/pnts/PNTSRootParser.js +69 -0
  62. package/src/main.js +14 -0
  63. package/src/materials/InstancedBasicMaterial.js +32 -0
  64. package/src/materials/InstancedPBRMaterial.js +32 -0
  65. package/src/materials/InstancedShaderChunks.js +23 -0
  66. package/src/math/Ellipsoid.js +41 -0
  67. package/src/math/EllipsoidRegion.js +160 -0
  68. package/src/math/FastFrustum.js +49 -0
  69. package/src/math/GeoUtils.js +31 -0
  70. package/src/math/OBB.js +395 -0
  71. package/src/math/TileBoundingVolume.js +172 -0
  72. package/src/math/TileOBB.js +103 -0
  73. package/src/utils/BatchTable.js +14 -0
  74. package/src/utils/CameraList.js +131 -0
  75. package/src/utils/FeatureTable.js +107 -0
  76. package/src/utils/IntersectionUtils.js +136 -0
  77. package/src/utils/LRUCache.js +159 -0
  78. package/src/utils/ModelLoader.js +150 -0
  79. package/src/utils/PriorityQueue.js +102 -0
  80. package/src/utils/RequestState.js +17 -0
  81. package/src/utils/TilesLoader.js +386 -0
  82. package/src/utils/TilesScheduler.js +375 -0
  83. 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
+ };