@jdultra/threedtiles 5.0.0 → 5.1.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 +34 -9
- package/package.json +4 -4
- package/src/decoder/B3DMDecoder.js +22 -5
- package/src/decoder/FeatureTable.js +169 -0
- package/src/index.js +70 -113
- package/src/tileset/OGC3DTile.js +76 -32
- package/src/tileset/instanced/InstancedTile.js +63 -10
- package/src/tileset/instanced/MeshTile.js +7 -1
package/README.md
CHANGED
|
@@ -20,7 +20,8 @@ import { OGC3DTile } from "./tileset/OGC3DTile";
|
|
|
20
20
|
...
|
|
21
21
|
|
|
22
22
|
const ogc3DTile = new OGC3DTile({
|
|
23
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json"
|
|
23
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
24
|
+
renderer: renderer
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
scene.add(ogc3DTile);
|
|
@@ -45,6 +46,7 @@ Currently, the library is limmited to B3DM files.
|
|
|
45
46
|
- Optimal tile load order
|
|
46
47
|
- Occlusion culling
|
|
47
48
|
- Instanced tilesets
|
|
49
|
+
- Center tileset and re-orient geolocated data
|
|
48
50
|
|
|
49
51
|
### geometric Error Multiplier
|
|
50
52
|
The geometric error multiplier allows you to multiply the geometric error by a factor.
|
|
@@ -56,6 +58,7 @@ you may also set this value at initialization:
|
|
|
56
58
|
```
|
|
57
59
|
const ogc3DTile = new OGC3DTile({
|
|
58
60
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
61
|
+
renderer: renderer,
|
|
59
62
|
geometricErrorMultiplier: 2.0
|
|
60
63
|
});
|
|
61
64
|
```
|
|
@@ -66,11 +69,12 @@ A value of 1.0 is the default.
|
|
|
66
69
|
|
|
67
70
|
### load tiles outside of view
|
|
68
71
|
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.
|
|
69
|
-
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
|
|
72
|
+
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 from parts of the scene that are not in the view.
|
|
70
73
|
|
|
71
74
|
```
|
|
72
75
|
const ogc3DTile = new OGC3DTile({
|
|
73
76
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
77
|
+
renderer: renderer,
|
|
74
78
|
loadOutsideView: true
|
|
75
79
|
});
|
|
76
80
|
```
|
|
@@ -84,11 +88,13 @@ This can be useful to position the tileset at a specific location when it is not
|
|
|
84
88
|
```
|
|
85
89
|
const ogc3DTile = new OGC3DTile({
|
|
86
90
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
renderer: renderer,
|
|
92
|
+
onLoadCallback: tileset => {
|
|
93
|
+
console.log(tileset.json.boundingVolume);
|
|
89
94
|
}
|
|
90
95
|
});
|
|
91
96
|
```
|
|
97
|
+
Note that the callback is called with the OGC3DTile object as parameter and that this object has a "json" property giving you access to the original tileset.json with it's transform, geometric error, bounding volume, etc...
|
|
92
98
|
|
|
93
99
|
#### Mesh callback
|
|
94
100
|
Add a callback on loaded tiles in order to set a material or do some logic on the meshes.
|
|
@@ -96,6 +102,7 @@ Add a callback on loaded tiles in order to set a material or do some logic on th
|
|
|
96
102
|
```
|
|
97
103
|
const ogc3DTile = new OGC3DTile({
|
|
98
104
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
105
|
+
renderer: renderer,
|
|
99
106
|
meshCallback: mesh => {
|
|
100
107
|
mesh.material.wireframe = true;
|
|
101
108
|
mesh.material.side = THREE.DoubleSide;
|
|
@@ -116,6 +123,7 @@ import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
|
|
|
116
123
|
|
|
117
124
|
const ogc3DTile = new OGC3DTile({
|
|
118
125
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
126
|
+
renderer: renderer,
|
|
119
127
|
tileLoader: new TileLoader(mesh => {
|
|
120
128
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
121
129
|
mesh.material.wireframe = false;
|
|
@@ -133,6 +141,7 @@ The OGC 3DTile object is a regular three.js Object3D so it can be transformed vi
|
|
|
133
141
|
```
|
|
134
142
|
const ogc3DTile = new OGC3DTile({
|
|
135
143
|
url: "https://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json"
|
|
144
|
+
renderer: renderer,
|
|
136
145
|
});
|
|
137
146
|
|
|
138
147
|
ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
|
|
@@ -156,6 +165,7 @@ This service must be passed to every OGC3DTiles object like so:
|
|
|
156
165
|
```
|
|
157
166
|
const ogc3DTile = new OGC3DTile({
|
|
158
167
|
url: "path/to/tileset.json",
|
|
168
|
+
renderer: renderer,
|
|
159
169
|
occlusionCullingService: occlusionCullingService
|
|
160
170
|
});
|
|
161
171
|
```
|
|
@@ -201,11 +211,12 @@ const instancedTileLoader = new InstancedTileLoader(scene, mesh => {
|
|
|
201
211
|
const instancedTilesets = [];
|
|
202
212
|
for (let i = 0; i < 100; i++) {
|
|
203
213
|
const tileset = new InstancedOGC3DTile({
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
215
|
+
renderer: renderer,
|
|
216
|
+
geometricErrorMultiplier: 1.0,
|
|
217
|
+
loadOutsideView: false,
|
|
218
|
+
tileLoader: instancedTileLoader,
|
|
219
|
+
static: true // when static is set to true, don't forget to call InstancedOGC3DTile#updateMatrix manually
|
|
209
220
|
});
|
|
210
221
|
|
|
211
222
|
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * i);
|
|
@@ -230,6 +241,19 @@ function animate() {
|
|
|
230
241
|
animate();
|
|
231
242
|
|
|
232
243
|
```
|
|
244
|
+
### Center tileset and re-orient geolocated data
|
|
245
|
+
|
|
246
|
+
OGC3DTiles data is not necessarily centered on the origin and when it's georeferenced, it's also rotated relative to the cartesian coordinate system.
|
|
247
|
+
The optional property "centerModel" will center the model on the origin. In the case of georeferenced models, identified as those using the "region" bounding volume, it will also rotate it so that it's up-axis alligns with the y axis.
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
const ogc3DTile = new OGC3DTile({
|
|
251
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
252
|
+
renderer: renderer,
|
|
253
|
+
centerModel:true
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
This property is also available for instanced models.
|
|
233
257
|
|
|
234
258
|
### static tilesets and other performance tips
|
|
235
259
|
When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
|
|
@@ -238,6 +262,7 @@ This will skip recalculating the transformation matrix of every tile each frame
|
|
|
238
262
|
```
|
|
239
263
|
const ogc3DTile = new OGC3DTile({
|
|
240
264
|
url: "path/to/tileset.json",
|
|
265
|
+
renderer: renderer,
|
|
241
266
|
static: true
|
|
242
267
|
});
|
|
243
268
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jdultra/threedtiles",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "An OGC 3DTiles viewer for Three.js",
|
|
5
5
|
"main": "tileset.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"path-browserify": "^1.0.1",
|
|
32
32
|
"regenerator-runtime": "^0.13.11",
|
|
33
33
|
"set-interval-async": "^2.0.3",
|
|
34
|
-
"three": "0.
|
|
34
|
+
"three": "0.148.0",
|
|
35
35
|
"uuid": "^8.3.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@babel/core": "^7.20.
|
|
38
|
+
"@babel/core": "^7.20.12",
|
|
39
39
|
"@babel/preset-env": "^7.20.2",
|
|
40
40
|
"babel-loader": "^8.3.0",
|
|
41
41
|
"copy-webpack-plugin": "^6.3.2",
|
|
42
|
-
"core-js": "^3.
|
|
42
|
+
"core-js": "^3.27.1",
|
|
43
43
|
"html-loader": "^1.3.2",
|
|
44
44
|
"html-webpack-plugin": "^4.5.0",
|
|
45
45
|
"mini-css-extract-plugin": "^1.6.2",
|
|
@@ -1,12 +1,18 @@
|
|
|
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
|
+
import {FeatureTable, BatchTable} from './FeatureTable';
|
|
4
5
|
|
|
5
6
|
const gltfLoader = new GLTFLoader();
|
|
6
7
|
const dracoLoader = new DRACOLoader();
|
|
8
|
+
const tempMatrix = new THREE.Matrix4();
|
|
7
9
|
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
8
10
|
gltfLoader.setDRACOLoader(dracoLoader);
|
|
9
|
-
const
|
|
11
|
+
const zUpToYUp = new THREE.Matrix4();
|
|
12
|
+
zUpToYUp.set(1,0,0,0,
|
|
13
|
+
0,0,-1,0,
|
|
14
|
+
0,1,0,0,
|
|
15
|
+
0,0,0,1);
|
|
10
16
|
//const legacyGLTFLoader = new LegacyGLTFLoader();
|
|
11
17
|
|
|
12
18
|
function parseB3DM(arrayBuffer, meshCallback) {
|
|
@@ -31,10 +37,10 @@ function parseB3DM(arrayBuffer, meshCallback) {
|
|
|
31
37
|
const batchTableBinaryByteLength = dataView.getUint32(24, true);
|
|
32
38
|
|
|
33
39
|
const featureTableStart = 28;
|
|
34
|
-
|
|
40
|
+
const featureTable = new FeatureTable(arrayBuffer, featureTableStart, featureTableJSONByteLength, featureTableBinaryByteLength);
|
|
35
41
|
|
|
36
42
|
const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
|
|
37
|
-
|
|
43
|
+
const batchTable = new BatchTable(arrayBuffer, featureTable.getData('BATCH_LENGTH'), batchTableStart, batchTableJSONByteLength, batchTableBinaryByteLength);
|
|
38
44
|
|
|
39
45
|
const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
|
|
40
46
|
const glbBytes = new Uint8Array(arrayBuffer, glbStart, byteLength - glbStart);
|
|
@@ -48,6 +54,7 @@ function parseB3DM(arrayBuffer, meshCallback) {
|
|
|
48
54
|
gltfLoader.parse(gltfBuffer, null, model => {
|
|
49
55
|
|
|
50
56
|
////TODO
|
|
57
|
+
|
|
51
58
|
//model.batchTable = b3dm.batchTable;
|
|
52
59
|
//model.featureTable = b3dm.featureTable;
|
|
53
60
|
|
|
@@ -55,8 +62,18 @@ function parseB3DM(arrayBuffer, meshCallback) {
|
|
|
55
62
|
//model.scene.featureTable = b3dm.featureTable;
|
|
56
63
|
|
|
57
64
|
//const scene = mergeColoredObject(model.scene);
|
|
65
|
+
|
|
66
|
+
//model.scene.applyMatrix4(ytozUpMatrix);
|
|
67
|
+
|
|
68
|
+
const rtcCenter = featureTable.getData('RTC_CENTER');
|
|
69
|
+
if (rtcCenter) {
|
|
70
|
+
tempMatrix.makeTranslation(rtcCenter[0], rtcCenter[1], rtcCenter[2])
|
|
71
|
+
model.scene.applyMatrix4(tempMatrix);
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
model.scene.traverse((o) => {
|
|
59
75
|
if (o.isMesh) {
|
|
76
|
+
o.applyMatrix4(zUpToYUp);
|
|
60
77
|
if (!!meshCallback) {
|
|
61
78
|
meshCallback(o);
|
|
62
79
|
}
|
|
@@ -76,11 +93,11 @@ const B3DMDecoder = {
|
|
|
76
93
|
|
|
77
94
|
return parseB3DM(arrayBuffer, meshCallback).then(mesh => {
|
|
78
95
|
let instancedMesh;
|
|
96
|
+
mesh.updateWorldMatrix(false, true)
|
|
79
97
|
mesh.traverse(child => {
|
|
80
98
|
if (child.isMesh) {
|
|
81
99
|
instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, maxCount);
|
|
82
|
-
instancedMesh.baseMatrix = child.
|
|
83
|
-
//console.log(child.matrix.elements[12])
|
|
100
|
+
instancedMesh.baseMatrix = child.matrixWorld;
|
|
84
101
|
}
|
|
85
102
|
});
|
|
86
103
|
return instancedMesh;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This class is taken straight from NASA-AMMOS library.
|
|
3
|
+
* https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/src/utilities/FeatureTable.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const utf8decoder = new TextDecoder();
|
|
7
|
+
export class FeatureTable {
|
|
8
|
+
|
|
9
|
+
constructor(buffer, start, headerLength, binLength) {
|
|
10
|
+
|
|
11
|
+
this.buffer = buffer;
|
|
12
|
+
this.binOffset = start + headerLength;
|
|
13
|
+
this.binLength = binLength;
|
|
14
|
+
|
|
15
|
+
let header = null;
|
|
16
|
+
if (headerLength !== 0) {
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const headerData = new Uint8Array(buffer, start, headerLength);
|
|
20
|
+
header = JSON.parse(utf8decoder.decode(headerData));
|
|
21
|
+
} catch (e) {
|
|
22
|
+
header = {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
} else {
|
|
26
|
+
|
|
27
|
+
header = {};
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
this.header = header;
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getKeys() {
|
|
35
|
+
|
|
36
|
+
return Object.keys(this.header);
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getData(key, count, defaultComponentType = null, defaultType = null) {
|
|
41
|
+
|
|
42
|
+
const header = this.header;
|
|
43
|
+
|
|
44
|
+
if (!(key in header)) {
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const feature = header[key];
|
|
51
|
+
if (!(feature instanceof Object)) {
|
|
52
|
+
|
|
53
|
+
return feature;
|
|
54
|
+
|
|
55
|
+
} else if (Array.isArray(feature)) {
|
|
56
|
+
|
|
57
|
+
return feature;
|
|
58
|
+
|
|
59
|
+
} else {
|
|
60
|
+
|
|
61
|
+
const { buffer, binOffset, binLength } = this;
|
|
62
|
+
const byteOffset = feature.byteOffset || 0;
|
|
63
|
+
const featureType = feature.type || defaultType;
|
|
64
|
+
const featureComponentType = feature.componentType || defaultComponentType;
|
|
65
|
+
|
|
66
|
+
if ('type' in feature && defaultType && feature.type !== defaultType) {
|
|
67
|
+
|
|
68
|
+
throw new Error('FeatureTable: Specified type does not match expected type.');
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let stride;
|
|
73
|
+
switch (featureType) {
|
|
74
|
+
|
|
75
|
+
case 'SCALAR':
|
|
76
|
+
stride = 1;
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'VEC2':
|
|
80
|
+
stride = 2;
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
case 'VEC3':
|
|
84
|
+
stride = 3;
|
|
85
|
+
break;
|
|
86
|
+
|
|
87
|
+
case 'VEC4':
|
|
88
|
+
stride = 4;
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(`FeatureTable : Feature type not provided for "${key}".`);
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let data;
|
|
97
|
+
const arrayStart = binOffset + byteOffset;
|
|
98
|
+
const arrayLength = count * stride;
|
|
99
|
+
|
|
100
|
+
switch (featureComponentType) {
|
|
101
|
+
|
|
102
|
+
case 'BYTE':
|
|
103
|
+
data = new Int8Array(buffer, arrayStart, arrayLength);
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
case 'UNSIGNED_BYTE':
|
|
107
|
+
data = new Uint8Array(buffer, arrayStart, arrayLength);
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
case 'SHORT':
|
|
111
|
+
data = new Int16Array(buffer, arrayStart, arrayLength);
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case 'UNSIGNED_SHORT':
|
|
115
|
+
data = new Uint16Array(buffer, arrayStart, arrayLength);
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
case 'INT':
|
|
119
|
+
data = new Int32Array(buffer, arrayStart, arrayLength);
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case 'UNSIGNED_INT':
|
|
123
|
+
data = new Uint32Array(buffer, arrayStart, arrayLength);
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case 'FLOAT':
|
|
127
|
+
data = new Float32Array(buffer, arrayStart, arrayLength);
|
|
128
|
+
break;
|
|
129
|
+
|
|
130
|
+
case 'DOUBLE':
|
|
131
|
+
data = new Float64Array(buffer, arrayStart, arrayLength);
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
default:
|
|
135
|
+
throw new Error(`FeatureTable : Feature component type not provided for "${key}".`);
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const dataEnd = arrayStart + arrayLength * data.BYTES_PER_ELEMENT;
|
|
140
|
+
if (dataEnd > binOffset + binLength) {
|
|
141
|
+
|
|
142
|
+
throw new Error('FeatureTable: Feature data read outside binary body length.');
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return data;
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export class BatchTable extends FeatureTable {
|
|
155
|
+
|
|
156
|
+
constructor(buffer, batchSize, start, headerLength, binLength) {
|
|
157
|
+
|
|
158
|
+
super(buffer, start, headerLength, binLength);
|
|
159
|
+
this.batchSize = batchSize;
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
getData(key, componentType = null, type = null) {
|
|
164
|
+
|
|
165
|
+
return super.getData(key, this.batchSize, componentType, type);
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
}
|
package/src/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
|
|
14
14
|
import { InstancedOGC3DTile } from "./tileset/instanced/InstancedOGC3DTile.js"
|
|
15
15
|
import { InstancedTileLoader } from "./tileset/instanced/InstancedTileLoader.js"
|
|
16
16
|
|
|
17
|
-
import { B3DMDecoder } from "./decoder/B3DMDecoder";
|
|
18
17
|
|
|
19
18
|
const occlusionCullingService = new OcclusionCullingService();
|
|
20
19
|
occlusionCullingService.setSide(THREE.DoubleSide);
|
|
@@ -24,35 +23,15 @@ const domContainer = initDomContainer("screen");
|
|
|
24
23
|
const camera = initCamera(domContainer.offsetWidth, domContainer.offsetHeight);
|
|
25
24
|
const stats = initStats(domContainer);
|
|
26
25
|
const renderer = initRenderer(camera, domContainer);
|
|
27
|
-
|
|
26
|
+
const ogc3DTiles = initTileset(scene, 1.0);
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
initInstancedTilesets(instancedTileLoader);
|
|
28
|
+
//const instancedTileLoader = createInstancedTileLoader(scene);
|
|
29
|
+
//initInstancedTilesets(instancedTileLoader);
|
|
32
30
|
|
|
33
31
|
const controller = initController(camera, domContainer);
|
|
34
32
|
|
|
35
33
|
const composer = initComposer(scene, camera, renderer);
|
|
36
34
|
|
|
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
|
-
}) */
|
|
54
|
-
|
|
55
|
-
|
|
56
35
|
animate();
|
|
57
36
|
|
|
58
37
|
|
|
@@ -70,10 +49,10 @@ function initScene() {
|
|
|
70
49
|
const scene = new THREE.Scene();
|
|
71
50
|
scene.matrixAutoUpdate = false;
|
|
72
51
|
//scene.matrixWorldAutoUpdate = false;
|
|
73
|
-
scene.background = new THREE.Color(
|
|
52
|
+
scene.background = new THREE.Color(0xffffff);
|
|
74
53
|
scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
|
|
75
54
|
|
|
76
|
-
/*const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
55
|
+
/* const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
77
56
|
const sphere = new THREE.SphereGeometry(2, 16, 8);
|
|
78
57
|
light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
|
|
79
58
|
scene.add(light);
|
|
@@ -84,9 +63,9 @@ function initScene() {
|
|
|
84
63
|
const sphere2 = new THREE.SphereGeometry(2, 16, 8);
|
|
85
64
|
light2.add(new THREE.Mesh(sphere2, new THREE.MeshBasicMaterial({ color: 0xffbbbb })));
|
|
86
65
|
scene.add(light2);
|
|
87
|
-
light2.position.set(200, 100, -100)
|
|
66
|
+
light2.position.set(200, 100, -100); */
|
|
67
|
+
|
|
88
68
|
|
|
89
|
-
|
|
90
69
|
return scene;
|
|
91
70
|
}
|
|
92
71
|
|
|
@@ -134,119 +113,94 @@ function initStats(dom) {
|
|
|
134
113
|
|
|
135
114
|
function initCamera(width, height) {
|
|
136
115
|
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
|
|
137
|
-
camera.position.set(
|
|
138
|
-
|
|
116
|
+
camera.position.set(10000,0,0);
|
|
117
|
+
camera.lookAt(0,0,0);
|
|
118
|
+
|
|
139
119
|
camera.matrixAutoUpdate = true;
|
|
140
120
|
return camera;
|
|
141
121
|
}
|
|
142
122
|
|
|
143
|
-
function initTileset(scene) {
|
|
123
|
+
function initTileset(scene, gem) {
|
|
144
124
|
|
|
145
125
|
const tileLoader = new TileLoader(mesh => {
|
|
146
126
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
147
127
|
mesh.material.wireframe = false;
|
|
148
128
|
mesh.material.side = THREE.DoubleSide;
|
|
149
129
|
mesh.material.metalness = 0.0
|
|
150
|
-
},
|
|
130
|
+
}, 100);
|
|
131
|
+
|
|
151
132
|
const ogc3DTile = new OGC3DTile({
|
|
152
|
-
//url: "
|
|
153
|
-
|
|
154
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/
|
|
155
|
-
//url: "
|
|
156
|
-
|
|
157
|
-
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
|
|
158
|
-
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/a88b3sungng/tileset.json",
|
|
159
|
-
geometricErrorMultiplier: 0.01,
|
|
133
|
+
//url: "https://sampledata.luciad.com/data/ogc3dtiles/LucerneAirborneMesh/tileset.json",
|
|
134
|
+
url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
|
|
135
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/baltimore/tileset.json",
|
|
136
|
+
//url: "http://localhost:8082/tileset.json",
|
|
137
|
+
geometricErrorMultiplier: gem,
|
|
160
138
|
loadOutsideView: false,
|
|
161
139
|
tileLoader: tileLoader,
|
|
162
140
|
//occlusionCullingService: occlusionCullingService,
|
|
163
141
|
static: false,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
//// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
|
|
172
|
-
ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
173
|
-
ogc3DTile.scale.set(100.0,100.0,100.0)
|
|
174
|
-
//// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
//// 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
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
var interval;
|
|
183
|
-
document.addEventListener('keyup', (e) => {
|
|
184
|
-
console.log(camera.position)
|
|
185
|
-
if (!!e.key && e.key !== "p") {
|
|
186
|
-
|
|
187
|
-
if (!!interval) {
|
|
188
|
-
clearInterval(interval);
|
|
189
|
-
interval = null;
|
|
190
|
-
} else {
|
|
191
|
-
startInterval();
|
|
142
|
+
centerModel:true,
|
|
143
|
+
renderer: renderer,
|
|
144
|
+
onLoadCallback: (tile)=>{
|
|
145
|
+
if (!!tile.json.boundingVolume.region) {
|
|
146
|
+
const halfHeight = (tile.json.boundingVolume.region[5] - tile.json.boundingVolume.region[4]) * 0.5;
|
|
147
|
+
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 1, 0), halfHeight);
|
|
148
|
+
//ogc3DTile.updateWorldMatrix(true, true);
|
|
192
149
|
}
|
|
193
150
|
}
|
|
194
|
-
if (!!e.key && e.key !== "l") {
|
|
195
|
-
|
|
196
|
-
console.log("new THREE.Vector3(" + camera.position.x + "," + camera.position.y + "," + camera.position.z + ")");
|
|
197
|
-
console.log("new THREE.Quaternion(" + camera.quaternion.x + "," + camera.quaternion.y + "," + camera.quaternion.z + "," + camera.quaternion.w + ")");
|
|
198
|
-
|
|
199
|
-
}
|
|
200
151
|
|
|
201
152
|
});
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
-
|
|
153
|
+
setIntervalAsync(function () {
|
|
154
|
+
ogc3DTile.update(camera);
|
|
155
|
+
}, 20);
|
|
205
156
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
//ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
160
|
+
//ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 1)
|
|
161
|
+
/*
|
|
162
|
+
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 10) // Z-UP to Y-UP
|
|
163
|
+
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 1, 0), 18.5) // Z-UP to Y-UP */
|
|
164
|
+
scene.add(ogc3DTile);
|
|
209
165
|
|
|
210
|
-
scene.add(ogc3DTile)
|
|
211
166
|
return ogc3DTile;
|
|
212
167
|
}
|
|
213
168
|
|
|
169
|
+
|
|
214
170
|
function createInstancedTileLoader(scene) {
|
|
215
171
|
return new InstancedTileLoader(scene, mesh => {
|
|
216
172
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
217
173
|
mesh.material.wireframe = false;
|
|
218
174
|
mesh.material.side = THREE.DoubleSide;
|
|
219
|
-
|
|
175
|
+
mesh.material.metalness = 0.0;
|
|
176
|
+
}, 0, 1);
|
|
220
177
|
}
|
|
221
178
|
function initInstancedTilesets(instancedTileLoader) {
|
|
222
179
|
|
|
180
|
+
/*new GLTFLoader().load('http://localhost:8080/test.glb', function ( gltf ) {
|
|
181
|
+
scene.add(gltf.scene);
|
|
182
|
+
} );*/
|
|
183
|
+
|
|
223
184
|
const instancedTilesets = [];
|
|
224
185
|
|
|
225
|
-
for (let x = 0; x < 15; x++) {
|
|
226
|
-
for (let y = 0; y < 15; y++) {
|
|
227
|
-
for (let z = 0; z < 15; z++) {
|
|
228
|
-
const tileset = new InstancedOGC3DTile({
|
|
229
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
230
|
-
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
231
|
-
//url: "http://localhost:8080/tileset.json",
|
|
232
|
-
geometricErrorMultiplier: 1.0,
|
|
233
|
-
loadOutsideView: true,
|
|
234
|
-
tileLoader: instancedTileLoader,
|
|
235
|
-
static: true,
|
|
236
|
-
renderer: renderer
|
|
237
|
-
});
|
|
238
|
-
//tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
239
|
-
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * x)
|
|
240
|
-
tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 50 * y)
|
|
241
|
-
tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
|
|
242
|
-
tileset.updateMatrix()
|
|
243
|
-
scene.add(tileset);
|
|
244
|
-
instancedTilesets.push(tileset);
|
|
245
186
|
|
|
187
|
+
const tileset = new InstancedOGC3DTile({
|
|
188
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
189
|
+
url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
|
|
190
|
+
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ab13lasdc9i/tileset.json",
|
|
191
|
+
//url: "http://localhost:8081/tileset.json",
|
|
192
|
+
geometricErrorMultiplier: 1.0,
|
|
193
|
+
loadOutsideView: true,
|
|
194
|
+
tileLoader: instancedTileLoader,
|
|
195
|
+
static: false,
|
|
196
|
+
centerModel:true,
|
|
197
|
+
renderer: renderer
|
|
198
|
+
});
|
|
199
|
+
//tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
246
200
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
201
|
+
tileset.updateMatrix()
|
|
202
|
+
scene.add(tileset);
|
|
203
|
+
instancedTilesets.push(tileset);
|
|
250
204
|
|
|
251
205
|
scene.updateMatrixWorld(true)
|
|
252
206
|
function now() {
|
|
@@ -255,14 +209,14 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
255
209
|
let updateIndex = 0;
|
|
256
210
|
setInterval(() => {
|
|
257
211
|
let startTime = now();
|
|
258
|
-
do{
|
|
212
|
+
do {
|
|
259
213
|
const frustum = new THREE.Frustum();
|
|
260
214
|
frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
|
|
261
215
|
instancedTilesets[updateIndex].update(camera, frustum);
|
|
262
|
-
updateIndex= (updateIndex+1)%instancedTilesets.length;
|
|
263
|
-
}while(updateIndex < instancedTilesets.length && now()-startTime<4);
|
|
264
|
-
},40);
|
|
265
|
-
|
|
216
|
+
updateIndex = (updateIndex + 1) % instancedTilesets.length;
|
|
217
|
+
} while (updateIndex < instancedTilesets.length && now() - startTime < 4);
|
|
218
|
+
}, 40);
|
|
219
|
+
|
|
266
220
|
//initLODMultiplierSlider(instancedTilesets);
|
|
267
221
|
}
|
|
268
222
|
|
|
@@ -273,7 +227,7 @@ function initLODMultiplierSlider(instancedTilesets) {
|
|
|
273
227
|
|
|
274
228
|
slider.oninput = () => {
|
|
275
229
|
instancedTilesets.forEach(tileset => {
|
|
276
|
-
tileset.setGeometricErrorMultiplier(slider.value*0.1)
|
|
230
|
+
tileset.setGeometricErrorMultiplier(slider.value * 0.1)
|
|
277
231
|
})
|
|
278
232
|
output.innerHTML = slider.value;
|
|
279
233
|
}
|
|
@@ -283,7 +237,10 @@ function initController(camera, dom) {
|
|
|
283
237
|
const controller = new OrbitControls(camera, dom);
|
|
284
238
|
|
|
285
239
|
controller.target.set(0,0,0);
|
|
286
|
-
|
|
240
|
+
//controller.target.set(0,0,0);
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
controller.minDistance = 0.1;
|
|
287
244
|
controller.maxDistance = 100000;
|
|
288
245
|
controller.update();
|
|
289
246
|
return controller;
|
|
@@ -292,7 +249,7 @@ function initController(camera, dom) {
|
|
|
292
249
|
|
|
293
250
|
function animate() {
|
|
294
251
|
requestAnimationFrame(animate);
|
|
295
|
-
instancedTileLoader.update();
|
|
252
|
+
//instancedTileLoader.update();
|
|
296
253
|
composer.render();
|
|
297
254
|
//occlusionCullingService.update(scene, renderer, camera)
|
|
298
255
|
stats.update();
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -5,8 +5,12 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
5
5
|
import * as path from "path-browserify"
|
|
6
6
|
import { clamp } from "three/src/math/MathUtils";
|
|
7
7
|
|
|
8
|
-
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1)
|
|
8
|
+
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
|
|
9
|
+
const tempVec1 = new THREE.Vector3(0, 0, 0);
|
|
10
|
+
const tempVec2 = new THREE.Vector3(0, 0, 0);
|
|
11
|
+
const upVector = new THREE.Vector3(0, 1, 0);
|
|
9
12
|
const rendererSize = new THREE.Vector2();
|
|
13
|
+
const tempQuaternion = new THREE.Quaternion();
|
|
10
14
|
|
|
11
15
|
class OGC3DTile extends THREE.Object3D {
|
|
12
16
|
|
|
@@ -28,6 +32,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
28
32
|
* onLoadCallback: function,
|
|
29
33
|
* occlusionCullingService: OcclusionCullingService,
|
|
30
34
|
* static: Boolean,
|
|
35
|
+
* centerModel: Boolean
|
|
31
36
|
* renderer: Renderer
|
|
32
37
|
* } properties
|
|
33
38
|
*/
|
|
@@ -63,6 +68,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
63
68
|
if (this.static) {
|
|
64
69
|
this.matrixAutoUpdate = false;
|
|
65
70
|
}
|
|
71
|
+
|
|
66
72
|
// declare properties specific to the tile for clarity
|
|
67
73
|
this.childrenTiles = [];
|
|
68
74
|
this.meshContent;
|
|
@@ -84,6 +90,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
84
90
|
if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
|
|
85
91
|
self.setup(properties);
|
|
86
92
|
if (properties.onLoadCallback) properties.onLoadCallback(self);
|
|
93
|
+
|
|
87
94
|
} else if (properties.url) { // If only the url to the tileset.json is provided
|
|
88
95
|
fetch(properties.url, { signal: self.abortController.signal }).then(result => {
|
|
89
96
|
if (!result.ok) {
|
|
@@ -92,6 +99,33 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
92
99
|
result.json().then(json => {
|
|
93
100
|
self.setup({ rootPath: path.dirname(properties.url), json: json });
|
|
94
101
|
if (properties.onLoadCallback) properties.onLoadCallback(self);
|
|
102
|
+
if (!!properties.centerModel) {
|
|
103
|
+
const tempSphere = new THREE.Sphere();
|
|
104
|
+
if (self.boundingVolume instanceof OBB) {
|
|
105
|
+
// box
|
|
106
|
+
tempSphere.copy(self.boundingVolume.sphere);
|
|
107
|
+
} else if (self.boundingVolume instanceof THREE.Sphere) {
|
|
108
|
+
//sphere
|
|
109
|
+
tempSphere.copy(self.boundingVolume);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//tempSphere.applyMatrix4(self.matrixWorld);
|
|
113
|
+
if (!!this.json.boundingVolume.region) {
|
|
114
|
+
this.transformWGS84ToCartesian(
|
|
115
|
+
(this.json.boundingVolume.region[0] + this.json.boundingVolume.region[2]) * 0.5,
|
|
116
|
+
(this.json.boundingVolume.region[1] + this.json.boundingVolume.region[3]) * 0.5,
|
|
117
|
+
(this.json.boundingVolume.region[4] + this.json.boundingVolume.region[5]) * 0.5,
|
|
118
|
+
tempVec1);
|
|
119
|
+
|
|
120
|
+
tempQuaternion.setFromUnitVectors(tempVec1.normalize(), upVector.normalize());
|
|
121
|
+
self.applyQuaternion(tempQuaternion);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
self.translateX(-tempSphere.center.x * self.scale.x);
|
|
125
|
+
self.translateY(-tempSphere.center.y * self.scale.y);
|
|
126
|
+
self.translateZ(-tempSphere.center.z * self.scale.z);
|
|
127
|
+
|
|
128
|
+
}
|
|
95
129
|
});
|
|
96
130
|
});
|
|
97
131
|
}
|
|
@@ -133,7 +167,10 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
133
167
|
this.boundingVolume = new OBB(this.json.boundingVolume.box);
|
|
134
168
|
} else if (!!this.json.boundingVolume.region) {
|
|
135
169
|
const region = this.json.boundingVolume.region;
|
|
136
|
-
this.
|
|
170
|
+
this.transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
|
|
171
|
+
this.transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
|
|
172
|
+
tempVec1.lerp(tempVec2, 0.5);
|
|
173
|
+
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(tempVec1.x, tempVec1.y, tempVec1.z), tempVec1.distanceTo(tempVec2));
|
|
137
174
|
} else if (!!this.json.boundingVolume.sphere) {
|
|
138
175
|
const sphere = this.json.boundingVolume.sphere;
|
|
139
176
|
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[2], -sphere[1]), sphere[3]);
|
|
@@ -518,39 +555,30 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
518
555
|
tempSphere.copy(this.boundingVolume);
|
|
519
556
|
tempSphere.applyMatrix4(this.matrixWorld);
|
|
520
557
|
if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
521
|
-
} else
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
return -1;
|
|
558
|
+
} else {
|
|
559
|
+
console.error("unsupported shape");
|
|
560
|
+
return -1
|
|
561
|
+
|
|
526
562
|
}
|
|
527
563
|
|
|
528
564
|
/////// return metric based on geometric error and distance
|
|
529
|
-
if (this.boundingVolume instanceof OBB || this.boundingVolume instanceof THREE.Sphere) {
|
|
530
|
-
// box
|
|
531
|
-
const distance = Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
532
|
-
if (distance == 0) {
|
|
533
|
-
return 0;
|
|
534
|
-
}
|
|
535
|
-
const scale = this.matrixWorld.getMaxScaleOnAxis();
|
|
536
|
-
this.renderer.getDrawingBufferSize(rendererSize);
|
|
537
|
-
let s = rendererSize.y;
|
|
538
|
-
let fov = camera.fov;
|
|
539
|
-
if(camera.aspect < 1){
|
|
540
|
-
fov *= camera.aspect;
|
|
541
|
-
s = rendererSize.x;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
|
|
545
|
-
|
|
546
|
-
return (window.devicePixelRatio * 16 * lambda) / (s * scale);
|
|
547
565
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
//throw Error("Region bounding volume not supported");
|
|
552
|
-
return -1;
|
|
566
|
+
const distance = Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
567
|
+
if (distance == 0) {
|
|
568
|
+
return 0;
|
|
553
569
|
}
|
|
570
|
+
const scale = this.matrixWorld.getMaxScaleOnAxis();
|
|
571
|
+
this.renderer.getDrawingBufferSize(rendererSize);
|
|
572
|
+
let s = rendererSize.y;
|
|
573
|
+
let fov = camera.fov;
|
|
574
|
+
if (camera.aspect < 1) {
|
|
575
|
+
fov *= camera.aspect;
|
|
576
|
+
s = rendererSize.x;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
|
|
580
|
+
|
|
581
|
+
return (window.devicePixelRatio * 16 * lambda) / (s * scale);
|
|
554
582
|
}
|
|
555
583
|
|
|
556
584
|
getSiblings() {
|
|
@@ -583,8 +611,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
583
611
|
tempSphere.applyMatrix4(this.matrixWorld);
|
|
584
612
|
//if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
585
613
|
}
|
|
586
|
-
|
|
587
|
-
|
|
614
|
+
else {
|
|
615
|
+
console.error("unsupported shape")
|
|
588
616
|
}
|
|
589
617
|
return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
590
618
|
}
|
|
@@ -592,5 +620,21 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
592
620
|
this.geometricErrorMultiplier = geometricErrorMultiplier;
|
|
593
621
|
this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
|
|
594
622
|
}
|
|
623
|
+
|
|
624
|
+
transformWGS84ToCartesian(lon, lat, h, sfct) {
|
|
625
|
+
const a = 6378137.0;
|
|
626
|
+
const e = 0.006694384442042;
|
|
627
|
+
const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
|
|
628
|
+
const cosLat = Math.cos(lat);
|
|
629
|
+
const cosLon = Math.cos(lon);
|
|
630
|
+
const sinLat = Math.sin(lat);
|
|
631
|
+
const sinLon = Math.sin(lon);
|
|
632
|
+
const nPh = (N + h);
|
|
633
|
+
const x = nPh * cosLat * cosLon;
|
|
634
|
+
const y = nPh * cosLat * sinLon;
|
|
635
|
+
const z = (0.993305615557957 * N + h) * sinLat;
|
|
636
|
+
|
|
637
|
+
sfct.set(x, y, z);
|
|
638
|
+
}
|
|
595
639
|
}
|
|
596
640
|
export { OGC3DTile };
|
|
@@ -4,8 +4,13 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
4
4
|
import * as path from "path-browserify";
|
|
5
5
|
import * as _ from "lodash";
|
|
6
6
|
|
|
7
|
-
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1)
|
|
7
|
+
const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
|
|
8
|
+
const tempVec1 = new THREE.Vector3(0, 0, 0);
|
|
9
|
+
const tempVec2 = new THREE.Vector3(0, 0, 0);
|
|
10
|
+
const upVector = new THREE.Vector3(0, 1, 0);
|
|
8
11
|
const rendererSize = new THREE.Vector2();
|
|
12
|
+
const tempQuaternion = new THREE.Quaternion();
|
|
13
|
+
const tempMatrix = new THREE.Matrix4();
|
|
9
14
|
|
|
10
15
|
class InstancedTile extends THREE.Object3D {
|
|
11
16
|
|
|
@@ -23,7 +28,8 @@ class InstancedTile extends THREE.Object3D {
|
|
|
23
28
|
* meshCallback: function,
|
|
24
29
|
* cameraOnLoad: camera,
|
|
25
30
|
* parentTile: OGC3DTile,
|
|
26
|
-
* onLoadCallback: function
|
|
31
|
+
* onLoadCallback: function,
|
|
32
|
+
* centerModel: Boolean
|
|
27
33
|
* } properties
|
|
28
34
|
*/
|
|
29
35
|
constructor(properties) {
|
|
@@ -76,7 +82,35 @@ class InstancedTile extends THREE.Object3D {
|
|
|
76
82
|
//json = JSON.parse(JSON.stringify(json))
|
|
77
83
|
const p = path.dirname(url);
|
|
78
84
|
self.setup({ rootPath: p, json: json });
|
|
85
|
+
if (!!properties.centerModel) {
|
|
86
|
+
const tempSphere = new THREE.Sphere();
|
|
87
|
+
if (self.boundingVolume instanceof OBB) {
|
|
88
|
+
// box
|
|
89
|
+
tempSphere.copy(self.boundingVolume.sphere);
|
|
90
|
+
} else if (self.boundingVolume instanceof THREE.Sphere) {
|
|
91
|
+
//sphere
|
|
92
|
+
tempSphere.copy(self.boundingVolume);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//tempSphere.applyMatrix4(self.matrixWorld);
|
|
96
|
+
if (!!this.json.boundingVolume.region) {
|
|
97
|
+
self.transformWGS84ToCartesian(
|
|
98
|
+
(self.json.boundingVolume.region[0] + self.json.boundingVolume.region[2]) * 0.5,
|
|
99
|
+
(self.json.boundingVolume.region[1] + self.json.boundingVolume.region[3]) * 0.5,
|
|
100
|
+
(self.json.boundingVolume.region[4] + self.json.boundingVolume.region[5]) * 0.5,
|
|
101
|
+
tempVec1);
|
|
102
|
+
|
|
103
|
+
tempQuaternion.setFromUnitVectors(tempVec1.normalize(), upVector.normalize());
|
|
104
|
+
self.master.applyQuaternion(tempQuaternion);
|
|
105
|
+
self.master.updateWorldMatrix(false, false)
|
|
106
|
+
}
|
|
107
|
+
tempMatrix.makeTranslation(-tempSphere.center.x * self.scale.x, -tempSphere.center.y * self.scale.y, -tempSphere.center.z * self.scale.z);
|
|
108
|
+
//self.master.applyMatrix4(tempMatrix);
|
|
109
|
+
self.master.matrix.multiply(tempMatrix);
|
|
110
|
+
self.master.matrix.decompose( self.master.position, self.master.quaternion, self.master.scale );
|
|
111
|
+
}
|
|
79
112
|
if (properties.onLoadCallback) properties.onLoadCallback(self);
|
|
113
|
+
|
|
80
114
|
}
|
|
81
115
|
self.tileLoader.get(self.abortController, properties.url, self.uuid, self);
|
|
82
116
|
|
|
@@ -132,7 +166,10 @@ class InstancedTile extends THREE.Object3D {
|
|
|
132
166
|
this.boundingVolume = new OBB(this.json.boundingVolume.box);
|
|
133
167
|
} else if (!!this.json.boundingVolume.region) {
|
|
134
168
|
const region = this.json.boundingVolume.region;
|
|
135
|
-
this.
|
|
169
|
+
this.transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
|
|
170
|
+
this.transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
|
|
171
|
+
tempVec1.lerp(tempVec2, 0.5);
|
|
172
|
+
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(tempVec1.x, tempVec1.y, tempVec1.z), tempVec1.distanceTo(tempVec2));
|
|
136
173
|
} else if (!!this.json.boundingVolume.sphere) {
|
|
137
174
|
const sphere = this.json.boundingVolume.sphere;
|
|
138
175
|
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[2], -sphere[1]), sphere[3]);
|
|
@@ -188,6 +225,8 @@ class InstancedTile extends THREE.Object3D {
|
|
|
188
225
|
|
|
189
226
|
}
|
|
190
227
|
}
|
|
228
|
+
self.matrixWorldNeedsUpdate = true;
|
|
229
|
+
self.updateWorldMatrix(true,true)
|
|
191
230
|
}
|
|
192
231
|
|
|
193
232
|
loadMesh(mesh) {
|
|
@@ -440,11 +479,9 @@ class InstancedTile extends THREE.Object3D {
|
|
|
440
479
|
tempSphere.copy(this.boundingVolume);
|
|
441
480
|
tempSphere.applyMatrix4(this.master.matrixWorld);
|
|
442
481
|
if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
443
|
-
} else
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
//throw Error("Region bounding volume not supported");
|
|
447
|
-
return -1;
|
|
482
|
+
} else {
|
|
483
|
+
console.error("unsupported shape");
|
|
484
|
+
return -1
|
|
448
485
|
}
|
|
449
486
|
|
|
450
487
|
/////// return metric based on geometric error and distance
|
|
@@ -505,8 +542,8 @@ class InstancedTile extends THREE.Object3D {
|
|
|
505
542
|
tempSphere.applyMatrix4(this.master.matrixWorld);
|
|
506
543
|
//if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
507
544
|
}
|
|
508
|
-
|
|
509
|
-
|
|
545
|
+
else {
|
|
546
|
+
console.error("unsupported shape")
|
|
510
547
|
}
|
|
511
548
|
return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
512
549
|
}
|
|
@@ -515,5 +552,21 @@ class InstancedTile extends THREE.Object3D {
|
|
|
515
552
|
const self = this;
|
|
516
553
|
return self.master.matrixWorld;
|
|
517
554
|
}
|
|
555
|
+
|
|
556
|
+
transformWGS84ToCartesian(lon, lat, h, sfct) {
|
|
557
|
+
const a = 6378137.0;
|
|
558
|
+
const e = 0.006694384442042;
|
|
559
|
+
const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
|
|
560
|
+
const cosLat = Math.cos(lat);
|
|
561
|
+
const cosLon = Math.cos(lon);
|
|
562
|
+
const sinLat = Math.sin(lat);
|
|
563
|
+
const sinLon = Math.sin(lon);
|
|
564
|
+
const nPh = (N + h);
|
|
565
|
+
const x = nPh * cosLat * cosLon;
|
|
566
|
+
const y = nPh * cosLat * sinLon;
|
|
567
|
+
const z = (0.993305615557957 * N + h) * sinLat;
|
|
568
|
+
|
|
569
|
+
sfct.set(x, y, z);
|
|
570
|
+
}
|
|
518
571
|
}
|
|
519
572
|
export { InstancedTile };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { InstancedMesh } from 'three';
|
|
3
3
|
|
|
4
|
+
const t = new THREE.Matrix4();
|
|
4
5
|
class MeshTile{
|
|
5
6
|
constructor(scene){
|
|
6
7
|
const self = this;
|
|
@@ -60,7 +61,12 @@ class MeshTile{
|
|
|
60
61
|
self.instancedTiles[i].meshContent = self.instancedMesh;
|
|
61
62
|
if(self.instancedTiles[i].materialVisibility && !!self.instancedTiles[i].meshContent){
|
|
62
63
|
self.instancedMesh.count++;
|
|
63
|
-
self.
|
|
64
|
+
self.reuseableMatrix.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);
|
|
65
|
+
self.reuseableMatrix.multiply(self.instancedTiles[i].master.matrixWorld);
|
|
66
|
+
self.reuseableMatrix.multiply(self.instancedMesh.baseMatrix);
|
|
67
|
+
self.instancedMesh.setMatrixAt(self.instancedMesh.count-1, self.reuseableMatrix );
|
|
68
|
+
self.instancedMesh.getMatrixAt(0, t);
|
|
69
|
+
console.log()
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
}
|