@jdultra/threedtiles 3.2.1 → 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 +47 -5
- package/index.html +1 -1
- package/package.json +1 -1
- package/src/index.js +18 -19
- package/src/tileset/OGC3DTile.js +90 -11
- package/src/tileset/OcclusionCullingService.js +79 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Photogrametry : https://ebeaufay.github.io/ThreedTilesViewer.github.io/
|
|
|
6
6
|
|
|
7
7
|
IFC : https://storage.googleapis.com/jdultra.com/ifc/index.html
|
|
8
8
|
|
|
9
|
-
Occlusion culling : https://storage.googleapis.com/jdultra.com/occlusionCulling/index.html
|
|
9
|
+
Occlusion culling : [https://storage.googleapis.com/jdultra.com/occlusionCulling/index.html](https://storage.googleapis.com/www.jdultra.com/occlusion/index.html)
|
|
10
10
|
|
|
11
11
|
Adding a tileset to a scene is as easy as :
|
|
12
12
|
|
|
@@ -39,6 +39,7 @@ Currently, the library is limmited to B3DM files.
|
|
|
39
39
|
- Optionally load low detail tiles outside of view frustum for correct shadows and basic mesh present when the camera moves quickly.
|
|
40
40
|
- Share a cache between tileset instances
|
|
41
41
|
- Optimal tile load order
|
|
42
|
+
- Occlusion culling (demo)
|
|
42
43
|
|
|
43
44
|
### geometric Error Multiplier
|
|
44
45
|
The geometric error multiplier allows you to multiply the geometric error by a factor.
|
|
@@ -116,12 +117,53 @@ const ogc3DTile = new OGC3DTile({
|
|
|
116
117
|
ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
|
|
117
118
|
ogc3DTile.rotateOnAxis(new THREE.Vector3(1,0,0), -Math.PI*0.5);
|
|
118
119
|
...
|
|
119
|
-
|
|
120
|
+
|
|
120
121
|
|
|
121
122
|
### Occlusion culling
|
|
122
|
-
Occlusion culling is
|
|
123
|
-
|
|
124
|
-
|
|
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
|
+
});
|
|
166
|
+
```
|
|
125
167
|
|
|
126
168
|
# Displaying meshes on a globe
|
|
127
169
|
I'm working on this project in parallel https://github.com/ebeaufay/UltraGlobe which allows displaying a globe with multi resolution imagery, elevation and 3DTiles.
|
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://
|
|
55
|
+
<a href="https://skfb.ly/6UoNJ">ORIGINAL MODEL</a>
|
|
56
56
|
</div>
|
|
57
57
|
</body>
|
|
58
58
|
|
package/package.json
CHANGED
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,10 +20,12 @@ 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
30
|
scene.add(new THREE.AmbientLight(0xFFFFFF, 0.2));
|
|
26
31
|
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
|
|
@@ -41,11 +46,9 @@ function initDomContainer(divID) {
|
|
|
41
46
|
|
|
42
47
|
function initRenderer(camera, dom) {
|
|
43
48
|
|
|
44
|
-
const renderer = new THREE.WebGLRenderer({ antialias: true,
|
|
45
|
-
renderer.antialias = true;
|
|
49
|
+
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
|
|
46
50
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
47
51
|
renderer.setSize(dom.offsetWidth, dom.offsetHeight);
|
|
48
|
-
|
|
49
52
|
renderer.outputEncoding = THREE.sRGBEncoding;
|
|
50
53
|
renderer.autoClear = false;
|
|
51
54
|
|
|
@@ -75,36 +78,32 @@ function initStats(dom) {
|
|
|
75
78
|
function initCamera() {
|
|
76
79
|
const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 0.1, 1000);
|
|
77
80
|
camera.position.set(-10, 5, 20);
|
|
78
|
-
|
|
81
|
+
camera.matrixAutoUpdate = true;
|
|
79
82
|
return camera;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
function initTileset(scene) {
|
|
83
86
|
|
|
84
87
|
const ogc3DTile = new OGC3DTile({
|
|
85
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/
|
|
86
|
-
//url: "http://localhost:
|
|
87
|
-
geometricErrorMultiplier:
|
|
88
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
|
|
89
|
+
//url: "http://localhost:8081/tileset.json",
|
|
90
|
+
geometricErrorMultiplier: 0.5,
|
|
88
91
|
loadOutsideView: false,
|
|
89
92
|
tileLoader: new TileLoader(mesh => {
|
|
90
93
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
91
94
|
mesh.material.wireframe = false;
|
|
92
|
-
//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)+")")})
|
|
93
95
|
mesh.material.side = THREE.DoubleSide;
|
|
94
|
-
}, 1000)
|
|
96
|
+
}, 1000),
|
|
97
|
+
occlusionCullingService: occlusionCullingService
|
|
95
98
|
});
|
|
96
99
|
|
|
97
100
|
|
|
98
101
|
|
|
99
102
|
//// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
105
|
-
// ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -16.5)
|
|
106
|
-
// ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), 0)
|
|
107
|
-
// ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -9.5)
|
|
103
|
+
//-172683.125,301451.125,1367762.21875
|
|
104
|
+
//ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
105
|
+
|
|
106
|
+
|
|
108
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
|
|
109
108
|
|
|
110
109
|
|
|
@@ -155,11 +154,11 @@ function initController(camera, dom) {
|
|
|
155
154
|
|
|
156
155
|
function animate() {
|
|
157
156
|
requestAnimationFrame(animate);
|
|
158
|
-
|
|
159
|
-
camera.updateMatrixWorld();
|
|
160
157
|
renderer.render(scene, camera);
|
|
158
|
+
occlusionCullingService.update(scene, renderer, camera)
|
|
161
159
|
stats.update();
|
|
162
160
|
|
|
161
|
+
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -3,6 +3,7 @@ import { OBB } from "../geometry/obb";
|
|
|
3
3
|
import { TileLoader } from "./TileLoader";
|
|
4
4
|
import { v4 as uuidv4 } from "uuid";
|
|
5
5
|
import * as path from "path-browserify"
|
|
6
|
+
import { clamp } from "three/src/math/MathUtils";
|
|
6
7
|
|
|
7
8
|
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
|
|
8
9
|
|
|
@@ -24,12 +25,19 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
24
25
|
* meshCallback: function,
|
|
25
26
|
* cameraOnLoad: camera,
|
|
26
27
|
* parentTile: OGC3DTile,
|
|
27
|
-
* onLoadCallback: function
|
|
28
|
+
* onLoadCallback: function,
|
|
29
|
+
* occlusionCullingService: OcclusionCullingService,
|
|
30
|
+
* static: Boolean
|
|
28
31
|
* } properties
|
|
29
32
|
*/
|
|
30
33
|
constructor(properties) {
|
|
31
34
|
super();
|
|
32
35
|
const self = this;
|
|
36
|
+
|
|
37
|
+
if(properties.static){
|
|
38
|
+
this.matrixAutoUpdate = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
this.uuid = uuidv4();
|
|
34
42
|
if (!!properties.tileLoader) {
|
|
35
43
|
this.tileLoader = properties.tileLoader;
|
|
@@ -47,6 +55,12 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
47
55
|
this.loadOutsideView = properties.loadOutsideView;
|
|
48
56
|
this.cameraOnLoad = properties.cameraOnLoad;
|
|
49
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
|
+
}
|
|
50
64
|
|
|
51
65
|
// declare properties specific to the tile for clarity
|
|
52
66
|
this.childrenTiles = [];
|
|
@@ -63,6 +77,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
63
77
|
this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
|
|
64
78
|
this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
|
|
65
79
|
|
|
80
|
+
this.layers.disable(0);
|
|
81
|
+
|
|
66
82
|
if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
|
|
67
83
|
self.setup(properties);
|
|
68
84
|
if (properties.onLoadCallback) properties.onLoadCallback(self);
|
|
@@ -165,10 +181,20 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
165
181
|
if (!!self.deleted) return;
|
|
166
182
|
mesh.traverse((o) => {
|
|
167
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
|
+
}
|
|
168
192
|
o.material.visible = false;
|
|
169
193
|
}
|
|
170
194
|
});
|
|
195
|
+
|
|
171
196
|
self.add(mesh);
|
|
197
|
+
self.updateWorldMatrix(false, true);
|
|
172
198
|
self.meshContent = mesh;
|
|
173
199
|
}, !self.cameraOnLoad ? () => 0 : () => {
|
|
174
200
|
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
@@ -192,6 +218,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
192
218
|
dispose() {
|
|
193
219
|
|
|
194
220
|
const self = this;
|
|
221
|
+
self.childrenTiles.forEach(tile => tile.dispose());
|
|
195
222
|
self.deleted = true;
|
|
196
223
|
this.traverse(function (element) {
|
|
197
224
|
if (!!element.contentURL) {
|
|
@@ -224,19 +251,24 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
224
251
|
_update(camera, frustum) {
|
|
225
252
|
const self = this;
|
|
226
253
|
|
|
227
|
-
|
|
254
|
+
const visibilityBeforeUpdate = self.materialVisibility;
|
|
255
|
+
|
|
228
256
|
if (!!self.boundingVolume && !!self.geometricError) {
|
|
229
257
|
self.metric = self.calculateUpdateMetric(camera, frustum);
|
|
230
258
|
}
|
|
259
|
+
self.childrenTiles.forEach(child => child._update(camera, frustum));
|
|
231
260
|
|
|
232
261
|
updateNodeVisibility(self.metric);
|
|
233
262
|
updateTree(self.metric);
|
|
234
|
-
trimTree(self.metric);
|
|
263
|
+
trimTree(self.metric, visibilityBeforeUpdate);
|
|
235
264
|
|
|
236
265
|
|
|
237
266
|
function updateTree(metric) {
|
|
238
267
|
// If this tile does not have mesh content but it has children
|
|
239
268
|
if (metric < 0 && self.hasMeshContent) return;
|
|
269
|
+
if (self.occlusionCullingService && self.hasMeshContent && !self.occlusionCullingService.hasID(self.colorID)) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
240
272
|
if (!self.hasMeshContent || (metric < self.geometricError && !!self.meshContent)) {
|
|
241
273
|
if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
|
|
242
274
|
loadJsonChildren();
|
|
@@ -276,7 +308,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
276
308
|
self.changeContentVisibility(true);
|
|
277
309
|
} else if (metric < self.geometricError) { // Ideal LOD is past this one
|
|
278
310
|
// if children are visible and have been displayed, can be hidden
|
|
279
|
-
|
|
311
|
+
let allChildrenReady = true;
|
|
280
312
|
self.childrenTiles.every(child => {
|
|
281
313
|
|
|
282
314
|
if (!child.isReady()) {
|
|
@@ -287,17 +319,26 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
287
319
|
});
|
|
288
320
|
if (allChildrenReady) {
|
|
289
321
|
self.changeContentVisibility(false);
|
|
290
|
-
} else {
|
|
291
|
-
//self.changeContentVisibility(true);
|
|
292
|
-
|
|
293
322
|
}
|
|
294
323
|
}
|
|
295
324
|
}
|
|
296
325
|
|
|
297
|
-
function trimTree(metric) {
|
|
326
|
+
function trimTree(metric, visibilityBeforeUpdate) {
|
|
298
327
|
if (!self.hasMeshContent) return;
|
|
299
|
-
if (
|
|
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
|
+
|
|
300
340
|
self.disposeChildren();
|
|
341
|
+
updateNodeVisibility(metric);
|
|
301
342
|
return;
|
|
302
343
|
}
|
|
303
344
|
if (metric >= self.geometricError) {
|
|
@@ -321,7 +362,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
321
362
|
loadOutsideView: self.loadOutsideView,
|
|
322
363
|
level: self.level + 1,
|
|
323
364
|
tileLoader: self.tileLoader,
|
|
324
|
-
cameraOnLoad: camera
|
|
365
|
+
cameraOnLoad: camera,
|
|
366
|
+
occlusionCullingService:self.occlusionCullingService
|
|
325
367
|
});
|
|
326
368
|
self.childrenTiles.push(childTile);
|
|
327
369
|
self.add(childTile);
|
|
@@ -330,6 +372,36 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
330
372
|
|
|
331
373
|
}
|
|
332
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
|
+
|
|
333
405
|
/**
|
|
334
406
|
* Node is ready if it is outside frustum, if it was drawn at least once or if all it's children are ready
|
|
335
407
|
* @returns true if ready
|
|
@@ -382,6 +454,13 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
382
454
|
|
|
383
455
|
changeContentVisibility(visibility) {
|
|
384
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
|
+
}
|
|
385
464
|
if (self.materialVisibility == visibility) {
|
|
386
465
|
return;
|
|
387
466
|
}
|
|
@@ -437,7 +516,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
437
516
|
return 0;
|
|
438
517
|
}
|
|
439
518
|
const scale = this.matrixWorld.getMaxScaleOnAxis();
|
|
440
|
-
return ((distance / 100) / this.geometricErrorMultiplier)
|
|
519
|
+
return (((distance / Math.pow(scale, 2)) / 100) / this.geometricErrorMultiplier);
|
|
441
520
|
} else if (this.boundingVolume instanceof THREE.Box3) {
|
|
442
521
|
// Region
|
|
443
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 };
|