@jdultra/threedtiles 3.2.0 → 3.3.1

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
@@ -2,7 +2,11 @@
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
38
43
 
39
44
  ### geometric Error Multiplier
40
45
  The geometric error multiplier allows you to multiply the geometric error by a factor.
@@ -55,8 +60,8 @@ A value of 1.0 is the default.
55
60
 
56
61
 
57
62
  ### load tiles outside of view
58
- By default, only the tiles that intersect the view frustum are loaded. When the camera moves, the scene will have to load the missing tiles.
59
- Instead of this behaviour, you can force the lowest possible LODs to be loaded for tiles around the view so that there are no gaps in the mesh when the camera moves or when displaying shadows.
63
+ By default, only the tiles that intersect the view frustum are loaded. When the camera moves, the scene will have to load the missing tiles and the user might see some holes in the model.
64
+ Instead of this behaviour, you can force the lowest possible LODs to be loaded for tiles outside the view so that there are no gaps in the mesh when the camera moves. This also allows displaying shadows correctly.
60
65
 
61
66
  ```
62
67
  const ogc3DTile = new OGC3DTile({
@@ -73,6 +78,7 @@ const ogc3DTile = new OGC3DTile({
73
78
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
74
79
  meshCallback: mesh => {
75
80
  mesh.material.wireframe = true;
81
+ mesh.material.side = THREE.DoubleSide;
76
82
  }
77
83
  });
78
84
  ```
@@ -111,7 +117,55 @@ const ogc3DTile = new OGC3DTile({
111
117
 
112
118
  ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
113
119
  ogc3DTile.rotateOnAxis(new THREE.Vector3(1,0,0), -Math.PI*0.5);
114
- ...
120
+ ```
121
+
122
+
123
+ ### Occlusion culling
124
+ 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.
125
+
126
+ A word of warning: activating occlusion culling causes an extra render-pass and as such, has an impact on frame-rate.
127
+ It will be most beneficial on interior scenes where most of the data is occluded by walls. All the tiles that don't need to be downloaded or drawn will balance out the cost of the extra render pass.
128
+
129
+
130
+ First, instantiate an OcclusionCullingService:
131
+ ```
132
+ const occlusionCullingService = new OcclusionCullingService();
133
+ ```
134
+
135
+ This service must be passed to every OGC3DTiles object like so:
136
+ ```
137
+ const ogc3DTile = new OGC3DTile({
138
+ url: "path/to/tileset.json",
139
+ occlusionCullingService: occlusionCullingService
140
+ });
141
+ ```
142
+
143
+ Then, you must update the occlusionCullingService within your render loop:
144
+ ```
145
+ function animate() {
146
+ requestAnimationFrame(animate);
147
+ renderer.render(scene, camera);
148
+ occlusionCullingService.update(scene, renderer, camera)
149
+ }
150
+ ```
151
+
152
+ Finally, if you are drawing the back-side of faces or both-sides (see Callback section), you'll need to specify it for the occlusion pass too. By default, THREE.FrontSide is used:
153
+
154
+ ```
155
+ const occlusionCullingService = new OcclusionCullingService();
156
+ occlusionCullingService.setSide(THREE.DoubleSide);
157
+ ```
158
+
159
+
160
+ ### static tilesets (Performance tip)
161
+ When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
162
+ This will skip recalculating the transformation matrix of every tile each frame and give a few extra frames per second.
163
+
164
+ ```
165
+ const ogc3DTile = new OGC3DTile({
166
+ url: "path/to/tileset.json",
167
+ static: true
168
+ });
115
169
  ```
116
170
 
117
171
  # Displaying meshes on a globe
@@ -121,5 +175,5 @@ I'm working on this project in parallel https://github.com/ebeaufay/UltraGlobe w
121
175
 
122
176
  I also have code to convert meshes to 3DTiles with no limit to the size of the dataset relative to faces or textures.
123
177
  It works for all types of meshes: photogrametry, BIM, colored or textured meshes with a single texture atlas or many individual textures.
124
- I'm keeping the code private for now but I'll convert any dataset you have for free.
178
+ I'm keeping the code private for now but feel free to contact me about it.
125
179
  Contact: emericbeaufays@gmail.com
package/index.html CHANGED
@@ -52,7 +52,7 @@
52
52
  <p style="color: #0439aa;">LOD multiplier: <span id="multiplierValue"></span></p>
53
53
  </div>
54
54
  <div style="position: absolute; bottom: 1%; z-index: 100;">
55
- <a href="https://openheritage3d.org/project.php?id=taz6-n215">ORIGINAL MODEL</a>
55
+ <a href="https://skfb.ly/6UoNJ">ORIGINAL MODEL</a>
56
56
  </div>
57
57
  </body>
58
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
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,14 +20,19 @@ 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();
28
+ scene.matrixAutoUpdate = false;
24
29
  scene.background = new THREE.Color(0xaaffcc);
25
- scene.add(new THREE.AmbientLight(0xFFFFFF, 0.5));
26
- const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
30
+ scene.add(new THREE.AmbientLight(0xFFFFFF, 0.8));
31
+ const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.2 );
32
+ directionalLight.position.set(100,100,100)
33
+ directionalLight.lookAt(-1,-1,-1)
27
34
  scene.add( directionalLight );
35
+ scene.autoUpdate = false;
28
36
  return scene;
29
37
  }
30
38
 
@@ -39,11 +47,9 @@ function initDomContainer(divID) {
39
47
 
40
48
  function initRenderer(camera, dom) {
41
49
 
42
- const renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true });
43
- renderer.antialias = true;
50
+ const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
44
51
  renderer.setPixelRatio(window.devicePixelRatio);
45
52
  renderer.setSize(dom.offsetWidth, dom.offsetHeight);
46
-
47
53
  renderer.outputEncoding = THREE.sRGBEncoding;
48
54
  renderer.autoClear = false;
49
55
 
@@ -71,41 +77,46 @@ function initStats(dom) {
71
77
 
72
78
 
73
79
  function initCamera() {
74
- const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 1, 10000);
75
- camera.position.set(20, 10, 20);
76
-
80
+ const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 0.1, 1000);
81
+ camera.position.set(-10, 5, 20);
82
+ camera.matrixAutoUpdate = true;
77
83
  return camera;
78
84
  }
79
85
 
80
86
  function initTileset(scene) {
81
87
 
88
+ const tileLoader = new TileLoader(mesh => {
89
+ //// Insert code to be called on every newly decoded mesh e.g.:
90
+ mesh.material.wireframe = false;
91
+ mesh.material.side = THREE.DoubleSide;
92
+ }, 1000)
82
93
  const ogc3DTile = new OGC3DTile({
83
94
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
84
95
  //url: "http://localhost:8080/tileset.json",
85
- geometricErrorMultiplier: 1.0,
86
- loadOutsideView: true,
87
- tileLoader: new TileLoader(mesh => {
88
- //// Insert code to be called on every newly decoded mesh e.g.:
89
- //mesh.material.wireframe = true;
90
- //mesh.material = new THREE.MeshBasicMaterial({color:new THREE.Color("rgb("+Math.floor(Math.random()*256)+", "+Math.floor(Math.random()*256)+", "+Math.floor(Math.random()*256)+")")})
91
- mesh.material.side = THREE.DoubleSide;
92
- }, 1000),
93
- onLoadCallback: tileset => {
94
- console.log(tileset.json)
95
- }
96
+ geometricErrorMultiplier: 0.5,
97
+ loadOutsideView: false,
98
+ tileLoader: tileLoader,
99
+ occlusionCullingService: occlusionCullingService,
100
+ static: false
101
+ });
102
+ ogc3DTile.translateZ(200) // Z-UP to Y-UP
103
+ const ogc3DTile2 = new OGC3DTile({
104
+ url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
105
+ //url: "http://localhost:8080/tileset.json",
106
+ geometricErrorMultiplier: 0.5,
107
+ loadOutsideView: false,
108
+ //tileLoader: tileLoader,
109
+ occlusionCullingService: occlusionCullingService,
110
+ static: false
96
111
  });
97
112
 
98
113
 
99
114
 
100
115
  //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
101
- //ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -10)
102
- //ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -65)
103
- //ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -80)
104
- ogc3DTile.scale.set(1,1,1);
105
- //ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * 0.5) // Z-UP to Y-UP
106
- // ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -16.5)
107
- // ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), 0)
108
- // ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -9.5)
116
+ //ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
117
+ //// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
118
+
119
+
109
120
  //// 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
110
121
 
111
122
 
@@ -124,11 +135,13 @@ function initTileset(scene) {
124
135
  function startInterval() {
125
136
  interval = setIntervalAsync(function () {
126
137
  ogc3DTile.update(camera);
138
+ ogc3DTile2.update(camera);
127
139
  }, 20);
128
140
  }
129
141
  startInterval();
130
142
 
131
143
  scene.add(ogc3DTile)
144
+ scene.add(ogc3DTile2)
132
145
  return ogc3DTile;
133
146
  }
134
147
 
@@ -156,11 +169,11 @@ function initController(camera, dom) {
156
169
 
157
170
  function animate() {
158
171
  requestAnimationFrame(animate);
159
-
160
- camera.updateMatrixWorld();
161
172
  renderer.render(scene, camera);
173
+ occlusionCullingService.update(scene, renderer, camera)
162
174
  stats.update();
163
175
 
176
+
164
177
  }
165
178
 
166
179
 
@@ -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,15 @@ 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
+
35
37
  this.uuid = uuidv4();
36
38
  if (!!properties.tileLoader) {
37
39
  this.tileLoader = properties.tileLoader;
@@ -41,15 +43,24 @@ class OGC3DTile extends THREE.Object3D {
41
43
  mesh.material.wireframe = false;
42
44
  mesh.material.side = THREE.DoubleSide;
43
45
  } : properties.meshCallback);
44
- }
45
- // set properties general to the entire tileset
46
- this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
47
-
46
+ }
47
+ // set properties general to the entire tileset
48
+ this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
49
+
48
50
  this.meshCallback = properties.meshCallback;
49
51
  this.loadOutsideView = properties.loadOutsideView;
50
52
  this.cameraOnLoad = properties.cameraOnLoad;
51
53
  this.parentTile = properties.parentTile;
52
-
54
+ this.occlusionCullingService = properties.occlusionCullingService;
55
+ this.static = properties.static;
56
+ if (this.occlusionCullingService) {
57
+ this.color = new THREE.Color();
58
+ this.color.setHex(Math.random() * 0xffffff);
59
+ 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
+ }
61
+ if(this.static){
62
+ this.matrixAutoUpdate = false;
63
+ }
53
64
  // declare properties specific to the tile for clarity
54
65
  this.childrenTiles = [];
55
66
  this.meshContent;
@@ -65,6 +76,8 @@ class OGC3DTile extends THREE.Object3D {
65
76
  this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
66
77
  this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
67
78
 
79
+ this.layers.disable(0);
80
+
68
81
  if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
69
82
  self.setup(properties);
70
83
  if (properties.onLoadCallback) properties.onLoadCallback(self);
@@ -167,10 +180,25 @@ class OGC3DTile extends THREE.Object3D {
167
180
  if (!!self.deleted) return;
168
181
  mesh.traverse((o) => {
169
182
  if (o.isMesh) {
183
+ o.layers.disable(0);
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
+ }
192
+ if(self.static){
193
+ o.matrixAutoUpdate = false;
194
+ }
170
195
  o.material.visible = false;
171
196
  }
172
197
  });
198
+
173
199
  self.add(mesh);
200
+ self.updateWorldMatrix(false, true);
201
+ // mesh.layers.disable(0);
174
202
  self.meshContent = mesh;
175
203
  }, !self.cameraOnLoad ? () => 0 : () => {
176
204
  return self.calculateDistanceToCamera(self.cameraOnLoad);
@@ -185,24 +213,6 @@ class OGC3DTile extends THREE.Object3D {
185
213
  self.hasUnloadedJSONContent = false;
186
214
  });
187
215
 
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
216
  }
207
217
 
208
218
  }
@@ -212,6 +222,7 @@ class OGC3DTile extends THREE.Object3D {
212
222
  dispose() {
213
223
 
214
224
  const self = this;
225
+ self.childrenTiles.forEach(tile => tile.dispose());
215
226
  self.deleted = true;
216
227
  this.traverse(function (element) {
217
228
  if (!!element.contentURL) {
@@ -244,19 +255,24 @@ class OGC3DTile extends THREE.Object3D {
244
255
  _update(camera, frustum) {
245
256
  const self = this;
246
257
 
247
- self.childrenTiles.forEach(child => child._update(camera, frustum));
258
+ const visibilityBeforeUpdate = self.materialVisibility;
259
+
248
260
  if (!!self.boundingVolume && !!self.geometricError) {
249
261
  self.metric = self.calculateUpdateMetric(camera, frustum);
250
262
  }
263
+ self.childrenTiles.forEach(child => child._update(camera, frustum));
251
264
 
252
265
  updateNodeVisibility(self.metric);
253
266
  updateTree(self.metric);
254
- trimTree(self.metric);
267
+ trimTree(self.metric, visibilityBeforeUpdate);
255
268
 
256
269
 
257
270
  function updateTree(metric) {
258
271
  // If this tile does not have mesh content but it has children
259
272
  if (metric < 0 && self.hasMeshContent) return;
273
+ if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
274
+ return;
275
+ }
260
276
  if (!self.hasMeshContent || (metric < self.geometricError && !!self.meshContent)) {
261
277
  if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
262
278
  loadJsonChildren();
@@ -296,7 +312,7 @@ class OGC3DTile extends THREE.Object3D {
296
312
  self.changeContentVisibility(true);
297
313
  } else if (metric < self.geometricError) { // Ideal LOD is past this one
298
314
  // if children are visible and have been displayed, can be hidden
299
- var allChildrenReady = true;
315
+ let allChildrenReady = true;
300
316
  self.childrenTiles.every(child => {
301
317
 
302
318
  if (!child.isReady()) {
@@ -307,24 +323,32 @@ class OGC3DTile extends THREE.Object3D {
307
323
  });
308
324
  if (allChildrenReady) {
309
325
  self.changeContentVisibility(false);
310
- } else {
311
- //self.changeContentVisibility(true);
312
-
313
326
  }
314
327
  }
315
328
  }
316
329
 
317
- function trimTree(metric) {
330
+ function trimTree(metric, visibilityBeforeUpdate) {
318
331
  if (!self.hasMeshContent) return;
319
- if (metric < 0) { // outside frustum
332
+ if (!self.inFrustum) { // outside frustum
320
333
  self.disposeChildren();
334
+ updateNodeVisibility(metric);
335
+ return;
336
+ }
337
+ if (self.occlusionCullingService &&
338
+ !visibilityBeforeUpdate &&
339
+ self.hasMeshContent &&
340
+ self.meshContent &&
341
+ self.meshesToDisplay == self.meshesDisplayed &&
342
+ self.areAllChildrenLoadedAndHidden()) {
343
+
344
+ self.disposeChildren();
345
+ updateNodeVisibility(metric);
321
346
  return;
322
347
  }
323
348
  if (metric >= self.geometricError) {
324
- if (self.isReady()) {
325
- self.disposeChildren();
326
- return;
327
- }
349
+ self.disposeChildren();
350
+ updateNodeVisibility();
351
+ return;
328
352
  }
329
353
 
330
354
  }
@@ -342,7 +366,9 @@ class OGC3DTile extends THREE.Object3D {
342
366
  loadOutsideView: self.loadOutsideView,
343
367
  level: self.level + 1,
344
368
  tileLoader: self.tileLoader,
345
- cameraOnLoad: camera
369
+ cameraOnLoad: camera,
370
+ occlusionCullingService:self.occlusionCullingService,
371
+ static: self.static
346
372
  });
347
373
  self.childrenTiles.push(childTile);
348
374
  self.add(childTile);
@@ -351,6 +377,36 @@ class OGC3DTile extends THREE.Object3D {
351
377
 
352
378
  }
353
379
 
380
+ areAllChildrenLoadedAndHidden() {
381
+ let allLoadedAndHidden = true;
382
+ const self = this;
383
+ this.childrenTiles.every(child => {
384
+ if (child.hasMeshContent) {
385
+ if(child.childrenTiles.length>0){
386
+ allLoadedAndHidden = false;
387
+ return false;
388
+ }
389
+ if (!child.inFrustum ) {
390
+ return true;
391
+ };
392
+ if (!child.materialVisibility || child.meshesToDisplay != child.meshesDisplayed) {
393
+ allLoadedAndHidden = false;
394
+ return false;
395
+ } else if (self.occlusionCullingService.hasID(child.colorID)) {
396
+ allLoadedAndHidden = false;
397
+ return false;
398
+ }
399
+ } else {
400
+ if (!child.areAllChildrenLoadedAndHidden()) {
401
+ allLoadedAndHidden = false;
402
+ return false;
403
+ }
404
+ }
405
+ return true;
406
+ });
407
+ return allLoadedAndHidden;
408
+ }
409
+
354
410
  /**
355
411
  * Node is ready if it is outside frustum, if it was drawn at least once or if all it's children are ready
356
412
  * @returns true if ready
@@ -403,6 +459,22 @@ class OGC3DTile extends THREE.Object3D {
403
459
 
404
460
  changeContentVisibility(visibility) {
405
461
  const self = this;
462
+ if(self.hasMeshContent && self.meshContent){
463
+ if(visibility){
464
+
465
+ self.meshContent.traverse((o) => {
466
+ if (o.isMesh) {
467
+ o.layers.enable(0);
468
+ }
469
+ });
470
+ }else{
471
+ self.meshContent.traverse((o) => {
472
+ if (o.isMesh) {
473
+ o.layers.disable(0);
474
+ }
475
+ });
476
+ }
477
+ }
406
478
  if (self.materialVisibility == visibility) {
407
479
  return;
408
480
  }
@@ -458,7 +530,7 @@ class OGC3DTile extends THREE.Object3D {
458
530
  return 0;
459
531
  }
460
532
  const scale = this.matrixWorld.getMaxScaleOnAxis();
461
- return ((distance / 100) / this.geometricErrorMultiplier) / scale;
533
+ return (((distance / Math.pow(scale, 2)) / 100) / this.geometricErrorMultiplier);
462
534
  } else if (this.boundingVolume instanceof THREE.Box3) {
463
535
  // Region
464
536
  // 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 };