@jdultra/threedtiles 4.0.3 → 5.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.
- package/README.md +88 -4
- package/package.json +1 -1
- package/src/index.js +38 -25
- package/src/tileset/OGC3DTile.js +37 -24
- package/src/tileset/instanced/InstancedOGC3DTile.js +13 -3
- package/src/tileset/instanced/InstancedTile.js +18 -10
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
|
@@ -21,7 +21,9 @@ occlusionCullingService.setSide(THREE.DoubleSide);
|
|
|
21
21
|
const scene = initScene();
|
|
22
22
|
|
|
23
23
|
const domContainer = initDomContainer("screen");
|
|
24
|
-
const camera = initCamera();
|
|
24
|
+
const camera = initCamera(domContainer.offsetWidth, domContainer.offsetHeight);
|
|
25
|
+
const stats = initStats(domContainer);
|
|
26
|
+
const renderer = initRenderer(camera, domContainer);
|
|
25
27
|
//const ogc3DTiles = initTileset(scene);
|
|
26
28
|
|
|
27
29
|
|
|
@@ -30,8 +32,6 @@ initInstancedTilesets(instancedTileLoader);
|
|
|
30
32
|
|
|
31
33
|
const controller = initController(camera, domContainer);
|
|
32
34
|
|
|
33
|
-
const stats = initStats(domContainer);
|
|
34
|
-
const renderer = initRenderer(camera, domContainer);
|
|
35
35
|
const composer = initComposer(scene, camera, renderer);
|
|
36
36
|
|
|
37
37
|
|
|
@@ -69,7 +69,8 @@ function initComposer(scene, camera, renderer) {
|
|
|
69
69
|
function initScene() {
|
|
70
70
|
const scene = new THREE.Scene();
|
|
71
71
|
scene.matrixAutoUpdate = false;
|
|
72
|
-
scene.
|
|
72
|
+
//scene.matrixWorldAutoUpdate = false;
|
|
73
|
+
scene.background = new THREE.Color(0x404040);
|
|
73
74
|
scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
|
|
74
75
|
|
|
75
76
|
/*const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
@@ -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
|
|
|
@@ -131,9 +132,10 @@ function initStats(dom) {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
|
|
134
|
-
function initCamera() {
|
|
135
|
-
const camera = new THREE.PerspectiveCamera(
|
|
136
|
-
camera.position.set(-
|
|
135
|
+
function initCamera(width, height) {
|
|
136
|
+
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
|
|
137
|
+
camera.position.set(-400.060421028462592,-14.561785966685625,700.123058268059668);
|
|
138
|
+
|
|
137
139
|
camera.matrixAutoUpdate = true;
|
|
138
140
|
return camera;
|
|
139
141
|
}
|
|
@@ -144,17 +146,22 @@ function initTileset(scene) {
|
|
|
144
146
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
145
147
|
mesh.material.wireframe = false;
|
|
146
148
|
mesh.material.side = THREE.DoubleSide;
|
|
149
|
+
mesh.material.metalness = 0.0
|
|
147
150
|
}, 1000)
|
|
148
151
|
const ogc3DTile = new OGC3DTile({
|
|
149
152
|
//url: "http://localhost:8080/tileset.json",
|
|
150
153
|
//url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
151
154
|
url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
152
155
|
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
|
|
156
|
+
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/an7opcnyije/tileset.json",
|
|
157
|
+
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
|
|
158
|
+
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/a88b3sungng/tileset.json",
|
|
153
159
|
geometricErrorMultiplier: 0.01,
|
|
154
160
|
loadOutsideView: false,
|
|
155
161
|
tileLoader: tileLoader,
|
|
156
162
|
//occlusionCullingService: occlusionCullingService,
|
|
157
163
|
static: false,
|
|
164
|
+
renderer: renderer
|
|
158
165
|
|
|
159
166
|
});
|
|
160
167
|
|
|
@@ -163,7 +170,7 @@ function initTileset(scene) {
|
|
|
163
170
|
|
|
164
171
|
//// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
|
|
165
172
|
ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
166
|
-
|
|
173
|
+
ogc3DTile.scale.set(100.0,100.0,100.0)
|
|
167
174
|
//// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
|
|
168
175
|
|
|
169
176
|
|
|
@@ -209,28 +216,30 @@ function createInstancedTileLoader(scene) {
|
|
|
209
216
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
210
217
|
mesh.material.wireframe = false;
|
|
211
218
|
mesh.material.side = THREE.DoubleSide;
|
|
212
|
-
}, 1000,
|
|
219
|
+
}, 1000, 3375);
|
|
213
220
|
}
|
|
214
221
|
function initInstancedTilesets(instancedTileLoader) {
|
|
215
222
|
|
|
216
223
|
const instancedTilesets = [];
|
|
217
224
|
|
|
218
|
-
for (let x = 0; x <
|
|
219
|
-
for (let y = 0; y <
|
|
220
|
-
for (let z = 0; z <
|
|
225
|
+
for (let x = 0; x < 15; x++) {
|
|
226
|
+
for (let y = 0; y < 15; y++) {
|
|
227
|
+
for (let z = 0; z < 15; z++) {
|
|
221
228
|
const tileset = new InstancedOGC3DTile({
|
|
222
|
-
|
|
223
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
229
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
230
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
224
231
|
//url: "http://localhost:8080/tileset.json",
|
|
225
|
-
geometricErrorMultiplier: 0
|
|
232
|
+
geometricErrorMultiplier: 1.0,
|
|
226
233
|
loadOutsideView: true,
|
|
227
234
|
tileLoader: instancedTileLoader,
|
|
228
|
-
static:
|
|
235
|
+
static: true,
|
|
236
|
+
renderer: renderer
|
|
229
237
|
});
|
|
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
|
-
|
|
238
|
+
//tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
239
|
+
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * x)
|
|
240
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 50 * y)
|
|
241
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
|
|
242
|
+
tileset.updateMatrix()
|
|
234
243
|
scene.add(tileset);
|
|
235
244
|
instancedTilesets.push(tileset);
|
|
236
245
|
|
|
@@ -238,17 +247,21 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
249
|
}
|
|
250
|
+
|
|
251
|
+
scene.updateMatrixWorld(true)
|
|
241
252
|
function now() {
|
|
242
|
-
return (typeof performance === 'undefined' ? Date : performance).now();
|
|
253
|
+
return (typeof performance === 'undefined' ? Date : performance).now();
|
|
243
254
|
}
|
|
244
255
|
let updateIndex = 0;
|
|
245
256
|
setInterval(() => {
|
|
246
257
|
let startTime = now();
|
|
247
258
|
do{
|
|
248
|
-
|
|
259
|
+
const frustum = new THREE.Frustum();
|
|
260
|
+
frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
|
|
261
|
+
instancedTilesets[updateIndex].update(camera, frustum);
|
|
249
262
|
updateIndex= (updateIndex+1)%instancedTilesets.length;
|
|
250
263
|
}while(updateIndex < instancedTilesets.length && now()-startTime<4);
|
|
251
|
-
},
|
|
264
|
+
},40);
|
|
252
265
|
|
|
253
266
|
//initLODMultiplierSlider(instancedTilesets);
|
|
254
267
|
}
|
|
@@ -269,7 +282,7 @@ function initLODMultiplierSlider(instancedTilesets) {
|
|
|
269
282
|
function initController(camera, dom) {
|
|
270
283
|
const controller = new OrbitControls(camera, dom);
|
|
271
284
|
|
|
272
|
-
controller.target.set(0,
|
|
285
|
+
controller.target.set(0,0,0);
|
|
273
286
|
controller.minDistance = 0.01;
|
|
274
287
|
controller.maxDistance = 100000;
|
|
275
288
|
controller.update();
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as path from "path-browserify"
|
|
|
6
6
|
import { clamp } from "three/src/math/MathUtils";
|
|
7
7
|
|
|
8
8
|
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
|
|
9
|
-
|
|
9
|
+
const rendererSize = new THREE.Vector2();
|
|
10
10
|
|
|
11
11
|
class OGC3DTile extends THREE.Object3D {
|
|
12
12
|
|
|
@@ -27,7 +27,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
27
27
|
* parentTile: OGC3DTile,
|
|
28
28
|
* onLoadCallback: function,
|
|
29
29
|
* occlusionCullingService: OcclusionCullingService,
|
|
30
|
-
* static: Boolean
|
|
30
|
+
* static: Boolean,
|
|
31
|
+
* renderer: Renderer
|
|
31
32
|
* } properties
|
|
32
33
|
*/
|
|
33
34
|
constructor(properties) {
|
|
@@ -43,10 +44,11 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
43
44
|
mesh.material.wireframe = false;
|
|
44
45
|
mesh.material.side = THREE.DoubleSide;
|
|
45
46
|
} : properties.meshCallback);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
}
|
|
48
|
+
// set properties general to the entire tileset
|
|
49
|
+
this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
|
|
50
|
+
|
|
51
|
+
this.renderer = properties.renderer;
|
|
50
52
|
this.meshCallback = properties.meshCallback;
|
|
51
53
|
this.loadOutsideView = properties.loadOutsideView;
|
|
52
54
|
this.cameraOnLoad = properties.cameraOnLoad;
|
|
@@ -58,7 +60,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
58
60
|
this.color.setHex(Math.random() * 0xffffff);
|
|
59
61
|
this.colorID = clamp(self.color.r * 255, 0, 255) << 16 ^ clamp(self.color.g * 255, 0, 255) << 8 ^ clamp(self.color.b * 255, 0, 255) << 0;
|
|
60
62
|
}
|
|
61
|
-
if(this.static){
|
|
63
|
+
if (this.static) {
|
|
62
64
|
this.matrixAutoUpdate = false;
|
|
63
65
|
}
|
|
64
66
|
// declare properties specific to the tile for clarity
|
|
@@ -176,7 +178,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
176
178
|
if (!!url) {
|
|
177
179
|
if (url.includes(".b3dm")) {
|
|
178
180
|
self.contentURL = url;
|
|
179
|
-
self.tileLoader.get(self.abortController,this.uuid, url, mesh => {
|
|
181
|
+
self.tileLoader.get(self.abortController, this.uuid, url, mesh => {
|
|
180
182
|
if (!!self.deleted) return;
|
|
181
183
|
mesh.traverse((o) => {
|
|
182
184
|
if (o.isMesh) {
|
|
@@ -189,13 +191,13 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
189
191
|
}
|
|
190
192
|
o.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
|
191
193
|
}
|
|
192
|
-
if(self.static){
|
|
194
|
+
if (self.static) {
|
|
193
195
|
o.matrixAutoUpdate = false;
|
|
194
196
|
}
|
|
195
197
|
//o.material.visible = false;
|
|
196
198
|
}
|
|
197
199
|
});
|
|
198
|
-
|
|
200
|
+
|
|
199
201
|
self.add(mesh);
|
|
200
202
|
self.updateWorldMatrix(false, true);
|
|
201
203
|
// mesh.layers.disable(0);
|
|
@@ -204,7 +206,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
204
206
|
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
205
207
|
}, () => self.getSiblings(), self.level);
|
|
206
208
|
} else if (url.includes(".json")) {
|
|
207
|
-
self.tileLoader.get(self.abortController,this.uuid, url, json => {
|
|
209
|
+
self.tileLoader.get(self.abortController, this.uuid, url, json => {
|
|
208
210
|
if (!!self.deleted) return;
|
|
209
211
|
if (!self.json.children) self.json.children = [];
|
|
210
212
|
json.rootPath = path.dirname(url);
|
|
@@ -273,7 +275,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
273
275
|
if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
|
|
274
276
|
return;
|
|
275
277
|
}
|
|
276
|
-
if (!self.hasMeshContent || (metric < self.geometricError && !!self.meshContent)) {
|
|
278
|
+
if (!self.hasMeshContent || (metric < self.geometricErrorMultiplier * self.geometricError && !!self.meshContent)) {
|
|
277
279
|
if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
|
|
278
280
|
loadJsonChildren();
|
|
279
281
|
return;
|
|
@@ -307,10 +309,10 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
307
309
|
}
|
|
308
310
|
|
|
309
311
|
// has children
|
|
310
|
-
if (metric >= self.geometricError) { // Ideal LOD or before ideal lod
|
|
312
|
+
if (metric >= self.geometricErrorMultiplier * self.geometricError) { // Ideal LOD or before ideal lod
|
|
311
313
|
|
|
312
314
|
self.changeContentVisibility(true);
|
|
313
|
-
} else if (metric < self.geometricError) { // Ideal LOD is past this one
|
|
315
|
+
} else if (metric < self.geometricErrorMultiplier * self.geometricError) { // Ideal LOD is past this one
|
|
314
316
|
// if children are visible and have been displayed, can be hidden
|
|
315
317
|
let allChildrenReady = true;
|
|
316
318
|
self.childrenTiles.every(child => {
|
|
@@ -345,7 +347,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
345
347
|
updateNodeVisibility(metric);
|
|
346
348
|
return;
|
|
347
349
|
}
|
|
348
|
-
if (metric >= self.geometricError) {
|
|
350
|
+
if (metric >= self.geometricErrorMultiplier * self.geometricError) {
|
|
349
351
|
self.disposeChildren();
|
|
350
352
|
updateNodeVisibility();
|
|
351
353
|
return;
|
|
@@ -367,7 +369,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
367
369
|
level: self.level + 1,
|
|
368
370
|
tileLoader: self.tileLoader,
|
|
369
371
|
cameraOnLoad: camera,
|
|
370
|
-
occlusionCullingService:self.occlusionCullingService,
|
|
372
|
+
occlusionCullingService: self.occlusionCullingService,
|
|
373
|
+
renderer: self.renderer,
|
|
371
374
|
static: self.static
|
|
372
375
|
});
|
|
373
376
|
self.childrenTiles.push(childTile);
|
|
@@ -382,11 +385,11 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
382
385
|
const self = this;
|
|
383
386
|
this.childrenTiles.every(child => {
|
|
384
387
|
if (child.hasMeshContent) {
|
|
385
|
-
if(child.childrenTiles.length>0){
|
|
388
|
+
if (child.childrenTiles.length > 0) {
|
|
386
389
|
allLoadedAndHidden = false;
|
|
387
390
|
return false;
|
|
388
391
|
}
|
|
389
|
-
if (!child.inFrustum
|
|
392
|
+
if (!child.inFrustum) {
|
|
390
393
|
return true;
|
|
391
394
|
};
|
|
392
395
|
if (!child.materialVisibility || child.meshesToDisplay != child.meshesDisplayed) {
|
|
@@ -459,15 +462,15 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
459
462
|
|
|
460
463
|
changeContentVisibility(visibility) {
|
|
461
464
|
const self = this;
|
|
462
|
-
if(self.hasMeshContent && self.meshContent){
|
|
463
|
-
if(visibility){
|
|
464
|
-
|
|
465
|
+
if (self.hasMeshContent && self.meshContent) {
|
|
466
|
+
if (visibility) {
|
|
467
|
+
|
|
465
468
|
self.meshContent.traverse((o) => {
|
|
466
469
|
if (o.isMesh) {
|
|
467
470
|
o.layers.enable(0);
|
|
468
471
|
}
|
|
469
472
|
});
|
|
470
|
-
}else{
|
|
473
|
+
} else {
|
|
471
474
|
self.meshContent.traverse((o) => {
|
|
472
475
|
if (o.isMesh) {
|
|
473
476
|
o.layers.disable(0);
|
|
@@ -530,8 +533,18 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
530
533
|
return 0;
|
|
531
534
|
}
|
|
532
535
|
const scale = this.matrixWorld.getMaxScaleOnAxis();
|
|
533
|
-
|
|
534
|
-
|
|
536
|
+
this.renderer.getDrawingBufferSize(rendererSize);
|
|
537
|
+
let s = rendererSize.y;
|
|
538
|
+
let fov = camera.fov;
|
|
539
|
+
if(camera.aspect < 1){
|
|
540
|
+
fov *= camera.aspect;
|
|
541
|
+
s = rendererSize.x;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
|
|
545
|
+
|
|
546
|
+
return (window.devicePixelRatio * 16 * lambda) / (s * scale);
|
|
547
|
+
|
|
535
548
|
} else if (this.boundingVolume instanceof THREE.Box3) {
|
|
536
549
|
// Region
|
|
537
550
|
// Region not supported
|
|
@@ -19,12 +19,14 @@ class InstancedOGC3DTile extends THREE.Object3D {
|
|
|
19
19
|
* cameraOnLoad: camera,
|
|
20
20
|
* parentTile: OGC3DTile,
|
|
21
21
|
* onLoadCallback: function,
|
|
22
|
+
* renderer: Renderer
|
|
22
23
|
* static: Boolean
|
|
23
24
|
* } properties
|
|
24
25
|
*/
|
|
25
26
|
constructor(properties) {
|
|
26
27
|
super();
|
|
27
28
|
properties.master = this;
|
|
29
|
+
this.renderer = properties.renderer;
|
|
28
30
|
this.geometricErrorMultiplier = properties.geometricErrorMultiplier? properties.geometricErrorMultiplier:1.0;
|
|
29
31
|
this.tileset = new InstancedTile(properties);
|
|
30
32
|
if (properties.static) {
|
|
@@ -32,9 +34,17 @@ class InstancedOGC3DTile extends THREE.Object3D {
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
update(camera){
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
update(camera, frustum){
|
|
38
|
+
if(!!frustum){
|
|
39
|
+
this.tileset._update(camera, frustum);
|
|
40
|
+
}else{
|
|
41
|
+
const frustum = new THREE.Frustum();
|
|
42
|
+
frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
|
|
43
|
+
this.tileset._update(camera, frustum);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
updateWithFrustum(camera, frustum){
|
|
38
48
|
this.tileset._update(camera, frustum);
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -5,7 +5,7 @@ import * as path from "path-browserify";
|
|
|
5
5
|
import * as _ from "lodash";
|
|
6
6
|
|
|
7
7
|
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
|
|
8
|
-
|
|
8
|
+
const rendererSize = new THREE.Vector2();
|
|
9
9
|
|
|
10
10
|
class InstancedTile extends THREE.Object3D {
|
|
11
11
|
|
|
@@ -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) {
|
|
@@ -251,7 +250,7 @@ class InstancedTile extends THREE.Object3D {
|
|
|
251
250
|
// If this tile does not have mesh content but it has children
|
|
252
251
|
if (metric < 0 && self.hasMeshContent) return;
|
|
253
252
|
|
|
254
|
-
if ((!self.hasMeshContent && self.rootPath) || (metric < self.geometricError && !!self.meshContent)) {
|
|
253
|
+
if ((!self.hasMeshContent && self.rootPath) || (metric < self.master.geometricErrorMultiplier * self.geometricError && !!self.meshContent)) {
|
|
255
254
|
if (!!self.json && !!self.jsonChildren && self.childrenTiles.length != self.jsonChildren.length) {
|
|
256
255
|
loadJsonChildren();
|
|
257
256
|
return;
|
|
@@ -285,10 +284,10 @@ class InstancedTile extends THREE.Object3D {
|
|
|
285
284
|
}
|
|
286
285
|
|
|
287
286
|
// has children
|
|
288
|
-
if (metric >= self.geometricError) { // Ideal LOD or before ideal lod
|
|
287
|
+
if (metric >= self.master.geometricErrorMultiplier * self.geometricError) { // Ideal LOD or before ideal lod
|
|
289
288
|
|
|
290
289
|
self.changeContentVisibility(true);
|
|
291
|
-
} else if (metric < self.geometricError) { // Ideal LOD is past this one
|
|
290
|
+
} else if (metric < self.master.geometricErrorMultiplier * self.geometricError) { // Ideal LOD is past this one
|
|
292
291
|
// if children are visible and have been displayed, can be hidden
|
|
293
292
|
let allChildrenReady = true;
|
|
294
293
|
self.childrenTiles.every(child => {
|
|
@@ -312,7 +311,7 @@ class InstancedTile extends THREE.Object3D {
|
|
|
312
311
|
updateNodeVisibility(metric);
|
|
313
312
|
return;
|
|
314
313
|
}
|
|
315
|
-
if (metric >= self.geometricError) {
|
|
314
|
+
if (metric >= self.master.geometricErrorMultiplier * self.geometricError) {
|
|
316
315
|
self.disposeChildren();
|
|
317
316
|
updateNodeVisibility();
|
|
318
317
|
return;
|
|
@@ -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,8 +455,18 @@ class InstancedTile extends THREE.Object3D {
|
|
|
457
455
|
return 0;
|
|
458
456
|
}
|
|
459
457
|
const scale = this.master.matrixWorld.getMaxScaleOnAxis();
|
|
460
|
-
|
|
461
|
-
|
|
458
|
+
|
|
459
|
+
this.master.renderer.getDrawingBufferSize(rendererSize);
|
|
460
|
+
let s = rendererSize.y;
|
|
461
|
+
let fov = camera.fov;
|
|
462
|
+
if(camera.aspect < 1){
|
|
463
|
+
fov *= camera.aspect;
|
|
464
|
+
s = rendererSize.x;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
|
|
468
|
+
|
|
469
|
+
return (window.devicePixelRatio * 16 * lambda) / (s * scale);
|
|
462
470
|
} else if (this.boundingVolume instanceof THREE.Box3) {
|
|
463
471
|
// Region
|
|
464
472
|
// Region not supported
|