@jdultra/threedtiles 5.0.0 → 5.1.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 +17 -7
- package/package.json +4 -4
- package/src/decoder/B3DMDecoder.js +16 -4
- package/src/decoder/FeatureTable.js +169 -0
- package/src/index.js +63 -116
- package/src/tileset/OGC3DTile.js +77 -32
- package/src/tileset/instanced/InstancedTile.js +64 -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);
|
|
@@ -56,6 +57,7 @@ you may also set this value at initialization:
|
|
|
56
57
|
```
|
|
57
58
|
const ogc3DTile = new OGC3DTile({
|
|
58
59
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
60
|
+
renderer: renderer,
|
|
59
61
|
geometricErrorMultiplier: 2.0
|
|
60
62
|
});
|
|
61
63
|
```
|
|
@@ -66,11 +68,12 @@ A value of 1.0 is the default.
|
|
|
66
68
|
|
|
67
69
|
### load tiles outside of view
|
|
68
70
|
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
|
|
71
|
+
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
72
|
|
|
71
73
|
```
|
|
72
74
|
const ogc3DTile = new OGC3DTile({
|
|
73
75
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
76
|
+
renderer: renderer,
|
|
74
77
|
loadOutsideView: true
|
|
75
78
|
});
|
|
76
79
|
```
|
|
@@ -84,6 +87,7 @@ This can be useful to position the tileset at a specific location when it is not
|
|
|
84
87
|
```
|
|
85
88
|
const ogc3DTile = new OGC3DTile({
|
|
86
89
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
90
|
+
renderer: renderer,
|
|
87
91
|
onLoadCallback: tilese => {
|
|
88
92
|
console.log(tileset.boundingVolume);
|
|
89
93
|
}
|
|
@@ -96,6 +100,7 @@ Add a callback on loaded tiles in order to set a material or do some logic on th
|
|
|
96
100
|
```
|
|
97
101
|
const ogc3DTile = new OGC3DTile({
|
|
98
102
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
103
|
+
renderer: renderer,
|
|
99
104
|
meshCallback: mesh => {
|
|
100
105
|
mesh.material.wireframe = true;
|
|
101
106
|
mesh.material.side = THREE.DoubleSide;
|
|
@@ -116,6 +121,7 @@ import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
|
|
|
116
121
|
|
|
117
122
|
const ogc3DTile = new OGC3DTile({
|
|
118
123
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
124
|
+
renderer: renderer,
|
|
119
125
|
tileLoader: new TileLoader(mesh => {
|
|
120
126
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
121
127
|
mesh.material.wireframe = false;
|
|
@@ -133,6 +139,7 @@ The OGC 3DTile object is a regular three.js Object3D so it can be transformed vi
|
|
|
133
139
|
```
|
|
134
140
|
const ogc3DTile = new OGC3DTile({
|
|
135
141
|
url: "https://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json"
|
|
142
|
+
renderer: renderer,
|
|
136
143
|
});
|
|
137
144
|
|
|
138
145
|
ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
|
|
@@ -156,6 +163,7 @@ This service must be passed to every OGC3DTiles object like so:
|
|
|
156
163
|
```
|
|
157
164
|
const ogc3DTile = new OGC3DTile({
|
|
158
165
|
url: "path/to/tileset.json",
|
|
166
|
+
renderer: renderer,
|
|
159
167
|
occlusionCullingService: occlusionCullingService
|
|
160
168
|
});
|
|
161
169
|
```
|
|
@@ -201,11 +209,12 @@ const instancedTileLoader = new InstancedTileLoader(scene, mesh => {
|
|
|
201
209
|
const instancedTilesets = [];
|
|
202
210
|
for (let i = 0; i < 100; i++) {
|
|
203
211
|
const tileset = new InstancedOGC3DTile({
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
|
|
213
|
+
renderer: renderer,
|
|
214
|
+
geometricErrorMultiplier: 1.0,
|
|
215
|
+
loadOutsideView: false,
|
|
216
|
+
tileLoader: instancedTileLoader,
|
|
217
|
+
static: true // when static is set to true, don't forget to call InstancedOGC3DTile#updateMatrix manually
|
|
209
218
|
});
|
|
210
219
|
|
|
211
220
|
tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * i);
|
|
@@ -238,6 +247,7 @@ This will skip recalculating the transformation matrix of every tile each frame
|
|
|
238
247
|
```
|
|
239
248
|
const ogc3DTile = new OGC3DTile({
|
|
240
249
|
url: "path/to/tileset.json",
|
|
250
|
+
renderer: renderer,
|
|
241
251
|
static: true
|
|
242
252
|
});
|
|
243
253
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jdultra/threedtiles",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
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,9 +1,11 @@
|
|
|
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
11
|
const dummy = new THREE.Object3D();
|
|
@@ -31,10 +33,10 @@ function parseB3DM(arrayBuffer, meshCallback) {
|
|
|
31
33
|
const batchTableBinaryByteLength = dataView.getUint32(24, true);
|
|
32
34
|
|
|
33
35
|
const featureTableStart = 28;
|
|
34
|
-
|
|
36
|
+
const featureTable = new FeatureTable(arrayBuffer, featureTableStart, featureTableJSONByteLength, featureTableBinaryByteLength);
|
|
35
37
|
|
|
36
38
|
const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
|
|
37
|
-
|
|
39
|
+
const batchTable = new BatchTable(arrayBuffer, featureTable.getData('BATCH_LENGTH'), batchTableStart, batchTableJSONByteLength, batchTableBinaryByteLength);
|
|
38
40
|
|
|
39
41
|
const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
|
|
40
42
|
const glbBytes = new Uint8Array(arrayBuffer, glbStart, byteLength - glbStart);
|
|
@@ -48,6 +50,7 @@ function parseB3DM(arrayBuffer, meshCallback) {
|
|
|
48
50
|
gltfLoader.parse(gltfBuffer, null, model => {
|
|
49
51
|
|
|
50
52
|
////TODO
|
|
53
|
+
|
|
51
54
|
//model.batchTable = b3dm.batchTable;
|
|
52
55
|
//model.featureTable = b3dm.featureTable;
|
|
53
56
|
|
|
@@ -55,6 +58,15 @@ function parseB3DM(arrayBuffer, meshCallback) {
|
|
|
55
58
|
//model.scene.featureTable = b3dm.featureTable;
|
|
56
59
|
|
|
57
60
|
//const scene = mergeColoredObject(model.scene);
|
|
61
|
+
|
|
62
|
+
//model.scene.applyMatrix4(ytozUpMatrix);
|
|
63
|
+
|
|
64
|
+
const rtcCenter = featureTable.getData('RTC_CENTER');
|
|
65
|
+
if (rtcCenter) {
|
|
66
|
+
tempMatrix.makeTranslation(rtcCenter[0], rtcCenter[2], -rtcCenter[1])
|
|
67
|
+
model.scene.applyMatrix4(tempMatrix);
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
model.scene.traverse((o) => {
|
|
59
71
|
if (o.isMesh) {
|
|
60
72
|
if (!!meshCallback) {
|
|
@@ -76,11 +88,11 @@ const B3DMDecoder = {
|
|
|
76
88
|
|
|
77
89
|
return parseB3DM(arrayBuffer, meshCallback).then(mesh => {
|
|
78
90
|
let instancedMesh;
|
|
91
|
+
mesh.updateWorldMatrix(false, true)
|
|
79
92
|
mesh.traverse(child => {
|
|
80
93
|
if (child.isMesh) {
|
|
81
94
|
instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, maxCount);
|
|
82
|
-
instancedMesh.baseMatrix = child.
|
|
83
|
-
//console.log(child.matrix.elements[12])
|
|
95
|
+
instancedMesh.baseMatrix = child.matrixWorld;
|
|
84
96
|
}
|
|
85
97
|
});
|
|
86
98
|
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,16 @@ 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
35
|
|
|
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
36
|
animate();
|
|
57
37
|
|
|
58
38
|
|
|
@@ -70,10 +50,10 @@ function initScene() {
|
|
|
70
50
|
const scene = new THREE.Scene();
|
|
71
51
|
scene.matrixAutoUpdate = false;
|
|
72
52
|
//scene.matrixWorldAutoUpdate = false;
|
|
73
|
-
scene.background = new THREE.Color(
|
|
53
|
+
scene.background = new THREE.Color(0xffffff);
|
|
74
54
|
scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
|
|
75
55
|
|
|
76
|
-
/*const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
56
|
+
/* const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
77
57
|
const sphere = new THREE.SphereGeometry(2, 16, 8);
|
|
78
58
|
light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
|
|
79
59
|
scene.add(light);
|
|
@@ -84,9 +64,9 @@ function initScene() {
|
|
|
84
64
|
const sphere2 = new THREE.SphereGeometry(2, 16, 8);
|
|
85
65
|
light2.add(new THREE.Mesh(sphere2, new THREE.MeshBasicMaterial({ color: 0xffbbbb })));
|
|
86
66
|
scene.add(light2);
|
|
87
|
-
light2.position.set(200, 100, -100)
|
|
67
|
+
light2.position.set(200, 100, -100); */
|
|
68
|
+
|
|
88
69
|
|
|
89
|
-
|
|
90
70
|
return scene;
|
|
91
71
|
}
|
|
92
72
|
|
|
@@ -134,119 +114,83 @@ function initStats(dom) {
|
|
|
134
114
|
|
|
135
115
|
function initCamera(width, height) {
|
|
136
116
|
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
|
|
137
|
-
camera.position.set(
|
|
138
|
-
|
|
117
|
+
camera.position.set(10000,0,0);
|
|
118
|
+
camera.lookAt(0,0,0);
|
|
119
|
+
|
|
139
120
|
camera.matrixAutoUpdate = true;
|
|
140
121
|
return camera;
|
|
141
122
|
}
|
|
142
123
|
|
|
143
|
-
function initTileset(scene) {
|
|
124
|
+
function initTileset(scene, gem) {
|
|
144
125
|
|
|
145
126
|
const tileLoader = new TileLoader(mesh => {
|
|
146
127
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
147
128
|
mesh.material.wireframe = false;
|
|
148
129
|
mesh.material.side = THREE.DoubleSide;
|
|
149
130
|
mesh.material.metalness = 0.0
|
|
150
|
-
},
|
|
131
|
+
}, 100)
|
|
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,
|
|
142
|
+
centerModel:true,
|
|
164
143
|
renderer: renderer
|
|
165
144
|
|
|
166
145
|
});
|
|
146
|
+
setIntervalAsync(function () {
|
|
147
|
+
ogc3DTile.update(camera);
|
|
148
|
+
}, 20);
|
|
149
|
+
//ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
150
|
+
//ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 1)
|
|
151
|
+
/*
|
|
152
|
+
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 10) // Z-UP to Y-UP
|
|
153
|
+
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 1, 0), 18.5) // Z-UP to Y-UP */
|
|
154
|
+
scene.add(ogc3DTile);
|
|
167
155
|
|
|
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();
|
|
192
|
-
}
|
|
193
|
-
}
|
|
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
|
-
|
|
201
|
-
});
|
|
202
|
-
function startInterval() {
|
|
203
|
-
interval = setIntervalAsync(function () {
|
|
204
|
-
ogc3DTile.update(camera);
|
|
205
|
-
|
|
206
|
-
}, 20);
|
|
207
|
-
}
|
|
208
|
-
startInterval();
|
|
209
|
-
|
|
210
|
-
scene.add(ogc3DTile)
|
|
211
156
|
return ogc3DTile;
|
|
212
157
|
}
|
|
213
158
|
|
|
159
|
+
|
|
214
160
|
function createInstancedTileLoader(scene) {
|
|
215
161
|
return new InstancedTileLoader(scene, mesh => {
|
|
216
162
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
217
163
|
mesh.material.wireframe = false;
|
|
218
164
|
mesh.material.side = THREE.DoubleSide;
|
|
219
|
-
|
|
165
|
+
mesh.material.metalness = 0.0;
|
|
166
|
+
}, 0, 1);
|
|
220
167
|
}
|
|
221
168
|
function initInstancedTilesets(instancedTileLoader) {
|
|
222
169
|
|
|
170
|
+
/*new GLTFLoader().load('http://localhost:8080/test.glb', function ( gltf ) {
|
|
171
|
+
scene.add(gltf.scene);
|
|
172
|
+
} );*/
|
|
173
|
+
|
|
223
174
|
const instancedTilesets = [];
|
|
224
175
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
scene.add(tileset);
|
|
244
|
-
instancedTilesets.push(tileset);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
176
|
+
|
|
177
|
+
const tileset = new InstancedOGC3DTile({
|
|
178
|
+
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
179
|
+
//url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
|
|
180
|
+
//url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ab13lasdc9i/tileset.json",
|
|
181
|
+
url: "http://localhost:8081/tileset.json",
|
|
182
|
+
geometricErrorMultiplier: 1.0,
|
|
183
|
+
loadOutsideView: true,
|
|
184
|
+
tileLoader: instancedTileLoader,
|
|
185
|
+
static: false,
|
|
186
|
+
centerModel:true,
|
|
187
|
+
renderer: renderer
|
|
188
|
+
});
|
|
189
|
+
//tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
190
|
+
|
|
191
|
+
tileset.updateMatrix()
|
|
192
|
+
scene.add(tileset);
|
|
193
|
+
instancedTilesets.push(tileset);
|
|
250
194
|
|
|
251
195
|
scene.updateMatrixWorld(true)
|
|
252
196
|
function now() {
|
|
@@ -255,14 +199,14 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
255
199
|
let updateIndex = 0;
|
|
256
200
|
setInterval(() => {
|
|
257
201
|
let startTime = now();
|
|
258
|
-
do{
|
|
202
|
+
do {
|
|
259
203
|
const frustum = new THREE.Frustum();
|
|
260
204
|
frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
|
|
261
205
|
instancedTilesets[updateIndex].update(camera, frustum);
|
|
262
|
-
updateIndex= (updateIndex+1)%instancedTilesets.length;
|
|
263
|
-
}while(updateIndex < instancedTilesets.length && now()-startTime<4);
|
|
264
|
-
},40);
|
|
265
|
-
|
|
206
|
+
updateIndex = (updateIndex + 1) % instancedTilesets.length;
|
|
207
|
+
} while (updateIndex < instancedTilesets.length && now() - startTime < 4);
|
|
208
|
+
}, 40);
|
|
209
|
+
|
|
266
210
|
//initLODMultiplierSlider(instancedTilesets);
|
|
267
211
|
}
|
|
268
212
|
|
|
@@ -273,7 +217,7 @@ function initLODMultiplierSlider(instancedTilesets) {
|
|
|
273
217
|
|
|
274
218
|
slider.oninput = () => {
|
|
275
219
|
instancedTilesets.forEach(tileset => {
|
|
276
|
-
tileset.setGeometricErrorMultiplier(slider.value*0.1)
|
|
220
|
+
tileset.setGeometricErrorMultiplier(slider.value * 0.1)
|
|
277
221
|
})
|
|
278
222
|
output.innerHTML = slider.value;
|
|
279
223
|
}
|
|
@@ -283,7 +227,10 @@ function initController(camera, dom) {
|
|
|
283
227
|
const controller = new OrbitControls(camera, dom);
|
|
284
228
|
|
|
285
229
|
controller.target.set(0,0,0);
|
|
286
|
-
|
|
230
|
+
//controller.target.set(0,0,0);
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
controller.minDistance = 0.1;
|
|
287
234
|
controller.maxDistance = 100000;
|
|
288
235
|
controller.update();
|
|
289
236
|
return controller;
|
|
@@ -292,7 +239,7 @@ function initController(camera, dom) {
|
|
|
292
239
|
|
|
293
240
|
function animate() {
|
|
294
241
|
requestAnimationFrame(animate);
|
|
295
|
-
instancedTileLoader.update();
|
|
242
|
+
//instancedTileLoader.update();
|
|
296
243
|
composer.render();
|
|
297
244
|
//occlusionCullingService.update(scene, renderer, camera)
|
|
298
245
|
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,34 @@ 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
|
+
tempVec2.set(tempVec1.x, tempVec1.z, -tempVec1.y);
|
|
120
|
+
|
|
121
|
+
tempQuaternion.setFromUnitVectors(tempVec2.normalize(), upVector.normalize());
|
|
122
|
+
self.applyQuaternion(tempQuaternion);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
self.translateX(-tempSphere.center.x * self.scale.x);
|
|
126
|
+
self.translateY(-tempSphere.center.y * self.scale.y);
|
|
127
|
+
self.translateZ(-tempSphere.center.z * self.scale.z);
|
|
128
|
+
|
|
129
|
+
}
|
|
95
130
|
});
|
|
96
131
|
});
|
|
97
132
|
}
|
|
@@ -133,7 +168,10 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
133
168
|
this.boundingVolume = new OBB(this.json.boundingVolume.box);
|
|
134
169
|
} else if (!!this.json.boundingVolume.region) {
|
|
135
170
|
const region = this.json.boundingVolume.region;
|
|
136
|
-
this.
|
|
171
|
+
this.transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
|
|
172
|
+
this.transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
|
|
173
|
+
tempVec1.lerp(tempVec2, 0.5);
|
|
174
|
+
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(tempVec1.x, tempVec1.z, -tempVec1.y), tempVec1.distanceTo(tempVec2));
|
|
137
175
|
} else if (!!this.json.boundingVolume.sphere) {
|
|
138
176
|
const sphere = this.json.boundingVolume.sphere;
|
|
139
177
|
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[2], -sphere[1]), sphere[3]);
|
|
@@ -518,39 +556,30 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
518
556
|
tempSphere.copy(this.boundingVolume);
|
|
519
557
|
tempSphere.applyMatrix4(this.matrixWorld);
|
|
520
558
|
if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
521
|
-
} else
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
return -1;
|
|
559
|
+
} else {
|
|
560
|
+
console.error("unsupported shape");
|
|
561
|
+
return -1
|
|
562
|
+
|
|
526
563
|
}
|
|
527
564
|
|
|
528
565
|
/////// 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
566
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
//throw Error("Region bounding volume not supported");
|
|
552
|
-
return -1;
|
|
567
|
+
const distance = Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
568
|
+
if (distance == 0) {
|
|
569
|
+
return 0;
|
|
553
570
|
}
|
|
571
|
+
const scale = this.matrixWorld.getMaxScaleOnAxis();
|
|
572
|
+
this.renderer.getDrawingBufferSize(rendererSize);
|
|
573
|
+
let s = rendererSize.y;
|
|
574
|
+
let fov = camera.fov;
|
|
575
|
+
if (camera.aspect < 1) {
|
|
576
|
+
fov *= camera.aspect;
|
|
577
|
+
s = rendererSize.x;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
|
|
581
|
+
|
|
582
|
+
return (window.devicePixelRatio * 16 * lambda) / (s * scale);
|
|
554
583
|
}
|
|
555
584
|
|
|
556
585
|
getSiblings() {
|
|
@@ -583,8 +612,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
583
612
|
tempSphere.applyMatrix4(this.matrixWorld);
|
|
584
613
|
//if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
585
614
|
}
|
|
586
|
-
|
|
587
|
-
|
|
615
|
+
else {
|
|
616
|
+
console.error("unsupported shape")
|
|
588
617
|
}
|
|
589
618
|
return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
590
619
|
}
|
|
@@ -592,5 +621,21 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
592
621
|
this.geometricErrorMultiplier = geometricErrorMultiplier;
|
|
593
622
|
this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
|
|
594
623
|
}
|
|
624
|
+
|
|
625
|
+
transformWGS84ToCartesian(lon, lat, h, sfct) {
|
|
626
|
+
const a = 6378137.0;
|
|
627
|
+
const e = 0.006694384442042;
|
|
628
|
+
const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
|
|
629
|
+
const cosLat = Math.cos(lat);
|
|
630
|
+
const cosLon = Math.cos(lon);
|
|
631
|
+
const sinLat = Math.sin(lat);
|
|
632
|
+
const sinLon = Math.sin(lon);
|
|
633
|
+
const nPh = (N + h);
|
|
634
|
+
const x = nPh * cosLat * cosLon;
|
|
635
|
+
const y = nPh * cosLat * sinLon;
|
|
636
|
+
const z = (0.993305615557957 * N + h) * sinLat;
|
|
637
|
+
|
|
638
|
+
sfct.set(x, y, z);
|
|
639
|
+
}
|
|
595
640
|
}
|
|
596
641
|
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,36 @@ 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
|
+
tempVec2.set(tempVec1.x, tempVec1.z, -tempVec1.y);
|
|
103
|
+
|
|
104
|
+
tempQuaternion.setFromUnitVectors(tempVec2.normalize(), upVector.normalize());
|
|
105
|
+
self.master.applyQuaternion(tempQuaternion);
|
|
106
|
+
self.master.updateWorldMatrix(false, false)
|
|
107
|
+
}
|
|
108
|
+
tempMatrix.makeTranslation(-tempSphere.center.x * self.scale.x, -tempSphere.center.y * self.scale.y, -tempSphere.center.z * self.scale.z);
|
|
109
|
+
//self.master.applyMatrix4(tempMatrix);
|
|
110
|
+
self.master.matrix.multiply(tempMatrix);
|
|
111
|
+
self.master.matrix.decompose( self.master.position, self.master.quaternion, self.master.scale );
|
|
112
|
+
}
|
|
79
113
|
if (properties.onLoadCallback) properties.onLoadCallback(self);
|
|
114
|
+
|
|
80
115
|
}
|
|
81
116
|
self.tileLoader.get(self.abortController, properties.url, self.uuid, self);
|
|
82
117
|
|
|
@@ -132,7 +167,10 @@ class InstancedTile extends THREE.Object3D {
|
|
|
132
167
|
this.boundingVolume = new OBB(this.json.boundingVolume.box);
|
|
133
168
|
} else if (!!this.json.boundingVolume.region) {
|
|
134
169
|
const region = this.json.boundingVolume.region;
|
|
135
|
-
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.z, -tempVec1.y), tempVec1.distanceTo(tempVec2));
|
|
136
174
|
} else if (!!this.json.boundingVolume.sphere) {
|
|
137
175
|
const sphere = this.json.boundingVolume.sphere;
|
|
138
176
|
this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[2], -sphere[1]), sphere[3]);
|
|
@@ -188,6 +226,8 @@ class InstancedTile extends THREE.Object3D {
|
|
|
188
226
|
|
|
189
227
|
}
|
|
190
228
|
}
|
|
229
|
+
self.matrixWorldNeedsUpdate = true;
|
|
230
|
+
self.updateWorldMatrix(true,true)
|
|
191
231
|
}
|
|
192
232
|
|
|
193
233
|
loadMesh(mesh) {
|
|
@@ -440,11 +480,9 @@ class InstancedTile extends THREE.Object3D {
|
|
|
440
480
|
tempSphere.copy(this.boundingVolume);
|
|
441
481
|
tempSphere.applyMatrix4(this.master.matrixWorld);
|
|
442
482
|
if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
443
|
-
} else
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
//throw Error("Region bounding volume not supported");
|
|
447
|
-
return -1;
|
|
483
|
+
} else {
|
|
484
|
+
console.error("unsupported shape");
|
|
485
|
+
return -1
|
|
448
486
|
}
|
|
449
487
|
|
|
450
488
|
/////// return metric based on geometric error and distance
|
|
@@ -505,8 +543,8 @@ class InstancedTile extends THREE.Object3D {
|
|
|
505
543
|
tempSphere.applyMatrix4(this.master.matrixWorld);
|
|
506
544
|
//if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
507
545
|
}
|
|
508
|
-
|
|
509
|
-
|
|
546
|
+
else {
|
|
547
|
+
console.error("unsupported shape")
|
|
510
548
|
}
|
|
511
549
|
return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
512
550
|
}
|
|
@@ -515,5 +553,21 @@ class InstancedTile extends THREE.Object3D {
|
|
|
515
553
|
const self = this;
|
|
516
554
|
return self.master.matrixWorld;
|
|
517
555
|
}
|
|
556
|
+
|
|
557
|
+
transformWGS84ToCartesian(lon, lat, h, sfct) {
|
|
558
|
+
const a = 6378137.0;
|
|
559
|
+
const e = 0.006694384442042;
|
|
560
|
+
const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
|
|
561
|
+
const cosLat = Math.cos(lat);
|
|
562
|
+
const cosLon = Math.cos(lon);
|
|
563
|
+
const sinLat = Math.sin(lat);
|
|
564
|
+
const sinLon = Math.sin(lon);
|
|
565
|
+
const nPh = (N + h);
|
|
566
|
+
const x = nPh * cosLat * cosLon;
|
|
567
|
+
const y = nPh * cosLat * sinLon;
|
|
568
|
+
const z = (0.993305615557957 * N + h) * sinLat;
|
|
569
|
+
|
|
570
|
+
sfct.set(x, y, z);
|
|
571
|
+
}
|
|
518
572
|
}
|
|
519
573
|
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
|
}
|