@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 CHANGED
@@ -14,7 +14,7 @@ import { OGC3DTile } from "./tileset/OGC3DTile";
14
14
  ...
15
15
 
16
16
  const ogc3DTile = new OGC3DTile({
17
- url: "https://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json"
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://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json",
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://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json",
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://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json",
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
@@ -4,6 +4,7 @@
4
4
  <head>
5
5
  <meta charset="utf-8" />
6
6
  <title>Three 3DTiles viewer sample</title>
7
+ <link rel="manifest" href="manifest.json">
7
8
  </head>
8
9
 
9
10
  <body>
package/manifest.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "display": "fullscreen",
3
+ "orientation": "landscape"
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "3.1.0",
3
+ "version": "3.1.4",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
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
- }, tilesetStats),
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
- // ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * 0.5) // Z-UP to Y-UP
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 = setInterval(function () {
116
+ interval = setIntervalAsync(function () {
122
117
  ogc3DTile.update(camera);
123
- }, 25);
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
  }
@@ -23,8 +23,9 @@ class OGC3DTile extends THREE.Object3D {
23
23
  * geometricErrorMultiplier: Double,
24
24
  * loadOutsideView: Boolean,
25
25
  * tileLoader : TileLoader,
26
- * stats: TilesetStats,
27
- * meshCallback: function
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.controller = new AbortController();
184
- fetch(url, { signal: self.controller.signal }).then(result => {
185
- if (!result.ok) {
186
- throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
187
- }
188
- result.json().then(json => {
189
- // when json content is downloaded, it is inserted into this tile's original JSON as a child
190
- // and the content object is deleted from the original JSON
191
- if (!self.json.children) self.json.children = [];
192
- json.rootPath = path.dirname(url);
193
- self.json.children.push(json);
194
- delete self.json.content;
195
- self.hasUnloadedJSONContent = false;
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
- }).catch(error => { });
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(downloads.length <=0) return;
12
- const nextDownload = downloads.shift();
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
- for (let i = 0; i < 1; i++) {
22
- const data = ready.shift();
23
- if (!data) return;
24
- const cache = data[0];
25
- const register = data[1];
26
- const key = data[2];
27
- const mesh = cache.get(key);
28
- if (!!mesh) {
29
- Object.keys(register[key]).forEach(tile => {
30
- const callback = register[key][tile];
31
- if (!!callback) {
32
- callback(mesh);
33
- register[key][tile] = null;
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
- loadBatch();
41
- }, 10)
42
- setIntervalAsync(() => {
43
- download();
44
- }, 10)
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, stats) {
137
+ constructor(meshCallback, maxCachedItems) {
48
138
  this.meshCallback = meshCallback;
49
139
  this.cache = new LinkedHashMap();
50
- this.maxSize = 1000;
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
- scheduleDownload({
77
- "shouldDoDownload":()=>{
78
- return Object.keys(self.register[key]).length > 0;
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
- meshReceived(key, mesh) {
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
- function memOverflowCheck(){
119
- if(!!self.stats && self.stats.memory()>0){
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
- if (Object.keys(self.register[entry.key]).length > 0) {
131
- self.cache.remove(entry.key);
132
- self.cache.put(entry.key, entry.value);
133
- } else {
134
- self.cache.remove(entry.key);
135
- delete self.register[entry.key];
136
- entry.value.traverse((o) => {
137
-
138
- if (o.material) {
139
- // dispose materials
140
- if (o.material.length) {
141
- for (let i = 0; i < o.material.length; ++i) {
142
- o.material[i].dispose();
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
- else {
146
- o.material.dispose()
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;