@jdultra/threedtiles 4.0.3 → 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
296
|
+
The code is not open source but feel free to contact me for a trial.
|
|
213
297
|
Contact: emeric.beaufays@jdultra.com
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -69,6 +69,7 @@ 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
74
|
scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
|
|
74
75
|
|
|
@@ -85,7 +86,7 @@ function initScene() {
|
|
|
85
86
|
scene.add(light2);
|
|
86
87
|
light2.position.set(200, 100, -100);*/
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
|
|
89
90
|
return scene;
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -209,28 +210,29 @@ function createInstancedTileLoader(scene) {
|
|
|
209
210
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
210
211
|
mesh.material.wireframe = false;
|
|
211
212
|
mesh.material.side = THREE.DoubleSide;
|
|
212
|
-
}, 1000,
|
|
213
|
+
}, 1000, 3375);
|
|
213
214
|
}
|
|
214
215
|
function initInstancedTilesets(instancedTileLoader) {
|
|
215
216
|
|
|
216
217
|
const instancedTilesets = [];
|
|
217
218
|
|
|
218
|
-
for (let x = 0; x <
|
|
219
|
-
for (let y = 0; y <
|
|
220
|
-
for (let z = 0; 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++) {
|
|
221
222
|
const tileset = new InstancedOGC3DTile({
|
|
222
|
-
|
|
223
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
223
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
224
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
224
225
|
//url: "http://localhost:8080/tileset.json",
|
|
225
|
-
geometricErrorMultiplier: 0
|
|
226
|
+
geometricErrorMultiplier: 1.0,
|
|
226
227
|
loadOutsideView: true,
|
|
227
228
|
tileLoader: instancedTileLoader,
|
|
228
|
-
static:
|
|
229
|
+
static: true,
|
|
229
230
|
});
|
|
230
|
-
tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
231
|
-
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0),
|
|
232
|
-
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0),
|
|
233
|
-
|
|
231
|
+
//tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
232
|
+
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * x)
|
|
233
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 50 * y)
|
|
234
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
|
|
235
|
+
tileset.updateMatrix()
|
|
234
236
|
scene.add(tileset);
|
|
235
237
|
instancedTilesets.push(tileset);
|
|
236
238
|
|
|
@@ -238,17 +240,21 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
238
240
|
}
|
|
239
241
|
}
|
|
240
242
|
}
|
|
243
|
+
|
|
244
|
+
scene.updateMatrixWorld(true)
|
|
241
245
|
function now() {
|
|
242
|
-
return (typeof performance === 'undefined' ? Date : performance).now();
|
|
246
|
+
return (typeof performance === 'undefined' ? Date : performance).now();
|
|
243
247
|
}
|
|
244
248
|
let updateIndex = 0;
|
|
245
249
|
setInterval(() => {
|
|
246
250
|
let startTime = now();
|
|
247
251
|
do{
|
|
248
|
-
|
|
252
|
+
const frustum = new THREE.Frustum();
|
|
253
|
+
frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
|
|
254
|
+
instancedTilesets[updateIndex].update(camera, frustum);
|
|
249
255
|
updateIndex= (updateIndex+1)%instancedTilesets.length;
|
|
250
256
|
}while(updateIndex < instancedTilesets.length && now()-startTime<4);
|
|
251
|
-
},
|
|
257
|
+
},40);
|
|
252
258
|
|
|
253
259
|
//initLODMultiplierSlider(instancedTilesets);
|
|
254
260
|
}
|
|
@@ -32,9 +32,17 @@ class InstancedOGC3DTile extends THREE.Object3D {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
update(camera){
|
|
36
|
-
|
|
37
|
-
|
|
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);
|