@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 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 (demo)
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 around the view so that there are no gaps in the mesh when the camera moves or when displaying shadows.
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
- A word of warning: activating occlusion culling causes an extra render-pass and as such, has an impact on frame-rate. It will be most beneficial on interior scenes where most of the data is occluded by walls.
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 may want to set what side of the faces are drawn in the occlusion pass. By default, THREE.FrontSide is used:
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 I'll convert any dataset you have for free.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
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.2));
31
- const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
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:8081/tileset.json",
95
+ //url: "http://localhost:8080/tileset.json",
90
96
  geometricErrorMultiplier: 0.5,
91
97
  loadOutsideView: false,
92
- tileLoader: new TileLoader(mesh => {
93
- //// Insert code to be called on every newly decoded mesh e.g.:
94
- mesh.material.wireframe = false;
95
- mesh.material.side = THREE.DoubleSide;
96
- }, 1000),
97
- occlusionCullingService: occlusionCullingService
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
 
@@ -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
- // set properties general to the entire tileset
52
- this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
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
- self.layers.enable(0);
464
+
465
+ self.meshContent.traverse((o) => {
466
+ if (o.isMesh) {
467
+ o.layers.enable(0);
468
+ }
469
+ });
460
470
  }else{
461
- self.layers.disable(0);
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
- function scheduleDownload(f) {
12
- downloads.unshift(f);
13
- }
14
- function download() {
15
- if (nextDownloads.length == 0) {
16
- getNextDownloads();
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
- return;
29
- }
30
- function meshReceived(cache, register, key, distanceFunction, getSiblings, level, uuid) {
31
- ready.unshift([cache, register, key, distanceFunction, getSiblings, level, uuid]);
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
- const data = nextReady.shift();
39
- if (!data) return 0;
40
- const cache = data[0];
41
- const register = data[1];
42
- const key = data[2];
43
- const mesh = cache.get(key);
44
- if (!!mesh && !!register[key]) {
45
- Object.keys(register[key]).forEach(tile => {
46
- const callback = register[key][tile];
47
- if (!!callback) {
48
- callback(mesh);
49
- register[key][tile] = null;
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
- function getNextDownloads() {
57
- let smallestLevel = Number.MAX_VALUE;
58
- let smallestDistance = Number.MAX_VALUE;
59
- let closest = -1;
60
- for (let i = downloads.length - 1; i >= 0; i--) {
61
- if (!downloads[i].shouldDoDownload()) {
62
- downloads.splice(i, 1);
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
- if (!downloads[i].distanceFunction) { // if no distance function, must be a json, give absolute priority!
66
- nextDownloads.push(downloads.splice(i, 1)[0]);
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
- if (nextDownloads.length > 0) return;
70
- for (let i = downloads.length - 1; i >= 0; i--) {
71
- const dist = downloads[i].distanceFunction();
72
- if (dist < smallestDistance) {
73
- smallestDistance = dist;
74
- closest = i;
75
- } else if (dist == smallestDistance && downloads[i].level < smallestLevel) {
76
- smallestLevel = downloads[i].level;
77
- closest = i
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
- if (closest >= 0) {
81
- const closestItem = downloads.splice(closest, 1).pop();
82
- nextDownloads.push(closestItem);
83
- const siblings = closestItem.getSiblings();
84
- for (let i = downloads.length - 1; i >= 0; i--) {
85
- if (siblings.includes(downloads[i].uuid)) {
86
- nextDownloads.push(downloads.splice(i, 1).pop());
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
- function getNextReady() {
93
- let smallestLevel = Number.MAX_VALUE;
94
- let smallestDistance = Number.MAX_VALUE;
95
- let closest = -1;
96
- for (let i = ready.length - 1; i >= 0; i--) {
97
-
98
- if (!ready[i][3]) {// if no distance function, must be a json, give absolute priority!
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
- if (nextReady.length > 0) return;
103
- for (let i = ready.length - 1; i >= 0; i--) {
104
- const dist = ready[i][3]();
105
- if (dist < smallestDistance) {
106
- smallestDistance = dist;
107
- smallestLevel = ready[i][5]
108
- closest = i
109
- } else if (dist == smallestDistance && ready[i][5] < smallestLevel) {
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
- if (closest >= 0) {
115
- const closestItem = ready.splice(closest, 1).pop();
116
- nextReady.push(closestItem);
117
- const siblings = closestItem[4]();
118
- for (let i = ready.length - 1; i >= 0; i--) {
119
- if (siblings.includes(ready[i][6])) {
120
- nextready.push(ready.splice(i, 1).pop());
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
  },