@jdultra/threedtiles 3.1.5 → 3.3.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 CHANGED
@@ -1,8 +1,12 @@
1
- # 3DTilesViewer
1
+ # threedtiles
2
2
 
3
3
  3DTiles viewer for three.js
4
4
 
5
- demo : https://ebeaufay.github.io/ThreedTilesViewer.github.io/
5
+ Photogrametry : https://ebeaufay.github.io/ThreedTilesViewer.github.io/
6
+
7
+ IFC : https://storage.googleapis.com/jdultra.com/ifc/index.html
8
+
9
+ Occlusion culling : [https://storage.googleapis.com/jdultra.com/occlusionCulling/index.html](https://storage.googleapis.com/www.jdultra.com/occlusion/index.html)
6
10
 
7
11
  Adding a tileset to a scene is as easy as :
8
12
 
@@ -35,6 +39,7 @@ Currently, the library is limmited to B3DM files.
35
39
  - Optionally load low detail tiles outside of view frustum for correct shadows and basic mesh present when the camera moves quickly.
36
40
  - Share a cache between tileset instances
37
41
  - Optimal tile load order
42
+ - Occlusion culling (demo)
38
43
 
39
44
  ### geometric Error Multiplier
40
45
  The geometric error multiplier allows you to multiply the geometric error by a factor.
@@ -112,6 +117,52 @@ const ogc3DTile = new OGC3DTile({
112
117
  ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
113
118
  ogc3DTile.rotateOnAxis(new THREE.Vector3(1,0,0), -Math.PI*0.5);
114
119
  ...
120
+
121
+
122
+ ### Occlusion culling
123
+ Occlusion culling prevents the refinment of data that is hidden by other data, like a wall. It can have a big impact on frame-rate and loading speed for interior scenes.
124
+ A word of warning: activating occlusion culling causes an extra render-pass and as such, has an impact on frame-rate. It will be most beneficial on interior scenes where most of the data is occluded by walls.
125
+
126
+
127
+ First, instantiate an OcclusionCullingService:
128
+ ```
129
+ const occlusionCullingService = new OcclusionCullingService();
130
+ ```
131
+
132
+ This service must be passed to every OGC3DTiles object like so:
133
+ ```
134
+ const ogc3DTile = new OGC3DTile({
135
+ url: "path/to/tileset.json",
136
+ occlusionCullingService: occlusionCullingService
137
+ });
138
+ ```
139
+
140
+ Then, you must update the occlusionCullingService within your render loop:
141
+ ```
142
+ function animate() {
143
+ requestAnimationFrame(animate);
144
+ renderer.render(scene, camera);
145
+ occlusionCullingService.update(scene, renderer, camera)
146
+ }
147
+ ```
148
+
149
+ Finally, you may want to set what side of the faces are drawn in the occlusion pass. By default, THREE.FrontSide is used:
150
+
151
+ ```
152
+ const occlusionCullingService = new OcclusionCullingService();
153
+ occlusionCullingService.setSide(THREE.DoubleSide);
154
+ ```
155
+
156
+
157
+ ### static tilesets (Performance tip)
158
+ When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
159
+ This will skip recalculating the transformation matrix of every tile each frame and give a few extra frames per second.
160
+
161
+ ```
162
+ const ogc3DTile = new OGC3DTile({
163
+ url: "path/to/tileset.json",
164
+ static: true
165
+ });
115
166
  ```
116
167
 
117
168
  # Displaying meshes on a globe
package/index.html CHANGED
@@ -4,7 +4,6 @@
4
4
  <head>
5
5
  <meta charset="utf-8" />
6
6
  <title>Three 3DTiles viewer sample</title>
7
- <link rel="manifest" href="manifest.json">
8
7
  <style>
9
8
  .slidecontainer {
10
9
  width: 100%;
@@ -49,11 +48,11 @@
49
48
  <body>
50
49
  <div id="screen"></div>
51
50
  <div style="position: absolute; top: 1%; z-index: 100; right:1%; ">
52
- <input type="range" min="0.1" max="2" value="1.0", step="0.1" class="slider" id="lodMultiplier" >
51
+ <input type="range" min="0.1" max="2" value="1.0", step="0.001" class="slider" id="lodMultiplier" >
53
52
  <p style="color: #0439aa;">LOD multiplier: <span id="multiplierValue"></span></p>
54
53
  </div>
55
54
  <div style="position: absolute; bottom: 1%; z-index: 100;">
56
- <a href="https://openheritage3d.org/project.php?id=taz6-n215">ORIGINAL MODEL</a>
55
+ <a href="https://skfb.ly/6UoNJ">ORIGINAL MODEL</a>
57
56
  </div>
58
57
  </body>
59
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "3.1.5",
3
+ "version": "3.3.0",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
@@ -31,7 +31,7 @@
31
31
  "path-browserify": "^1.0.1",
32
32
  "regenerator-runtime": ">=0.13.7",
33
33
  "set-interval-async": "^2.0.3",
34
- "three": "0.131.0",
34
+ "three": "0.140.2",
35
35
  "uuid": "^8.3.2"
36
36
  },
37
37
  "devDependencies": {
@@ -1,12 +1,12 @@
1
1
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
2
2
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
3
- import { LegacyGLTFLoader } from './LegacyGLTFLoader.js';
3
+ //import { LegacyGLTFLoader } from './LegacyGLTFLoader.js';
4
4
 
5
5
  const gltfLoader = new GLTFLoader();
6
6
  const dracoLoader = new DRACOLoader();
7
7
  dracoLoader.setDecoderPath( 'https://www.gstatic.com/draco/versioned/decoders/1.4.3/' );
8
8
  gltfLoader.setDRACOLoader( dracoLoader );
9
- const legacyGLTFLoader = new LegacyGLTFLoader();
9
+ //const legacyGLTFLoader = new LegacyGLTFLoader();
10
10
  const B3DMDecoder = {
11
11
  parseB3DM: (arrayBuffer, meshCallback) => {
12
12
  const dataView = new DataView(arrayBuffer);
@@ -64,7 +64,8 @@ const B3DMDecoder = {
64
64
  });
65
65
  resolve(model.scene);
66
66
  }, error=>{
67
- legacyGLTFLoader.parse(gltfBuffer, model => {
67
+ console.error(error);
68
+ /* legacyGLTFLoader.parse(gltfBuffer, model => {
68
69
 
69
70
  ////TODO
70
71
  //model.batchTable = b3dm.batchTable;
@@ -83,7 +84,7 @@ const B3DMDecoder = {
83
84
  }
84
85
  });
85
86
  resolve(model.scene);
86
- }, null);
87
+ }, null); */
87
88
  });
88
89
  });
89
90
  }
package/src/index.js CHANGED
@@ -5,8 +5,11 @@ import { OGC3DTile } from "./tileset/OGC3DTile";
5
5
  import { TileLoader } from "./tileset/TileLoader";
6
6
  import { MapControls, OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
7
7
  import { setIntervalAsync } from 'set-interval-async/dynamic';
8
+ import { OcclusionCullingService } from "./tileset/OcclusionCullingService";
8
9
 
9
10
 
11
+ const occlusionCullingService = new OcclusionCullingService();
12
+ occlusionCullingService.setSide(THREE.DoubleSide);
10
13
  const scene = initScene();
11
14
  const domContainer = initDomContainer("screen");
12
15
  const camera = initCamera();
@@ -17,12 +20,18 @@ const controller = initController(camera, domContainer);
17
20
  const stats = initStats(domContainer);
18
21
  const renderer = initRenderer(camera, domContainer);
19
22
 
23
+
20
24
  animate();
21
25
 
22
26
  function initScene() {
23
27
  const scene = new THREE.Scene();
24
- scene.background = new THREE.Color(0x000000);
25
- scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
28
+ scene.matrixAutoUpdate = false;
29
+ scene.background = new THREE.Color(0xaaffcc);
30
+ scene.add(new THREE.AmbientLight(0xFFFFFF, 0.2));
31
+ const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
32
+ directionalLight.position.set(100,100,100)
33
+ directionalLight.lookAt(-1,-1,-1)
34
+ scene.add( directionalLight );
26
35
  return scene;
27
36
  }
28
37
 
@@ -37,11 +46,9 @@ function initDomContainer(divID) {
37
46
 
38
47
  function initRenderer(camera, dom) {
39
48
 
40
- const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true });
41
- renderer.antialias = true;
49
+ const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
42
50
  renderer.setPixelRatio(window.devicePixelRatio);
43
51
  renderer.setSize(dom.offsetWidth, dom.offsetHeight);
44
-
45
52
  renderer.outputEncoding = THREE.sRGBEncoding;
46
53
  renderer.autoClear = false;
47
54
 
@@ -69,9 +76,9 @@ function initStats(dom) {
69
76
 
70
77
 
71
78
  function initCamera() {
72
- const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 1, 10000);
73
- camera.position.set(10, 10, 10);
74
-
79
+ const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 0.1, 1000);
80
+ camera.position.set(-10, 5, 20);
81
+ camera.matrixAutoUpdate = true;
75
82
  return camera;
76
83
  }
77
84
 
@@ -79,32 +86,28 @@ function initTileset(scene) {
79
86
 
80
87
  const ogc3DTile = new OGC3DTile({
81
88
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
82
- geometricErrorMultiplier: 0.8,
83
- loadOutsideView: true,
89
+ //url: "http://localhost:8081/tileset.json",
90
+ geometricErrorMultiplier: 0.5,
91
+ loadOutsideView: false,
84
92
  tileLoader: new TileLoader(mesh => {
85
93
  //// Insert code to be called on every newly decoded mesh e.g.:
86
94
  mesh.material.wireframe = false;
87
95
  mesh.material.side = THREE.DoubleSide;
88
96
  }, 1000),
89
- onLoadCallback: tileset => {
90
- console.log(tileset.json)
91
- }
97
+ occlusionCullingService: occlusionCullingService
92
98
  });
93
99
 
94
100
 
101
+
95
102
  //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
96
- //ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -10)
97
- //ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -65)
98
- //ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -80)
99
- //ogc3DTile.scale.set(0.0001,0.0001,0.0001);
103
+ //-172683.125,301451.125,1367762.21875
100
104
  //ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
101
- // ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -16.5)
102
- // ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), 0)
103
- // ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -9.5)
105
+
106
+
104
107
  //// It's up to the user to call updates on the tileset. You might call them whenever the camera moves or at regular time intervals like here
105
108
 
106
109
 
107
-
110
+
108
111
  var interval;
109
112
  document.addEventListener('keyup', (e) => {
110
113
  console.log(camera.position)
@@ -141,7 +144,7 @@ function initLODMultiplierSlider(tileset) {
141
144
  function initController(camera, dom) {
142
145
  const controller = new OrbitControls(camera, dom);
143
146
 
144
- controller.target.set(-11.50895, 0.058452500000001, 3.1369285);
147
+ controller.target.set(0, 0, 0);
145
148
  controller.minDistance = 1;
146
149
  controller.maxDistance = 5000;
147
150
  controller.update();
@@ -151,11 +154,11 @@ function initController(camera, dom) {
151
154
 
152
155
  function animate() {
153
156
  requestAnimationFrame(animate);
154
-
155
- camera.updateMatrixWorld();
156
157
  renderer.render(scene, camera);
158
+ occlusionCullingService.update(scene, renderer, camera)
157
159
  stats.update();
158
160
 
161
+
159
162
  }
160
163
 
161
164
 
@@ -2,9 +2,8 @@ import * as THREE from 'three';
2
2
  import { OBB } from "../geometry/obb";
3
3
  import { TileLoader } from "./TileLoader";
4
4
  import { v4 as uuidv4 } from "uuid";
5
- import { setIntervalAsync } from 'set-interval-async/dynamic';
6
- // import { clearIntervalAsync } from 'set-interval-async';
7
- const path = require('path');
5
+ import * as path from "path-browserify"
6
+ import { clamp } from "three/src/math/MathUtils";
8
7
 
9
8
  const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
10
9
 
@@ -26,12 +25,19 @@ class OGC3DTile extends THREE.Object3D {
26
25
  * meshCallback: function,
27
26
  * cameraOnLoad: camera,
28
27
  * parentTile: OGC3DTile,
29
- * onLoadCallback: function
28
+ * onLoadCallback: function,
29
+ * occlusionCullingService: OcclusionCullingService,
30
+ * static: Boolean
30
31
  * } properties
31
32
  */
32
33
  constructor(properties) {
33
34
  super();
34
35
  const self = this;
36
+
37
+ if(properties.static){
38
+ this.matrixAutoUpdate = false;
39
+ }
40
+
35
41
  this.uuid = uuidv4();
36
42
  if (!!properties.tileLoader) {
37
43
  this.tileLoader = properties.tileLoader;
@@ -49,6 +55,12 @@ class OGC3DTile extends THREE.Object3D {
49
55
  this.loadOutsideView = properties.loadOutsideView;
50
56
  this.cameraOnLoad = properties.cameraOnLoad;
51
57
  this.parentTile = properties.parentTile;
58
+ this.occlusionCullingService = properties.occlusionCullingService;
59
+ if (this.occlusionCullingService) {
60
+ this.color = new THREE.Color();
61
+ this.color.setHex(Math.random() * 0xffffff);
62
+ 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;
63
+ }
52
64
 
53
65
  // declare properties specific to the tile for clarity
54
66
  this.childrenTiles = [];
@@ -65,6 +77,8 @@ class OGC3DTile extends THREE.Object3D {
65
77
  this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
66
78
  this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
67
79
 
80
+ this.layers.disable(0);
81
+
68
82
  if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
69
83
  self.setup(properties);
70
84
  if (properties.onLoadCallback) properties.onLoadCallback(self);
@@ -167,14 +181,24 @@ class OGC3DTile extends THREE.Object3D {
167
181
  if (!!self.deleted) return;
168
182
  mesh.traverse((o) => {
169
183
  if (o.isMesh) {
184
+ if (self.occlusionCullingService) {
185
+ const position = o.geometry.attributes.position;
186
+ const colors = [];
187
+ for (let i = 0; i < position.count; i++) {
188
+ colors.push(self.color.r, self.color.g, self.color.b);
189
+ }
190
+ o.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
191
+ }
170
192
  o.material.visible = false;
171
193
  }
172
194
  });
195
+
173
196
  self.add(mesh);
197
+ self.updateWorldMatrix(false, true);
174
198
  self.meshContent = mesh;
175
199
  }, !self.cameraOnLoad ? () => 0 : () => {
176
200
  return self.calculateDistanceToCamera(self.cameraOnLoad);
177
- }, () => self.getSiblings(), self.level, self.uuid);
201
+ }, () => self.getSiblings(), self.level);
178
202
  } else if (url.includes(".json")) {
179
203
  self.tileLoader.get(this.uuid, url, json => {
180
204
  if (!!self.deleted) return;
@@ -185,24 +209,6 @@ class OGC3DTile extends THREE.Object3D {
185
209
  self.hasUnloadedJSONContent = false;
186
210
  });
187
211
 
188
- /* self.controller = new AbortController();
189
- setTimeout(() => {
190
- fetch(url, { signal: self.controller.signal }).then(result => {
191
- if (!result.ok) {
192
- throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
193
- }
194
- result.json().then(json => {
195
- // when json content is downloaded, it is inserted into this tile's original JSON as a child
196
- // and the content object is deleted from the original JSON
197
- if (!self.json.children) self.json.children = [];
198
- json.rootPath = path.dirname(url);
199
- self.json.children.push(json);
200
- delete self.json.content;
201
- self.hasUnloadedJSONContent = false;
202
- }).catch(error => { });
203
- }).catch(error => { });
204
- }, 0); */
205
-
206
212
  }
207
213
 
208
214
  }
@@ -212,6 +218,7 @@ class OGC3DTile extends THREE.Object3D {
212
218
  dispose() {
213
219
 
214
220
  const self = this;
221
+ self.childrenTiles.forEach(tile => tile.dispose());
215
222
  self.deleted = true;
216
223
  this.traverse(function (element) {
217
224
  if (!!element.contentURL) {
@@ -244,19 +251,24 @@ class OGC3DTile extends THREE.Object3D {
244
251
  _update(camera, frustum) {
245
252
  const self = this;
246
253
 
247
- self.childrenTiles.forEach(child => child._update(camera, frustum));
254
+ const visibilityBeforeUpdate = self.materialVisibility;
255
+
248
256
  if (!!self.boundingVolume && !!self.geometricError) {
249
257
  self.metric = self.calculateUpdateMetric(camera, frustum);
250
258
  }
259
+ self.childrenTiles.forEach(child => child._update(camera, frustum));
251
260
 
252
261
  updateNodeVisibility(self.metric);
253
262
  updateTree(self.metric);
254
- trimTree(self.metric);
263
+ trimTree(self.metric, visibilityBeforeUpdate);
255
264
 
256
265
 
257
266
  function updateTree(metric) {
258
267
  // If this tile does not have mesh content but it has children
259
268
  if (metric < 0 && self.hasMeshContent) return;
269
+ if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
270
+ return;
271
+ }
260
272
  if (!self.hasMeshContent || (metric < self.geometricError && !!self.meshContent)) {
261
273
  if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
262
274
  loadJsonChildren();
@@ -296,7 +308,7 @@ class OGC3DTile extends THREE.Object3D {
296
308
  self.changeContentVisibility(true);
297
309
  } else if (metric < self.geometricError) { // Ideal LOD is past this one
298
310
  // if children are visible and have been displayed, can be hidden
299
- var allChildrenReady = true;
311
+ let allChildrenReady = true;
300
312
  self.childrenTiles.every(child => {
301
313
 
302
314
  if (!child.isReady()) {
@@ -307,24 +319,32 @@ class OGC3DTile extends THREE.Object3D {
307
319
  });
308
320
  if (allChildrenReady) {
309
321
  self.changeContentVisibility(false);
310
- } else {
311
- //self.changeContentVisibility(true);
312
-
313
322
  }
314
323
  }
315
324
  }
316
325
 
317
- function trimTree(metric) {
326
+ function trimTree(metric, visibilityBeforeUpdate) {
318
327
  if (!self.hasMeshContent) return;
319
- if (metric < 0) { // outside frustum
328
+ if (!self.inFrustum) { // outside frustum
329
+ self.disposeChildren();
330
+ updateNodeVisibility(metric);
331
+ return;
332
+ }
333
+ if (self.occlusionCullingService &&
334
+ !visibilityBeforeUpdate &&
335
+ self.hasMeshContent &&
336
+ self.meshContent &&
337
+ self.meshesToDisplay == self.meshesDisplayed &&
338
+ self.areAllChildrenLoadedAndHidden()) {
339
+
320
340
  self.disposeChildren();
341
+ updateNodeVisibility(metric);
321
342
  return;
322
343
  }
323
344
  if (metric >= self.geometricError) {
324
- if (self.isReady()) {
325
- self.disposeChildren();
326
- return;
327
- }
345
+ self.disposeChildren();
346
+ updateNodeVisibility();
347
+ return;
328
348
  }
329
349
 
330
350
  }
@@ -342,7 +362,8 @@ class OGC3DTile extends THREE.Object3D {
342
362
  loadOutsideView: self.loadOutsideView,
343
363
  level: self.level + 1,
344
364
  tileLoader: self.tileLoader,
345
- cameraOnLoad: camera
365
+ cameraOnLoad: camera,
366
+ occlusionCullingService:self.occlusionCullingService
346
367
  });
347
368
  self.childrenTiles.push(childTile);
348
369
  self.add(childTile);
@@ -351,6 +372,36 @@ class OGC3DTile extends THREE.Object3D {
351
372
 
352
373
  }
353
374
 
375
+ areAllChildrenLoadedAndHidden() {
376
+ let allLoadedAndHidden = true;
377
+ const self = this;
378
+ this.childrenTiles.every(child => {
379
+ if (child.hasMeshContent) {
380
+ if(child.childrenTiles.length>0){
381
+ allLoadedAndHidden = false;
382
+ return false;
383
+ }
384
+ if (!child.inFrustum ) {
385
+ return true;
386
+ };
387
+ if (!child.materialVisibility || child.meshesToDisplay != child.meshesDisplayed) {
388
+ allLoadedAndHidden = false;
389
+ return false;
390
+ } else if (self.occlusionCullingService.hasID(child.colorID)) {
391
+ allLoadedAndHidden = false;
392
+ return false;
393
+ }
394
+ } else {
395
+ if (!child.areAllChildrenLoadedAndHidden()) {
396
+ allLoadedAndHidden = false;
397
+ return false;
398
+ }
399
+ }
400
+ return true;
401
+ });
402
+ return allLoadedAndHidden;
403
+ }
404
+
354
405
  /**
355
406
  * Node is ready if it is outside frustum, if it was drawn at least once or if all it's children are ready
356
407
  * @returns true if ready
@@ -403,6 +454,13 @@ class OGC3DTile extends THREE.Object3D {
403
454
 
404
455
  changeContentVisibility(visibility) {
405
456
  const self = this;
457
+ if(self.hasMeshContent && self.meshContent){
458
+ if(visibility){
459
+ self.layers.enable(0);
460
+ }else{
461
+ self.layers.disable(0);
462
+ }
463
+ }
406
464
  if (self.materialVisibility == visibility) {
407
465
  return;
408
466
  }
@@ -458,7 +516,7 @@ class OGC3DTile extends THREE.Object3D {
458
516
  return 0;
459
517
  }
460
518
  const scale = this.matrixWorld.getMaxScaleOnAxis();
461
- return ((distance / 100) / this.geometricErrorMultiplier) / scale;
519
+ return (((distance / Math.pow(scale, 2)) / 100) / this.geometricErrorMultiplier);
462
520
  } else if (this.boundingVolume instanceof THREE.Box3) {
463
521
  // Region
464
522
  // Region not supported
@@ -0,0 +1,79 @@
1
+ import * as THREE from 'three';
2
+ import { clamp } from "three/src/math/MathUtils";
3
+
4
+
5
+
6
+ class OcclusionCullingService {
7
+
8
+ /**
9
+ *
10
+ * @param {
11
+ * json: optional,
12
+ * url: optional,
13
+ * rootPath: optional,
14
+ * parentGeometricError: optional,
15
+ * parentBoundingVolume: optional,
16
+ * parentRefinement: optional,
17
+ * geometricErrorMultiplier: Double,
18
+ * loadOutsideView: Boolean,
19
+ * tileLoader : TileLoader,
20
+ * meshCallback: function,
21
+ * cameraOnLoad: camera,
22
+ * parentTile: OGC3DTile,
23
+ * onLoadCallback: function,
24
+ * occlusionCullingService: OcclusionCullingService
25
+ * } properties
26
+ */
27
+ constructor() {
28
+ this.cullMap = [];
29
+ this.cullMaterial = new THREE.MeshBasicMaterial({ vertexColors: true });
30
+ this.cullMaterial.side = THREE.FrontSide;
31
+ this.cullTarget = this.createCullTarget();
32
+ this.cullPixels = new Uint8Array(4 * this.cullTarget.width * this.cullTarget.height);
33
+ }
34
+
35
+ setSide(side){
36
+ this.cullMaterial.side = side;
37
+ }
38
+
39
+ createCullTarget() {
40
+ const target = new THREE.WebGLRenderTarget(Math.floor(window.innerWidth * 0.05), Math.floor(window.innerHeight * 0.05));
41
+ target.texture.format = THREE.RGBAFormat;
42
+ target.texture.encoding = THREE.LinearEncoding;
43
+ target.texture.minFilter = THREE.NearestFilter;
44
+ target.texture.magFilter = THREE.NearestFilter;
45
+ target.texture.generateMipmaps = false;
46
+ target.stencilBuffer = false;
47
+ target.depthBuffer = true;
48
+ target.depthTexture = new THREE.DepthTexture();
49
+ target.depthTexture.format = THREE.DepthFormat;
50
+ target.depthTexture.type = THREE.UnsignedShortType;
51
+ return target;
52
+ }
53
+
54
+ update(scene, renderer, camera) {
55
+ let tempRenderTarget = renderer.getRenderTarget();
56
+ let tempOverrideMaterial = scene.overrideMaterial;
57
+
58
+ scene.overrideMaterial = this.cullMaterial;
59
+ renderer.setRenderTarget(this.cullTarget);
60
+ renderer.render(scene, camera);
61
+
62
+ scene.overrideMaterial = tempOverrideMaterial;
63
+ renderer.setRenderTarget(tempRenderTarget);
64
+
65
+ renderer.readRenderTargetPixels(this.cullTarget, 0, 0, this.cullTarget.width, this.cullTarget.height, this.cullPixels);
66
+ this.cullMap = [];
67
+
68
+ for (let i = 0; i < this.cullPixels.length; i += 4) {
69
+ const c = clamp(this.cullPixels[i], 0, 255) << 16 ^ clamp(this.cullPixels[i + 1], 0, 255) << 8 ^ clamp(this.cullPixels[i + 2], 0, 255) << 0;
70
+ this.cullMap[c] = true;
71
+ }
72
+
73
+ }
74
+
75
+ hasID(id) {
76
+ return this.cullMap[id];
77
+ }
78
+ }
79
+ export { OcclusionCullingService };
@@ -6,6 +6,7 @@ const ready = [];
6
6
  const downloads = [];
7
7
  const nextReady = [];
8
8
  const nextDownloads = [];
9
+ let concurentDownloads = 0;
9
10
 
10
11
  function scheduleDownload(f) {
11
12
  downloads.unshift(f);
@@ -13,13 +14,18 @@ function scheduleDownload(f) {
13
14
  function download() {
14
15
  if (nextDownloads.length == 0) {
15
16
  getNextDownloads();
16
- if (nextDownloads.length == 0) return 0;
17
+ if (nextDownloads.length == 0) return;
17
18
  }
18
- const nextDownload = nextDownloads.shift();
19
- if (!!nextDownload && nextDownload.shouldDoDownload()) {
20
- nextDownload.doDownload();
19
+ while (nextDownloads.length > 0 && concurentDownloads < 500) {
20
+ const nextDownload = nextDownloads.shift();
21
+ if (!!nextDownload && nextDownload.shouldDoDownload()) {
22
+ nextDownload.doDownload();
23
+ }
21
24
  }
22
- return 1;
25
+
26
+
27
+
28
+ return;
23
29
  }
24
30
  function meshReceived(cache, register, key, distanceFunction, getSiblings, level, uuid) {
25
31
  ready.unshift([cache, register, key, distanceFunction, getSiblings, level, uuid]);
@@ -56,11 +62,11 @@ function getNextDownloads() {
56
62
  downloads.splice(i, 1);
57
63
  continue;
58
64
  }
59
- if(!downloads[i].distanceFunction){ // if no distance function, must be a json, give absolute priority!
65
+ if (!downloads[i].distanceFunction) { // if no distance function, must be a json, give absolute priority!
60
66
  nextDownloads.push(downloads.splice(i, 1)[0]);
61
67
  }
62
68
  }
63
- if(nextDownloads.length>0) return;
69
+ if (nextDownloads.length > 0) return;
64
70
  for (let i = downloads.length - 1; i >= 0; i--) {
65
71
  const dist = downloads[i].distanceFunction();
66
72
  if (dist < smallestDistance) {
@@ -88,12 +94,12 @@ function getNextReady() {
88
94
  let smallestDistance = Number.MAX_VALUE;
89
95
  let closest = -1;
90
96
  for (let i = ready.length - 1; i >= 0; i--) {
91
-
92
- if(!ready[i][3]){// if no distance function, must be a json, give absolute priority!
93
- nextReady.push(ready.splice(i,1)[0]);
97
+
98
+ if (!ready[i][3]) {// if no distance function, must be a json, give absolute priority!
99
+ nextReady.push(ready.splice(i, 1)[0]);
94
100
  }
95
101
  }
96
- if(nextReady.length>0) return;
102
+ if (nextReady.length > 0) return;
97
103
  for (let i = ready.length - 1; i >= 0; i--) {
98
104
  const dist = ready[i][3]();
99
105
  if (dist < smallestDistance) {
@@ -116,22 +122,23 @@ function getNextReady() {
116
122
  }
117
123
  }
118
124
  }
119
- setIntervalAsync(()=>{
120
- const start = Date.now();
125
+ setIntervalAsync(() => {
126
+ download();
127
+ /* const start = Date.now();
121
128
  let uploaded = 0;
122
129
  do{
123
130
  uploaded = download();
124
- }while(uploaded > 0 && (Date.now() - start)<= 2 )
125
-
126
- },10);
127
- setIntervalAsync(()=>{
131
+ }while(uploaded > 0 && (Date.now() - start)<= 2 ) */
132
+
133
+ }, 10);
134
+ setIntervalAsync(() => {
128
135
  const start = Date.now();
129
136
  let loaded = 0;
130
- do{
137
+ do {
131
138
  loaded = loadBatch();
132
- }while(loaded > 0 && (Date.now() - start)<= 2 )
133
-
134
- },10);
139
+ } while (loaded > 0 && (Date.now() - start) <= 1)
140
+
141
+ }, 10);
135
142
 
136
143
  class TileLoader {
137
144
  constructor(meshCallback, maxCachedItems) {
@@ -141,7 +148,7 @@ class TileLoader {
141
148
  this.register = {};
142
149
  }
143
150
 
144
- get(tileIdentifier, path, callback, distanceFunction, getSiblings, level, uuid) {
151
+ get(tileIdentifier, path, callback, distanceFunction, getSiblings, level) {
145
152
  const self = this;
146
153
  const key = simplifyPath(path);
147
154
 
@@ -160,12 +167,14 @@ class TileLoader {
160
167
 
161
168
  const cachedObject = self.cache.get(key);
162
169
  if (!!cachedObject) {
163
- meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, uuid);
170
+ meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
164
171
  } else if (Object.keys(self.register[key]).length == 1) {
165
172
  let downloadFunction;
166
173
  if (path.includes(".b3dm")) {
167
174
  downloadFunction = () => {
175
+ concurentDownloads++;
168
176
  fetch(path).then(result => {
177
+ concurentDownloads--;
169
178
  if (!result.ok) {
170
179
  console.error("could not load tile with path : " + path)
171
180
  throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
@@ -173,14 +182,16 @@ class TileLoader {
173
182
  result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
174
183
  self.cache.put(key, mesh);
175
184
  self.checkSize();
176
- meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, uuid);
185
+ meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
177
186
  });
178
187
 
179
188
  });
180
189
  }
181
- }else if (path.includes(".json")) {
190
+ } else if (path.includes(".json")) {
182
191
  downloadFunction = () => {
192
+ concurentDownloads++;
183
193
  fetch(path).then(result => {
194
+ concurentDownloads--;
184
195
  if (!result.ok) {
185
196
  console.error("could not load tile with path : " + path)
186
197
  throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
@@ -201,7 +212,7 @@ class TileLoader {
201
212
  "distanceFunction": distanceFunction,
202
213
  "getSiblings": getSiblings,
203
214
  "level": level,
204
- "uuid": uuid
215
+ "uuid": tileIdentifier
205
216
  })
206
217
  }
207
218
  }
package/manifest.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "display": "fullscreen",
3
- "orientation": "landscape"
4
- }