@jdultra/threedtiles 3.1.0 → 3.1.4
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 +32 -4
- package/index.html +1 -0
- package/manifest.json +4 -0
- package/package.json +1 -1
- package/src/index.js +7 -13
- package/src/tileset/OGC3DTile.js +73 -33
- package/src/tileset/TileLoader.js +179 -81
- package/src/tileset/TilesetStats.js +0 -65
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ import { OGC3DTile } from "./tileset/OGC3DTile";
|
|
|
14
14
|
...
|
|
15
15
|
|
|
16
16
|
const ogc3DTile = new OGC3DTile({
|
|
17
|
-
url: "https://
|
|
17
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json"
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
scene.add(ogc3DTile);
|
|
@@ -33,6 +33,8 @@ setInterval(function () {
|
|
|
33
33
|
- Allows tilesets transformations. Translate, scale and rotate a tilesets in real-time.
|
|
34
34
|
- callback on loaded geometry to assign a custom material or use the meshes for computations.
|
|
35
35
|
- Optionally load low detail tiles outside of view frustum for correct shadows and basic mesh present when the camera moves quickly.
|
|
36
|
+
- Share a cache between tileset instances
|
|
37
|
+
- Optimal tile load order
|
|
36
38
|
|
|
37
39
|
### geometric Error Multiplier
|
|
38
40
|
The geometric error multiplier allows you to multiply the geometric error by a factor.
|
|
@@ -43,20 +45,22 @@ you may also set this value at initialization:
|
|
|
43
45
|
|
|
44
46
|
```
|
|
45
47
|
const ogc3DTile = new OGC3DTile({
|
|
46
|
-
url: "https://
|
|
48
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
47
49
|
geometricErrorMultiplier: 2.0
|
|
48
50
|
});
|
|
49
51
|
```
|
|
50
52
|
A lower value will result in lower detail tiles being loaded and a higher value results in higher detail tiles being loaded.
|
|
51
53
|
A value of 1.0 is the default.
|
|
52
54
|
|
|
55
|
+
|
|
56
|
+
|
|
53
57
|
### load tiles outside of view
|
|
54
58
|
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.
|
|
55
59
|
Instead of this behaviour, you can force the lowest possible LODs to be loaded for tiles around the view so that there are no gaps in the mesh when the camera moves or when displaying shadows.
|
|
56
60
|
|
|
57
61
|
```
|
|
58
62
|
const ogc3DTile = new OGC3DTile({
|
|
59
|
-
url: "https://
|
|
63
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
60
64
|
loadOutsideView: true
|
|
61
65
|
});
|
|
62
66
|
```
|
|
@@ -66,12 +70,36 @@ Add a callback on loaded tiles in order to set a material or do some logic on th
|
|
|
66
70
|
|
|
67
71
|
```
|
|
68
72
|
const ogc3DTile = new OGC3DTile({
|
|
69
|
-
url: "https://
|
|
73
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
70
74
|
meshCallback: mesh => {
|
|
71
75
|
mesh.material.wireframe = true;
|
|
72
76
|
}
|
|
73
77
|
});
|
|
74
78
|
```
|
|
79
|
+
If using a shared cache between tilesets, check out the next section.
|
|
80
|
+
|
|
81
|
+
### Cache
|
|
82
|
+
You may instanciate a cache through the TileLoader class and re-use it for several or all your tilesets.
|
|
83
|
+
The limitation is that all the tilesets using the same cache will have the same callback.
|
|
84
|
+
|
|
85
|
+
The TileLoader constructor takes 2 arguments. The first is a callback for meshes (see above section) and the second is
|
|
86
|
+
the maximum number of items in the cache (default is 1000).
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
|
|
90
|
+
|
|
91
|
+
const ogc3DTile = new OGC3DTile({
|
|
92
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
93
|
+
tileLoader: new TileLoader(mesh => {
|
|
94
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
95
|
+
mesh.material.wireframe = false;
|
|
96
|
+
mesh.material.side = THREE.DoubleSide;
|
|
97
|
+
},
|
|
98
|
+
2000
|
|
99
|
+
),
|
|
100
|
+
meshCallback: mesh => { mesh.material.wireframe = true;} // This callback will not be used as the callback provided to the TileLoader takes priority
|
|
101
|
+
});
|
|
102
|
+
```
|
|
75
103
|
|
|
76
104
|
### Transformations
|
|
77
105
|
The OGC 3DTile object is a regular three.js Object3D so it can be transformed via the standard three.js API:
|
package/index.html
CHANGED
package/manifest.json
ADDED
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import "regenerator-runtime/runtime.js";
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
import Stats from 'three/examples/jsm/libs/stats.module.js';
|
|
4
|
-
import TilesetStats from './tileset/TilesetStats';
|
|
5
4
|
import { OGC3DTile } from "./tileset/OGC3DTile";
|
|
6
5
|
import { TileLoader } from "./tileset/TileLoader";
|
|
7
6
|
import { MapControls, OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import { setIntervalAsync } from 'set-interval-async/dynamic';
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
const scene = initScene();
|
|
13
|
-
const tilesetStats = TilesetStats();
|
|
14
11
|
const domContainer = initDomContainer("screen");
|
|
15
12
|
const camera = initCamera();
|
|
16
13
|
const ogc3DTiles = initTileset(scene);
|
|
@@ -21,7 +18,6 @@ const renderer = initRenderer(camera, domContainer);
|
|
|
21
18
|
|
|
22
19
|
animate();
|
|
23
20
|
|
|
24
|
-
|
|
25
21
|
function initScene() {
|
|
26
22
|
const scene = new THREE.Scene();
|
|
27
23
|
scene.background = new THREE.Color(0x000000);
|
|
@@ -81,15 +77,14 @@ function initCamera() {
|
|
|
81
77
|
function initTileset(scene) {
|
|
82
78
|
|
|
83
79
|
const ogc3DTile = new OGC3DTile({
|
|
84
|
-
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
85
|
-
geometricErrorMultiplier: 1,
|
|
80
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
|
|
81
|
+
geometricErrorMultiplier: 1.0,
|
|
86
82
|
loadOutsideView: true,
|
|
87
83
|
tileLoader: new TileLoader(mesh => {
|
|
88
84
|
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
89
85
|
mesh.material.wireframe = false;
|
|
90
86
|
mesh.material.side = THREE.DoubleSide;
|
|
91
|
-
},
|
|
92
|
-
stats: tilesetStats
|
|
87
|
+
}, 1000)
|
|
93
88
|
});
|
|
94
89
|
|
|
95
90
|
|
|
@@ -98,7 +93,7 @@ function initTileset(scene) {
|
|
|
98
93
|
//ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -65)
|
|
99
94
|
//ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -80)
|
|
100
95
|
//ogc3DTile.scale.set(0.0001,0.0001,0.0001);
|
|
101
|
-
//
|
|
96
|
+
//ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
102
97
|
// ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -16.5)
|
|
103
98
|
// ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), 0)
|
|
104
99
|
// ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -9.5)
|
|
@@ -118,9 +113,9 @@ function initTileset(scene) {
|
|
|
118
113
|
}
|
|
119
114
|
});
|
|
120
115
|
function startInterval(){
|
|
121
|
-
interval =
|
|
116
|
+
interval = setIntervalAsync(function () {
|
|
122
117
|
ogc3DTile.update(camera);
|
|
123
|
-
},
|
|
118
|
+
}, 20);
|
|
124
119
|
}
|
|
125
120
|
startInterval();
|
|
126
121
|
|
|
@@ -144,7 +139,6 @@ function animate() {
|
|
|
144
139
|
|
|
145
140
|
camera.updateMatrixWorld();
|
|
146
141
|
renderer.render(scene, camera);
|
|
147
|
-
tilesetStats.update();
|
|
148
142
|
stats.update();
|
|
149
143
|
|
|
150
144
|
}
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -23,8 +23,9 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
23
23
|
* geometricErrorMultiplier: Double,
|
|
24
24
|
* loadOutsideView: Boolean,
|
|
25
25
|
* tileLoader : TileLoader,
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* meshCallback: function,
|
|
27
|
+
* cameraOnLoad: camera,
|
|
28
|
+
* parentTile: OGC3DTile
|
|
28
29
|
* } properties
|
|
29
30
|
*/
|
|
30
31
|
constructor(properties) {
|
|
@@ -42,23 +43,11 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
42
43
|
}
|
|
43
44
|
// set properties general to the entire tileset
|
|
44
45
|
this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
|
|
45
|
-
if (properties.stats) {
|
|
46
|
-
// Automatic geometric error multiplier
|
|
47
|
-
this.stats = properties.stats;
|
|
48
|
-
setIntervalAsync(() => {
|
|
49
|
-
const framerate = self.stats.fps();
|
|
50
|
-
if (framerate < 0) return;
|
|
51
|
-
if (framerate < 58) {
|
|
52
|
-
self.setGeometricErrorMultiplier(Math.max(0.05, self.geometricErrorMultiplier - 0.05));
|
|
53
|
-
} else if (framerate > 58) {
|
|
54
|
-
self.setGeometricErrorMultiplier(self.geometricErrorMultiplier + 0.05);
|
|
55
|
-
}
|
|
56
|
-
self.setGeometricErrorMultiplier(self.geometricErrorMultiplier * (self.stats.fps() / 60));
|
|
57
|
-
}, 1000);
|
|
58
|
-
}
|
|
59
46
|
|
|
60
47
|
this.meshCallback = properties.meshCallback;
|
|
61
48
|
this.loadOutsideView = properties.loadOutsideView;
|
|
49
|
+
this.cameraOnLoad = properties.cameraOnLoad;
|
|
50
|
+
this.parentTile = properties.parentTile;
|
|
62
51
|
|
|
63
52
|
// declare properties specific to the tile for clarity
|
|
64
53
|
this.childrenTiles = [];
|
|
@@ -178,23 +167,37 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
178
167
|
});
|
|
179
168
|
self.add(mesh);
|
|
180
169
|
self.meshContent = mesh;
|
|
181
|
-
})
|
|
170
|
+
}, !self.cameraOnLoad ? () => 0 : () => {
|
|
171
|
+
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
172
|
+
}, () => self.getSiblings(), self.level, self.uuid);
|
|
182
173
|
} else if (url.includes(".json")) {
|
|
183
|
-
self.
|
|
184
|
-
|
|
185
|
-
if (!
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
174
|
+
self.tileLoader.get(this.uuid, url, json => {
|
|
175
|
+
if (!!self.deleted) return;
|
|
176
|
+
if (!self.json.children) self.json.children = [];
|
|
177
|
+
json.rootPath = path.dirname(url);
|
|
178
|
+
self.json.children.push(json);
|
|
179
|
+
delete self.json.content;
|
|
180
|
+
self.hasUnloadedJSONContent = false;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/* self.controller = new AbortController();
|
|
184
|
+
setTimeout(() => {
|
|
185
|
+
fetch(url, { signal: self.controller.signal }).then(result => {
|
|
186
|
+
if (!result.ok) {
|
|
187
|
+
throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
|
|
188
|
+
}
|
|
189
|
+
result.json().then(json => {
|
|
190
|
+
// when json content is downloaded, it is inserted into this tile's original JSON as a child
|
|
191
|
+
// and the content object is deleted from the original JSON
|
|
192
|
+
if (!self.json.children) self.json.children = [];
|
|
193
|
+
json.rootPath = path.dirname(url);
|
|
194
|
+
self.json.children.push(json);
|
|
195
|
+
delete self.json.content;
|
|
196
|
+
self.hasUnloadedJSONContent = false;
|
|
197
|
+
}).catch(error => { });
|
|
196
198
|
}).catch(error => { });
|
|
197
|
-
}
|
|
199
|
+
}, 0); */
|
|
200
|
+
|
|
198
201
|
}
|
|
199
202
|
|
|
200
203
|
}
|
|
@@ -215,6 +218,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
215
218
|
|
|
216
219
|
});
|
|
217
220
|
this.parent = null;
|
|
221
|
+
this.parentTile = null;
|
|
218
222
|
this.dispatchEvent({ type: 'removed' });
|
|
219
223
|
}
|
|
220
224
|
disposeChildren() {
|
|
@@ -323,6 +327,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
323
327
|
function loadJsonChildren() {
|
|
324
328
|
self.json.children.forEach(childJSON => {
|
|
325
329
|
let childTile = new OGC3DTile({
|
|
330
|
+
parentTile: self,
|
|
326
331
|
parentGeometricError: self.geometricError,
|
|
327
332
|
parentBoundingVolume: self.boundingVolume,
|
|
328
333
|
parentRefinement: self.refinement,
|
|
@@ -331,7 +336,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
331
336
|
geometricErrorMultiplier: self.geometricErrorMultiplier,
|
|
332
337
|
loadOutsideView: self.loadOutsideView,
|
|
333
338
|
level: self.level + 1,
|
|
334
|
-
tileLoader: self.tileLoader
|
|
339
|
+
tileLoader: self.tileLoader,
|
|
340
|
+
cameraOnLoad: camera
|
|
335
341
|
});
|
|
336
342
|
self.childrenTiles.push(childTile);
|
|
337
343
|
self.add(childTile);
|
|
@@ -454,9 +460,43 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
454
460
|
//throw Error("Region bounding volume not supported");
|
|
455
461
|
return -1;
|
|
456
462
|
}
|
|
457
|
-
|
|
458
463
|
}
|
|
459
464
|
|
|
465
|
+
getSiblings() {
|
|
466
|
+
const self = this;
|
|
467
|
+
const tiles = [];
|
|
468
|
+
if (!self.parentTile) return tiles;
|
|
469
|
+
let p = self.parentTile;
|
|
470
|
+
while (!p.hasMeshContent && !!p.parentTile) {
|
|
471
|
+
p = p.parentTile;
|
|
472
|
+
}
|
|
473
|
+
p.childrenTiles.forEach(child => {
|
|
474
|
+
if (!!child && child != self) {
|
|
475
|
+
while (!child.hasMeshContent && !!child.childrenTiles[0]) {
|
|
476
|
+
child = child.childrenTiles[0];
|
|
477
|
+
}
|
|
478
|
+
tiles.push(child);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
return tiles;
|
|
482
|
+
}
|
|
483
|
+
calculateDistanceToCamera(camera) {
|
|
484
|
+
if (this.boundingVolume instanceof OBB) {
|
|
485
|
+
// box
|
|
486
|
+
tempSphere.copy(this.boundingVolume.sphere);
|
|
487
|
+
tempSphere.applyMatrix4(this.matrixWorld);
|
|
488
|
+
//if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
489
|
+
} else if (this.boundingVolume instanceof THREE.Sphere) {
|
|
490
|
+
//sphere
|
|
491
|
+
tempSphere.copy(this.boundingVolume);
|
|
492
|
+
tempSphere.applyMatrix4(this.matrixWorld);
|
|
493
|
+
//if (!frustum.intersectsSphere(tempSphere)) return -1;
|
|
494
|
+
}
|
|
495
|
+
if (this.boundingVolume instanceof THREE.Box3) {
|
|
496
|
+
return -1; // region not supported
|
|
497
|
+
}
|
|
498
|
+
return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
|
|
499
|
+
}
|
|
460
500
|
setGeometricErrorMultiplier(geometricErrorMultiplier) {
|
|
461
501
|
this.geometricErrorMultiplier = geometricErrorMultiplier;
|
|
462
502
|
this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
|
|
@@ -4,61 +4,150 @@ import { setIntervalAsync } from 'set-interval-async/dynamic';
|
|
|
4
4
|
|
|
5
5
|
const ready = [];
|
|
6
6
|
const downloads = [];
|
|
7
|
+
const nextReady = [];
|
|
8
|
+
const nextDownloads = [];
|
|
9
|
+
|
|
7
10
|
function scheduleDownload(f) {
|
|
8
11
|
downloads.unshift(f);
|
|
9
12
|
}
|
|
10
13
|
function download() {
|
|
11
|
-
if(
|
|
12
|
-
|
|
14
|
+
if (nextDownloads.length == 0) {
|
|
15
|
+
getNextDownloads();
|
|
16
|
+
if (nextDownloads.length == 0) return 0;
|
|
17
|
+
}
|
|
18
|
+
const nextDownload = nextDownloads.shift();
|
|
13
19
|
if (!!nextDownload && nextDownload.shouldDoDownload()) {
|
|
14
20
|
nextDownload.doDownload();
|
|
15
21
|
}
|
|
22
|
+
return 1;
|
|
16
23
|
}
|
|
17
|
-
function meshReceived(cache, register, key) {
|
|
18
|
-
ready.unshift([cache, register, key]);
|
|
24
|
+
function meshReceived(cache, register, key, distanceFunction, getSiblings, level, uuid) {
|
|
25
|
+
ready.unshift([cache, register, key, distanceFunction, getSiblings, level, uuid]);
|
|
19
26
|
}
|
|
20
27
|
function loadBatch() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
if (nextReady.length == 0) {
|
|
29
|
+
getNextReady();
|
|
30
|
+
if (nextReady.length == 0) return 0;
|
|
31
|
+
}
|
|
32
|
+
const data = nextReady.shift();
|
|
33
|
+
if (!data) return 0;
|
|
34
|
+
const cache = data[0];
|
|
35
|
+
const register = data[1];
|
|
36
|
+
const key = data[2];
|
|
37
|
+
const mesh = cache.get(key);
|
|
38
|
+
if (!!mesh && !!register[key]) {
|
|
39
|
+
Object.keys(register[key]).forEach(tile => {
|
|
40
|
+
const callback = register[key][tile];
|
|
41
|
+
if (!!callback) {
|
|
42
|
+
callback(mesh);
|
|
43
|
+
register[key][tile] = null;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getNextDownloads() {
|
|
51
|
+
let smallestLevel = Number.MAX_VALUE;
|
|
52
|
+
let smallestDistance = Number.MAX_VALUE;
|
|
53
|
+
let closest = -1;
|
|
54
|
+
for (let i = downloads.length - 1; i >= 0; i--) {
|
|
55
|
+
if (!downloads[i].shouldDoDownload()) {
|
|
56
|
+
downloads.splice(i, 1);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if(!downloads[i].distanceFunction){ // if no distance function, must be a json, give absolute priority!
|
|
60
|
+
nextDownloads.push(downloads.splice(i, 1)[0]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if(nextDownloads.length>0) return;
|
|
64
|
+
for (let i = downloads.length - 1; i >= 0; i--) {
|
|
65
|
+
const dist = downloads[i].distanceFunction();
|
|
66
|
+
if (dist < smallestDistance) {
|
|
67
|
+
smallestDistance = dist;
|
|
68
|
+
closest = i;
|
|
69
|
+
} else if (dist == smallestDistance && downloads[i].level < smallestLevel) {
|
|
70
|
+
smallestLevel = downloads[i].level;
|
|
71
|
+
closest = i
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (closest >= 0) {
|
|
75
|
+
const closestItem = downloads.splice(closest, 1).pop();
|
|
76
|
+
nextDownloads.push(closestItem);
|
|
77
|
+
const siblings = closestItem.getSiblings();
|
|
78
|
+
for (let i = downloads.length - 1; i >= 0; i--) {
|
|
79
|
+
if (siblings.includes(downloads[i].uuid)) {
|
|
80
|
+
nextDownloads.push(downloads.splice(i, 1).pop());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getNextReady() {
|
|
87
|
+
let smallestLevel = Number.MAX_VALUE;
|
|
88
|
+
let smallestDistance = Number.MAX_VALUE;
|
|
89
|
+
let closest = -1;
|
|
90
|
+
for (let i = ready.length - 1; i >= 0; i--) {
|
|
91
|
+
|
|
92
|
+
if(!ready[i][3]){// if no distance function, must be a json, give absolute priority!
|
|
93
|
+
nextReady.push(ready.splice(i,1)[0]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if(nextReady.length>0) return;
|
|
97
|
+
for (let i = ready.length - 1; i >= 0; i--) {
|
|
98
|
+
const dist = ready[i][3]();
|
|
99
|
+
if (dist < smallestDistance) {
|
|
100
|
+
smallestDistance = dist;
|
|
101
|
+
smallestLevel = ready[i][5]
|
|
102
|
+
closest = i
|
|
103
|
+
} else if (dist == smallestDistance && ready[i][5] < smallestLevel) {
|
|
104
|
+
smallestLevel = ready[i][5]
|
|
105
|
+
closest = i
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (closest >= 0) {
|
|
109
|
+
const closestItem = ready.splice(closest, 1).pop();
|
|
110
|
+
nextReady.push(closestItem);
|
|
111
|
+
const siblings = closestItem[4]();
|
|
112
|
+
for (let i = ready.length - 1; i >= 0; i--) {
|
|
113
|
+
if (siblings.includes(ready[i][6])) {
|
|
114
|
+
nextready.push(ready.splice(i, 1).pop());
|
|
115
|
+
}
|
|
36
116
|
}
|
|
37
117
|
}
|
|
38
118
|
}
|
|
39
|
-
setIntervalAsync(()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
119
|
+
setIntervalAsync(()=>{
|
|
120
|
+
const start = Date.now();
|
|
121
|
+
let uploaded = 0;
|
|
122
|
+
do{
|
|
123
|
+
uploaded = download();
|
|
124
|
+
}while(uploaded > 0 && (Date.now() - start)<= 2 )
|
|
125
|
+
|
|
126
|
+
},10);
|
|
127
|
+
setIntervalAsync(()=>{
|
|
128
|
+
const start = Date.now();
|
|
129
|
+
let loaded = 0;
|
|
130
|
+
do{
|
|
131
|
+
loaded = loadBatch();
|
|
132
|
+
}while(loaded > 0 && (Date.now() - start)<= 2 )
|
|
133
|
+
|
|
134
|
+
},10);
|
|
45
135
|
|
|
46
136
|
class TileLoader {
|
|
47
|
-
constructor(meshCallback,
|
|
137
|
+
constructor(meshCallback, maxCachedItems) {
|
|
48
138
|
this.meshCallback = meshCallback;
|
|
49
139
|
this.cache = new LinkedHashMap();
|
|
50
|
-
this.
|
|
51
|
-
this.stats = stats;
|
|
140
|
+
this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 1000;
|
|
52
141
|
this.register = {};
|
|
53
142
|
}
|
|
54
143
|
|
|
55
|
-
get(tileIdentifier, path, callback) {
|
|
144
|
+
get(tileIdentifier, path, callback, distanceFunction, getSiblings, level, uuid) {
|
|
56
145
|
const self = this;
|
|
57
146
|
const key = simplifyPath(path);
|
|
58
147
|
|
|
59
148
|
|
|
60
|
-
if (!path.includes(".b3dm")) {
|
|
61
|
-
console.error("the 3DTiles cache can only be used to load B3DM data");
|
|
149
|
+
if (!path.includes(".b3dm") && !path.includes(".json")) {
|
|
150
|
+
console.error("the 3DTiles cache can only be used to load B3DM and json data");
|
|
62
151
|
return;
|
|
63
152
|
}
|
|
64
153
|
if (!self.register[key]) {
|
|
@@ -71,13 +160,11 @@ class TileLoader {
|
|
|
71
160
|
|
|
72
161
|
const cachedObject = self.cache.get(key);
|
|
73
162
|
if (!!cachedObject) {
|
|
74
|
-
meshReceived(self.cache, self.register, key);
|
|
163
|
+
meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, uuid);
|
|
75
164
|
} else if (Object.keys(self.register[key]).length == 1) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
"doDownload": () => {
|
|
165
|
+
let downloadFunction;
|
|
166
|
+
if (path.includes(".b3dm")) {
|
|
167
|
+
downloadFunction = () => {
|
|
81
168
|
fetch(path).then(result => {
|
|
82
169
|
if (!result.ok) {
|
|
83
170
|
console.error("could not load tile with path : " + path)
|
|
@@ -86,25 +173,40 @@ class TileLoader {
|
|
|
86
173
|
result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
|
|
87
174
|
self.cache.put(key, mesh);
|
|
88
175
|
self.checkSize();
|
|
89
|
-
meshReceived(self.cache, self.register, key);
|
|
176
|
+
meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, uuid);
|
|
90
177
|
});
|
|
91
178
|
|
|
92
179
|
});
|
|
93
180
|
}
|
|
181
|
+
}else if (path.includes(".json")) {
|
|
182
|
+
downloadFunction = () => {
|
|
183
|
+
fetch(path).then(result => {
|
|
184
|
+
if (!result.ok) {
|
|
185
|
+
console.error("could not load tile with path : " + path)
|
|
186
|
+
throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
|
|
187
|
+
}
|
|
188
|
+
result.json().then(json => {
|
|
189
|
+
self.cache.put(key, json);
|
|
190
|
+
self.checkSize();
|
|
191
|
+
meshReceived(self.cache, self.register, key);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
scheduleDownload({
|
|
197
|
+
"shouldDoDownload": () => {
|
|
198
|
+
return !!self.register[key] && Object.keys(self.register[key]).length > 0;
|
|
199
|
+
},
|
|
200
|
+
"doDownload": downloadFunction,
|
|
201
|
+
"distanceFunction": distanceFunction,
|
|
202
|
+
"getSiblings": getSiblings,
|
|
203
|
+
"level": level,
|
|
204
|
+
"uuid": uuid
|
|
94
205
|
})
|
|
95
206
|
}
|
|
96
207
|
}
|
|
97
208
|
|
|
98
|
-
|
|
99
|
-
const self = this;
|
|
100
|
-
Object.keys(self.register[key]).forEach(tile => {
|
|
101
|
-
const callback = self.register[key][tile];
|
|
102
|
-
if (!!callback) {
|
|
103
|
-
callback(mesh);
|
|
104
|
-
self.register[key][tile] = null;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
209
|
+
|
|
108
210
|
|
|
109
211
|
invalidate(path, tileIdentifier) {
|
|
110
212
|
const key = simplifyPath(path);
|
|
@@ -113,46 +215,42 @@ class TileLoader {
|
|
|
113
215
|
|
|
114
216
|
checkSize() {
|
|
115
217
|
const self = this;
|
|
116
|
-
|
|
218
|
+
|
|
117
219
|
let i = 0;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if(self.stats.memory()/self.stats.maxMemory()<0.25){
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
return self.cache.size() > self.maxSize;
|
|
126
|
-
}
|
|
127
|
-
while (memOverflowCheck() && i < self.cache.size()) {
|
|
220
|
+
|
|
221
|
+
while (self.cache.size() > self.maxCachedItems && i < self.cache.size()) {
|
|
128
222
|
i++;
|
|
129
223
|
const entry = self.cache.head();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (o.material
|
|
141
|
-
|
|
142
|
-
|
|
224
|
+
const reg = self.register[entry.key];
|
|
225
|
+
if (!!reg) {
|
|
226
|
+
if (Object.keys(reg).length > 0) {
|
|
227
|
+
self.cache.remove(entry.key);
|
|
228
|
+
self.cache.put(entry.key, entry.value);
|
|
229
|
+
} else {
|
|
230
|
+
self.cache.remove(entry.key);
|
|
231
|
+
delete self.register[entry.key];
|
|
232
|
+
entry.value.traverse((o) => {
|
|
233
|
+
|
|
234
|
+
if (o.material) {
|
|
235
|
+
// dispose materials
|
|
236
|
+
if (o.material.length) {
|
|
237
|
+
for (let i = 0; i < o.material.length; ++i) {
|
|
238
|
+
o.material[i].dispose();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
o.material.dispose()
|
|
143
243
|
}
|
|
144
244
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
if (o.geometry) {
|
|
150
|
-
// dispose geometry
|
|
151
|
-
o.geometry.dispose();
|
|
245
|
+
if (o.geometry) {
|
|
246
|
+
// dispose geometry
|
|
247
|
+
o.geometry.dispose();
|
|
152
248
|
|
|
153
|
-
|
|
154
|
-
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
155
252
|
}
|
|
253
|
+
|
|
156
254
|
}
|
|
157
255
|
}
|
|
158
256
|
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
var TilesetStats = function () {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0;
|
|
5
|
-
|
|
6
|
-
var fps = -1;
|
|
7
|
-
|
|
8
|
-
if ( self.performance && self.performance.memory ) {
|
|
9
|
-
|
|
10
|
-
var mem = -1;
|
|
11
|
-
var maxMem = -1;
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
|
|
18
|
-
begin: function () {
|
|
19
|
-
|
|
20
|
-
beginTime = ( performance || Date ).now();
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
end: function () {
|
|
25
|
-
|
|
26
|
-
frames ++;
|
|
27
|
-
|
|
28
|
-
var time = ( performance || Date ).now();
|
|
29
|
-
|
|
30
|
-
if ( time >= prevTime + 1000 ) {
|
|
31
|
-
|
|
32
|
-
fps = ( frames * 1000 ) / ( time - prevTime );
|
|
33
|
-
|
|
34
|
-
prevTime = time;
|
|
35
|
-
frames = 0;
|
|
36
|
-
|
|
37
|
-
if ( !!mem ) {
|
|
38
|
-
|
|
39
|
-
var memory = performance.memory;
|
|
40
|
-
mem = memory.usedJSHeapSize;
|
|
41
|
-
maxMem = memory.jsHeapSizeLimit;
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return time;
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
update: function () {
|
|
52
|
-
|
|
53
|
-
beginTime = this.end();
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
fps: ()=>fps,
|
|
58
|
-
memory: ()=>mem,
|
|
59
|
-
maxMemory: ()=>maxMem
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export default TilesetStats;
|