@jdultra/threedtiles 3.3.1 → 4.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 +39 -12
- package/index.html +1 -4
- package/package.json +14 -13
- package/src/decoder/B3DMDecoder.js +63 -64
- package/src/index.js +149 -47
- package/src/tileset/OGC3DTile.js +8 -8
- package/src/tileset/TileLoader.js +29 -29
- package/src/tileset/instanced/InstancedOGC3DTile.js +45 -0
- package/src/tileset/instanced/InstancedTile.js +510 -0
- package/src/tileset/instanced/InstancedTileLoader.js +322 -0
- package/src/tileset/instanced/JsonTile.js +41 -0
- package/src/tileset/instanced/MeshTile.js +75 -0
- package/webpack.config.js +117 -107
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# T H R E E D T I L E S : http://www.jdultra.com/
|
|
2
2
|
|
|
3
|
-
3DTiles viewer for three.js
|
|
4
3
|
|
|
5
|
-
Photogrametry : https://ebeaufay.github.io/ThreedTilesViewer.github.io/
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
The fastest 3DTiles viewer for three.js
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
[Photogrametry (OBJ conversion)](https://ebeaufay.github.io/ThreedTilesViewer.github.io/)
|
|
8
|
+
|
|
9
|
+
[PBR material (GlTF conversion)](https://www.jdultra.com/pbr/)
|
|
10
|
+
|
|
11
|
+
[Occlusion culling (IFC conversion)](https://www.jdultra.com/occlusion/index.html)
|
|
10
12
|
|
|
11
13
|
Adding a tileset to a scene is as easy as :
|
|
12
14
|
|
|
@@ -26,7 +28,7 @@ It's up to the user to call updates on the tileset. You might call them whenever
|
|
|
26
28
|
```
|
|
27
29
|
setInterval(function () {
|
|
28
30
|
ogc3DTile.update(camera);
|
|
29
|
-
},
|
|
31
|
+
}, 20);
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
Currently, the library is limmited to B3DM files.
|
|
@@ -71,6 +73,21 @@ const ogc3DTile = new OGC3DTile({
|
|
|
71
73
|
```
|
|
72
74
|
|
|
73
75
|
### Callback
|
|
76
|
+
|
|
77
|
+
#### onLoadCallback
|
|
78
|
+
Add a callback that is called once when the first tile is loaded and geometry is available.
|
|
79
|
+
This can be useful to position the tileset at a specific location when it is not centered on origin for example.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
const ogc3DTile = new OGC3DTile({
|
|
83
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
84
|
+
onLoadCallback: tilese => {
|
|
85
|
+
console.log(tileset.boundingVolume);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Mesh callback
|
|
74
91
|
Add a callback on loaded tiles in order to set a material or do some logic on the meshes.
|
|
75
92
|
|
|
76
93
|
```
|
|
@@ -123,8 +140,8 @@ ogc3DTile.rotateOnAxis(new THREE.Vector3(1,0,0), -Math.PI*0.5);
|
|
|
123
140
|
### Occlusion culling
|
|
124
141
|
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
142
|
|
|
126
|
-
A word of warning: activating occlusion culling
|
|
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
|
|
143
|
+
A word of warning: activating occlusion culling has an impact on frame-rate.
|
|
144
|
+
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 occlusion logic.
|
|
128
145
|
|
|
129
146
|
|
|
130
147
|
First, instantiate an OcclusionCullingService:
|
|
@@ -157,7 +174,7 @@ occlusionCullingService.setSide(THREE.DoubleSide);
|
|
|
157
174
|
```
|
|
158
175
|
|
|
159
176
|
|
|
160
|
-
### static tilesets
|
|
177
|
+
### static tilesets and other performance tips
|
|
161
178
|
When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
|
|
162
179
|
This will skip recalculating the transformation matrix of every tile each frame and give a few extra frames per second.
|
|
163
180
|
|
|
@@ -168,12 +185,22 @@ const ogc3DTile = new OGC3DTile({
|
|
|
168
185
|
});
|
|
169
186
|
```
|
|
170
187
|
|
|
171
|
-
|
|
172
|
-
|
|
188
|
+
Either way, it's advised to set the autoUpdate property of the scene to false and update the matrices manually whenever you move things around.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
scene.autoUpdate = false;
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
# Projects that use this library
|
|
195
|
+
https://github.com/ebeaufay/UltraGlobe allows displaying a globe with multi resolution imagery, elevation and 3DTiles.
|
|
196
|
+
|
|
197
|
+
Don't hesitate to tell me 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.
|
|
198
|
+
Contact: emeric.beaufays@jdultra.com
|
|
199
|
+
|
|
173
200
|
|
|
174
201
|
# Mesh to 3DTiles Converter
|
|
175
202
|
|
|
176
203
|
I also have code to convert meshes to 3DTiles with no limit to the size of the dataset relative to faces or textures.
|
|
177
204
|
It works for all types of meshes: photogrametry, BIM, colored or textured meshes with a single texture atlas or many individual textures.
|
|
178
205
|
I'm keeping the code private for now but feel free to contact me about it.
|
|
179
|
-
Contact:
|
|
206
|
+
Contact: emeric.beaufays@jdultra.com
|
package/index.html
CHANGED
|
@@ -48,12 +48,9 @@
|
|
|
48
48
|
<body>
|
|
49
49
|
<div id="screen"></div>
|
|
50
50
|
<div style="position: absolute; top: 1%; z-index: 100; right:1%; ">
|
|
51
|
-
<input type="range" min="0.
|
|
51
|
+
<input type="range" min="0.0" max="1.0" value="1.0", step="0.001" class="slider" id="lodMultiplier" >
|
|
52
52
|
<p style="color: #0439aa;">LOD multiplier: <span id="multiplierValue"></span></p>
|
|
53
53
|
</div>
|
|
54
|
-
<div style="position: absolute; bottom: 1%; z-index: 100;">
|
|
55
|
-
<a href="https://skfb.ly/6UoNJ">ORIGINAL MODEL</a>
|
|
56
|
-
</div>
|
|
57
54
|
</body>
|
|
58
55
|
|
|
59
56
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jdultra/threedtiles",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "An OGC 3DTiles viewer for Three.js",
|
|
5
5
|
"main": "tileset.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,29 +23,30 @@
|
|
|
23
23
|
"author": "Emeric Beaufays",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"gltf-validator": "
|
|
26
|
+
"gltf-validator": "^2.0.0-dev.3.9",
|
|
27
27
|
"js-utils-z": "1.2.1",
|
|
28
28
|
"lodash": ">=4.17.20",
|
|
29
|
-
"lru-cache": "^7.
|
|
30
|
-
"mnemonist": "^0.39.
|
|
29
|
+
"lru-cache": "^7.14.1",
|
|
30
|
+
"mnemonist": "^0.39.5",
|
|
31
31
|
"path-browserify": "^1.0.1",
|
|
32
|
-
"regenerator-runtime": "
|
|
32
|
+
"regenerator-runtime": "^0.13.11",
|
|
33
33
|
"set-interval-async": "^2.0.3",
|
|
34
|
-
"three": "0.
|
|
34
|
+
"three": "0.146.0",
|
|
35
35
|
"uuid": "^8.3.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@babel/core": "^7.
|
|
39
|
-
"@babel/preset-env": "^7.
|
|
40
|
-
"babel-loader": "^8.
|
|
38
|
+
"@babel/core": "^7.20.2",
|
|
39
|
+
"@babel/preset-env": "^7.20.2",
|
|
40
|
+
"babel-loader": "^8.3.0",
|
|
41
41
|
"copy-webpack-plugin": "^6.3.2",
|
|
42
|
-
"core-js": "^3.
|
|
42
|
+
"core-js": "^3.26.1",
|
|
43
43
|
"html-loader": "^1.3.2",
|
|
44
44
|
"html-webpack-plugin": "^4.5.0",
|
|
45
|
-
"mini-css-extract-plugin": "^1.
|
|
46
|
-
"webpack": "^5.
|
|
45
|
+
"mini-css-extract-plugin": "^1.6.2",
|
|
46
|
+
"webpack": "^5.75.0",
|
|
47
47
|
"webpack-cli": "4.9.1",
|
|
48
|
-
"webpack-dev-server": "^4.
|
|
48
|
+
"webpack-dev-server": "^4.11.1",
|
|
49
|
+
"webpack-glsl-loader": "^1.0.1",
|
|
49
50
|
"whatwg-fetch": "^3.5.0"
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -1,92 +1,91 @@
|
|
|
1
1
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
2
2
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
3
|
-
|
|
3
|
+
import * as THREE from 'three';
|
|
4
4
|
|
|
5
5
|
const gltfLoader = new GLTFLoader();
|
|
6
6
|
const dracoLoader = new DRACOLoader();
|
|
7
|
-
dracoLoader.setDecoderPath(
|
|
8
|
-
gltfLoader.setDRACOLoader(
|
|
7
|
+
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
8
|
+
gltfLoader.setDRACOLoader(dracoLoader);
|
|
9
|
+
const dummy = new THREE.Object3D();
|
|
9
10
|
//const legacyGLTFLoader = new LegacyGLTFLoader();
|
|
10
|
-
const B3DMDecoder = {
|
|
11
|
-
parseB3DM: (arrayBuffer, meshCallback) => {
|
|
12
|
-
const dataView = new DataView(arrayBuffer);
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
String.fromCharCode(dataView.getUint8(1)) +
|
|
17
|
-
String.fromCharCode(dataView.getUint8(2)) +
|
|
18
|
-
String.fromCharCode(dataView.getUint8(3));
|
|
19
|
-
console.assert(magic === 'b3dm');
|
|
12
|
+
function parseB3DM(arrayBuffer, meshCallback) {
|
|
13
|
+
const dataView = new DataView(arrayBuffer);
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const magic =
|
|
16
|
+
String.fromCharCode(dataView.getUint8(0)) +
|
|
17
|
+
String.fromCharCode(dataView.getUint8(1)) +
|
|
18
|
+
String.fromCharCode(dataView.getUint8(2)) +
|
|
19
|
+
String.fromCharCode(dataView.getUint8(3));
|
|
20
|
+
console.assert(magic === 'b3dm');
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const version = dataView.getUint32(4, true);
|
|
23
|
+
console.assert(version === 1);
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const batchTableJSONByteLength = dataView.getUint32(20, true);
|
|
30
|
-
const batchTableBinaryByteLength = dataView.getUint32(24, true);
|
|
25
|
+
const byteLength = dataView.getUint32(8, true);
|
|
26
|
+
console.assert(byteLength === arrayBuffer.byteLength);
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const featureTableJSONByteLength = dataView.getUint32(12, true);
|
|
29
|
+
const featureTableBinaryByteLength = dataView.getUint32(16, true);
|
|
30
|
+
const batchTableJSONByteLength = dataView.getUint32(20, true);
|
|
31
|
+
const batchTableBinaryByteLength = dataView.getUint32(24, true);
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const featureTableStart = 28;
|
|
34
|
+
//const featureTable = new FeatureTable( arrayBuffer, featureTableStart, featureTableJSONByteLength, featureTableBinaryByteLength );
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
|
|
37
|
+
//const batchTable = new BatchTable( arrayBuffer, featureTable.getData( 'BATCH_LENGTH' ), batchTableStart, batchTableJSONByteLength, batchTableBinaryByteLength );
|
|
40
38
|
|
|
39
|
+
const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
|
|
40
|
+
const glbBytes = new Uint8Array(arrayBuffer, glbStart, byteLength - glbStart);
|
|
41
41
|
|
|
42
|
-
const gltfBuffer = glbBytes.slice().buffer;
|
|
43
42
|
|
|
43
|
+
const gltfBuffer = glbBytes.slice().buffer;
|
|
44
44
|
|
|
45
|
-
return new Promise((resolve, reject) => {
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
//model.batchTable = b3dm.batchTable;
|
|
51
|
-
//model.featureTable = b3dm.featureTable;
|
|
48
|
+
gltfLoader.parse(gltfBuffer, null, model => {
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
////TODO
|
|
51
|
+
//model.batchTable = b3dm.batchTable;
|
|
52
|
+
//model.featureTable = b3dm.featureTable;
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (o.isMesh) {
|
|
59
|
-
if (!!meshCallback) {
|
|
60
|
-
meshCallback(o);
|
|
61
|
-
}
|
|
54
|
+
//model.scene.batchTable = b3dm.batchTable;
|
|
55
|
+
//model.scene.featureTable = b3dm.featureTable;
|
|
62
56
|
|
|
57
|
+
//const scene = mergeColoredObject(model.scene);
|
|
58
|
+
model.scene.traverse((o) => {
|
|
59
|
+
if (o.isMesh) {
|
|
60
|
+
if (!!meshCallback) {
|
|
61
|
+
meshCallback(o);
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}, error=>{
|
|
67
|
-
console.error(error);
|
|
68
|
-
/* legacyGLTFLoader.parse(gltfBuffer, model => {
|
|
69
|
-
|
|
70
|
-
////TODO
|
|
71
|
-
//model.batchTable = b3dm.batchTable;
|
|
72
|
-
//model.featureTable = b3dm.featureTable;
|
|
73
|
-
|
|
74
|
-
//model.scene.batchTable = b3dm.batchTable;
|
|
75
|
-
//model.scene.featureTable = b3dm.featureTable;
|
|
76
|
-
|
|
77
|
-
//const scene = mergeColoredObject(model.scene);
|
|
78
|
-
model.scene.traverse((o) => {
|
|
79
|
-
if (o.isMesh) {
|
|
80
|
-
if (!!meshCallback) {
|
|
81
|
-
meshCallback(o);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
resolve(model.scene);
|
|
87
|
-
}, null); */
|
|
63
|
+
|
|
64
|
+
}
|
|
88
65
|
});
|
|
66
|
+
resolve(model.scene);
|
|
67
|
+
}, error => {
|
|
68
|
+
console.error(error);
|
|
89
69
|
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const B3DMDecoder = {
|
|
74
|
+
parseB3DM: parseB3DM,
|
|
75
|
+
parseB3DMInstanced: (arrayBuffer, meshCallback, maxCount) => { // expects GLTF with one node level
|
|
76
|
+
|
|
77
|
+
return parseB3DM(arrayBuffer, meshCallback).then(mesh => {
|
|
78
|
+
let instancedMesh;
|
|
79
|
+
mesh.traverse(child => {
|
|
80
|
+
if (child.isMesh) {
|
|
81
|
+
instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, maxCount);
|
|
82
|
+
instancedMesh.baseMatrix = child.matrix;
|
|
83
|
+
//console.log(child.matrix.elements[12])
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return instancedMesh;
|
|
87
|
+
});
|
|
88
|
+
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
|
package/src/index.js
CHANGED
|
@@ -6,33 +6,86 @@ 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
8
|
import { OcclusionCullingService } from "./tileset/OcclusionCullingService";
|
|
9
|
+
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
|
|
10
|
+
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
|
11
|
+
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
|
|
12
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
9
13
|
|
|
14
|
+
import { InstancedOGC3DTile} from "./tileset/instanced/InstancedOGC3DTile.js"
|
|
15
|
+
import { InstancedTileLoader} from "./tileset/instanced/InstancedTileLoader.js"
|
|
16
|
+
|
|
17
|
+
import { B3DMDecoder } from "./decoder/B3DMDecoder";
|
|
10
18
|
|
|
11
19
|
const occlusionCullingService = new OcclusionCullingService();
|
|
12
20
|
occlusionCullingService.setSide(THREE.DoubleSide);
|
|
13
21
|
const scene = initScene();
|
|
22
|
+
|
|
14
23
|
const domContainer = initDomContainer("screen");
|
|
15
24
|
const camera = initCamera();
|
|
16
|
-
const ogc3DTiles = initTileset(scene);
|
|
17
|
-
|
|
25
|
+
//const ogc3DTiles = initTileset(scene);
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const instancedTileLoader = createInstancedTileLoader(scene);
|
|
29
|
+
initInstancedTilesets(instancedTileLoader);
|
|
30
|
+
|
|
18
31
|
const controller = initController(camera, domContainer);
|
|
19
32
|
|
|
20
33
|
const stats = initStats(domContainer);
|
|
21
34
|
const renderer = initRenderer(camera, domContainer);
|
|
35
|
+
const composer = initComposer(scene, camera, renderer);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/* fetch("https://storage.googleapis.com/ogc-3d-tiles/droneship/1/2007.b3dm").then(result => {
|
|
39
|
+
|
|
40
|
+
if (!result.ok) {
|
|
41
|
+
console.error("could not load tile with path : " + path)
|
|
42
|
+
throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
|
|
43
|
+
}
|
|
44
|
+
return result.arrayBuffer();
|
|
45
|
+
|
|
46
|
+
})
|
|
47
|
+
.then(resultArrayBuffer=>{
|
|
48
|
+
return B3DMDecoder.parseB3DMInstanced(resultArrayBuffer, self.meshCallback, 1);
|
|
49
|
+
})
|
|
50
|
+
.then(mesh=>{
|
|
51
|
+
scene.add(mesh)
|
|
52
|
+
|
|
53
|
+
}) */
|
|
22
54
|
|
|
23
55
|
|
|
24
56
|
animate();
|
|
25
57
|
|
|
58
|
+
|
|
59
|
+
function initComposer(scene, camera, renderer) {
|
|
60
|
+
const renderScene = new RenderPass(scene, camera);
|
|
61
|
+
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.4, 0.5, 0);
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const composer = new EffectComposer(renderer);
|
|
65
|
+
composer.addPass(renderScene);
|
|
66
|
+
composer.addPass(bloomPass);
|
|
67
|
+
return composer;
|
|
68
|
+
}
|
|
26
69
|
function initScene() {
|
|
27
70
|
const scene = new THREE.Scene();
|
|
28
71
|
scene.matrixAutoUpdate = false;
|
|
29
|
-
scene.background = new THREE.Color(
|
|
30
|
-
scene.add(new THREE.AmbientLight(0xFFFFFF, 0.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
scene.
|
|
72
|
+
scene.background = new THREE.Color(0x000000);
|
|
73
|
+
scene.add(new THREE.AmbientLight(0xFFFFFF, 0.2));
|
|
74
|
+
|
|
75
|
+
const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
76
|
+
const sphere = new THREE.SphereGeometry(2, 16, 8);
|
|
77
|
+
light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
|
|
78
|
+
scene.add(light);
|
|
79
|
+
light.position.set(200, 200, 200);
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
const light2 = new THREE.PointLight(0xffbbbb, 2, 5000);
|
|
83
|
+
const sphere2 = new THREE.SphereGeometry(2, 16, 8);
|
|
84
|
+
light2.add(new THREE.Mesh(sphere2, new THREE.MeshBasicMaterial({ color: 0xffbbbb })));
|
|
85
|
+
scene.add(light2);
|
|
86
|
+
light2.position.set(200, 100, -100);
|
|
87
|
+
|
|
88
|
+
scene.matrixWorldAutoUpdate = true;
|
|
36
89
|
return scene;
|
|
37
90
|
}
|
|
38
91
|
|
|
@@ -51,6 +104,8 @@ function initRenderer(camera, dom) {
|
|
|
51
104
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
52
105
|
renderer.setSize(dom.offsetWidth, dom.offsetHeight);
|
|
53
106
|
renderer.outputEncoding = THREE.sRGBEncoding;
|
|
107
|
+
//renderer.toneMapping = THREE.ReinhardToneMapping;
|
|
108
|
+
//renderer.toneMappingExposure = Math.pow(0.8, 4.0);
|
|
54
109
|
renderer.autoClear = false;
|
|
55
110
|
|
|
56
111
|
dom.appendChild(renderer.domElement);
|
|
@@ -77,8 +132,8 @@ function initStats(dom) {
|
|
|
77
132
|
|
|
78
133
|
|
|
79
134
|
function initCamera() {
|
|
80
|
-
const camera = new THREE.PerspectiveCamera(
|
|
81
|
-
camera.position.set(
|
|
135
|
+
const camera = new THREE.PerspectiveCamera(40, window.offsetWidth / window.offsetHeight, 1, 100000);
|
|
136
|
+
camera.position.set(100, 10, 100);
|
|
82
137
|
camera.matrixAutoUpdate = true;
|
|
83
138
|
return camera;
|
|
84
139
|
}
|
|
@@ -88,70 +143,117 @@ function initTileset(scene) {
|
|
|
88
143
|
const tileLoader = new TileLoader(mesh => {
|
|
89
144
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
90
145
|
mesh.material.wireframe = false;
|
|
91
|
-
mesh.material.side = THREE.
|
|
146
|
+
mesh.material.side = THREE.FrontSide;
|
|
92
147
|
}, 1000)
|
|
93
148
|
const ogc3DTile = new OGC3DTile({
|
|
94
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/
|
|
95
|
-
//url: "
|
|
96
|
-
geometricErrorMultiplier: 0
|
|
149
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
150
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
151
|
+
geometricErrorMultiplier: 1.0,
|
|
97
152
|
loadOutsideView: false,
|
|
98
153
|
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
|
|
154
|
+
//occlusionCullingService: occlusionCullingService,
|
|
155
|
+
static: false,
|
|
156
|
+
|
|
111
157
|
});
|
|
112
158
|
|
|
113
159
|
|
|
114
|
-
|
|
160
|
+
|
|
161
|
+
|
|
115
162
|
//// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
|
|
116
163
|
//ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
117
164
|
//// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
|
|
118
|
-
|
|
119
|
-
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
120
168
|
//// 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
|
|
121
169
|
|
|
122
170
|
|
|
123
|
-
|
|
171
|
+
|
|
124
172
|
var interval;
|
|
125
173
|
document.addEventListener('keyup', (e) => {
|
|
126
174
|
console.log(camera.position)
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
175
|
+
if (!!e.key && e.key !== "p") {
|
|
176
|
+
|
|
177
|
+
if (!!interval) {
|
|
178
|
+
clearInterval(interval);
|
|
179
|
+
interval = null;
|
|
180
|
+
} else {
|
|
181
|
+
startInterval();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!!e.key && e.key !== "l") {
|
|
185
|
+
|
|
186
|
+
console.log("new THREE.Vector3(" + camera.position.x + "," + camera.position.y + "," + camera.position.z + ")");
|
|
187
|
+
console.log("new THREE.Quaternion(" + camera.quaternion.x + "," + camera.quaternion.y + "," + camera.quaternion.z + "," + camera.quaternion.w + ")");
|
|
188
|
+
|
|
133
189
|
}
|
|
190
|
+
|
|
134
191
|
});
|
|
135
192
|
function startInterval() {
|
|
136
193
|
interval = setIntervalAsync(function () {
|
|
137
194
|
ogc3DTile.update(camera);
|
|
138
|
-
|
|
195
|
+
|
|
139
196
|
}, 20);
|
|
140
197
|
}
|
|
141
198
|
startInterval();
|
|
142
199
|
|
|
143
200
|
scene.add(ogc3DTile)
|
|
144
|
-
scene.add(ogc3DTile2)
|
|
145
201
|
return ogc3DTile;
|
|
146
202
|
}
|
|
147
203
|
|
|
148
|
-
function
|
|
204
|
+
function createInstancedTileLoader(scene){
|
|
205
|
+
return new InstancedTileLoader(scene, mesh => {
|
|
206
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
207
|
+
mesh.material.wireframe = false;
|
|
208
|
+
mesh.material.side = THREE.FrontSide;
|
|
209
|
+
}, 1000, 1000);
|
|
210
|
+
}
|
|
211
|
+
function initInstancedTilesets(instancedTileLoader){
|
|
212
|
+
|
|
213
|
+
const instancedTilesets = [];
|
|
214
|
+
|
|
215
|
+
for(let x = 0; x<10; x++){
|
|
216
|
+
for(let y = 0; y<10; y++){
|
|
217
|
+
for(let z = 0; z<10; z++){
|
|
218
|
+
const tileset = new InstancedOGC3DTile({
|
|
219
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
220
|
+
//url: "http://localhost:8080/tileset.json",
|
|
221
|
+
geometricErrorMultiplier: 0.5,
|
|
222
|
+
loadOutsideView: false,
|
|
223
|
+
tileLoader: instancedTileLoader,
|
|
224
|
+
static: false,
|
|
225
|
+
});
|
|
226
|
+
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50*x)
|
|
227
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 15*y)
|
|
228
|
+
tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 25*z)
|
|
229
|
+
scene.add(tileset);
|
|
230
|
+
instancedTilesets.push(tileset);
|
|
231
|
+
|
|
232
|
+
idleCallback();
|
|
233
|
+
|
|
234
|
+
function idleCallback(){
|
|
235
|
+
tileset.update(camera);
|
|
236
|
+
setTimeout(()=>{
|
|
237
|
+
window.requestIdleCallback(idleCallback,{timeout:50})
|
|
238
|
+
},20)
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
initLODMultiplierSlider(instancedTilesets);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function initLODMultiplierSlider(instancedTilesets) {
|
|
149
249
|
var slider = document.getElementById("lodMultiplier");
|
|
150
250
|
var output = document.getElementById("multiplierValue");
|
|
151
251
|
output.innerHTML = slider.value;
|
|
152
252
|
|
|
153
253
|
slider.oninput = () => {
|
|
154
|
-
|
|
254
|
+
instancedTilesets.forEach(tileset=>{
|
|
255
|
+
tileset.setGeometricErrorMultiplier(slider.value)
|
|
256
|
+
})
|
|
155
257
|
output.innerHTML = slider.value;
|
|
156
258
|
}
|
|
157
259
|
}
|
|
@@ -160,8 +262,8 @@ function initController(camera, dom) {
|
|
|
160
262
|
const controller = new OrbitControls(camera, dom);
|
|
161
263
|
|
|
162
264
|
controller.target.set(0, 0, 0);
|
|
163
|
-
controller.minDistance =
|
|
164
|
-
controller.maxDistance =
|
|
265
|
+
controller.minDistance = 0.01;
|
|
266
|
+
controller.maxDistance = 100000;
|
|
165
267
|
controller.update();
|
|
166
268
|
return controller;
|
|
167
269
|
}
|
|
@@ -169,13 +271,13 @@ function initController(camera, dom) {
|
|
|
169
271
|
|
|
170
272
|
function animate() {
|
|
171
273
|
requestAnimationFrame(animate);
|
|
172
|
-
|
|
173
|
-
|
|
274
|
+
instancedTileLoader.update();
|
|
275
|
+
composer.render();
|
|
276
|
+
//occlusionCullingService.update(scene, renderer, camera)
|
|
174
277
|
stats.update();
|
|
175
|
-
|
|
176
|
-
|
|
177
278
|
}
|
|
178
279
|
|
|
179
280
|
|
|
180
281
|
|
|
181
282
|
|
|
283
|
+
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -76,14 +76,14 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
76
76
|
this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
|
|
77
77
|
this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
|
|
78
78
|
|
|
79
|
+
this.abortController = new AbortController();
|
|
79
80
|
this.layers.disable(0);
|
|
80
81
|
|
|
81
82
|
if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
|
|
82
83
|
self.setup(properties);
|
|
83
84
|
if (properties.onLoadCallback) properties.onLoadCallback(self);
|
|
84
85
|
} else if (properties.url) { // If only the url to the tileset.json is provided
|
|
85
|
-
self.
|
|
86
|
-
fetch(properties.url, { signal: self.controller.signal }).then(result => {
|
|
86
|
+
fetch(properties.url, { signal: self.abortController.signal }).then(result => {
|
|
87
87
|
if (!result.ok) {
|
|
88
88
|
throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
|
|
89
89
|
}
|
|
@@ -176,7 +176,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
176
176
|
if (!!url) {
|
|
177
177
|
if (url.includes(".b3dm")) {
|
|
178
178
|
self.contentURL = url;
|
|
179
|
-
self.tileLoader.get(this.uuid, url, mesh => {
|
|
179
|
+
self.tileLoader.get(self.abortController,this.uuid, url, mesh => {
|
|
180
180
|
if (!!self.deleted) return;
|
|
181
181
|
mesh.traverse((o) => {
|
|
182
182
|
if (o.isMesh) {
|
|
@@ -192,7 +192,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
192
192
|
if(self.static){
|
|
193
193
|
o.matrixAutoUpdate = false;
|
|
194
194
|
}
|
|
195
|
-
o.material.visible = false;
|
|
195
|
+
//o.material.visible = false;
|
|
196
196
|
}
|
|
197
197
|
});
|
|
198
198
|
|
|
@@ -204,7 +204,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
204
204
|
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
205
205
|
}, () => self.getSiblings(), self.level);
|
|
206
206
|
} else if (url.includes(".json")) {
|
|
207
|
-
self.tileLoader.get(this.uuid, url, json => {
|
|
207
|
+
self.tileLoader.get(self.abortController,this.uuid, url, json => {
|
|
208
208
|
if (!!self.deleted) return;
|
|
209
209
|
if (!self.json.children) self.json.children = [];
|
|
210
210
|
json.rootPath = path.dirname(url);
|
|
@@ -228,8 +228,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
228
228
|
if (!!element.contentURL) {
|
|
229
229
|
self.tileLoader.invalidate(element.contentURL, element.uuid);
|
|
230
230
|
}
|
|
231
|
-
if (!!element.
|
|
232
|
-
element.
|
|
231
|
+
if (!!element.abortController) { // abort tile request
|
|
232
|
+
element.abortController.abort();
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
});
|
|
@@ -450,7 +450,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
450
450
|
return true;
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
-
return
|
|
453
|
+
return false;
|
|
454
454
|
|
|
455
455
|
}
|
|
456
456
|
|