@jdultra/threedtiles 7.0.2 → 8.0.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/.babelrc +8 -0
- package/README.md +45 -20
- package/package.json +8 -7
- package/src/decoder/B3DMDecoder.js +82 -203
- package/src/index.js +71 -31
- package/src/tileset/OGC3DTile.js +30 -25
- package/src/tileset/TileLoader.js +44 -10
- package/src/tileset/instanced/InstancedTile.js +1 -1
- package/src/tileset/instanced/InstancedTileLoader.js +85 -51
- package/threedtiles.js +80023 -20211
- package/threedtiles.js.map +1 -1
- package/webpack.config.js +17 -16
package/.babelrc
ADDED
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ The fastest 3DTiles viewer for three.js
|
|
|
14
14
|
|
|
15
15
|
[Instanced Tileset (pilot a swarm of highly detailed spaceships)](https://www.jdultra.com/instanced/index.html)
|
|
16
16
|
|
|
17
|
-
NPM dependency: "@jdultra/threedtiles": "^
|
|
17
|
+
NPM dependency: "@jdultra/threedtiles": "^8.0.0"
|
|
18
18
|
|
|
19
19
|
Adding a tileset to a scene is as easy as :
|
|
20
20
|
|
|
@@ -68,6 +68,7 @@ Contact: emeric.beaufays@jdultra.com
|
|
|
68
68
|
- Instanced tilesets
|
|
69
69
|
- Center tileset and re-orient geolocated data
|
|
70
70
|
- gltf/glb tiles (OGC3DTiles 1.1)
|
|
71
|
+
- draco and ktx2 compression support
|
|
71
72
|
- point clouds (only through gltf/glb tiles)
|
|
72
73
|
|
|
73
74
|
Support for OGC3DTiles 1.1 is underway. gltf/glb tiles are already supported but not yet implicit tiling.
|
|
@@ -90,7 +91,10 @@ const ogc3DTile = new OGC3DTile({
|
|
|
90
91
|
A lower value will result in lower detail tiles being loaded and a higher value results in higher detail tiles being loaded.
|
|
91
92
|
A value of 1.0 is the default.
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
#### geometricErrorMultiplier vs maxScreenSpaceError
|
|
95
|
+
Many viewers use the maxScreenSpaceError instead of a geometric error multiplier and there is a direct correspondance.
|
|
96
|
+
A geometricErrorMultiplier of 1 corresponds to a maxScreenSpaceError of 16.
|
|
97
|
+
A geometricErrorMultiplier of 0.5 corresponds to a maxScreenSpaceError of 32.
|
|
94
98
|
|
|
95
99
|
### load tiles outside of view
|
|
96
100
|
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.
|
|
@@ -154,25 +158,34 @@ If using a shared cache between tilesets, check out the next section.
|
|
|
154
158
|
You may instanciate a cache through the TileLoader class and re-use it for several or all your tilesets.
|
|
155
159
|
The limitation is that all the tilesets using the same cache will have the same callback.
|
|
156
160
|
|
|
157
|
-
The TileLoader
|
|
158
|
-
|
|
161
|
+
The TileLoader takes an optional object as argument:
|
|
162
|
+
@param {Object} [options] - Optional configuration object.
|
|
163
|
+
@param {number} [options.maxCachedItems=100] - the cache size.
|
|
164
|
+
@param {function} [options.meshCallback] - A callback to call on newly decoded meshes.
|
|
165
|
+
@param {function} [options.pointsCallback] - A callback to call on newly decoded points.
|
|
166
|
+
@param {renderer} [options.renderer] - The renderer, this is required for KTX2 support.
|
|
159
167
|
|
|
160
168
|
```
|
|
161
169
|
import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
|
|
162
170
|
|
|
163
|
-
const
|
|
164
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
171
|
+
const tileLoader = new TileLoader({
|
|
165
172
|
renderer: renderer,
|
|
166
|
-
|
|
173
|
+
maxCachedItems: 100,
|
|
174
|
+
meshCallback: mesh => {
|
|
167
175
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
168
176
|
mesh.material.wireframe = false;
|
|
169
177
|
mesh.material.side = THREE.DoubleSide;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
//mesh.material.metalness = 0.0
|
|
179
|
+
},
|
|
180
|
+
pointsCallback: points => {
|
|
181
|
+
points.material.size = Math.min(1.0, 0.5 * Math.sqrt(points.geometricError));
|
|
182
|
+
points.material.sizeAttenuation = true;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
const ogc3DTile = new OGC3DTile({
|
|
186
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
187
|
+
renderer: renderer,
|
|
188
|
+
tileLoader: tileLoader,
|
|
176
189
|
meshCallback: mesh => { mesh.material.wireframe = true;} // This callback will not be used as the callback provided to the TileLoader takes priority
|
|
177
190
|
});
|
|
178
191
|
```
|
|
@@ -242,12 +255,19 @@ higher performance when displaying the same Tileset many times.
|
|
|
242
255
|
// First create the InstancedTileLoader that will manage caching
|
|
243
256
|
const instancedTileLoader = new InstancedTileLoader(
|
|
244
257
|
scene,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
mesh
|
|
250
|
-
|
|
258
|
+
{
|
|
259
|
+
renderer: renderer,
|
|
260
|
+
maxCachedItems : 100,
|
|
261
|
+
maxInstances : 1,
|
|
262
|
+
meshCallback: mesh => {
|
|
263
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
264
|
+
mesh.material.wireframe = false;
|
|
265
|
+
mesh.material.side = THREE.DoubleSide;
|
|
266
|
+
},
|
|
267
|
+
pointsCallback: points => {
|
|
268
|
+
points.material.size = Math.min(1.0, 0.5 * Math.sqrt(points.geometricError));
|
|
269
|
+
points.material.sizeAttenuation = true;
|
|
270
|
+
}
|
|
251
271
|
}
|
|
252
272
|
);
|
|
253
273
|
|
|
@@ -321,7 +341,12 @@ scene.matrixWorldAutoUpdate = false;
|
|
|
321
341
|
scene.updateMatrixWorld(true);
|
|
322
342
|
|
|
323
343
|
```
|
|
324
|
-
|
|
344
|
+
### Draco and Ktx2
|
|
345
|
+
Compressed meshes via Draco and compressed textures in Ktx2 format are supported automatically using the threejs plugins.
|
|
346
|
+
KTX uses an external wasm loaded at runtime so if you have trouble packaging your app correctly, check out the
|
|
347
|
+
[Getting started](https://drive.google.com/file/d/1kJ-yfYmy8ShOMMPPXgqW2gMgGkLOIidf/view?usp=share_link) project for a sample webpack configuration.
|
|
348
|
+
|
|
349
|
+
### tileset update loop
|
|
325
350
|
Updating a single tileset via OGC3DTile#update or InstancedOGC3DTile#update is quite fast, even when the tree is deep.
|
|
326
351
|
For a single tileset, it's safe to call it regularly with a setInterval:
|
|
327
352
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jdultra/threedtiles",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.1",
|
|
4
4
|
"description": "An OGC 3DTiles viewer for Three.js",
|
|
5
5
|
"main": "tileset.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,17 +35,18 @@
|
|
|
35
35
|
"uuid": "^8.3.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@babel/core": "^7.
|
|
39
|
-
"@babel/
|
|
40
|
-
"babel-
|
|
38
|
+
"@babel/core": "^7.21.4",
|
|
39
|
+
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
40
|
+
"@babel/preset-env": "^7.21.4",
|
|
41
|
+
"babel-loader": "^9.1.2",
|
|
41
42
|
"copy-webpack-plugin": "^6.3.2",
|
|
42
43
|
"core-js": "^3.27.1",
|
|
43
44
|
"html-loader": "^1.3.2",
|
|
44
45
|
"html-webpack-plugin": "^4.5.0",
|
|
45
46
|
"mini-css-extract-plugin": "^1.6.2",
|
|
46
|
-
"webpack": "^5.
|
|
47
|
-
"webpack-cli": "
|
|
48
|
-
"webpack-dev-server": "^4.
|
|
47
|
+
"webpack": "^5.79.0",
|
|
48
|
+
"webpack-cli": "^5.0.1",
|
|
49
|
+
"webpack-dev-server": "^4.13.2",
|
|
49
50
|
"webpack-glsl-loader": "^1.0.1",
|
|
50
51
|
"whatwg-fetch": "^3.5.0"
|
|
51
52
|
}
|
|
@@ -1,109 +1,114 @@
|
|
|
1
1
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
2
2
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
3
|
+
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
|
|
3
4
|
import * as THREE from 'three';
|
|
4
|
-
import {FeatureTable, BatchTable} from './FeatureTable';
|
|
5
|
+
import { FeatureTable, BatchTable } from './FeatureTable';
|
|
5
6
|
|
|
6
|
-
const gltfLoader = new GLTFLoader();
|
|
7
|
-
const dracoLoader = new DRACOLoader();
|
|
8
|
-
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
9
|
-
gltfLoader.setDRACOLoader(dracoLoader);
|
|
10
|
-
const tempMatrix = new THREE.Matrix4();
|
|
11
7
|
const zUpToYUpMatrix = new THREE.Matrix4();
|
|
12
|
-
zUpToYUpMatrix.set(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
zUpToYUpMatrix.set(
|
|
9
|
+
1, 0, 0, 0,
|
|
10
|
+
0, 0, -1, 0,
|
|
11
|
+
0, 1, 0, 0,
|
|
12
|
+
0, 0, 0, 1);
|
|
13
|
+
|
|
14
|
+
export class B3DMDecoder {
|
|
15
|
+
constructor(renderer) {
|
|
16
|
+
this.gltfLoader = new GLTFLoader();
|
|
17
|
+
const dracoLoader = new DRACOLoader();
|
|
18
|
+
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
19
|
+
this.gltfLoader.setDRACOLoader(dracoLoader);
|
|
20
|
+
if (renderer) {
|
|
21
|
+
const ktx2Loader = new KTX2Loader();
|
|
22
|
+
ktx2Loader.setTranscoderPath('https://storage.googleapis.com/ogc-3d-tiles/basis/').detectSupport(renderer);
|
|
23
|
+
this.gltfLoader.setKTX2Loader(ktx2Loader)
|
|
24
|
+
}
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
this.tempMatrix = new THREE.Matrix4();
|
|
27
|
+
}
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
String.fromCharCode(dataView.getUint8(1)) +
|
|
25
|
-
String.fromCharCode(dataView.getUint8(2)) +
|
|
26
|
-
String.fromCharCode(dataView.getUint8(3));
|
|
27
|
-
console.assert(magic === 'b3dm');
|
|
29
|
+
parseB3DM(arrayBuffer, meshCallback, geometricError, zUpToYUp) {
|
|
30
|
+
const dataView = new DataView(arrayBuffer);
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
const magic =
|
|
33
|
+
String.fromCharCode(dataView.getUint8(0)) +
|
|
34
|
+
String.fromCharCode(dataView.getUint8(1)) +
|
|
35
|
+
String.fromCharCode(dataView.getUint8(2)) +
|
|
36
|
+
String.fromCharCode(dataView.getUint8(3));
|
|
37
|
+
console.assert(magic === 'b3dm');
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
const byteLength = dataView.getUint32(8, true);
|
|
40
|
+
console.assert(byteLength === arrayBuffer.byteLength);
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
const featureTableJSONByteLength = dataView.getUint32(12, true);
|
|
43
|
+
const featureTableBinaryByteLength = dataView.getUint32(16, true);
|
|
44
|
+
const batchTableJSONByteLength = dataView.getUint32(20, true);
|
|
45
|
+
const batchTableBinaryByteLength = dataView.getUint32(24, true);
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
const featureTableStart = 28;
|
|
48
|
+
const featureTable = new FeatureTable(arrayBuffer, featureTableStart, featureTableJSONByteLength, featureTableBinaryByteLength);
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
|
|
51
|
+
const batchTable = new BatchTable(arrayBuffer, featureTable.getData('BATCH_LENGTH'), batchTableStart, batchTableJSONByteLength, batchTableBinaryByteLength);
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
|
|
54
|
+
const glbBytes = new Uint8Array(arrayBuffer, glbStart, byteLength - glbStart);
|
|
48
55
|
|
|
49
56
|
|
|
50
|
-
|
|
57
|
+
const gltfBuffer = glbBytes.slice().buffer;
|
|
51
58
|
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
54
61
|
|
|
55
|
-
|
|
62
|
+
this.gltfLoader.parse(gltfBuffer, null, model => {
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
////TODO
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
//model.batchTable = b3dm.batchTable;
|
|
67
|
+
//model.featureTable = b3dm.featureTable;
|
|
61
68
|
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
//model.scene.batchTable = b3dm.batchTable;
|
|
70
|
+
//model.scene.featureTable = b3dm.featureTable;
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
//const scene = mergeColoredObject(model.scene);
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
//model.scene.applyMatrix4(ytozUpMatrix);
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if(!zUpToYUp){
|
|
79
|
-
model.scene.applyMatrix4(zUpToYUpMatrix);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
model.scene.traverse((o) => {
|
|
83
|
-
|
|
84
|
-
if (o.isMesh) {
|
|
85
|
-
o.geometricError = geometricError
|
|
86
|
-
if(zUpToYUp){
|
|
87
|
-
o.applyMatrix4(zUpToYUpMatrix);
|
|
88
|
-
}
|
|
89
|
-
if (!!meshCallback) {
|
|
90
|
-
meshCallback(o);
|
|
91
|
-
}
|
|
76
|
+
const rtcCenter = featureTable.getData('RTC_CENTER');
|
|
77
|
+
if (rtcCenter) {
|
|
78
|
+
this.tempMatrix.makeTranslation(rtcCenter[0], rtcCenter[1], rtcCenter[2])
|
|
79
|
+
model.scene.applyMatrix4(this.tempMatrix);
|
|
80
|
+
} else if (!!model.userData.gltfExtensions && !!model.userData.gltfExtensions.CESIUM_RTC) {
|
|
81
|
+
this.tempMatrix.makeTranslation(model.userData.gltfExtensions.CESIUM_RTC.center[0], model.userData.gltfExtensions.CESIUM_RTC.center[1], model.userData.gltfExtensions.CESIUM_RTC.center[2])
|
|
82
|
+
model.scene.applyMatrix4(this.tempMatrix);
|
|
83
|
+
}
|
|
92
84
|
|
|
85
|
+
if (!zUpToYUp) {
|
|
86
|
+
model.scene.applyMatrix4(zUpToYUpMatrix);
|
|
93
87
|
}
|
|
88
|
+
|
|
89
|
+
model.scene.traverse((o) => {
|
|
90
|
+
|
|
91
|
+
if (o.isMesh) {
|
|
92
|
+
o.geometricError = geometricError
|
|
93
|
+
if (zUpToYUp) {
|
|
94
|
+
o.applyMatrix4(zUpToYUpMatrix);
|
|
95
|
+
}
|
|
96
|
+
if (!!meshCallback) {
|
|
97
|
+
meshCallback(o);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
resolve(model.scene);
|
|
103
|
+
}, error => {
|
|
104
|
+
console.error(error);
|
|
94
105
|
});
|
|
95
|
-
resolve(model.scene);
|
|
96
|
-
}, error => {
|
|
97
|
-
console.error(error);
|
|
98
106
|
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
107
|
+
}
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
parseB3DM: parseB3DM,
|
|
104
|
-
parseB3DMInstanced: (arrayBuffer, meshCallback, maxCount, zUpToYUp) => { // expects GLTF with one node level
|
|
109
|
+
parseB3DMInstanced(arrayBuffer, meshCallback, maxCount, zUpToYUp) { // expects GLTF with one node level
|
|
105
110
|
|
|
106
|
-
return parseB3DM(arrayBuffer, meshCallback, zUpToYUp).then(mesh => {
|
|
111
|
+
return this.parseB3DM(arrayBuffer, meshCallback, zUpToYUp).then(mesh => {
|
|
107
112
|
// todo several meshes in a single gltf
|
|
108
113
|
let instancedMesh;
|
|
109
114
|
mesh.updateWorldMatrix(false, true)
|
|
@@ -117,130 +122,4 @@ const B3DMDecoder = {
|
|
|
117
122
|
});
|
|
118
123
|
|
|
119
124
|
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* //TODO find something else than this workaround
|
|
124
|
-
*
|
|
125
|
-
* Because B3DM doesn't support colored faces, they are usually encoded as separate meshes each one with a global color.
|
|
126
|
-
* However, when a mesh has many different face colors, this becomes very inneficient.
|
|
127
|
-
* This method doesn't fix the slow decoding of the GLTFLoader but at least merges meshes together and transfers the face color to vertex color
|
|
128
|
-
* which is much more efficient at render time.
|
|
129
|
-
* Textured meshes with the same texture are also merged and color is discarded
|
|
130
|
-
*
|
|
131
|
-
* Big assumption! all the meshes are assumed to have the same transformation matrix
|
|
132
|
-
*
|
|
133
|
-
* @param {*} scene
|
|
134
|
-
* @returns
|
|
135
|
-
*/
|
|
136
|
-
/*function mergeColoredObject(scene) {
|
|
137
|
-
|
|
138
|
-
const coloredMeshes = {};
|
|
139
|
-
const texturedMeshes = {};
|
|
140
|
-
scene.traverse((element) => {
|
|
141
|
-
if (element.isMesh) {
|
|
142
|
-
if (element.material) {
|
|
143
|
-
// dispose materials
|
|
144
|
-
if (element.material.length) {
|
|
145
|
-
// not supported
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
if (!element.material.map) {
|
|
149
|
-
let color = element.material.color;
|
|
150
|
-
color = "rgb("+Math.floor(color.r*255)+","+Math.floor(color.g*255)+","+Math.floor(color.b*255)+")";
|
|
151
|
-
if (!coloredMeshes[color]) {
|
|
152
|
-
coloredMeshes[color] = [];
|
|
153
|
-
}
|
|
154
|
-
coloredMeshes[color].push(element);
|
|
155
|
-
} else {
|
|
156
|
-
if (!texturedMeshes[element.material.map]) {
|
|
157
|
-
texturedMeshes[element.material.map] = [];
|
|
158
|
-
}
|
|
159
|
-
texturedMeshes[element.material.map].push(element);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
let coloredMeshMaterial;
|
|
167
|
-
const fullColoredGeometriesToMerge = [];
|
|
168
|
-
for (const color in coloredMeshes) {
|
|
169
|
-
if (coloredMeshes.hasOwnProperty(color)) {
|
|
170
|
-
const threeColor = new Color(color);
|
|
171
|
-
//const geometriesToMerge = [];
|
|
172
|
-
coloredMeshes[color].forEach(mesh => {
|
|
173
|
-
if(!coloredMeshMaterial){
|
|
174
|
-
coloredMeshMaterial = mesh.material.clone();
|
|
175
|
-
delete coloredMeshMaterial.color;
|
|
176
|
-
coloredMeshMaterial.vertexColors = true;
|
|
177
|
-
}
|
|
178
|
-
const colors = [];
|
|
179
|
-
for (let i = 0; i < mesh.geometry.attributes.position.count; i++) {
|
|
180
|
-
colors.push(threeColor.r);
|
|
181
|
-
colors.push(threeColor.g);
|
|
182
|
-
colors.push(threeColor.b);
|
|
183
|
-
}
|
|
184
|
-
mesh.geometry.setAttribute('color', new BufferAttribute(new Float32Array(colors), 3));
|
|
185
|
-
|
|
186
|
-
fullColoredGeometriesToMerge.push(mesh.geometry);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
let mergedColoredMesh;
|
|
193
|
-
if(fullColoredGeometriesToMerge.length>0){
|
|
194
|
-
let mergedColoredGeometry = BufferGeometryUtils.mergeBufferGeometries(fullColoredGeometriesToMerge, false);
|
|
195
|
-
mergedColoredMesh = new Mesh(mergedColoredGeometry, coloredMeshMaterial);
|
|
196
|
-
//mergedColoredMesh.matrix = matrix;
|
|
197
|
-
for (const color in coloredMeshes) {
|
|
198
|
-
if (coloredMeshes.hasOwnProperty(color)) {
|
|
199
|
-
coloredMeshes[color].forEach(mesh => {
|
|
200
|
-
mesh.material.dispose();
|
|
201
|
-
mesh.geometry.dispose();
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const mergedTexturedMeshes = [];
|
|
209
|
-
for(const map in texturedMeshes){
|
|
210
|
-
if (texturedMeshes.hasOwnProperty(map)) {
|
|
211
|
-
if(texturedMeshes[map].length==1){
|
|
212
|
-
mergedTexturedMeshes.push(texturedMeshes[map][0]);
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
const geometries = [];
|
|
216
|
-
let material;
|
|
217
|
-
texturedMeshes[map].forEach(mesh => {
|
|
218
|
-
if(!material){
|
|
219
|
-
material = mesh.material.clone();
|
|
220
|
-
delete material.color;
|
|
221
|
-
material.vertexColors = false;
|
|
222
|
-
}
|
|
223
|
-
geometries.push(mesh.geometry);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries, false);
|
|
227
|
-
const mesh = new Mesh(mergedGeometry, material);
|
|
228
|
-
//mesh.matrix = matrix;
|
|
229
|
-
mergedTexturedMeshes.push(mesh);
|
|
230
|
-
texturedMeshes[map].forEach(mesh => {
|
|
231
|
-
mesh.material.dispose();
|
|
232
|
-
mesh.geometry.dispose();
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
scene.clear();
|
|
238
|
-
if(!!mergedColoredMesh) scene.add(mergedColoredMesh);
|
|
239
|
-
mergedTexturedMeshes.forEach(mesh=>scene.add(mesh));
|
|
240
|
-
console.log();
|
|
241
|
-
scene.matrix = new THREE.Matrix4();
|
|
242
|
-
return scene;
|
|
243
|
-
}*/
|
|
244
|
-
|
|
245
|
-
export { B3DMDecoder }
|
|
246
|
-
|
|
125
|
+
}
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,9 @@ import { InstancedOGC3DTile } from "./tileset/instanced/InstancedOGC3DTile.js"
|
|
|
13
13
|
import { InstancedTileLoader } from "./tileset/instanced/InstancedTileLoader.js"
|
|
14
14
|
import { Sky } from 'three/addons/objects/Sky.js';
|
|
15
15
|
|
|
16
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
17
|
+
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
18
|
+
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";
|
|
16
19
|
|
|
17
20
|
const occlusionCullingService = new OcclusionCullingService();
|
|
18
21
|
occlusionCullingService.setSide(THREE.DoubleSide);
|
|
@@ -22,7 +25,44 @@ const domContainer = initDomContainer("screen");
|
|
|
22
25
|
const camera = initCamera(domContainer.offsetWidth, domContainer.offsetHeight);
|
|
23
26
|
const stats = initStats(domContainer);
|
|
24
27
|
const renderer = initRenderer(camera, domContainer);
|
|
25
|
-
const ogc3DTiles = initTileset(scene,
|
|
28
|
+
const ogc3DTiles = initTileset(scene, 1.0);
|
|
29
|
+
//const instancedTileLoader = createInstancedTileLoader(scene);
|
|
30
|
+
//const ogc3DTiles = initInstancedTilesets(instancedTileLoader);
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/*const gltfLoader = new GLTFLoader();
|
|
34
|
+
const dracoLoader = new DRACOLoader();
|
|
35
|
+
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
36
|
+
gltfLoader.setDRACOLoader(dracoLoader);
|
|
37
|
+
|
|
38
|
+
const ktx2Loader = new KTX2Loader();
|
|
39
|
+
ktx2Loader.setTranscoderPath('jsm/libs/basis/');
|
|
40
|
+
//gltfLoader.setKTX2Loader(ktx2Loader)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
gltfLoader.load(
|
|
44
|
+
// resource URL
|
|
45
|
+
'http://localhost:8080/3/15.glb',
|
|
46
|
+
// called when the resource is loaded
|
|
47
|
+
function (gltf) {
|
|
48
|
+
|
|
49
|
+
scene.add(gltf.scene);
|
|
50
|
+
|
|
51
|
+
},
|
|
52
|
+
// called while loading is progressing
|
|
53
|
+
function (xhr) {
|
|
54
|
+
|
|
55
|
+
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
|
|
56
|
+
|
|
57
|
+
},
|
|
58
|
+
// called when loading has errors
|
|
59
|
+
function (error) {
|
|
60
|
+
|
|
61
|
+
console.log('An error happened : ' + error);
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
);*/
|
|
65
|
+
// Optional: Provide a DRACOLoader instance to decode compressed mesh data
|
|
26
66
|
|
|
27
67
|
const controller = initController(camera, domContainer);
|
|
28
68
|
|
|
@@ -150,21 +190,26 @@ function initStats(dom) {
|
|
|
150
190
|
|
|
151
191
|
function initTileset(scene, gem) {
|
|
152
192
|
|
|
153
|
-
const tileLoader = new TileLoader(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
mesh
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
193
|
+
const tileLoader = new TileLoader({
|
|
194
|
+
renderer: renderer,
|
|
195
|
+
maxCachedItems: 100,
|
|
196
|
+
meshCallback: mesh => {
|
|
197
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
198
|
+
mesh.material.wireframe = false;
|
|
199
|
+
mesh.material.side = THREE.DoubleSide;
|
|
200
|
+
//mesh.material.metalness = 0.0
|
|
201
|
+
},
|
|
202
|
+
pointsCallback: points => {
|
|
203
|
+
points.material.size = Math.min(1.0, 0.5 * Math.sqrt(points.geometricError));
|
|
204
|
+
points.material.sizeAttenuation = true;
|
|
205
|
+
}
|
|
161
206
|
});
|
|
162
207
|
|
|
163
208
|
const ogc3DTile = new OGC3DTile({
|
|
164
209
|
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
165
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
|
|
166
|
-
|
|
167
|
-
geometricErrorMultiplier:
|
|
210
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
|
|
211
|
+
url: "http://localhost:8080/tileset.json",
|
|
212
|
+
geometricErrorMultiplier: gem,
|
|
168
213
|
loadOutsideView: true,
|
|
169
214
|
tileLoader: tileLoader,
|
|
170
215
|
//occlusionCullingService: occlusionCullingService,
|
|
@@ -179,7 +224,7 @@ function initTileset(scene, gem) {
|
|
|
179
224
|
|
|
180
225
|
//ogc3DTile.scale.set(0.1,0.1,0.1)
|
|
181
226
|
|
|
182
|
-
|
|
227
|
+
ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), -Math.PI * 0.5) // Z-UP to Y-UP
|
|
183
228
|
//ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 1)
|
|
184
229
|
/*
|
|
185
230
|
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 10) // Z-UP to Y-UP
|
|
@@ -191,13 +236,20 @@ function initTileset(scene, gem) {
|
|
|
191
236
|
|
|
192
237
|
|
|
193
238
|
function createInstancedTileLoader(scene) {
|
|
194
|
-
return new InstancedTileLoader(scene,
|
|
195
|
-
|
|
239
|
+
return new InstancedTileLoader(scene, {
|
|
240
|
+
renderer: renderer,
|
|
241
|
+
maxCachedItems : 100,
|
|
242
|
+
maxInstances : 1,
|
|
243
|
+
meshCallback: mesh => {
|
|
196
244
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
197
245
|
mesh.material.wireframe = false;
|
|
198
246
|
mesh.material.side = THREE.DoubleSide;
|
|
199
|
-
|
|
200
|
-
|
|
247
|
+
},
|
|
248
|
+
pointsCallback: points => {
|
|
249
|
+
points.material.size = Math.min(1.0, 0.5 * Math.sqrt(points.geometricError));
|
|
250
|
+
points.material.sizeAttenuation = true;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
201
253
|
}
|
|
202
254
|
function initInstancedTilesets(instancedTileLoader) {
|
|
203
255
|
|
|
@@ -211,14 +263,14 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
211
263
|
const tileset = new InstancedOGC3DTile({
|
|
212
264
|
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
213
265
|
url: "http://localhost:8080/tileset.json",
|
|
214
|
-
geometricErrorMultiplier:
|
|
266
|
+
geometricErrorMultiplier: 1,
|
|
215
267
|
loadOutsideView: true,
|
|
216
268
|
tileLoader: instancedTileLoader,
|
|
217
269
|
static: false,
|
|
218
270
|
centerModel: true,
|
|
219
271
|
renderer: renderer
|
|
220
272
|
});
|
|
221
|
-
|
|
273
|
+
tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -1) // Z-UP to Y-UP
|
|
222
274
|
|
|
223
275
|
tileset.updateMatrix()
|
|
224
276
|
scene.add(tileset);
|
|
@@ -242,18 +294,6 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
242
294
|
//initLODMultiplierSlider(instancedTilesets);
|
|
243
295
|
}
|
|
244
296
|
|
|
245
|
-
function initLODMultiplierSlider(instancedTilesets) {
|
|
246
|
-
var slider = document.getElementById("lodMultiplier");
|
|
247
|
-
var output = document.getElementById("multiplierValue");
|
|
248
|
-
output.innerHTML = slider.value;
|
|
249
|
-
|
|
250
|
-
slider.oninput = () => {
|
|
251
|
-
instancedTilesets.forEach(tileset => {
|
|
252
|
-
tileset.setGeometricErrorMultiplier(slider.value * 0.1)
|
|
253
|
-
})
|
|
254
|
-
output.innerHTML = slider.value;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
297
|
|
|
258
298
|
function initCamera(width, height) {
|
|
259
299
|
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 20000);
|