@jdultra/threedtiles 3.3.0 → 3.3.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 +10 -7
- package/package.json +1 -1
- package/src/index.js +25 -10
- package/src/tileset/OGC3DTile.js +26 -12
- package/src/tileset/TileLoader.js +144 -128
- package/src/draco/draco_decoder.js +0 -48
- package/src/draco/draco_decoder.wasm +0 -0
- package/src/draco/draco_encoder.js +0 -33
- package/src/draco/draco_wasm_wrapper.js +0 -104
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Currently, the library is limmited to B3DM files.
|
|
|
39
39
|
- Optionally load low detail tiles outside of view frustum for correct shadows and basic mesh present when the camera moves quickly.
|
|
40
40
|
- Share a cache between tileset instances
|
|
41
41
|
- Optimal tile load order
|
|
42
|
-
- Occlusion culling
|
|
42
|
+
- Occlusion culling
|
|
43
43
|
|
|
44
44
|
### geometric Error Multiplier
|
|
45
45
|
The geometric error multiplier allows you to multiply the geometric error by a factor.
|
|
@@ -60,8 +60,8 @@ A value of 1.0 is the default.
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
### load tiles outside of view
|
|
63
|
-
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.
|
|
64
|
-
Instead of this behaviour, you can force the lowest possible LODs to be loaded for tiles
|
|
63
|
+
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.
|
|
64
|
+
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 correctly.
|
|
65
65
|
|
|
66
66
|
```
|
|
67
67
|
const ogc3DTile = new OGC3DTile({
|
|
@@ -78,6 +78,7 @@ const ogc3DTile = new OGC3DTile({
|
|
|
78
78
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
|
|
79
79
|
meshCallback: mesh => {
|
|
80
80
|
mesh.material.wireframe = true;
|
|
81
|
+
mesh.material.side = THREE.DoubleSide;
|
|
81
82
|
}
|
|
82
83
|
});
|
|
83
84
|
```
|
|
@@ -116,12 +117,14 @@ const ogc3DTile = new OGC3DTile({
|
|
|
116
117
|
|
|
117
118
|
ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
|
|
118
119
|
ogc3DTile.rotateOnAxis(new THREE.Vector3(1,0,0), -Math.PI*0.5);
|
|
119
|
-
|
|
120
|
+
```
|
|
120
121
|
|
|
121
122
|
|
|
122
123
|
### Occlusion culling
|
|
123
124
|
Occlusion culling prevents the refinment of data that is hidden by other data, like a wall. It can have a big impact on frame-rate and loading speed for interior scenes.
|
|
124
|
-
|
|
125
|
+
|
|
126
|
+
A word of warning: activating occlusion culling causes an extra render-pass and as such, has an impact on frame-rate.
|
|
127
|
+
It will be most beneficial on interior scenes where most of the data is occluded by walls. All the tiles that don't need to be downloaded or drawn will balance out the cost of the extra render pass.
|
|
125
128
|
|
|
126
129
|
|
|
127
130
|
First, instantiate an OcclusionCullingService:
|
|
@@ -146,7 +149,7 @@ function animate() {
|
|
|
146
149
|
}
|
|
147
150
|
```
|
|
148
151
|
|
|
149
|
-
Finally, you
|
|
152
|
+
Finally, if you are drawing the back-side of faces or both-sides (see Callback section), you'll need to specify it for the occlusion pass too. By default, THREE.FrontSide is used:
|
|
150
153
|
|
|
151
154
|
```
|
|
152
155
|
const occlusionCullingService = new OcclusionCullingService();
|
|
@@ -172,5 +175,5 @@ I'm working on this project in parallel https://github.com/ebeaufay/UltraGlobe w
|
|
|
172
175
|
|
|
173
176
|
I also have code to convert meshes to 3DTiles with no limit to the size of the dataset relative to faces or textures.
|
|
174
177
|
It works for all types of meshes: photogrametry, BIM, colored or textured meshes with a single texture atlas or many individual textures.
|
|
175
|
-
I'm keeping the code private for now but
|
|
178
|
+
I'm keeping the code private for now but feel free to contact me about it.
|
|
176
179
|
Contact: emericbeaufays@gmail.com
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -27,11 +27,12 @@ function initScene() {
|
|
|
27
27
|
const scene = new THREE.Scene();
|
|
28
28
|
scene.matrixAutoUpdate = false;
|
|
29
29
|
scene.background = new THREE.Color(0xaaffcc);
|
|
30
|
-
scene.add(new THREE.AmbientLight(0xFFFFFF, 0.
|
|
31
|
-
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.
|
|
30
|
+
scene.add(new THREE.AmbientLight(0xFFFFFF, 0.8));
|
|
31
|
+
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.2 );
|
|
32
32
|
directionalLight.position.set(100,100,100)
|
|
33
33
|
directionalLight.lookAt(-1,-1,-1)
|
|
34
34
|
scene.add( directionalLight );
|
|
35
|
+
scene.autoUpdate = false;
|
|
35
36
|
return scene;
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -84,24 +85,36 @@ function initCamera() {
|
|
|
84
85
|
|
|
85
86
|
function initTileset(scene) {
|
|
86
87
|
|
|
88
|
+
const tileLoader = new TileLoader(mesh => {
|
|
89
|
+
//// Insert code to be called on every newly decoded mesh e.g.:
|
|
90
|
+
mesh.material.wireframe = false;
|
|
91
|
+
mesh.material.side = THREE.DoubleSide;
|
|
92
|
+
}, 1000)
|
|
87
93
|
const ogc3DTile = new OGC3DTile({
|
|
88
94
|
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
|
|
89
|
-
//url: "http://localhost:
|
|
95
|
+
//url: "http://localhost:8080/tileset.json",
|
|
90
96
|
geometricErrorMultiplier: 0.5,
|
|
91
97
|
loadOutsideView: false,
|
|
92
|
-
tileLoader:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
tileLoader: tileLoader,
|
|
99
|
+
occlusionCullingService: occlusionCullingService,
|
|
100
|
+
static: false
|
|
101
|
+
});
|
|
102
|
+
ogc3DTile.translateZ(200) // Z-UP to Y-UP
|
|
103
|
+
const ogc3DTile2 = new OGC3DTile({
|
|
104
|
+
url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
|
|
105
|
+
//url: "http://localhost:8080/tileset.json",
|
|
106
|
+
geometricErrorMultiplier: 0.5,
|
|
107
|
+
loadOutsideView: false,
|
|
108
|
+
//tileLoader: tileLoader,
|
|
109
|
+
occlusionCullingService: occlusionCullingService,
|
|
110
|
+
static: false
|
|
98
111
|
});
|
|
99
112
|
|
|
100
113
|
|
|
101
114
|
|
|
102
115
|
//// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
|
|
103
|
-
//-172683.125,301451.125,1367762.21875
|
|
104
116
|
//ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
|
|
117
|
+
//// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
|
|
105
118
|
|
|
106
119
|
|
|
107
120
|
//// 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
|
|
@@ -122,11 +135,13 @@ function initTileset(scene) {
|
|
|
122
135
|
function startInterval() {
|
|
123
136
|
interval = setIntervalAsync(function () {
|
|
124
137
|
ogc3DTile.update(camera);
|
|
138
|
+
ogc3DTile2.update(camera);
|
|
125
139
|
}, 20);
|
|
126
140
|
}
|
|
127
141
|
startInterval();
|
|
128
142
|
|
|
129
143
|
scene.add(ogc3DTile)
|
|
144
|
+
scene.add(ogc3DTile2)
|
|
130
145
|
return ogc3DTile;
|
|
131
146
|
}
|
|
132
147
|
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -34,10 +34,6 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
34
34
|
super();
|
|
35
35
|
const self = this;
|
|
36
36
|
|
|
37
|
-
if(properties.static){
|
|
38
|
-
this.matrixAutoUpdate = false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
37
|
this.uuid = uuidv4();
|
|
42
38
|
if (!!properties.tileLoader) {
|
|
43
39
|
this.tileLoader = properties.tileLoader;
|
|
@@ -47,21 +43,24 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
47
43
|
mesh.material.wireframe = false;
|
|
48
44
|
mesh.material.side = THREE.DoubleSide;
|
|
49
45
|
} : properties.meshCallback);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
}
|
|
47
|
+
// set properties general to the entire tileset
|
|
48
|
+
this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
|
|
49
|
+
|
|
54
50
|
this.meshCallback = properties.meshCallback;
|
|
55
51
|
this.loadOutsideView = properties.loadOutsideView;
|
|
56
52
|
this.cameraOnLoad = properties.cameraOnLoad;
|
|
57
53
|
this.parentTile = properties.parentTile;
|
|
58
54
|
this.occlusionCullingService = properties.occlusionCullingService;
|
|
55
|
+
this.static = properties.static;
|
|
59
56
|
if (this.occlusionCullingService) {
|
|
60
57
|
this.color = new THREE.Color();
|
|
61
58
|
this.color.setHex(Math.random() * 0xffffff);
|
|
62
59
|
this.colorID = clamp(self.color.r * 255, 0, 255) << 16 ^ clamp(self.color.g * 255, 0, 255) << 8 ^ clamp(self.color.b * 255, 0, 255) << 0;
|
|
63
60
|
}
|
|
64
|
-
|
|
61
|
+
if(this.static){
|
|
62
|
+
this.matrixAutoUpdate = false;
|
|
63
|
+
}
|
|
65
64
|
// declare properties specific to the tile for clarity
|
|
66
65
|
this.childrenTiles = [];
|
|
67
66
|
this.meshContent;
|
|
@@ -181,6 +180,7 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
181
180
|
if (!!self.deleted) return;
|
|
182
181
|
mesh.traverse((o) => {
|
|
183
182
|
if (o.isMesh) {
|
|
183
|
+
o.layers.disable(0);
|
|
184
184
|
if (self.occlusionCullingService) {
|
|
185
185
|
const position = o.geometry.attributes.position;
|
|
186
186
|
const colors = [];
|
|
@@ -189,12 +189,16 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
189
189
|
}
|
|
190
190
|
o.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
|
191
191
|
}
|
|
192
|
+
if(self.static){
|
|
193
|
+
o.matrixAutoUpdate = false;
|
|
194
|
+
}
|
|
192
195
|
o.material.visible = false;
|
|
193
196
|
}
|
|
194
197
|
});
|
|
195
198
|
|
|
196
199
|
self.add(mesh);
|
|
197
200
|
self.updateWorldMatrix(false, true);
|
|
201
|
+
// mesh.layers.disable(0);
|
|
198
202
|
self.meshContent = mesh;
|
|
199
203
|
}, !self.cameraOnLoad ? () => 0 : () => {
|
|
200
204
|
return self.calculateDistanceToCamera(self.cameraOnLoad);
|
|
@@ -363,7 +367,8 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
363
367
|
level: self.level + 1,
|
|
364
368
|
tileLoader: self.tileLoader,
|
|
365
369
|
cameraOnLoad: camera,
|
|
366
|
-
occlusionCullingService:self.occlusionCullingService
|
|
370
|
+
occlusionCullingService:self.occlusionCullingService,
|
|
371
|
+
static: self.static
|
|
367
372
|
});
|
|
368
373
|
self.childrenTiles.push(childTile);
|
|
369
374
|
self.add(childTile);
|
|
@@ -456,9 +461,18 @@ class OGC3DTile extends THREE.Object3D {
|
|
|
456
461
|
const self = this;
|
|
457
462
|
if(self.hasMeshContent && self.meshContent){
|
|
458
463
|
if(visibility){
|
|
459
|
-
|
|
464
|
+
|
|
465
|
+
self.meshContent.traverse((o) => {
|
|
466
|
+
if (o.isMesh) {
|
|
467
|
+
o.layers.enable(0);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
460
470
|
}else{
|
|
461
|
-
self.
|
|
471
|
+
self.meshContent.traverse((o) => {
|
|
472
|
+
if (o.isMesh) {
|
|
473
|
+
o.layers.disable(0);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
462
476
|
}
|
|
463
477
|
}
|
|
464
478
|
if (self.materialVisibility == visibility) {
|
|
@@ -1,152 +1,168 @@
|
|
|
1
1
|
import { LinkedHashMap } from 'js-utils-z';
|
|
2
2
|
import { B3DMDecoder } from "../decoder/B3DMDecoder";
|
|
3
3
|
import { setIntervalAsync } from 'set-interval-async/dynamic';
|
|
4
|
+
import { initial } from 'lodash';
|
|
5
|
+
import * as THREE from 'three';
|
|
4
6
|
|
|
5
|
-
const ready = [];
|
|
6
|
-
const downloads = [];
|
|
7
|
-
const nextReady = [];
|
|
8
|
-
const nextDownloads = [];
|
|
9
7
|
let concurentDownloads = 0;
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (nextDownloads.length == 0) return;
|
|
18
|
-
}
|
|
19
|
-
while (nextDownloads.length > 0 && concurentDownloads < 500) {
|
|
20
|
-
const nextDownload = nextDownloads.shift();
|
|
21
|
-
if (!!nextDownload && nextDownload.shouldDoDownload()) {
|
|
22
|
-
nextDownload.doDownload();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
9
|
+
class TileLoader {
|
|
10
|
+
constructor(meshCallback, maxCachedItems) {
|
|
11
|
+
this.meshCallback = meshCallback;
|
|
12
|
+
this.cache = new LinkedHashMap();
|
|
13
|
+
this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 100;
|
|
14
|
+
this.register = {};
|
|
26
15
|
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function loadBatch() {
|
|
34
|
-
if (nextReady.length == 0) {
|
|
35
|
-
getNextReady();
|
|
36
|
-
if (nextReady.length == 0) return 0;
|
|
17
|
+
this.ready = [];
|
|
18
|
+
this.downloads = [];
|
|
19
|
+
this.nextReady = [];
|
|
20
|
+
this.nextDownloads = [];
|
|
21
|
+
this.init();
|
|
37
22
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
23
|
+
|
|
24
|
+
init(){
|
|
25
|
+
|
|
26
|
+
const self = this;
|
|
27
|
+
setIntervalAsync(() => {
|
|
28
|
+
self.download();
|
|
29
|
+
/* const start = Date.now();
|
|
30
|
+
let uploaded = 0;
|
|
31
|
+
do{
|
|
32
|
+
uploaded = download();
|
|
33
|
+
}while(uploaded > 0 && (Date.now() - start)<= 2 ) */
|
|
34
|
+
|
|
35
|
+
}, 10);
|
|
36
|
+
setIntervalAsync(() => {
|
|
37
|
+
const start = Date.now();
|
|
38
|
+
let loaded = 0;
|
|
39
|
+
do {
|
|
40
|
+
loaded = self.loadBatch();
|
|
41
|
+
} while (loaded > 0 && (Date.now() - start) <= 0)
|
|
42
|
+
|
|
43
|
+
}, 10);
|
|
52
44
|
}
|
|
53
|
-
return 1;
|
|
54
|
-
}
|
|
55
45
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
continue;
|
|
46
|
+
scheduleDownload(f) {
|
|
47
|
+
this.downloads.unshift(f);
|
|
48
|
+
}
|
|
49
|
+
download() {
|
|
50
|
+
if (this.nextDownloads.length == 0) {
|
|
51
|
+
this.getNextDownloads();
|
|
52
|
+
if (this.nextDownloads.length == 0) return;
|
|
64
53
|
}
|
|
65
|
-
|
|
66
|
-
nextDownloads.
|
|
54
|
+
while (this.nextDownloads.length > 0 && concurentDownloads < 500) {
|
|
55
|
+
const nextDownload = this.nextDownloads.shift();
|
|
56
|
+
if (!!nextDownload && nextDownload.shouldDoDownload()) {
|
|
57
|
+
nextDownload.doDownload();
|
|
58
|
+
}
|
|
67
59
|
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
meshReceived(cache, register, key, distanceFunction, getSiblings, level, uuid) {
|
|
66
|
+
this.ready.unshift([cache, register, key, distanceFunction, getSiblings, level, uuid]);
|
|
68
67
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
loadBatch() {
|
|
69
|
+
if (this.nextReady.length == 0) {
|
|
70
|
+
this.getNextReady();
|
|
71
|
+
if (this.nextReady.length == 0) return 0;
|
|
72
|
+
}
|
|
73
|
+
const data = this.nextReady.shift();
|
|
74
|
+
if (!data) return 0;
|
|
75
|
+
const cache = data[0];
|
|
76
|
+
const register = data[1];
|
|
77
|
+
const key = data[2];
|
|
78
|
+
const mesh = cache.get(key);
|
|
79
|
+
if(mesh instanceof THREE.InstancedMesh){
|
|
80
|
+
console.log("instanced");
|
|
81
|
+
}else{
|
|
82
|
+
console.log(" not instanced");
|
|
83
|
+
}
|
|
84
|
+
if (!!mesh && !!register[key]) {
|
|
85
|
+
Object.keys(register[key]).forEach(tile => {
|
|
86
|
+
const callback = register[key][tile];
|
|
87
|
+
if (!!callback) {
|
|
88
|
+
callback(mesh);
|
|
89
|
+
register[key][tile] = null;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
78
92
|
}
|
|
93
|
+
return 1;
|
|
79
94
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
95
|
+
|
|
96
|
+
getNextDownloads() {
|
|
97
|
+
let smallestLevel = Number.MAX_VALUE;
|
|
98
|
+
let smallestDistance = Number.MAX_VALUE;
|
|
99
|
+
let closest = -1;
|
|
100
|
+
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
101
|
+
if (!this.downloads[i].shouldDoDownload()) {
|
|
102
|
+
this.downloads.splice(i, 1);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (!this.downloads[i].distanceFunction) { // if no distance function, must be a json, give absolute priority!
|
|
106
|
+
this.nextDownloads.push(this.downloads.splice(i, 1)[0]);
|
|
87
107
|
}
|
|
88
108
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
nextReady.push(ready.splice(i, 1)[0]);
|
|
109
|
+
if (this.nextDownloads.length > 0) return;
|
|
110
|
+
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
111
|
+
const dist = this.downloads[i].distanceFunction();
|
|
112
|
+
if (dist < smallestDistance) {
|
|
113
|
+
smallestDistance = dist;
|
|
114
|
+
closest = i;
|
|
115
|
+
} else if (dist == smallestDistance && this.downloads[i].level < smallestLevel) {
|
|
116
|
+
smallestLevel = this.downloads[i].level;
|
|
117
|
+
closest = i
|
|
118
|
+
}
|
|
100
119
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
smallestLevel = ready[i][5]
|
|
111
|
-
closest = i
|
|
120
|
+
if (closest >= 0) {
|
|
121
|
+
const closestItem = this.downloads.splice(closest, 1).pop();
|
|
122
|
+
this.nextDownloads.push(closestItem);
|
|
123
|
+
const siblings = closestItem.getSiblings();
|
|
124
|
+
for (let i = this.downloads.length - 1; i >= 0; i--) {
|
|
125
|
+
if (siblings.includes(this.downloads[i].uuid)) {
|
|
126
|
+
this.nextDownloads.push(this.downloads.splice(i, 1).pop());
|
|
127
|
+
}
|
|
128
|
+
}
|
|
112
129
|
}
|
|
113
130
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
131
|
+
|
|
132
|
+
getNextReady() {
|
|
133
|
+
let smallestLevel = Number.MAX_VALUE;
|
|
134
|
+
let smallestDistance = Number.MAX_VALUE;
|
|
135
|
+
let closest = -1;
|
|
136
|
+
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
137
|
+
|
|
138
|
+
if (!this.ready[i][3]) {// if no distance function, must be a json, give absolute priority!
|
|
139
|
+
this.nextReady.push(this.ready.splice(i, 1)[0]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (this.nextReady.length > 0) return;
|
|
143
|
+
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
144
|
+
const dist = this.ready[i][3]();
|
|
145
|
+
if (dist < smallestDistance) {
|
|
146
|
+
smallestDistance = dist;
|
|
147
|
+
smallestLevel = this.ready[i][5]
|
|
148
|
+
closest = i
|
|
149
|
+
} else if (dist == smallestDistance && this.ready[i][5] < smallestLevel) {
|
|
150
|
+
smallestLevel = this.ready[i][5]
|
|
151
|
+
closest = i
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (closest >= 0) {
|
|
155
|
+
const closestItem = this.ready.splice(closest, 1).pop();
|
|
156
|
+
this.nextReady.push(closestItem);
|
|
157
|
+
const siblings = closestItem[4]();
|
|
158
|
+
for (let i = this.ready.length - 1; i >= 0; i--) {
|
|
159
|
+
if (siblings.includes(this.ready[i][6])) {
|
|
160
|
+
this.nextready.push(this.ready.splice(i, 1).pop());
|
|
161
|
+
}
|
|
121
162
|
}
|
|
122
163
|
}
|
|
123
164
|
}
|
|
124
|
-
}
|
|
125
|
-
setIntervalAsync(() => {
|
|
126
|
-
download();
|
|
127
|
-
/* const start = Date.now();
|
|
128
|
-
let uploaded = 0;
|
|
129
|
-
do{
|
|
130
|
-
uploaded = download();
|
|
131
|
-
}while(uploaded > 0 && (Date.now() - start)<= 2 ) */
|
|
132
165
|
|
|
133
|
-
}, 10);
|
|
134
|
-
setIntervalAsync(() => {
|
|
135
|
-
const start = Date.now();
|
|
136
|
-
let loaded = 0;
|
|
137
|
-
do {
|
|
138
|
-
loaded = loadBatch();
|
|
139
|
-
} while (loaded > 0 && (Date.now() - start) <= 1)
|
|
140
|
-
|
|
141
|
-
}, 10);
|
|
142
|
-
|
|
143
|
-
class TileLoader {
|
|
144
|
-
constructor(meshCallback, maxCachedItems) {
|
|
145
|
-
this.meshCallback = meshCallback;
|
|
146
|
-
this.cache = new LinkedHashMap();
|
|
147
|
-
this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 1000;
|
|
148
|
-
this.register = {};
|
|
149
|
-
}
|
|
150
166
|
|
|
151
167
|
get(tileIdentifier, path, callback, distanceFunction, getSiblings, level) {
|
|
152
168
|
const self = this;
|
|
@@ -167,7 +183,7 @@ class TileLoader {
|
|
|
167
183
|
|
|
168
184
|
const cachedObject = self.cache.get(key);
|
|
169
185
|
if (!!cachedObject) {
|
|
170
|
-
meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
|
|
186
|
+
this.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
|
|
171
187
|
} else if (Object.keys(self.register[key]).length == 1) {
|
|
172
188
|
let downloadFunction;
|
|
173
189
|
if (path.includes(".b3dm")) {
|
|
@@ -182,7 +198,7 @@ class TileLoader {
|
|
|
182
198
|
result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
|
|
183
199
|
self.cache.put(key, mesh);
|
|
184
200
|
self.checkSize();
|
|
185
|
-
meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
|
|
201
|
+
this.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
|
|
186
202
|
});
|
|
187
203
|
|
|
188
204
|
});
|
|
@@ -199,12 +215,12 @@ class TileLoader {
|
|
|
199
215
|
result.json().then(json => {
|
|
200
216
|
self.cache.put(key, json);
|
|
201
217
|
self.checkSize();
|
|
202
|
-
meshReceived(self.cache, self.register, key);
|
|
218
|
+
this.meshReceived(self.cache, self.register, key);
|
|
203
219
|
});
|
|
204
220
|
});
|
|
205
221
|
}
|
|
206
222
|
}
|
|
207
|
-
scheduleDownload({
|
|
223
|
+
this.scheduleDownload({
|
|
208
224
|
"shouldDoDownload": () => {
|
|
209
225
|
return !!self.register[key] && Object.keys(self.register[key]).length > 0;
|
|
210
226
|
},
|