@jdultra/threedtiles 7.0.2 → 8.0.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.
@@ -5,6 +5,7 @@ import { initial } from 'lodash';
5
5
  import * as THREE from 'three';
6
6
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
7
7
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
8
+ import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
8
9
 
9
10
  let concurentDownloads = 0;
10
11
  const zUpToYUpMatrix = new THREE.Matrix4();
@@ -12,17 +13,49 @@ zUpToYUpMatrix.set(1, 0, 0, 0,
12
13
  0, 0, -1, 0,
13
14
  0, 1, 0, 0,
14
15
  0, 0, 0, 1);
15
- const gltfLoader = new GLTFLoader();
16
- const dracoLoader = new DRACOLoader();
17
- dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
18
- gltfLoader.setDRACOLoader(dracoLoader);
19
16
 
17
+
18
+
19
+
20
+ /**
21
+ * A Tile loader that manages caching and load order.
22
+ * The cache is an LRU cache and is defined by the number of items it can hold.
23
+ * The actual number of cached items might grow beyond max if all items are in use.
24
+ *
25
+ * The load order is designed for optimal perceived loading speed (nearby tiles are refined first).
26
+ *
27
+ * @param {Object} [options] - Optional configuration object.
28
+ * @param {number} [options.maxCachedItems=100] - the cache size.
29
+ * @param {function} [options.meshCallback] - A callback to call on newly decoded meshes.
30
+ * @param {function} [options.pointsCallback] - A callback to call on newly decoded points.
31
+ * @param {renderer} [options.renderer] - The renderer, this is required for KTX2 support.
32
+ */
20
33
  class TileLoader {
21
- constructor(maxCachedItems, meshCallback, pointsCallback) {
22
- this.meshCallback = meshCallback;
23
- this.pointsCallback = pointsCallback;
34
+ constructor(options) {
35
+ this.maxCachedItems = 100;
36
+ if (!!options) {
37
+ this.meshCallback = options.meshCallback;
38
+ this.pointsCallback = options.pointsCallback;
39
+ if (options.maxCachedItems) this.maxCachedItems = options.maxCachedItems;
40
+
41
+ }
42
+
43
+ this.gltfLoader = new GLTFLoader();
44
+ const dracoLoader = new DRACOLoader();
45
+ dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
46
+ this.gltfLoader.setDRACOLoader(dracoLoader);
47
+
48
+ if(!!options && !!options.renderer){
49
+ const ktx2Loader = new KTX2Loader();
50
+ ktx2Loader.setTranscoderPath('https://storage.googleapis.com/ogc-3d-tiles/basis/').detectSupport(options.renderer);
51
+ this.gltfLoader.setKTX2Loader(ktx2Loader);
52
+
53
+ this.b3dmDecoder=new B3DMDecoder(options.renderer);
54
+ }else{
55
+ this.b3dmDecoder=new B3DMDecoder(null);
56
+ }
57
+
24
58
  this.cache = new LinkedHashMap();
25
- this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 100;
26
59
  this.register = {};
27
60
 
28
61
 
@@ -33,6 +66,7 @@ class TileLoader {
33
66
  this.init();
34
67
  }
35
68
 
69
+
36
70
  init() {
37
71
 
38
72
  const self = this;
@@ -195,7 +229,7 @@ class TileLoader {
195
229
 
196
230
  })
197
231
  .then(resultArrayBuffer => {
198
- return B3DMDecoder.parseB3DM(resultArrayBuffer, self.meshCallback, geometricError, zUpToYUp);
232
+ return this.b3dmDecoder.parseB3DM(resultArrayBuffer, self.meshCallback, geometricError, zUpToYUp);
199
233
  })
200
234
  .then(mesh => {
201
235
  self.cache.put(key, mesh);
@@ -207,7 +241,7 @@ class TileLoader {
207
241
  } else if (path.includes(".glb") || path.includes(".gltf")) {
208
242
  downloadFunction = () => {
209
243
  concurentDownloads++;
210
- gltfLoader.load(path, gltf => {
244
+ this.gltfLoader.load(path, gltf => {
211
245
  gltf.scene.traverse((o) => {
212
246
  o.geometricError = geometricError;
213
247
  if (o.isMesh) {
@@ -367,7 +367,7 @@ class InstancedTile extends THREE.Object3D {
367
367
  }
368
368
  if (metric >= self.master.geometricErrorMultiplier * self.geometricError) {
369
369
  self.disposeChildren();
370
- updateNodeVisibility();
370
+ updateNodeVisibility(metric);
371
371
  return;
372
372
  }
373
373
 
@@ -6,25 +6,58 @@ import { MeshTile } from './MeshTile';
6
6
  import { JsonTile } from './JsonTile';
7
7
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
8
8
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
9
-
9
+ import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
10
10
 
11
11
  let concurentDownloads = 0;
12
- const gltfLoader = new GLTFLoader();
13
- const dracoLoader = new DRACOLoader();
14
- dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
15
- gltfLoader.setDRACOLoader(dracoLoader);
12
+
16
13
  const zUpToYUpMatrix = new THREE.Matrix4();
17
14
  zUpToYUpMatrix.set(1, 0, 0, 0,
18
15
  0, 0, -1, 0,
19
16
  0, 1, 0, 0,
20
17
  0, 0, 0, 1);
21
18
 
19
+ /**
20
+ * A Tile loader that manages caching and load order for instanced tiles.
21
+ * The cache is an LRU cache and is defined by the number of items it can hold.
22
+ * The actual number of cached items might grow beyond max if all items are in use.
23
+ *
24
+ * The load order is designed for optimal perceived loading speed (nearby tiles are refined first).
25
+ *
26
+ * @param {scene} [scene] - a threejs scene.
27
+ * @param {Object} [options] - Optional configuration object.
28
+ * @param {number} [options.maxCachedItems=100] - the cache size.
29
+ * @param {number} [options.maxInstances=1] - the cache size.
30
+ * @param {function} [options.meshCallback] - A callback to call on newly decoded meshes.
31
+ * @param {function} [options.pointsCallback] - A callback to call on newly decoded points.
32
+ * @param {renderer} [options.renderer] - The renderer, this is required for KTX2 support.
33
+ */
22
34
  class InstancedTileLoader {
23
- constructor(scene, maxCachedItems, maxInstances, meshCallback) {
24
- this.meshCallback = meshCallback;
25
- this.maxInstances = maxInstances;
35
+ constructor(scene, options) {
36
+ this.maxCachedItems = 100;
37
+ this.maxInstances = 1;
38
+ if (!!options) {
39
+ this.meshCallback = options.meshCallback;
40
+ this.pointsCallback = options.pointsCallback;
41
+ if (options.maxCachedItems) this.maxCachedItems = options.maxCachedItems;
42
+ if (options.maxInstances) this.maxInstances = options.maxInstances;
43
+
44
+ }
45
+ this.gltfLoader = new GLTFLoader();
46
+ const dracoLoader = new DRACOLoader();
47
+ dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
48
+ this.gltfLoader.setDRACOLoader(dracoLoader);
49
+
50
+ if(!!options && !!options.renderer){
51
+ const ktx2Loader = new KTX2Loader();
52
+ ktx2Loader.setTranscoderPath('https://storage.googleapis.com/ogc-3d-tiles/basis/').detectSupport(options.renderer);
53
+ this.gltfLoader.setKTX2Loader(ktx2Loader);
54
+
55
+ this.b3dmDecoder=new B3DMDecoder(options.renderer);
56
+ }else{
57
+ this.b3dmDecoder=new B3DMDecoder(null);
58
+ }
59
+
26
60
  this.cache = new LinkedHashMap();
27
- this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 100;
28
61
  this.scene = scene;
29
62
 
30
63
  this.ready = [];
@@ -34,16 +67,17 @@ class InstancedTileLoader {
34
67
  this.init();
35
68
  }
36
69
 
37
- update(){
70
+
71
+ update() {
38
72
  const self = this;
39
-
40
- self.cache._data.forEach(v=>{
73
+
74
+ self.cache._data.forEach(v => {
41
75
  v.update();
42
76
  })
43
-
77
+
44
78
  }
45
- init(){
46
-
79
+ init() {
80
+
47
81
  const self = this;
48
82
  setIntervalAsync(() => {
49
83
  self.download();
@@ -54,7 +88,7 @@ class InstancedTileLoader {
54
88
  do {
55
89
  loaded = self.loadBatch();
56
90
  } while (loaded > 0 && (Date.now() - start) <= 0)
57
-
91
+
58
92
  }, 10);
59
93
  }
60
94
 
@@ -69,8 +103,8 @@ class InstancedTileLoader {
69
103
  if (!!nextDownload && nextDownload.shouldDoDownload()) {
70
104
  //nextDownload.doDownload();
71
105
  concurentDownloads++;
72
- if(nextDownload.path.includes(".b3dm")){
73
- fetch(nextDownload.path, {signal: nextDownload.abortController.signal}).then(result => {
106
+ if (nextDownload.path.includes(".b3dm")) {
107
+ fetch(nextDownload.path, { signal: nextDownload.abortController.signal }).then(result => {
74
108
  concurentDownloads--;
75
109
  if (!result.ok) {
76
110
  console.error("could not load tile with path : " + path)
@@ -79,17 +113,17 @@ class InstancedTileLoader {
79
113
  return result.arrayBuffer();
80
114
 
81
115
  })
82
- .then(resultArrayBuffer=>{
83
- return B3DMDecoder.parseB3DMInstanced(resultArrayBuffer, self.meshCallback, self.maxInstances, nextDownload.zUpToYUp);
84
- })
85
- .then(mesh=>{
86
- nextDownload.tile.setObject(mesh);
87
- self.ready.unshift(nextDownload);
88
-
89
- })
90
- .catch(e=>console.error(e));
91
- }if(nextDownload.path.includes(".glb") || (nextDownload.path.includes(".gltf"))){
92
- gltfLoader.load(nextDownload.path, gltf => {
116
+ .then(resultArrayBuffer => {
117
+ return this.b3dmDecoder.parseB3DMInstanced(resultArrayBuffer, self.meshCallback, self.maxInstances, nextDownload.zUpToYUp);
118
+ })
119
+ .then(mesh => {
120
+ nextDownload.tile.setObject(mesh);
121
+ self.ready.unshift(nextDownload);
122
+
123
+ })
124
+ .catch(e => console.error(e));
125
+ } if (nextDownload.path.includes(".glb") || (nextDownload.path.includes(".gltf"))) {
126
+ this.gltfLoader.load(nextDownload.path, gltf => {
93
127
  gltf.scene.traverse((o) => {
94
128
  o.geometricError = nextDownload.geometricError;
95
129
  if (o.isMesh) {
@@ -112,34 +146,34 @@ class InstancedTileLoader {
112
146
  instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, self.maxInstances);
113
147
  instancedMesh.baseMatrix = child.matrixWorld;
114
148
  }
115
-
149
+
116
150
  });
117
151
  self.ready.unshift(nextDownload);
118
- if(!instancedMesh){
119
- gltf.scene.traverse(c=>{
120
- if(c.dispose) c.dispose();
121
- if(c.material) c.material.dispose();
152
+ if (!instancedMesh) {
153
+ gltf.scene.traverse(c => {
154
+ if (c.dispose) c.dispose();
155
+ if (c.material) c.material.dispose();
122
156
  });
123
- }else{
157
+ } else {
124
158
  nextDownload.tile.setObject(instancedMesh);
125
159
  }
126
160
  });
127
-
128
- }else if(nextDownload.path.includes(".json")){
161
+
162
+ } else if (nextDownload.path.includes(".json")) {
129
163
  concurentDownloads++;
130
- fetch(nextDownload.path, {signal: nextDownload.abortController.signal}).then(result => {
164
+ fetch(nextDownload.path, { signal: nextDownload.abortController.signal }).then(result => {
131
165
  concurentDownloads--;
132
166
  if (!result.ok) {
133
167
  console.error("could not load tile with path : " + path)
134
168
  throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
135
169
  }
136
170
  return result.json();
137
-
171
+
138
172
  }).then(json => {
139
173
  nextDownload.tile.setObject(json, nextDownload.path);
140
174
  self.ready.unshift(nextDownload);
141
175
  })
142
- .catch(e=>console.error(e))
176
+ .catch(e => console.error(e))
143
177
  }
144
178
  }
145
179
  }
@@ -153,8 +187,8 @@ class InstancedTileLoader {
153
187
  }
154
188
  const download = this.nextReady.shift();
155
189
  if (!download) return 0;
156
-
157
- if(!!download.tile.addToScene)download.tile.addToScene();
190
+
191
+ if (!!download.tile.addToScene) download.tile.addToScene();
158
192
  return 1;
159
193
  }
160
194
 
@@ -162,7 +196,7 @@ class InstancedTileLoader {
162
196
  let smallestDistance = Number.MAX_VALUE;
163
197
  let closest = -1;
164
198
  for (let i = this.ready.length - 1; i >= 0; i--) {
165
-
199
+
166
200
  if (!this.ready[i].distanceFunction) {// if no distance function, must be a json, give absolute priority!
167
201
  this.nextReady.push(this.ready.splice(i, 1)[0]);
168
202
  }
@@ -205,7 +239,7 @@ class InstancedTileLoader {
205
239
  if (path.includes(".b3dm") || path.includes(".glb") || path.includes(".gltf")) {
206
240
  const tile = new MeshTile(self.scene);
207
241
  tile.addInstance(instancedOGC3DTile);
208
-
242
+
209
243
  self.cache.put(key, tile);
210
244
 
211
245
  const realAbortController = new AbortController();
@@ -229,7 +263,7 @@ class InstancedTileLoader {
229
263
  return true;
230
264
  },
231
265
  })
232
- }else if(path.includes(".json")){
266
+ } else if (path.includes(".json")) {
233
267
  const tile = new JsonTile();
234
268
  tile.addInstance(instancedOGC3DTile);
235
269
  self.cache.put(key, tile);
@@ -258,7 +292,7 @@ class InstancedTileLoader {
258
292
  }
259
293
 
260
294
 
261
-
295
+
262
296
  getNextDownloads() {
263
297
  let smallestDistance = Number.MAX_VALUE;
264
298
  let closest = -1;
@@ -267,7 +301,7 @@ class InstancedTileLoader {
267
301
  if (!download.shouldDoDownload()) {
268
302
  this.downloads.splice(i, 1);
269
303
  continue;
270
- }
304
+ }
271
305
  if (!download.distanceFunction) { // if no distance function, must be a json, give absolute priority!
272
306
  this.nextDownloads.push(this.downloads.splice(i, 1)[0]);
273
307
  }
@@ -275,7 +309,7 @@ class InstancedTileLoader {
275
309
  if (this.nextDownloads.length > 0) return;
276
310
  for (let i = this.downloads.length - 1; i >= 0; i--) {
277
311
  const download = this.downloads[i];
278
- const dist = download.distanceFunction()*download.level;
312
+ const dist = download.distanceFunction() * download.level;
279
313
  if (dist < smallestDistance) {
280
314
  smallestDistance = dist;
281
315
  closest = i;
@@ -301,12 +335,12 @@ class InstancedTileLoader {
301
335
  while (self.cache.size() > self.maxCachedItems && i < self.cache.size()) {
302
336
  i++;
303
337
  const entry = self.cache.head();
304
- if(entry.value.getCount()>0){
338
+ if (entry.value.getCount() > 0) {
305
339
  self.cache.remove(entry.key);
306
340
  self.cache.put(entry.key, entry.value);
307
- }else{
341
+ } else {
308
342
  self.cache.remove(entry.key);
309
- if(entry.value.instancedMesh){
343
+ if (entry.value.instancedMesh) {
310
344
  entry.value.instancedMesh.traverse((o) => {
311
345
  if (o.material) {
312
346
  // dispose materials