@jdultra/threedtiles 3.0.8 → 3.1.0

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.
@@ -0,0 +1,2 @@
1
+ {
2
+ }
package/index.html CHANGED
@@ -4,53 +4,10 @@
4
4
  <head>
5
5
  <meta charset="utf-8" />
6
6
  <title>Three 3DTiles viewer sample</title>
7
- <style>
8
- .slidecontainer {
9
- width: 100%;
10
- }
11
-
12
- .slider {
13
- -webkit-appearance: none;
14
- width: 100%;
15
- height: 15px;
16
- border-radius: 5px;
17
- background: #d3d3d3;
18
- outline: none;
19
- opacity: 0.7;
20
- -webkit-transition: .2s;
21
- transition: opacity .2s;
22
- }
23
-
24
- .slider:hover {
25
- opacity: 1;
26
- }
27
-
28
- .slider::-webkit-slider-thumb {
29
- -webkit-appearance: none;
30
- appearance: none;
31
- width: 25px;
32
- height: 25px;
33
- border-radius: 50%;
34
- background: #0439aa;
35
- cursor: pointer;
36
- }
37
-
38
- .slider::-moz-range-thumb {
39
- width: 25px;
40
- height: 25px;
41
- border-radius: 50%;
42
- background: #04AA6D;
43
- cursor: pointer;
44
- }
45
- </style>
46
7
  </head>
47
8
 
48
9
  <body>
49
10
  <div id="screen"></div>
50
- <div style="position: absolute; top: 1%; z-index: 100; right:1%; ">
51
- <input type="range" min="0.1" max="4" value="1.0", step="0.1" class="slider" id="lodMultiplier" >
52
- <p style="color: #0439aa;">LOD multiplier: <span id="multiplierValue"></span></p>
53
- </div>
54
11
  <div style="position: absolute; bottom: 1%; z-index: 100;">
55
12
  <a href="https://openheritage3d.org/project.php?id=taz6-n215">ORIGINAL MODEL</a>
56
13
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "3.0.8",
3
+ "version": "3.1.0",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
@@ -26,9 +26,13 @@
26
26
  "gltf-validator": ">=2.0.0-dev.3.3",
27
27
  "js-utils-z": "1.2.1",
28
28
  "lodash": ">=4.17.20",
29
+ "lru-cache": "^7.4.1",
30
+ "mnemonist": "^0.39.0",
29
31
  "path-browserify": "^1.0.1",
30
32
  "regenerator-runtime": ">=0.13.7",
31
- "three": "0.131.0"
33
+ "set-interval-async": "^2.0.3",
34
+ "three": "0.131.0",
35
+ "uuid": "^8.3.2"
32
36
  },
33
37
  "devDependencies": {
34
38
  "@babel/core": "^7.12.9",
@@ -41,7 +45,7 @@
41
45
  "mini-css-extract-plugin": "^1.3.1",
42
46
  "webpack": "^5.65.0",
43
47
  "webpack-cli": "4.9.1",
44
- "webpack-dev-server": "4.7.1",
48
+ "webpack-dev-server": "^4.7.4",
45
49
  "whatwg-fetch": "^3.5.0"
46
50
  }
47
51
  }
@@ -1,13 +1,10 @@
1
1
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
2
- import * as THREE from 'three';
3
2
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
4
3
  import { LegacyGLTFLoader } from './LegacyGLTFLoader.js';
5
- import { Color, DoubleSide, BufferAttribute, Mesh } from "three";
6
- import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
7
4
 
8
5
  const gltfLoader = new GLTFLoader();
9
6
  const dracoLoader = new DRACOLoader();
10
- dracoLoader.setDecoderPath( '/src/draco/' );
7
+ dracoLoader.setDecoderPath( 'https://www.gstatic.com/draco/versioned/decoders/1.4.3/' );
11
8
  gltfLoader.setDRACOLoader( dracoLoader );
12
9
  const legacyGLTFLoader = new LegacyGLTFLoader();
13
10
  const B3DMDecoder = {
@@ -106,7 +103,7 @@ const B3DMDecoder = {
106
103
  * @param {*} scene
107
104
  * @returns
108
105
  */
109
- function mergeColoredObject(scene) {
106
+ /*function mergeColoredObject(scene) {
110
107
 
111
108
  const coloredMeshes = {};
112
109
  const texturedMeshes = {};
@@ -213,7 +210,7 @@ function mergeColoredObject(scene) {
213
210
  console.log();
214
211
  scene.matrix = new THREE.Matrix4();
215
212
  return scene;
216
- }
213
+ }*/
217
214
 
218
215
  export { B3DMDecoder }
219
216
 
package/src/index.js CHANGED
@@ -1,81 +1,31 @@
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';
4
5
  import { OGC3DTile } from "./tileset/OGC3DTile";
6
+ import { TileLoader } from "./tileset/TileLoader";
5
7
  import { MapControls, OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
6
- import back from './images/skybox/back.png';
7
- import front from './images/skybox/front.png';
8
- import top from './images/skybox/top.png';
9
- import bottom from './images/skybox/bottom.png';
10
- import right from './images/skybox/right.png';
11
- import left from './images/skybox/left.png';
8
+
9
+
12
10
 
13
11
 
14
12
  const scene = initScene();
13
+ const tilesetStats = TilesetStats();
15
14
  const domContainer = initDomContainer("screen");
16
15
  const camera = initCamera();
17
16
  const ogc3DTiles = initTileset(scene);
18
- initLODMultiplierSlider(ogc3DTiles);
19
17
  const controller = initController(camera, domContainer);
20
- const skybox = initSkybox(controller, camera, scene);
21
18
 
22
19
  const stats = initStats(domContainer);
23
20
  const renderer = initRenderer(camera, domContainer);
24
21
 
25
22
  animate();
26
23
 
27
- function initSkybox(controller, camera, scene) {
28
- const geometry = new THREE.BoxGeometry(8000, 8000, 8000);
29
- const textures = [
30
- loadTexture(back),
31
- loadTexture(front),
32
- loadTexture(top),
33
- loadTexture(bottom),
34
- loadTexture(right),
35
- loadTexture(left),
36
- ];
37
- function loadTexture(url) {
38
- return new THREE.TextureLoader().load(url, (texture => {
39
- texture.wrapS = THREE.ClampToEdgeWrapping;
40
- texture.wrapT = THREE.ClampToEdgeWrapping;
41
- texture.magFilter = THREE.LinearFilter;
42
- texture.minFilter = THREE.LinearFilter;
43
- }))
44
24
 
45
- }
46
- const materials = [];
47
- textures.forEach(tex => {
48
- materials.push(new THREE.MeshBasicMaterial({ map: tex, side: THREE.BackSide }));
49
- })
50
- const mesh = new THREE.Mesh(geometry, materials);
51
- mesh.position.copy(camera.position);
52
- controller.addEventListener("change", () => {
53
- mesh.position.copy(camera.position);
54
- });
55
- scene.add(mesh);
56
- return mesh;
57
- }
58
- function initLODMultiplierSlider(tileset) {
59
- var slider = document.getElementById("lodMultiplier");
60
- var output = document.getElementById("multiplierValue");
61
- output.innerHTML = slider.value;
62
-
63
- slider.oninput = () => {
64
- tileset.setGeometricErrorMultiplier(slider.value)
65
- output.innerHTML = slider.value;
66
- }
67
- }
68
25
  function initScene() {
69
26
  const scene = new THREE.Scene();
70
- scene.background = new THREE.Color(0xFF0000);
71
- scene.add(new THREE.AmbientLight(0xFFFFFF, 0.5));
72
-
73
- var dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
74
- dirLight.position.set(-400, 500, -100);
75
- dirLight.target.position.set(0, 0, 0);
76
-
77
- scene.add(dirLight);
78
- scene.add(dirLight.target);
27
+ scene.background = new THREE.Color(0x000000);
28
+ scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
79
29
  return scene;
80
30
  }
81
31
 
@@ -115,16 +65,15 @@ function initRenderer(camera, dom) {
115
65
  }
116
66
 
117
67
  function initStats(dom) {
118
- const stats = new Stats();
119
- dom.appendChild(stats.dom);
68
+ const stats = Stats();
69
+ document.body.appendChild(stats.dom);
120
70
  return stats;
121
71
  }
122
72
 
123
73
 
124
74
  function initCamera() {
125
75
  const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 1, 10000);
126
- camera.position.set(-60, 80, -30);
127
- camera.lookAt(-100, 40, 0);
76
+ camera.position.set(10, 10, 10);
128
77
 
129
78
  return camera;
130
79
  }
@@ -132,28 +81,34 @@ function initCamera() {
132
81
  function initTileset(scene) {
133
82
 
134
83
  const ogc3DTile = new OGC3DTile({
135
- //url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
136
- //url: "https://storage.googleapis.com/ogc-3d-tiles/castleX/tileset.json",
137
- url: "https://storage.googleapis.com/ogc-3d-tiles/berlinSubsetTiled/tileset.json",
84
+ url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
138
85
  geometricErrorMultiplier: 1,
139
86
  loadOutsideView: true,
140
- meshCallback: mesh => {
87
+ tileLoader: new TileLoader(mesh => {
141
88
  //// Insert code to be called on every newly decoded mesh e.g.:
142
89
  mesh.material.wireframe = false;
143
90
  mesh.material.side = THREE.DoubleSide;
144
- }
91
+ }, tilesetStats),
92
+ stats: tilesetStats
145
93
  });
94
+
146
95
 
147
96
  //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
148
97
  //ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -10)
149
98
  //ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -65)
150
99
  //ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -80)
151
- //ogc3DTile.scale.set(0.1,0.1,0.1);
152
- //ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), -Math.PI * 0.5) // Z-UP to Y-UP
100
+ //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
102
+ // ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -16.5)
103
+ // ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), 0)
104
+ // ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -9.5)
153
105
  //// 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
154
106
 
107
+
108
+
155
109
  var interval ;
156
110
  document.addEventListener('keyup', (e) => {
111
+ console.log(camera.position)
157
112
  if(!e.key || e.key !== "p") return;
158
113
  if(!!interval){
159
114
  clearInterval(interval);
@@ -165,10 +120,9 @@ function initTileset(scene) {
165
120
  function startInterval(){
166
121
  interval = setInterval(function () {
167
122
  ogc3DTile.update(camera);
168
- }, 200);
123
+ }, 25);
169
124
  }
170
125
  startInterval();
171
-
172
126
 
173
127
  scene.add(ogc3DTile)
174
128
  return ogc3DTile;
@@ -177,9 +131,9 @@ function initTileset(scene) {
177
131
  function initController(camera, dom) {
178
132
  const controller = new OrbitControls(camera, dom);
179
133
 
180
- controller.target.set(-20, 40, 35);
134
+ controller.target.set(-11.50895,0.058452500000001, 3.1369285);
181
135
  controller.minDistance = 1;
182
- controller.maxDistance = 500;
136
+ controller.maxDistance = 5000;
183
137
  controller.update();
184
138
  return controller;
185
139
  }
@@ -190,7 +144,7 @@ function animate() {
190
144
 
191
145
  camera.updateMatrixWorld();
192
146
  renderer.render(scene, camera);
193
-
147
+ tilesetStats.update();
194
148
  stats.update();
195
149
 
196
150
  }
@@ -1,19 +1,11 @@
1
1
  import * as THREE from 'three';
2
2
  import { OBB } from "../geometry/obb";
3
- import { B3DMDecoder } from "../decoder/B3DMDecoder";
4
- import { Cache } from "../cache/Cache";
3
+ import { TileLoader } from "./TileLoader";
4
+ import { v4 as uuidv4 } from "uuid";
5
+ import { setIntervalAsync } from 'set-interval-async/dynamic';
6
+ // import { clearIntervalAsync } from 'set-interval-async';
5
7
  const path = require('path');
6
8
 
7
- const tilesToLoad = [];
8
- function scheduleLoadTile(tile) {
9
- tilesToLoad.push(tile);
10
- }
11
-
12
- setInterval(() => {
13
- const tile = tilesToLoad.shift();
14
- if (!!tile) tile.load();
15
- }, 5)
16
-
17
9
  const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
18
10
 
19
11
 
@@ -30,13 +22,41 @@ class OGC3DTile extends THREE.Object3D {
30
22
  * parentRefinement: optional,
31
23
  * geometricErrorMultiplier: Double,
32
24
  * loadOutsideView: Boolean,
33
- * cache : Cache
25
+ * tileLoader : TileLoader,
26
+ * stats: TilesetStats,
27
+ * meshCallback: function
34
28
  * } properties
35
29
  */
36
30
  constructor(properties) {
37
31
  super();
32
+ const self = this;
33
+ this.uuid = uuidv4();
34
+ if (!!properties.tileLoader) {
35
+ this.tileLoader = properties.tileLoader;
36
+ } else {
37
+ this.tileLoader = new TileLoader(!properties.meshCallback ?
38
+ mesh => {
39
+ mesh.material.wireframe = false;
40
+ mesh.material.side = THREE.DoubleSide;
41
+ } : properties.meshCallback);
42
+ }
38
43
  // set properties general to the entire tileset
39
44
  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
+
40
60
  this.meshCallback = properties.meshCallback;
41
61
  this.loadOutsideView = properties.loadOutsideView;
42
62
 
@@ -55,7 +75,6 @@ class OGC3DTile extends THREE.Object3D {
55
75
  this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
56
76
  this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
57
77
 
58
- const self = this;
59
78
  if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
60
79
  self.setup(properties);
61
80
  } else if (properties.url) { // If only the url to the tileset.json is provided
@@ -95,9 +114,9 @@ class OGC3DTile extends THREE.Object3D {
95
114
  }
96
115
  // decode transform
97
116
  if (!!this.json.transform) {
98
- //this.matrix = new THREE.Matrix4();
99
- this.matrix.elements = this.json.transform;
100
- this.updateWorldMatrix(false, false);
117
+ let mat = new THREE.Matrix4();
118
+ mat.elements = this.json.transform;
119
+ this.applyMatrix4(mat);
101
120
  }
102
121
  // decode volume
103
122
  if (!!this.json.boundingVolume) {
@@ -124,11 +143,13 @@ class OGC3DTile extends THREE.Object3D {
124
143
  } else {
125
144
  this.hasMeshContent = true;
126
145
  }
127
- scheduleLoadTile(this);
146
+ this.load();
147
+ //scheduleLoadTile(this);
128
148
  }
129
149
  }
130
150
  load() {
131
151
  var self = this;
152
+ if (self.deleted) return;
132
153
  if (!!self.json.content) {
133
154
  let url;
134
155
  if (!!self.json.content.uri) {
@@ -146,24 +167,24 @@ class OGC3DTile extends THREE.Object3D {
146
167
  }
147
168
 
148
169
  if (!!url) {
149
- self.controller = new AbortController();
150
- fetch(url, { signal: self.controller.signal }).then(result => {
151
- if (!result.ok) {
152
- throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
153
- }
154
- if (url.includes("b3dm")) {// if the content is B3DM
155
- result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
156
- mesh.traverse((o) => {
157
- if (o.isMesh) {
158
- o.material.visible = false;
159
- }
160
- });
161
- self.add(mesh);
162
- self.meshContent = mesh;
163
-
164
-
165
- }).catch(error => { });
166
- } else if (url.includes("json")) {// if the content is json
170
+ if (url.includes(".b3dm")) {
171
+ self.contentURL = url;
172
+ self.tileLoader.get(this.uuid, url, mesh => {
173
+ if (!!self.deleted) return;
174
+ mesh.traverse((o) => {
175
+ if (o.isMesh) {
176
+ o.material.visible = false;
177
+ }
178
+ });
179
+ self.add(mesh);
180
+ self.meshContent = mesh;
181
+ })
182
+ } 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
+ }
167
188
  result.json().then(json => {
168
189
  // when json content is downloaded, it is inserted into this tile's original JSON as a child
169
190
  // and the content object is deleted from the original JSON
@@ -173,50 +194,36 @@ class OGC3DTile extends THREE.Object3D {
173
194
  delete self.json.content;
174
195
  self.hasUnloadedJSONContent = false;
175
196
  }).catch(error => { });
176
- }
177
- }).catch(error => {
178
- });
197
+ }).catch(error => { });
198
+ }
199
+
179
200
  }
180
201
  }
181
202
  }
182
203
 
183
- disposeChildren() {
184
- var self = this;
204
+ dispose() {
185
205
 
186
- self.childrenTiles.forEach(tile => tile.traverse(function (element) {
206
+ const self = this;
207
+ self.deleted = true;
208
+ this.traverse(function (element) {
209
+ if (!!element.contentURL) {
210
+ self.tileLoader.invalidate(element.contentURL, element.uuid);
211
+ }
187
212
  if (!!element.controller) { // abort tile request
188
213
  element.controller.abort();
189
214
  }
190
- if (element.material) {
191
- // dispose materials
192
- if (element.material.length) {
193
- for (let i = 0; i < element.material.length; ++i) {
194
- element.material[i].dispose();
195
- }
196
- }
197
- else {
198
- element.material.dispose()
199
- }
200
-
201
- }
202
- if (element.geometry) {
203
- // dispose geometry
204
- element.geometry.dispose();
205
-
206
- }
207
- }));
208
- for (let i = 0; i < this.childrenTiles.length; i++) {
209
215
 
210
- const object = this.childrenTiles[i];
211
-
212
- object.parent = null;
213
-
214
- object.dispatchEvent({ type: 'removed' });
216
+ });
217
+ this.parent = null;
218
+ this.dispatchEvent({ type: 'removed' });
219
+ }
220
+ disposeChildren() {
221
+ var self = this;
215
222
 
216
- }
217
- this.childrenTiles = [];
218
- this.children = [];
219
- if (!!this.meshContent) this.children.push(this.meshContent);
223
+ self.childrenTiles.forEach(tile => tile.dispose());
224
+ self.childrenTiles = [];
225
+ self.children = [];
226
+ if (!!self.meshContent) self.children.push(self.meshContent);
220
227
  }
221
228
 
222
229
 
@@ -228,14 +235,14 @@ class OGC3DTile extends THREE.Object3D {
228
235
  _update(camera, frustum) {
229
236
  const self = this;
230
237
 
238
+ self.childrenTiles.forEach(child => child._update(camera, frustum));
231
239
  if (!!self.boundingVolume && !!self.geometricError) {
232
- var metric = self.calculateUpdateMetric(camera, frustum);
240
+ self.metric = self.calculateUpdateMetric(camera, frustum);
233
241
  }
234
242
 
235
- updateNodeVisibility(metric);
236
- updateTree(metric);
237
- self.childrenTiles.forEach(child => child._update(camera, frustum));
238
- trimTree(metric);
243
+ updateNodeVisibility(self.metric);
244
+ updateTree(self.metric);
245
+ trimTree(self.metric);
239
246
 
240
247
 
241
248
  function updateTree(metric) {
@@ -293,7 +300,7 @@ class OGC3DTile extends THREE.Object3D {
293
300
  self.changeContentVisibility(false);
294
301
  } else {
295
302
  //self.changeContentVisibility(true);
296
-
303
+
297
304
  }
298
305
  }
299
306
  }
@@ -322,9 +329,9 @@ class OGC3DTile extends THREE.Object3D {
322
329
  json: childJSON,
323
330
  rootPath: self.rootPath,
324
331
  geometricErrorMultiplier: self.geometricErrorMultiplier,
325
- meshCallback: self.meshCallback,
326
332
  loadOutsideView: self.loadOutsideView,
327
- level: self.level + 1
333
+ level: self.level + 1,
334
+ tileLoader: self.tileLoader
328
335
  });
329
336
  self.childrenTiles.push(childTile);
330
337
  self.add(childTile);
@@ -376,7 +383,7 @@ class OGC3DTile extends THREE.Object3D {
376
383
  return true;
377
384
  }
378
385
 
379
- return false;
386
+ return true;
380
387
 
381
388
  }
382
389
 
@@ -455,28 +462,4 @@ class OGC3DTile extends THREE.Object3D {
455
462
  this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
456
463
  }
457
464
  }
458
-
459
- /**
460
- *
461
- * @param {Integer} size a number of vertices
462
- */
463
- function createMeshCache(size = 5000000, meshCallback = () => { }) {
464
- /* return new Cache(
465
- (url, self)=>{
466
- fetch(url, { signal: self.controller.signal }).then(result => {
467
- if (!result.ok) {
468
- throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
469
- }
470
- result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
471
- mesh.traverse((o) => {
472
- if (o.isMesh) {
473
- o.material.visible = false;
474
- }
475
- });
476
- return mesh;
477
- }).catch(error => { });
478
- }
479
- }
480
- ) */
481
- }
482
- export { OGC3DTile, createMeshCache };
465
+ export { OGC3DTile };
@@ -0,0 +1,188 @@
1
+ import { LinkedHashMap } from 'js-utils-z';
2
+ import { B3DMDecoder } from "../decoder/B3DMDecoder";
3
+ import { setIntervalAsync } from 'set-interval-async/dynamic';
4
+
5
+ const ready = [];
6
+ const downloads = [];
7
+ function scheduleDownload(f) {
8
+ downloads.unshift(f);
9
+ }
10
+ function download() {
11
+ if(downloads.length <=0) return;
12
+ const nextDownload = downloads.shift();
13
+ if (!!nextDownload && nextDownload.shouldDoDownload()) {
14
+ nextDownload.doDownload();
15
+ }
16
+ }
17
+ function meshReceived(cache, register, key) {
18
+ ready.unshift([cache, register, key]);
19
+ }
20
+ 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
+ });
36
+ }
37
+ }
38
+ }
39
+ setIntervalAsync(() => {
40
+ loadBatch();
41
+ }, 10)
42
+ setIntervalAsync(() => {
43
+ download();
44
+ }, 10)
45
+
46
+ class TileLoader {
47
+ constructor(meshCallback, stats) {
48
+ this.meshCallback = meshCallback;
49
+ this.cache = new LinkedHashMap();
50
+ this.maxSize = 1000;
51
+ this.stats = stats;
52
+ this.register = {};
53
+ }
54
+
55
+ get(tileIdentifier, path, callback) {
56
+ const self = this;
57
+ const key = simplifyPath(path);
58
+
59
+
60
+ if (!path.includes(".b3dm")) {
61
+ console.error("the 3DTiles cache can only be used to load B3DM data");
62
+ return;
63
+ }
64
+ if (!self.register[key]) {
65
+ self.register[key] = {};
66
+ }
67
+ if (!!self.register[key][tileIdentifier]) {
68
+ console.error(" a tile should only be loaded once");
69
+ }
70
+ self.register[key][tileIdentifier] = callback;
71
+
72
+ const cachedObject = self.cache.get(key);
73
+ if (!!cachedObject) {
74
+ meshReceived(self.cache, self.register, key);
75
+ } 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": () => {
81
+ fetch(path).then(result => {
82
+ if (!result.ok) {
83
+ console.error("could not load tile with path : " + path)
84
+ throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
85
+ }
86
+ result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
87
+ self.cache.put(key, mesh);
88
+ self.checkSize();
89
+ meshReceived(self.cache, self.register, key);
90
+ });
91
+
92
+ });
93
+ }
94
+ })
95
+ }
96
+ }
97
+
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
+ }
108
+
109
+ invalidate(path, tileIdentifier) {
110
+ const key = simplifyPath(path);
111
+ delete this.register[key][tileIdentifier];
112
+ }
113
+
114
+ checkSize() {
115
+ const self = this;
116
+
117
+ 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()) {
128
+ i++;
129
+ 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();
143
+ }
144
+ }
145
+ else {
146
+ o.material.dispose()
147
+ }
148
+ }
149
+ if (o.geometry) {
150
+ // dispose geometry
151
+ o.geometry.dispose();
152
+
153
+ }
154
+ });
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ function simplifyPath(main_path) {
161
+
162
+ var parts = main_path.split('/'),
163
+ new_path = [],
164
+ length = 0;
165
+ for (var i = 0; i < parts.length; i++) {
166
+ var part = parts[i];
167
+ if (part === '.' || part === '' || part === '..') {
168
+ if (part === '..' && length > 0) {
169
+ length--;
170
+ }
171
+ continue;
172
+ }
173
+ new_path[length++] = part;
174
+ }
175
+
176
+ if (length === 0) {
177
+ return '/';
178
+ }
179
+
180
+ var result = '';
181
+ for (var i = 0; i < length; i++) {
182
+ result += '/' + new_path[i];
183
+ }
184
+
185
+ return result;
186
+ }
187
+
188
+ export { TileLoader };
@@ -0,0 +1,65 @@
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;
@@ -1,44 +0,0 @@
1
-
2
- import { LinkedHashMap } from 'js-utils-z';
3
-
4
- class Cache {
5
- constructor(loader, counter, dispose, max) {
6
- this.loader = loader;
7
- this.counter = counter;
8
- this.dispose = dispose;
9
- this.max = max;
10
- this.currentSize = 0;
11
- this.objects = new LinkedHashMap();
12
- }
13
-
14
- get(name){
15
- if(this.objects.has(name)){
16
- const item = this.objects.remove(name);
17
- item.users++;
18
- this.objects.put(name, item, false);
19
- return new Promise.resolve({dispose:()=>item.users--,content:item.content});
20
- }else{
21
- return this.loader(name).then(content=>{
22
- const item = { users: 1, content: content };
23
- this.objects.put(name, item, false);
24
- currentSize+=this.counter(item);
25
- checkSize();
26
- return {dispose:()=>item.users--,content:item.content};
27
- });
28
- }
29
- }
30
-
31
- checkSize(){
32
- let object = this.objects.head();
33
- while(this.currentSize > this.max && !!object){
34
- if(object.value.users <=0){
35
- const item = this.objects.remove(object.key);
36
- this.currentSize -= this.counter(item.content);
37
- this.dispose(item.content);
38
- }
39
- object = object.next();
40
- }
41
- }
42
- }
43
-
44
- export{Cache};