@jdultra/threedtiles 5.1.4 → 6.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/README.md +33 -9
- package/package.json +1 -1
- package/src/decoder/B3DMDecoder.js +4 -2
- package/src/index.js +22 -22
- package/src/tileset/OGC3DTile.js +17 -7
- package/src/tileset/TileLoader.js +68 -27
- package/src/tileset/instanced/InstancedTile.js +3 -2
- package/src/tileset/instanced/InstancedTileLoader.js +55 -5
package/README.md
CHANGED
|
@@ -47,6 +47,11 @@ Currently, the library is limmited to B3DM files.
|
|
|
47
47
|
- Occlusion culling
|
|
48
48
|
- Instanced tilesets
|
|
49
49
|
- Center tileset and re-orient geolocated data
|
|
50
|
+
- gltf/glb tiles (OGC3DTiles 1.1)
|
|
51
|
+
- point clouds (only through gltf/glb tiles)
|
|
52
|
+
|
|
53
|
+
Support for OGC3DTiles 1.1 is underway. gltf/glb tiles are already supported but not yet implicit tiling.
|
|
54
|
+
There is no plan to support .pnts or .i3dm tiles as points and instanced meshes are already supported through gltf tiles.
|
|
50
55
|
|
|
51
56
|
### geometric Error Multiplier
|
|
52
57
|
The geometric error multiplier allows you to multiply the geometric error by a factor.
|
|
@@ -109,6 +114,20 @@ const ogc3DTile = new OGC3DTile({
|
|
|
109
114
|
}
|
|
110
115
|
});
|
|
111
116
|
```
|
|
117
|
+
|
|
118
|
+
#### Points callback
|
|
119
|
+
Add a callback on loaded point tiles in order to set a material or do some logic on the points.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
const ogc3DTile = new OGC3DTile({
|
|
123
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
124
|
+
renderer: renderer,
|
|
125
|
+
pointsCallback: points => {
|
|
126
|
+
points.material.size = 0.1;
|
|
127
|
+
points.material.sizeAttenuation = true;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
```
|
|
112
131
|
If using a shared cache between tilesets, check out the next section.
|
|
113
132
|
|
|
114
133
|
### Cache
|
|
@@ -124,12 +143,15 @@ import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
|
|
|
124
143
|
const ogc3DTile = new OGC3DTile({
|
|
125
144
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
126
145
|
renderer: renderer,
|
|
127
|
-
tileLoader: new TileLoader(mesh => {
|
|
146
|
+
tileLoader: new TileLoader(2000, mesh => {
|
|
128
147
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
129
148
|
mesh.material.wireframe = false;
|
|
130
149
|
mesh.material.side = THREE.DoubleSide;
|
|
131
150
|
},
|
|
132
|
-
|
|
151
|
+
points=>{
|
|
152
|
+
points.material.size = 0.1;
|
|
153
|
+
points.material.sizeAttenuation = true;
|
|
154
|
+
}
|
|
133
155
|
),
|
|
134
156
|
meshCallback: mesh => { mesh.material.wireframe = true;} // This callback will not be used as the callback provided to the TileLoader takes priority
|
|
135
157
|
});
|
|
@@ -198,13 +220,15 @@ higher performance when displaying the same Tileset many times.
|
|
|
198
220
|
|
|
199
221
|
```
|
|
200
222
|
// First create the InstancedTileLoader that will manage caching
|
|
201
|
-
const instancedTileLoader = new InstancedTileLoader(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
223
|
+
const instancedTileLoader = new InstancedTileLoader(
|
|
224
|
+
scene,
|
|
225
|
+
100, // cache size as in the number of tiles cached in memory
|
|
226
|
+
1, // max number of tilesets from the same source
|
|
227
|
+
mesh => {
|
|
228
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
229
|
+
mesh.material.wireframe = false;
|
|
230
|
+
mesh.material.side = THREE.DoubleSide;
|
|
231
|
+
}
|
|
208
232
|
);
|
|
209
233
|
|
|
210
234
|
// then create some tilesets
|
package/package.json
CHANGED
|
@@ -5,9 +5,9 @@ import {FeatureTable, BatchTable} from './FeatureTable';
|
|
|
5
5
|
|
|
6
6
|
const gltfLoader = new GLTFLoader();
|
|
7
7
|
const dracoLoader = new DRACOLoader();
|
|
8
|
-
const tempMatrix = new THREE.Matrix4();
|
|
9
8
|
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
10
9
|
gltfLoader.setDRACOLoader(dracoLoader);
|
|
10
|
+
const tempMatrix = new THREE.Matrix4();
|
|
11
11
|
const zUpToYUpMatrix = new THREE.Matrix4();
|
|
12
12
|
zUpToYUpMatrix.set(1,0,0,0,
|
|
13
13
|
0,0,-1,0,
|
|
@@ -16,7 +16,7 @@ zUpToYUpMatrix.set(1,0,0,0,
|
|
|
16
16
|
|
|
17
17
|
//const legacyGLTFLoader = new LegacyGLTFLoader();
|
|
18
18
|
|
|
19
|
-
function parseB3DM(arrayBuffer, meshCallback, zUpToYUp) {
|
|
19
|
+
function parseB3DM(arrayBuffer, meshCallback, geometricError, zUpToYUp) {
|
|
20
20
|
const dataView = new DataView(arrayBuffer);
|
|
21
21
|
|
|
22
22
|
const magic =
|
|
@@ -75,6 +75,7 @@ function parseB3DM(arrayBuffer, meshCallback, zUpToYUp) {
|
|
|
75
75
|
model.scene.traverse((o) => {
|
|
76
76
|
|
|
77
77
|
if (o.isMesh) {
|
|
78
|
+
o.geometricError = geometricError
|
|
78
79
|
if(zUpToYUp){
|
|
79
80
|
o.applyMatrix4(zUpToYUpMatrix);
|
|
80
81
|
}
|
|
@@ -96,6 +97,7 @@ const B3DMDecoder = {
|
|
|
96
97
|
parseB3DMInstanced: (arrayBuffer, meshCallback, maxCount, zUpToYUp) => { // expects GLTF with one node level
|
|
97
98
|
|
|
98
99
|
return parseB3DM(arrayBuffer, meshCallback, zUpToYUp).then(mesh => {
|
|
100
|
+
// todo several meshes in a single gltf
|
|
99
101
|
let instancedMesh;
|
|
100
102
|
mesh.updateWorldMatrix(false, true)
|
|
101
103
|
mesh.traverse(child => {
|
package/src/index.js
CHANGED
|
@@ -49,7 +49,7 @@ function initScene() {
|
|
|
49
49
|
const scene = new THREE.Scene();
|
|
50
50
|
scene.matrixAutoUpdate = false;
|
|
51
51
|
//scene.matrixWorldAutoUpdate = false;
|
|
52
|
-
scene.background = new THREE.Color(
|
|
52
|
+
scene.background = new THREE.Color(0x888888);
|
|
53
53
|
scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
|
|
54
54
|
|
|
55
55
|
/* const light = new THREE.PointLight(0xbbbbff, 2, 5000);
|
|
@@ -112,8 +112,8 @@ function initStats(dom) {
|
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
function initCamera(width, height) {
|
|
115
|
-
const camera = new THREE.PerspectiveCamera(60, width / height, 1,
|
|
116
|
-
camera.position.set(
|
|
115
|
+
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
|
|
116
|
+
camera.position.set(-0.5,-0.7,10);
|
|
117
117
|
camera.lookAt(0,0,0);
|
|
118
118
|
|
|
119
119
|
camera.matrixAutoUpdate = true;
|
|
@@ -122,34 +122,32 @@ function initCamera(width, height) {
|
|
|
122
122
|
|
|
123
123
|
function initTileset(scene, gem) {
|
|
124
124
|
|
|
125
|
-
const tileLoader = new TileLoader(mesh => {
|
|
125
|
+
const tileLoader = new TileLoader(100, mesh => {
|
|
126
126
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
127
127
|
mesh.material.wireframe = false;
|
|
128
128
|
mesh.material.side = THREE.DoubleSide;
|
|
129
129
|
mesh.material.metalness = 0.0
|
|
130
|
-
},
|
|
130
|
+
}, points=>{
|
|
131
|
+
points.material.size = Math.min(1.0,0.5*Math.sqrt(points.geometricError));
|
|
132
|
+
points.material.sizeAttenuation = true;
|
|
133
|
+
});
|
|
131
134
|
|
|
132
135
|
const ogc3DTile = new OGC3DTile({
|
|
133
136
|
url: "https://storage.googleapis.com/ogc-3d-tiles/baltimore/tileset.json",
|
|
137
|
+
//url: "http://localhost:8080/tileset.json",
|
|
134
138
|
//url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
135
|
-
geometricErrorMultiplier:
|
|
139
|
+
geometricErrorMultiplier: 1,
|
|
136
140
|
loadOutsideView: false,
|
|
137
141
|
tileLoader: tileLoader,
|
|
138
142
|
//occlusionCullingService: occlusionCullingService,
|
|
139
143
|
static: false,
|
|
140
144
|
centerModel:true,
|
|
141
145
|
renderer: renderer,
|
|
142
|
-
|
|
143
|
-
if (!!tile.json.boundingVolume.region) {
|
|
144
|
-
const halfHeight = (tile.json.boundingVolume.region[5] - tile.json.boundingVolume.region[4]) * 0.5;
|
|
145
|
-
ogc3DTile.translateOnAxis(new THREE.Vector3(0, 1, 0), halfHeight);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
146
|
+
|
|
149
147
|
});
|
|
150
148
|
setIntervalAsync(function () {
|
|
151
149
|
ogc3DTile.update(camera);
|
|
152
|
-
},
|
|
150
|
+
}, 10);
|
|
153
151
|
|
|
154
152
|
|
|
155
153
|
|
|
@@ -165,12 +163,13 @@ function initTileset(scene, gem) {
|
|
|
165
163
|
|
|
166
164
|
|
|
167
165
|
function createInstancedTileLoader(scene) {
|
|
168
|
-
return new InstancedTileLoader(scene,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
166
|
+
return new InstancedTileLoader(scene, 100, 1,
|
|
167
|
+
mesh => {
|
|
168
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
169
|
+
mesh.material.wireframe = false;
|
|
170
|
+
mesh.material.side = THREE.DoubleSide;
|
|
171
|
+
mesh.material.metalness = 0.0;
|
|
172
|
+
});
|
|
174
173
|
}
|
|
175
174
|
function initInstancedTilesets(instancedTileLoader) {
|
|
176
175
|
|
|
@@ -183,7 +182,8 @@ function initInstancedTilesets(instancedTileLoader) {
|
|
|
183
182
|
|
|
184
183
|
const tileset = new InstancedOGC3DTile({
|
|
185
184
|
url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
|
|
186
|
-
|
|
185
|
+
//url: "http://localhost:8080/tileset.json",
|
|
186
|
+
geometricErrorMultiplier: 0.1,
|
|
187
187
|
loadOutsideView: true,
|
|
188
188
|
tileLoader: instancedTileLoader,
|
|
189
189
|
static: false,
|
|
@@ -235,7 +235,7 @@ function initController(camera, dom) {
|
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
controller.minDistance = 0.1;
|
|
238
|
-
controller.maxDistance =
|
|
238
|
+
controller.maxDistance = 1000;
|
|
239
239
|
controller.update();
|
|
240
240
|
return controller;
|
|
241
241
|
}
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -28,6 +28,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
28
28
|
* loadOutsideView: Boolean,
|
|
29
29
|
* tileLoader : TileLoader,
|
|
30
30
|
* meshCallback: function,
|
|
31
|
+
* pointsCallback: function,
|
|
31
32
|
* cameraOnLoad: camera,
|
|
32
33
|
* parentTile: OGC3DTile,
|
|
33
34
|
* onLoadCallback: function,
|
|
@@ -45,11 +46,16 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
45
46
|
if (!!properties.tileLoader) {
|
|
46
47
|
this.tileLoader = properties.tileLoader;
|
|
47
48
|
} else {
|
|
48
|
-
this.tileLoader = new TileLoader(
|
|
49
|
-
|
|
49
|
+
this.tileLoader = new TileLoader(
|
|
50
|
+
200,
|
|
51
|
+
!properties.meshCallback ? mesh => {
|
|
50
52
|
mesh.material.wireframe = false;
|
|
51
53
|
mesh.material.side = THREE.DoubleSide;
|
|
52
|
-
} : properties.meshCallback
|
|
54
|
+
} : properties.meshCallback,
|
|
55
|
+
!properties.pointsCallback ? points => {
|
|
56
|
+
points.material.size = 0.1;
|
|
57
|
+
points.material.sizeAttenuation = true;
|
|
58
|
+
} : properties.pointsCallback);
|
|
53
59
|
}
|
|
54
60
|
// set properties general to the entire tileset
|
|
55
61
|
this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
|
|
@@ -214,7 +220,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
214
220
|
}
|
|
215
221
|
|
|
216
222
|
if (!!url) {
|
|
217
|
-
if (url.includes(".b3dm")) {
|
|
223
|
+
if (url.includes(".b3dm") || url.includes(".glb") || url.includes(".gltf")) {
|
|
218
224
|
self.contentURL = url;
|
|
219
225
|
if(!!self.json.boundingVolume.region){
|
|
220
226
|
//self.applyMatrix4(zUpToYUp);
|
|
@@ -248,7 +254,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
248
254
|
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
249
255
|
}, () => self.getSiblings(),
|
|
250
256
|
self.level,
|
|
251
|
-
!!
|
|
257
|
+
!!self.json.boundingVolume.region,
|
|
258
|
+
self.geometricError
|
|
252
259
|
);
|
|
253
260
|
} else if (url.includes(".json")) {
|
|
254
261
|
self.tileLoader.get(self.abortController, this.uuid, url, json => {
|
|
@@ -371,6 +378,9 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
371
378
|
if (allChildrenReady) {
|
|
372
379
|
self.changeContentVisibility(false);
|
|
373
380
|
}
|
|
381
|
+
else{
|
|
382
|
+
console.log("loading...")
|
|
383
|
+
}
|
|
374
384
|
}
|
|
375
385
|
}
|
|
376
386
|
|
|
@@ -542,10 +552,10 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
542
552
|
function setMeshVisibility(mesh, visibility) {
|
|
543
553
|
mesh.material.visible = visibility;
|
|
544
554
|
if (!!visibility) {
|
|
545
|
-
self.meshesToDisplay
|
|
555
|
+
self.meshesToDisplay=1;
|
|
546
556
|
mesh.onAfterRender = () => {
|
|
547
557
|
delete mesh.onAfterRender;
|
|
548
|
-
self.meshesDisplayed
|
|
558
|
+
self.meshesDisplayed=1;
|
|
549
559
|
};
|
|
550
560
|
}
|
|
551
561
|
|
|
@@ -3,12 +3,24 @@ import { B3DMDecoder } from "../decoder/B3DMDecoder";
|
|
|
3
3
|
import { setIntervalAsync } from 'set-interval-async/dynamic';
|
|
4
4
|
import { initial } from 'lodash';
|
|
5
5
|
import * as THREE from 'three';
|
|
6
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
7
|
+
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
6
8
|
|
|
7
9
|
let concurentDownloads = 0;
|
|
10
|
+
const zUpToYUpMatrix = new THREE.Matrix4();
|
|
11
|
+
zUpToYUpMatrix.set(1, 0, 0, 0,
|
|
12
|
+
0, 0, -1, 0,
|
|
13
|
+
0, 1, 0, 0,
|
|
14
|
+
0, 0, 0, 1);
|
|
15
|
+
const gltfLoader = new GLTFLoader();
|
|
16
|
+
const dracoLoader = new DRACOLoader();
|
|
17
|
+
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
18
|
+
gltfLoader.setDRACOLoader(dracoLoader);
|
|
8
19
|
|
|
9
20
|
class TileLoader {
|
|
10
|
-
constructor(meshCallback,
|
|
21
|
+
constructor(maxCachedItems, meshCallback, pointsCallback) {
|
|
11
22
|
this.meshCallback = meshCallback;
|
|
23
|
+
this.pointsCallback = pointsCallback;
|
|
12
24
|
this.cache = new LinkedHashMap();
|
|
13
25
|
this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 100;
|
|
14
26
|
this.register = {};
|
|
@@ -21,8 +33,8 @@ class TileLoader {
|
|
|
21
33
|
this.init();
|
|
22
34
|
}
|
|
23
35
|
|
|
24
|
-
init(){
|
|
25
|
-
|
|
36
|
+
init() {
|
|
37
|
+
|
|
26
38
|
const self = this;
|
|
27
39
|
setIntervalAsync(() => {
|
|
28
40
|
self.download();
|
|
@@ -33,7 +45,7 @@ class TileLoader {
|
|
|
33
45
|
do {
|
|
34
46
|
loaded = self.loadBatch();
|
|
35
47
|
} while (loaded > 0 && (Date.now() - start) <= 0)
|
|
36
|
-
|
|
48
|
+
|
|
37
49
|
}, 10);
|
|
38
50
|
}
|
|
39
51
|
|
|
@@ -67,7 +79,7 @@ class TileLoader {
|
|
|
67
79
|
const register = data[1];
|
|
68
80
|
const key = data[2];
|
|
69
81
|
const mesh = cache.get(key);
|
|
70
|
-
|
|
82
|
+
|
|
71
83
|
if (!!mesh && !!register[key]) {
|
|
72
84
|
Object.keys(register[key]).forEach(tile => {
|
|
73
85
|
const callback = register[key][tile];
|
|
@@ -79,7 +91,7 @@ class TileLoader {
|
|
|
79
91
|
}
|
|
80
92
|
return 1;
|
|
81
93
|
}
|
|
82
|
-
|
|
94
|
+
|
|
83
95
|
getNextDownloads() {
|
|
84
96
|
let smallestDistance = Number.MAX_VALUE;
|
|
85
97
|
let closest = -1;
|
|
@@ -94,7 +106,7 @@ class TileLoader {
|
|
|
94
106
|
}
|
|
95
107
|
if (this.nextDownloads.length > 0) return;
|
|
96
108
|
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
97
|
-
const dist = this.downloads[i].distanceFunction()*this.downloads[i].level;
|
|
109
|
+
const dist = this.downloads[i].distanceFunction() * this.downloads[i].level;
|
|
98
110
|
if (dist < smallestDistance) {
|
|
99
111
|
smallestDistance = dist;
|
|
100
112
|
closest = i;
|
|
@@ -111,12 +123,12 @@ class TileLoader {
|
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
}
|
|
114
|
-
|
|
126
|
+
|
|
115
127
|
getNextReady() {
|
|
116
128
|
let smallestDistance = Number.MAX_VALUE;
|
|
117
129
|
let closest = -1;
|
|
118
130
|
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
119
|
-
|
|
131
|
+
|
|
120
132
|
if (!this.ready[i][3]) {// if no distance function, must be a json, give absolute priority!
|
|
121
133
|
this.nextReady.push(this.ready.splice(i, 1)[0]);
|
|
122
134
|
}
|
|
@@ -142,19 +154,19 @@ class TileLoader {
|
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
|
|
145
|
-
get(abortController, tileIdentifier, path, callback, distanceFunction, getSiblings, level, zUpToYUp) {
|
|
157
|
+
get(abortController, tileIdentifier, path, callback, distanceFunction, getSiblings, level, zUpToYUp, geometricError) {
|
|
146
158
|
const self = this;
|
|
147
159
|
const key = simplifyPath(path);
|
|
148
160
|
|
|
149
161
|
const realAbortController = new AbortController();
|
|
150
|
-
abortController.signal.addEventListener("abort", ()=>{
|
|
151
|
-
if(!self.register[key] || Object.keys(self.register[key]).length == 0){
|
|
162
|
+
abortController.signal.addEventListener("abort", () => {
|
|
163
|
+
if (!self.register[key] || Object.keys(self.register[key]).length == 0) {
|
|
152
164
|
realAbortController.abort();
|
|
153
165
|
}
|
|
154
166
|
})
|
|
155
167
|
|
|
156
|
-
if (!path.includes(".b3dm") && !path.includes(".json")) {
|
|
157
|
-
console.error("the 3DTiles cache can only be used to load B3DM and json data");
|
|
168
|
+
if (!path.includes(".b3dm") && !path.includes(".json") && !path.includes(".gltf") && !path.includes(".glb")) {
|
|
169
|
+
console.error("the 3DTiles cache can only be used to load B3DM, gltf and json data");
|
|
158
170
|
return;
|
|
159
171
|
}
|
|
160
172
|
if (!self.register[key]) {
|
|
@@ -173,7 +185,7 @@ class TileLoader {
|
|
|
173
185
|
if (path.includes(".b3dm")) {
|
|
174
186
|
downloadFunction = () => {
|
|
175
187
|
concurentDownloads++;
|
|
176
|
-
fetch(path, {signal: realAbortController.signal}).then(result => {
|
|
188
|
+
fetch(path, { signal: realAbortController.signal }).then(result => {
|
|
177
189
|
concurentDownloads--;
|
|
178
190
|
if (!result.ok) {
|
|
179
191
|
console.error("could not load tile with path : " + path)
|
|
@@ -182,33 +194,62 @@ class TileLoader {
|
|
|
182
194
|
return result.arrayBuffer();
|
|
183
195
|
|
|
184
196
|
})
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
.then(resultArrayBuffer => {
|
|
198
|
+
return B3DMDecoder.parseB3DM(resultArrayBuffer, self.meshCallback, geometricError, zUpToYUp);
|
|
199
|
+
})
|
|
200
|
+
.then(mesh => {
|
|
201
|
+
self.cache.put(key, mesh);
|
|
190
202
|
self.checkSize();
|
|
191
203
|
this.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
|
|
192
|
-
|
|
193
|
-
|
|
204
|
+
})
|
|
205
|
+
.catch(() => { });;
|
|
206
|
+
}
|
|
207
|
+
} else if (path.includes(".glb") || path.includes(".gltf")) {
|
|
208
|
+
downloadFunction = () => {
|
|
209
|
+
concurentDownloads++;
|
|
210
|
+
gltfLoader.load(path, gltf => {
|
|
211
|
+
gltf.scene.traverse((o) => {
|
|
212
|
+
o.geometricError = geometricError;
|
|
213
|
+
if (o.isMesh) {
|
|
214
|
+
if (zUpToYUp) {
|
|
215
|
+
o.applyMatrix4(zUpToYUpMatrix);
|
|
216
|
+
}
|
|
217
|
+
if (!!self.meshCallback) {
|
|
218
|
+
self.meshCallback(o);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (o.isPoints) {
|
|
222
|
+
if (zUpToYUp) {
|
|
223
|
+
o.applyMatrix4(zUpToYUpMatrix);
|
|
224
|
+
}
|
|
225
|
+
if (!!self.pointsCallback) {
|
|
226
|
+
self.pointsCallback(o);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
self.cache.put(key, gltf.scene);
|
|
231
|
+
self.checkSize();
|
|
232
|
+
self.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
|
|
233
|
+
});
|
|
234
|
+
|
|
194
235
|
}
|
|
195
236
|
} else if (path.includes(".json")) {
|
|
196
237
|
downloadFunction = () => {
|
|
197
238
|
concurentDownloads++;
|
|
198
|
-
fetch(path, {signal: realAbortController.signal}).then(result => {
|
|
239
|
+
fetch(path, { signal: realAbortController.signal }).then(result => {
|
|
199
240
|
concurentDownloads--;
|
|
200
241
|
if (!result.ok) {
|
|
201
242
|
console.error("could not load tile with path : " + path)
|
|
202
243
|
throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
|
|
203
244
|
}
|
|
204
245
|
return result.json();
|
|
205
|
-
|
|
246
|
+
|
|
206
247
|
}).then(json => {
|
|
207
248
|
self.cache.put(key, json);
|
|
208
249
|
self.checkSize();
|
|
209
|
-
|
|
250
|
+
self.meshReceived(self.cache, self.register, key);
|
|
210
251
|
})
|
|
211
|
-
|
|
252
|
+
.catch(e => console.error("tile download aborted"));
|
|
212
253
|
}
|
|
213
254
|
}
|
|
214
255
|
this.scheduleDownload({
|
|
@@ -228,7 +269,7 @@ class TileLoader {
|
|
|
228
269
|
|
|
229
270
|
invalidate(path, tileIdentifier) {
|
|
230
271
|
const key = simplifyPath(path);
|
|
231
|
-
if(!!this.register[key]){
|
|
272
|
+
if (!!this.register[key]) {
|
|
232
273
|
delete this.register[key][tileIdentifier];
|
|
233
274
|
}
|
|
234
275
|
}
|
|
@@ -212,14 +212,15 @@ class InstancedTile extends THREE.Object3D {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
if (!!url) {
|
|
215
|
-
if (url.includes(".b3dm")) {
|
|
215
|
+
if (url.includes(".b3dm")|| url.includes(".glb") || url.includes(".gltf")) {
|
|
216
216
|
self.contentURL = url;
|
|
217
217
|
|
|
218
218
|
self.tileLoader.get(self.abortController, url, self.uuid, self, !self.cameraOnLoad ? () => 0 : () => {
|
|
219
219
|
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
220
220
|
}, () => self.getSiblings(),
|
|
221
221
|
self.level,
|
|
222
|
-
!!self.json.boundingVolume.region
|
|
222
|
+
!!self.json.boundingVolume.region,
|
|
223
|
+
self.geometricError);
|
|
223
224
|
} else if (url.includes(".json")) {
|
|
224
225
|
self.tileLoader.get(self.abortController, url, self.uuid, self);
|
|
225
226
|
|
|
@@ -4,11 +4,23 @@ import { setIntervalAsync } from 'set-interval-async/dynamic';
|
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { MeshTile } from './MeshTile';
|
|
6
6
|
import { JsonTile } from './JsonTile';
|
|
7
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
8
|
+
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
let concurentDownloads = 0;
|
|
12
|
+
const gltfLoader = new GLTFLoader();
|
|
13
|
+
const dracoLoader = new DRACOLoader();
|
|
14
|
+
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
|
|
15
|
+
gltfLoader.setDRACOLoader(dracoLoader);
|
|
16
|
+
const zUpToYUpMatrix = new THREE.Matrix4();
|
|
17
|
+
zUpToYUpMatrix.set(1, 0, 0, 0,
|
|
18
|
+
0, 0, -1, 0,
|
|
19
|
+
0, 1, 0, 0,
|
|
20
|
+
0, 0, 0, 1);
|
|
9
21
|
|
|
10
22
|
class InstancedTileLoader {
|
|
11
|
-
constructor(scene,
|
|
23
|
+
constructor(scene, maxCachedItems, maxInstances, meshCallback) {
|
|
12
24
|
this.meshCallback = meshCallback;
|
|
13
25
|
this.maxInstances = maxInstances;
|
|
14
26
|
this.cache = new LinkedHashMap();
|
|
@@ -76,6 +88,43 @@ class InstancedTileLoader {
|
|
|
76
88
|
|
|
77
89
|
})
|
|
78
90
|
.catch(e=>console.error(e));
|
|
91
|
+
}if(nextDownload.path.includes(".glb") || (nextDownload.path.includes(".gltf"))){
|
|
92
|
+
gltfLoader.load(nextDownload.path, gltf => {
|
|
93
|
+
gltf.scene.traverse((o) => {
|
|
94
|
+
o.geometricError = nextDownload.geometricError;
|
|
95
|
+
if (o.isMesh) {
|
|
96
|
+
if (nextDownload.zUpToYUp) {
|
|
97
|
+
o.applyMatrix4(zUpToYUpMatrix);
|
|
98
|
+
}
|
|
99
|
+
if (!!self.meshCallback) {
|
|
100
|
+
self.meshCallback(o);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (o.isPoints) {
|
|
104
|
+
console.error("instanced point cloud is not supported");
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
let instancedMesh;
|
|
108
|
+
gltf.scene.updateWorldMatrix(false, true)
|
|
109
|
+
gltf.scene.traverse(child => {
|
|
110
|
+
//TODO several meshes in a single gltf
|
|
111
|
+
if (child.isMesh) {
|
|
112
|
+
instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, self.maxInstances);
|
|
113
|
+
instancedMesh.baseMatrix = child.matrixWorld;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
});
|
|
117
|
+
self.ready.unshift(nextDownload);
|
|
118
|
+
if(!instancedMesh){
|
|
119
|
+
gltf.scene.traverse(c=>{
|
|
120
|
+
if(c.dispose) c.dispose();
|
|
121
|
+
if(c.material) c.material.dispose();
|
|
122
|
+
});
|
|
123
|
+
}else{
|
|
124
|
+
nextDownload.tile.setObject(instancedMesh);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
79
128
|
}else if(nextDownload.path.includes(".json")){
|
|
80
129
|
concurentDownloads++;
|
|
81
130
|
fetch(nextDownload.path, {signal: nextDownload.abortController.signal}).then(result => {
|
|
@@ -138,12 +187,12 @@ class InstancedTileLoader {
|
|
|
138
187
|
}
|
|
139
188
|
}
|
|
140
189
|
|
|
141
|
-
get(abortController, path, uuid, instancedOGC3DTile, distanceFunction, getSiblings, level, zUpToYUp) {
|
|
190
|
+
get(abortController, path, uuid, instancedOGC3DTile, distanceFunction, getSiblings, level, zUpToYUp, geometricError) {
|
|
142
191
|
const self = this;
|
|
143
192
|
const key = simplifyPath(path);
|
|
144
193
|
|
|
145
|
-
if (!path.includes(".b3dm") && !path.includes(".json")) {
|
|
146
|
-
console.error("the 3DTiles cache can only be used to load B3DM and json data");
|
|
194
|
+
if (!path.includes(".b3dm") && !path.includes(".json") && !path.includes(".glb") && !path.includes(".gltf")) {
|
|
195
|
+
console.error("the 3DTiles cache can only be used to load B3DM, gltf and json data");
|
|
147
196
|
return;
|
|
148
197
|
}
|
|
149
198
|
|
|
@@ -153,7 +202,7 @@ class InstancedTileLoader {
|
|
|
153
202
|
return;
|
|
154
203
|
} else {
|
|
155
204
|
|
|
156
|
-
if (path.includes(".b3dm")) {
|
|
205
|
+
if (path.includes(".b3dm") || path.includes(".glb") || path.includes(".gltf")) {
|
|
157
206
|
const tile = new MeshTile(self.scene);
|
|
158
207
|
tile.addInstance(instancedOGC3DTile);
|
|
159
208
|
|
|
@@ -175,6 +224,7 @@ class InstancedTileLoader {
|
|
|
175
224
|
level: level,
|
|
176
225
|
uuid: uuid,
|
|
177
226
|
zUpToYUp: zUpToYUp,
|
|
227
|
+
geometricError: geometricError,
|
|
178
228
|
shouldDoDownload: () => {
|
|
179
229
|
return true;
|
|
180
230
|
},
|