@jdultra/threedtiles 4.0.1 → 4.0.3
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
|
@@ -10,6 +10,8 @@ The fastest 3DTiles viewer for three.js
|
|
|
10
10
|
|
|
11
11
|
[Occlusion culling (IFC conversion)](https://www.jdultra.com/occlusion/index.html)
|
|
12
12
|
|
|
13
|
+
[Instanced Tileset (pilot a swarm of highly detailed spaceships)](https://www.jdultra.com/instanced/index.html)
|
|
14
|
+
|
|
13
15
|
Adding a tileset to a scene is as easy as :
|
|
14
16
|
|
|
15
17
|
```
|
|
@@ -42,6 +44,7 @@ Currently, the library is limmited to B3DM files.
|
|
|
42
44
|
- Share a cache between tileset instances
|
|
43
45
|
- Optimal tile load order
|
|
44
46
|
- Occlusion culling
|
|
47
|
+
- Instanced tilesets
|
|
45
48
|
|
|
46
49
|
### geometric Error Multiplier
|
|
47
50
|
The geometric error multiplier allows you to multiply the geometric error by a factor.
|
|
@@ -173,6 +176,10 @@ const occlusionCullingService = new OcclusionCullingService();
|
|
|
173
176
|
occlusionCullingService.setSide(THREE.DoubleSide);
|
|
174
177
|
```
|
|
175
178
|
|
|
179
|
+
### Instanced Tilesets
|
|
180
|
+
Using InstancedTileLoader and InstancedOGC3DTile allows displaying the same Tileset at many different places with little impact on performance.
|
|
181
|
+
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
|
+
higher performance when displaying the same Tileset many times.
|
|
176
183
|
|
|
177
184
|
### static tilesets and other performance tips
|
|
178
185
|
When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
|
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="
|
|
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
package/src/index.js
CHANGED
|
@@ -58,21 +58,21 @@ animate();
|
|
|
58
58
|
|
|
59
59
|
function initComposer(scene, camera, renderer) {
|
|
60
60
|
const renderScene = new RenderPass(scene, camera);
|
|
61
|
-
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.4, 0.5, 0);
|
|
61
|
+
//const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.4, 0.5, 0);
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
const composer = new EffectComposer(renderer);
|
|
65
65
|
composer.addPass(renderScene);
|
|
66
|
-
composer.addPass(bloomPass);
|
|
66
|
+
//composer.addPass(bloomPass);
|
|
67
67
|
return composer;
|
|
68
68
|
}
|
|
69
69
|
function initScene() {
|
|
70
70
|
const scene = new THREE.Scene();
|
|
71
71
|
scene.matrixAutoUpdate = false;
|
|
72
72
|
scene.background = new THREE.Color(0x000000);
|
|
73
|
-
scene.add(new THREE.AmbientLight(0xFFFFFF, 0
|
|
73
|
+
scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
|
|
74
74
|
|
|
75
|
-
const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
75
|
+
/*const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
76
76
|
const sphere = new THREE.SphereGeometry(2, 16, 8);
|
|
77
77
|
light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
|
|
78
78
|
scene.add(light);
|
|
@@ -83,7 +83,7 @@ function initScene() {
|
|
|
83
83
|
const sphere2 = new THREE.SphereGeometry(2, 16, 8);
|
|
84
84
|
light2.add(new THREE.Mesh(sphere2, new THREE.MeshBasicMaterial({ color: 0xffbbbb })));
|
|
85
85
|
scene.add(light2);
|
|
86
|
-
light2.position.set(200, 100, -100)
|
|
86
|
+
light2.position.set(200, 100, -100);*/
|
|
87
87
|
|
|
88
88
|
scene.matrixWorldAutoUpdate = true;
|
|
89
89
|
return scene;
|
|
@@ -132,8 +132,8 @@ function initStats(dom) {
|
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
function initCamera() {
|
|
135
|
-
const camera = new THREE.PerspectiveCamera(
|
|
136
|
-
camera.position.set(
|
|
135
|
+
const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 1.0, 100000);
|
|
136
|
+
camera.position.set(-800, 800, 800);
|
|
137
137
|
camera.matrixAutoUpdate = true;
|
|
138
138
|
return camera;
|
|
139
139
|
}
|
|
@@ -143,12 +143,14 @@ function initTileset(scene) {
|
|
|
143
143
|
const tileLoader = new TileLoader(mesh => {
|
|
144
144
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
145
145
|
mesh.material.wireframe = false;
|
|
146
|
-
mesh.material.side = THREE.
|
|
146
|
+
mesh.material.side = THREE.DoubleSide;
|
|
147
147
|
}, 1000)
|
|
148
148
|
const ogc3DTile = new OGC3DTile({
|
|
149
|
-
url: "
|
|
150
|
-
//url: "https://storage.googleapis.com/ogc-3d-tiles/
|
|
151
|
-
|
|
149
|
+
//url: "http://localhost:8080/tileset.json",
|
|
150
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
151
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
152
|
+
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
|
|
153
|
+
geometricErrorMultiplier: 0.01,
|
|
152
154
|
loadOutsideView: false,
|
|
153
155
|
tileLoader: tileLoader,
|
|
154
156
|
//occlusionCullingService: occlusionCullingService,
|
|
@@ -160,7 +162,8 @@ function initTileset(scene) {
|
|
|
160
162
|
|
|
161
163
|
|
|
162
164
|
//// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
|
|
163
|
-
|
|
165
|
+
ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
166
|
+
//ogc3DTile.scale.set(10.0,10.0,10.0)
|
|
164
167
|
//// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
|
|
165
168
|
|
|
166
169
|
|
|
@@ -205,28 +208,29 @@ function createInstancedTileLoader(scene) {
|
|
|
205
208
|
return new InstancedTileLoader(scene, mesh => {
|
|
206
209
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
207
210
|
mesh.material.wireframe = false;
|
|
208
|
-
mesh.material.side = THREE.
|
|
209
|
-
}, 1000,
|
|
211
|
+
mesh.material.side = THREE.DoubleSide;
|
|
212
|
+
}, 1000, 125);
|
|
210
213
|
}
|
|
211
214
|
function initInstancedTilesets(instancedTileLoader) {
|
|
212
215
|
|
|
213
216
|
const instancedTilesets = [];
|
|
214
217
|
|
|
215
|
-
for (let x = 0; x <
|
|
216
|
-
for (let y = 0; y <
|
|
217
|
-
for (let z = 0; z <
|
|
218
|
+
for (let x = 0; x < 5; x++) {
|
|
219
|
+
for (let y = 0; y < 5; y++) {
|
|
220
|
+
for (let z = 0; z < 5; z++) {
|
|
218
221
|
const tileset = new InstancedOGC3DTile({
|
|
219
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
222
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
223
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
220
224
|
//url: "http://localhost:8080/tileset.json",
|
|
221
|
-
geometricErrorMultiplier: 0.
|
|
222
|
-
loadOutsideView:
|
|
225
|
+
geometricErrorMultiplier: 0.001,
|
|
226
|
+
loadOutsideView: true,
|
|
223
227
|
tileLoader: instancedTileLoader,
|
|
224
228
|
static: false,
|
|
225
229
|
});
|
|
226
|
-
tileset.rotateOnAxis(new THREE.Vector3(
|
|
227
|
-
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0),
|
|
228
|
-
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0),
|
|
229
|
-
tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
|
|
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), 3500 * x)
|
|
232
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 3500 * y)
|
|
233
|
+
//tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
|
|
230
234
|
scene.add(tileset);
|
|
231
235
|
instancedTilesets.push(tileset);
|
|
232
236
|
|
|
@@ -234,19 +238,19 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
234
238
|
}
|
|
235
239
|
}
|
|
236
240
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
function idleCallback() {
|
|
241
|
-
instancedTilesets.forEach(tileset=>{
|
|
242
|
-
tileset.update(camera);
|
|
243
|
-
})
|
|
244
|
-
setTimeout(() => {
|
|
245
|
-
window.requestIdleCallback(idleCallback, { timeout: 50 })
|
|
246
|
-
}, 20)
|
|
247
|
-
|
|
241
|
+
function now() {
|
|
242
|
+
return (typeof performance === 'undefined' ? Date : performance).now(); // see #10732
|
|
248
243
|
}
|
|
249
|
-
|
|
244
|
+
let updateIndex = 0;
|
|
245
|
+
setInterval(() => {
|
|
246
|
+
let startTime = now();
|
|
247
|
+
do{
|
|
248
|
+
instancedTilesets[updateIndex].update(camera);
|
|
249
|
+
updateIndex= (updateIndex+1)%instancedTilesets.length;
|
|
250
|
+
}while(updateIndex < instancedTilesets.length && now()-startTime<4);
|
|
251
|
+
},50);
|
|
252
|
+
|
|
253
|
+
//initLODMultiplierSlider(instancedTilesets);
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
function initLODMultiplierSlider(instancedTilesets) {
|
|
@@ -256,7 +260,7 @@ function initLODMultiplierSlider(instancedTilesets) {
|
|
|
256
260
|
|
|
257
261
|
slider.oninput = () => {
|
|
258
262
|
instancedTilesets.forEach(tileset => {
|
|
259
|
-
tileset.setGeometricErrorMultiplier(slider.value)
|
|
263
|
+
tileset.setGeometricErrorMultiplier(slider.value*0.1)
|
|
260
264
|
})
|
|
261
265
|
output.innerHTML = slider.value;
|
|
262
266
|
}
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -530,7 +530,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
530
530
|
return 0;
|
|
531
531
|
}
|
|
532
532
|
const scale = this.matrixWorld.getMaxScaleOnAxis();
|
|
533
|
-
return
|
|
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
|
|
@@ -81,7 +81,6 @@ class TileLoader {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
getNextDownloads() {
|
|
84
|
-
let smallestLevel = Number.MAX_VALUE;
|
|
85
84
|
let smallestDistance = Number.MAX_VALUE;
|
|
86
85
|
let closest = -1;
|
|
87
86
|
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
@@ -95,13 +94,10 @@ class TileLoader {
|
|
|
95
94
|
}
|
|
96
95
|
if (this.nextDownloads.length > 0) return;
|
|
97
96
|
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
98
|
-
const dist = this.downloads[i].distanceFunction();
|
|
97
|
+
const dist = this.downloads[i].distanceFunction()*this.downloads[i].level;
|
|
99
98
|
if (dist < smallestDistance) {
|
|
100
99
|
smallestDistance = dist;
|
|
101
100
|
closest = i;
|
|
102
|
-
} else if (dist == smallestDistance && this.downloads[i].level < smallestLevel) {
|
|
103
|
-
smallestLevel = this.downloads[i].level;
|
|
104
|
-
closest = i
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
103
|
if (closest >= 0) {
|
|
@@ -117,7 +113,6 @@ class TileLoader {
|
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
getNextReady() {
|
|
120
|
-
let smallestLevel = Number.MAX_VALUE;
|
|
121
116
|
let smallestDistance = Number.MAX_VALUE;
|
|
122
117
|
let closest = -1;
|
|
123
118
|
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
@@ -128,13 +123,9 @@ class TileLoader {
|
|
|
128
123
|
}
|
|
129
124
|
if (this.nextReady.length > 0) return;
|
|
130
125
|
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
131
|
-
const dist = this.ready[i][3]();
|
|
126
|
+
const dist = this.ready[i][3]() * this.ready[i][5];
|
|
132
127
|
if (dist < smallestDistance) {
|
|
133
128
|
smallestDistance = dist;
|
|
134
|
-
smallestLevel = this.ready[i][5]
|
|
135
|
-
closest = i
|
|
136
|
-
} else if (dist == smallestDistance && this.ready[i][5] < smallestLevel) {
|
|
137
|
-
smallestLevel = this.ready[i][5]
|
|
138
129
|
closest = i
|
|
139
130
|
}
|
|
140
131
|
}
|
|
@@ -457,7 +457,8 @@ class InstancedTile extends THREE.Object3D {
|
|
|
457
457
|
return 0;
|
|
458
458
|
}
|
|
459
459
|
const scale = this.master.matrixWorld.getMaxScaleOnAxis();
|
|
460
|
-
return (((distance / Math.pow(scale, 2)) / 100) / this.master.geometricErrorMultiplier);
|
|
460
|
+
//return (((distance / Math.pow(scale, 2)) / 100) / this.master.geometricErrorMultiplier);
|
|
461
|
+
return Math.pow(distance, 2) /(this.master.geometricErrorMultiplier*this.geometricError*Math.pow(scale,2.0)*35);
|
|
461
462
|
} else if (this.boundingVolume instanceof THREE.Box3) {
|
|
462
463
|
// Region
|
|
463
464
|
// Region not supported
|
|
@@ -110,7 +110,6 @@ class InstancedTileLoader {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
getNextReady() {
|
|
113
|
-
let smallestLevel = Number.MAX_VALUE;
|
|
114
113
|
let smallestDistance = Number.MAX_VALUE;
|
|
115
114
|
let closest = -1;
|
|
116
115
|
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
@@ -121,13 +120,9 @@ class InstancedTileLoader {
|
|
|
121
120
|
}
|
|
122
121
|
if (this.nextReady.length > 0) return;
|
|
123
122
|
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
124
|
-
const dist = this.ready[i].distanceFunction();
|
|
123
|
+
const dist = this.ready[i].distanceFunction() * this.ready[i].level;
|
|
125
124
|
if (dist < smallestDistance) {
|
|
126
125
|
smallestDistance = dist;
|
|
127
|
-
smallestLevel = this.ready[i].level
|
|
128
|
-
closest = i
|
|
129
|
-
} else if (dist == smallestDistance && this.ready[i].level < smallestLevel) {
|
|
130
|
-
smallestLevel = this.ready[i].level
|
|
131
126
|
closest = i
|
|
132
127
|
}
|
|
133
128
|
}
|
|
@@ -216,7 +211,6 @@ class InstancedTileLoader {
|
|
|
216
211
|
|
|
217
212
|
|
|
218
213
|
getNextDownloads() {
|
|
219
|
-
let smallestLevel = Number.MAX_VALUE;
|
|
220
214
|
let smallestDistance = Number.MAX_VALUE;
|
|
221
215
|
let closest = -1;
|
|
222
216
|
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
@@ -232,13 +226,10 @@ class InstancedTileLoader {
|
|
|
232
226
|
if (this.nextDownloads.length > 0) return;
|
|
233
227
|
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
234
228
|
const download = this.downloads[i];
|
|
235
|
-
const dist = download.distanceFunction();
|
|
229
|
+
const dist = download.distanceFunction()*download.level;
|
|
236
230
|
if (dist < smallestDistance) {
|
|
237
231
|
smallestDistance = dist;
|
|
238
232
|
closest = i;
|
|
239
|
-
} else if (dist == smallestDistance && download.level < smallestLevel) {
|
|
240
|
-
smallestLevel = download.level;
|
|
241
|
-
closest = i
|
|
242
233
|
}
|
|
243
234
|
}
|
|
244
235
|
if (closest >= 0) {
|