@jdultra/threedtiles 4.0.2 → 4.0.4

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/README.md CHANGED
@@ -177,10 +177,60 @@ occlusionCullingService.setSide(THREE.DoubleSide);
177
177
  ```
178
178
 
179
179
  ### Instanced Tilesets
180
+
181
+ <p align="center">
182
+ <img src="https://storage.googleapis.com/jdultra-website/assets/instancedPic.png" width="800" style="display: block; margin: 0 auto"/>
183
+ </p>
184
+
180
185
  Using InstancedTileLoader and InstancedOGC3DTile allows displaying the same Tileset at many different places with little impact on performance.
181
186
  Each Tileset is independent in terms of it's position, orientation and level of detail but each tile is created as an "InstancedMesh" giving much
182
187
  higher performance when displaying the same Tileset many times.
183
188
 
189
+ ```
190
+ // First create the InstancedTileLoader that will manage caching
191
+ const instancedTileLoader = new InstancedTileLoader(scene, mesh => {
192
+ //// Insert code to be called on every newly decoded mesh e.g.:
193
+ mesh.material.wireframe = false;
194
+ mesh.material.side = THREE.DoubleSide;
195
+ },
196
+ 1000, // cache size as in the number of tiles cached in memory
197
+ 100, // max number of tilesets from the same source
198
+ );
199
+
200
+ // then create some tilesets
201
+ const instancedTilesets = [];
202
+ for (let i = 0; i < 100; i++) {
203
+ const tileset = new InstancedOGC3DTile({
204
+ url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
205
+ geometricErrorMultiplier: 1.0,
206
+ loadOutsideView: false,
207
+ tileLoader: instancedTileLoader,
208
+ static: true // when static is set to true, don't forget to call InstancedOGC3DTile#updateMatrix manually
209
+ });
210
+
211
+ tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * i);
212
+ tileset.updateMatrix();
213
+ scene.add(tileset);
214
+ instancedTilesets.push(tileset);
215
+ }
216
+
217
+ //setup an update loop for the LODs
218
+ setInterval(() => {
219
+ instancedTilesets[updateIndex].update(camera);
220
+ updateIndex= (updateIndex+1)%instancedTilesets.length;
221
+ },50);
222
+
223
+ //in the animate function, you also need to update the instancedTileLoader
224
+ function animate() {
225
+ requestAnimationFrame(animate);
226
+ instancedTileLoader.update();
227
+
228
+ ... // rest of render loop
229
+ }
230
+ animate();
231
+
232
+ ```
233
+
184
234
  ### static tilesets and other performance tips
185
235
  When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
186
236
  This will skip recalculating the transformation matrix of every tile each frame and give a few extra frames per second.
@@ -192,16 +242,50 @@ const ogc3DTile = new OGC3DTile({
192
242
  });
193
243
  ```
194
244
 
195
- Either way, it's advised to set the autoUpdate property of the scene to false and update the matrices manually whenever you move things around.
245
+ Either way, it's advised to set the autoUpdate property of the scene to false and call Scene#updateMatrixWorld manually whenever you move things around.
246
+
247
+ ```
248
+ scene.matrixAutoUpdate = false;
249
+ scene.matrixWorldAutoUpdate = false;
250
+
251
+ // and when objects move:
252
+ scene.updateMatrixWorld(true);
196
253
 
197
254
  ```
198
- scene.autoUpdate = false;
255
+ #### tileset update loop
256
+ Updating a single tileset via OGC3DTile#update or InstancedOGC3DTile#update is quite fast, even when the tree is deep.
257
+ For a single tileset, it's safe to call it regularly with a setInterval:
258
+ ```
259
+ function startInterval() {
260
+ interval = setIntervalAsync(function () {
261
+ ogc3DTile.update(camera);
262
+ }, 20);
263
+ }
199
264
  ```
200
265
 
266
+ However, with instancedTilesets, you may have hundreds or even thousands of LOD trees that need to be updated individually. In order to preserve frame-rate,
267
+ you may want to implement something a little smarter that yields the CPU to the render loop. In the example below, the process tries to update as many tilesets as it can in under 4 ms.
268
+
269
+ ```
270
+ function now() {
271
+ return (typeof performance === 'undefined' ? Date : performance).now();
272
+ }
273
+ let updateIndex = 0;
274
+ setInterval(() => {
275
+ let startTime = now();
276
+ do{
277
+ instancedTilesets[updateIndex].update(camera);
278
+ updateIndex= (updateIndex+1)%instancedTilesets.length;
279
+ }while(updateIndex < instancedTilesets.length && now()-startTime<4);
280
+ },50);
281
+ ```
282
+
283
+ window#requestIdleCallback is also a good option but the rate of updates becomes slightly unpredictable.
284
+
201
285
  # Projects that use this library
202
286
  https://github.com/ebeaufay/UltraGlobe allows displaying a globe with multi resolution imagery, elevation and 3DTiles.
203
287
 
204
- Don't hesitate to tell me if you have a project that stems from this code. I'd love to link to it here and I'm always open to implementing extra features.
288
+ If you have a project that stems from this code. I'd love to link to it here and I'm always open to implementing extra features.
205
289
  Contact: emeric.beaufays@jdultra.com
206
290
 
207
291
 
@@ -209,5 +293,5 @@ Contact: emeric.beaufays@jdultra.com
209
293
 
210
294
  I also have code to convert meshes to 3DTiles with no limit to the size of the dataset relative to faces or textures.
211
295
  It works for all types of meshes: photogrametry, BIM, colored or textured meshes with a single texture atlas or many individual textures.
212
- I'm keeping the code private for now but feel free to contact me about it.
296
+ The code is not open source but feel free to contact me for a trial.
213
297
  Contact: emeric.beaufays@jdultra.com
package/index.html CHANGED
@@ -47,10 +47,10 @@
47
47
 
48
48
  <body>
49
49
  <div id="screen"></div>
50
- <div style="position: absolute; top: 1%; z-index: 100; right:1%; ">
51
- <input type="range" min="0.0" max="1.0" value="1.0", step="0.001" class="slider" id="lodMultiplier" >
50
+ <!-- <div style="position: absolute; top: 1%; z-index: 100; right:1%; ">
51
+ <input type="range" min="0.0" max="1.0" value="0.5", step="0.001" class="slider" id="lodMultiplier" >
52
52
  <p style="color: #0439aa;">LOD multiplier: <span id="multiplierValue"></span></p>
53
- </div>
53
+ </div> -->
54
54
  </body>
55
55
 
56
56
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -69,10 +69,11 @@ function initComposer(scene, camera, renderer) {
69
69
  function initScene() {
70
70
  const scene = new THREE.Scene();
71
71
  scene.matrixAutoUpdate = false;
72
+ scene.matrixWorldAutoUpdate = false;
72
73
  scene.background = new THREE.Color(0x000000);
73
- scene.add(new THREE.AmbientLight(0xFFFFFF, 0.2));
74
+ scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
74
75
 
75
- const light = new THREE.PointLight(0xbbbbff, 2, 5000);
76
+ /*const light = new THREE.PointLight(0xbbbbff, 2, 5000);
76
77
  const sphere = new THREE.SphereGeometry(2, 16, 8);
77
78
  light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
78
79
  scene.add(light);
@@ -83,9 +84,9 @@ function initScene() {
83
84
  const sphere2 = new THREE.SphereGeometry(2, 16, 8);
84
85
  light2.add(new THREE.Mesh(sphere2, new THREE.MeshBasicMaterial({ color: 0xffbbbb })));
85
86
  scene.add(light2);
86
- light2.position.set(200, 100, -100);
87
+ light2.position.set(200, 100, -100);*/
87
88
 
88
- scene.matrixWorldAutoUpdate = true;
89
+
89
90
  return scene;
90
91
  }
91
92
 
@@ -132,8 +133,8 @@ function initStats(dom) {
132
133
 
133
134
 
134
135
  function initCamera() {
135
- const camera = new THREE.PerspectiveCamera(40, window.offsetWidth / window.offsetHeight, 1, 100000);
136
- camera.position.set(100, 10, 100);
136
+ const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 1.0, 100000);
137
+ camera.position.set(-800, 800, 800);
137
138
  camera.matrixAutoUpdate = true;
138
139
  return camera;
139
140
  }
@@ -143,12 +144,14 @@ function initTileset(scene) {
143
144
  const tileLoader = new TileLoader(mesh => {
144
145
  //// Insert code to be called on every newly decoded mesh e.g.:
145
146
  mesh.material.wireframe = false;
146
- mesh.material.side = THREE.FrontSide;
147
+ mesh.material.side = THREE.DoubleSide;
147
148
  }, 1000)
148
149
  const ogc3DTile = new OGC3DTile({
150
+ //url: "http://localhost:8080/tileset.json",
149
151
  //url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
150
152
  url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
151
- geometricErrorMultiplier: 0.1,
153
+ //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
154
+ geometricErrorMultiplier: 0.01,
152
155
  loadOutsideView: false,
153
156
  tileLoader: tileLoader,
154
157
  //occlusionCullingService: occlusionCullingService,
@@ -161,6 +164,7 @@ function initTileset(scene) {
161
164
 
162
165
  //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
163
166
  ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
167
+ //ogc3DTile.scale.set(10.0,10.0,10.0)
164
168
  //// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
165
169
 
166
170
 
@@ -205,28 +209,30 @@ function createInstancedTileLoader(scene) {
205
209
  return new InstancedTileLoader(scene, mesh => {
206
210
  //// Insert code to be called on every newly decoded mesh e.g.:
207
211
  mesh.material.wireframe = false;
208
- mesh.material.side = THREE.FrontSide;
209
- }, 1000, 1000);
212
+ mesh.material.side = THREE.DoubleSide;
213
+ }, 1000, 3375);
210
214
  }
211
215
  function initInstancedTilesets(instancedTileLoader) {
212
216
 
213
217
  const instancedTilesets = [];
214
218
 
215
- for (let x = 0; x < 10; x++) {
216
- for (let y = 0; y < 10; y++) {
217
- for (let z = 0; z < 10; z++) {
219
+ for (let x = 0; x < 15; x++) {
220
+ for (let y = 0; y < 15; y++) {
221
+ for (let z = 0; z < 15; z++) {
218
222
  const tileset = new InstancedOGC3DTile({
219
223
  url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
224
+ //url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
220
225
  //url: "http://localhost:8080/tileset.json",
221
- geometricErrorMultiplier: 0.5,
222
- loadOutsideView: false,
226
+ geometricErrorMultiplier: 1.0,
227
+ loadOutsideView: true,
223
228
  tileLoader: instancedTileLoader,
224
- static: false,
229
+ static: true,
225
230
  });
226
- tileset.rotateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
231
+ //tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
227
232
  tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * x)
228
233
  tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 50 * y)
229
234
  tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
235
+ tileset.updateMatrix()
230
236
  scene.add(tileset);
231
237
  instancedTilesets.push(tileset);
232
238
 
@@ -235,18 +241,22 @@ function initInstancedTilesets(instancedTileLoader) {
235
241
  }
236
242
  }
237
243
 
238
- idleCallback();
239
-
240
- function idleCallback() {
241
- instancedTilesets.forEach(tileset=>{
242
- tileset.update(camera);
243
- })
244
- setTimeout(() => {
245
- window.requestIdleCallback(idleCallback, { timeout: 50 })
246
- }, 20)
247
-
244
+ scene.updateMatrixWorld(true)
245
+ function now() {
246
+ return (typeof performance === 'undefined' ? Date : performance).now();
248
247
  }
249
- initLODMultiplierSlider(instancedTilesets);
248
+ let updateIndex = 0;
249
+ setInterval(() => {
250
+ let startTime = now();
251
+ do{
252
+ const frustum = new THREE.Frustum();
253
+ frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
254
+ instancedTilesets[updateIndex].update(camera, frustum);
255
+ updateIndex= (updateIndex+1)%instancedTilesets.length;
256
+ }while(updateIndex < instancedTilesets.length && now()-startTime<4);
257
+ },40);
258
+
259
+ //initLODMultiplierSlider(instancedTilesets);
250
260
  }
251
261
 
252
262
  function initLODMultiplierSlider(instancedTilesets) {
@@ -256,7 +266,7 @@ function initLODMultiplierSlider(instancedTilesets) {
256
266
 
257
267
  slider.oninput = () => {
258
268
  instancedTilesets.forEach(tileset => {
259
- tileset.setGeometricErrorMultiplier(slider.value)
269
+ tileset.setGeometricErrorMultiplier(slider.value*0.1)
260
270
  })
261
271
  output.innerHTML = slider.value;
262
272
  }
@@ -530,7 +530,8 @@ class OGC3DTile extends THREE.Object3D {
530
530
  return 0;
531
531
  }
532
532
  const scale = this.matrixWorld.getMaxScaleOnAxis();
533
- return (((distance / Math.pow(scale, 2)) / 100) / this.geometricErrorMultiplier);
533
+ return Math.pow(distance, 2) /(this.geometricErrorMultiplier*this.geometricError*Math.pow(scale,2.0)*35);
534
+ //return (((distance / Math.pow(scale, 2)) / 100) / this.geometricErrorMultiplier);
534
535
  } else if (this.boundingVolume instanceof THREE.Box3) {
535
536
  // Region
536
537
  // Region not supported
@@ -32,9 +32,17 @@ class InstancedOGC3DTile extends THREE.Object3D {
32
32
  }
33
33
  }
34
34
 
35
- update(camera){
36
- const frustum = new THREE.Frustum();
37
- frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
35
+ update(camera, frustum){
36
+ if(!!frustum){
37
+ this.tileset._update(camera, frustum);
38
+ }else{
39
+ const frustum = new THREE.Frustum();
40
+ frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
41
+ this.tileset._update(camera, frustum);
42
+ }
43
+
44
+ }
45
+ updateWithFrustum(camera, frustum){
38
46
  this.tileset._update(camera, frustum);
39
47
  }
40
48
 
@@ -23,8 +23,7 @@ class InstancedTile extends THREE.Object3D {
23
23
  * meshCallback: function,
24
24
  * cameraOnLoad: camera,
25
25
  * parentTile: OGC3DTile,
26
- * onLoadCallback: function,
27
- * static: Boolean
26
+ * onLoadCallback: function
28
27
  * } properties
29
28
  */
30
29
  constructor(properties) {
@@ -333,7 +332,6 @@ class InstancedTile extends THREE.Object3D {
333
332
  level: self.level + 1,
334
333
  tileLoader: self.tileLoader,
335
334
  cameraOnLoad: camera,
336
- static: self.static,
337
335
  master: self.master
338
336
  });
339
337
  self.childrenTiles.push(childTile);
@@ -457,7 +455,8 @@ class InstancedTile extends THREE.Object3D {
457
455
  return 0;
458
456
  }
459
457
  const scale = this.master.matrixWorld.getMaxScaleOnAxis();
460
- return (((distance / Math.pow(scale, 2)) / 100) / this.master.geometricErrorMultiplier);
458
+ //return (((distance / Math.pow(scale, 2)) / 100) / this.master.geometricErrorMultiplier);
459
+ return Math.pow(distance, 2) /(this.master.geometricErrorMultiplier*this.geometricError*Math.pow(scale,2.0)*35);
461
460
  } else if (this.boundingVolume instanceof THREE.Box3) {
462
461
  // Region
463
462
  // Region not supported