@jdultra/threedtiles 3.0.6 → 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.6",
3
+ "version": "3.1.0",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
@@ -24,10 +24,15 @@
24
24
  "license": "MIT",
25
25
  "dependencies": {
26
26
  "gltf-validator": ">=2.0.0-dev.3.3",
27
+ "js-utils-z": "1.2.1",
27
28
  "lodash": ">=4.17.20",
29
+ "lru-cache": "^7.4.1",
30
+ "mnemonist": "^0.39.0",
31
+ "path-browserify": "^1.0.1",
28
32
  "regenerator-runtime": ">=0.13.7",
33
+ "set-interval-async": "^2.0.3",
29
34
  "three": "0.131.0",
30
- "js-utils-z": "1.2.1"
35
+ "uuid": "^8.3.2"
31
36
  },
32
37
  "devDependencies": {
33
38
  "@babel/core": "^7.12.9",
@@ -38,9 +43,9 @@
38
43
  "html-loader": "^1.3.2",
39
44
  "html-webpack-plugin": "^4.5.0",
40
45
  "mini-css-extract-plugin": "^1.3.1",
41
- "webpack": "4.44.2",
42
- "webpack-cli": "^3.3.12",
43
- "webpack-dev-server": "3.11.0",
46
+ "webpack": "^5.65.0",
47
+ "webpack-cli": "4.9.1",
48
+ "webpack-dev-server": "^4.7.4",
44
49
  "whatwg-fetch": "^3.5.0"
45
50
  }
46
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
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/src/index.js CHANGED
@@ -1,75 +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
8
 
7
9
 
10
+
11
+
8
12
  const scene = initScene();
13
+ const tilesetStats = TilesetStats();
9
14
  const domContainer = initDomContainer("screen");
10
15
  const camera = initCamera();
11
16
  const ogc3DTiles = initTileset(scene);
12
- initLODMultiplierSlider(ogc3DTiles);
13
17
  const controller = initController(camera, domContainer);
14
- const skybox = initSkybox(controller, camera, scene);
15
18
 
16
19
  const stats = initStats(domContainer);
17
20
  const renderer = initRenderer(camera, domContainer);
18
21
 
19
22
  animate();
20
23
 
21
- function initSkybox(controller, camera, scene) {
22
- const geometry = new THREE.BoxGeometry(8000, 8000, 8000);
23
- const textures = [
24
- loadTexture("./skybox/back.png"),
25
- loadTexture("./skybox/front.png"),
26
- loadTexture("./skybox/top.png"),
27
- loadTexture("./skybox/bottom.png"),
28
- loadTexture("./skybox/right.png"),
29
- loadTexture("./skybox/left.png"),
30
- ];
31
- function loadTexture(url) {
32
- return new THREE.TextureLoader().load(url, (texture => {
33
- texture.wrapS = THREE.ClampToEdgeWrapping;
34
- texture.wrapT = THREE.ClampToEdgeWrapping;
35
- texture.magFilter = THREE.LinearFilter;
36
- texture.minFilter = THREE.LinearFilter;
37
- }))
38
24
 
39
- }
40
- const materials = [];
41
- textures.forEach(tex => {
42
- materials.push(new THREE.MeshBasicMaterial({ map: tex, side: THREE.BackSide }));
43
- })
44
- const mesh = new THREE.Mesh(geometry, materials);
45
- mesh.position.copy(camera.position);
46
- controller.addEventListener("change", () => {
47
- mesh.position.copy(camera.position);
48
- });
49
- scene.add(mesh);
50
- return mesh;
51
- }
52
- function initLODMultiplierSlider(tileset) {
53
- var slider = document.getElementById("lodMultiplier");
54
- var output = document.getElementById("multiplierValue");
55
- output.innerHTML = slider.value;
56
-
57
- slider.oninput = () => {
58
- tileset.setGeometricErrorMultiplier(slider.value)
59
- output.innerHTML = slider.value;
60
- }
61
- }
62
25
  function initScene() {
63
26
  const scene = new THREE.Scene();
64
- scene.background = new THREE.Color(0xFF0000);
65
- scene.add(new THREE.AmbientLight(0xFFFFFF, 0.5));
66
-
67
- var dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
68
- dirLight.position.set(-400, 500, -100);
69
- dirLight.target.position.set(0, 0, 0);
70
-
71
- scene.add(dirLight);
72
- scene.add(dirLight.target);
27
+ scene.background = new THREE.Color(0x000000);
28
+ scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
73
29
  return scene;
74
30
  }
75
31
 
@@ -109,16 +65,15 @@ function initRenderer(camera, dom) {
109
65
  }
110
66
 
111
67
  function initStats(dom) {
112
- const stats = new Stats();
113
- dom.appendChild(stats.dom);
68
+ const stats = Stats();
69
+ document.body.appendChild(stats.dom);
114
70
  return stats;
115
71
  }
116
72
 
117
73
 
118
74
  function initCamera() {
119
75
  const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 1, 10000);
120
- camera.position.set(-60, 5, -30);
121
- camera.lookAt(-100, 0, 0);
76
+ camera.position.set(10, 10, 10);
122
77
 
123
78
  return camera;
124
79
  }
@@ -127,34 +82,33 @@ function initTileset(scene) {
127
82
 
128
83
  const ogc3DTile = new OGC3DTile({
129
84
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
130
- //url: "https://storage.googleapis.com/ogc-3d-tiles/castleX/tileset.json",
131
- //url: "./apartment/tileset.json",
132
- //url: "https://storage.googleapis.com/ogc-3d-tiles/berlinSubsetTiled/tileset.json",
133
- //url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
134
- //url: "https://s3.us-east-2.wasabisys.com/construkted-assets/arkcnfuk9fw/tileset.json",
135
- //url: "https://s3.us-east-2.wasabisys.com/construkted-assets/a73faxnydqk/tileset.json",
136
- //url: "https://s3.us-east-2.wasabisys.com/construkted-assets/e63mubnpmg/tileset.json",
137
- //url: "https://assets.cesium.com/697512/tileset.json?v=1",
138
- //url:"https://a.3d.blc.shc.eu/WAB/base_layer/cesium_mesh_2020/tileset.json",
139
85
  geometricErrorMultiplier: 1,
140
86
  loadOutsideView: true,
141
- meshCallback: mesh => {
87
+ tileLoader: new TileLoader(mesh => {
142
88
  //// Insert code to be called on every newly decoded mesh e.g.:
143
89
  mesh.material.wireframe = false;
144
90
  mesh.material.side = THREE.DoubleSide;
145
- }
91
+ }, tilesetStats),
92
+ stats: tilesetStats
146
93
  });
94
+
147
95
 
148
96
  //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
149
97
  //ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -10)
150
98
  //ogc3DTile.translateOnAxis(new THREE.Vector3(1,0,0), -65)
151
99
  //ogc3DTile.translateOnAxis(new THREE.Vector3(0,0,1), -80)
152
- //ogc3DTile.scale.set(0.01,0.01,0.01);
153
- //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)
154
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
155
106
 
107
+
108
+
156
109
  var interval ;
157
110
  document.addEventListener('keyup', (e) => {
111
+ console.log(camera.position)
158
112
  if(!e.key || e.key !== "p") return;
159
113
  if(!!interval){
160
114
  clearInterval(interval);
@@ -166,10 +120,9 @@ function initTileset(scene) {
166
120
  function startInterval(){
167
121
  interval = setInterval(function () {
168
122
  ogc3DTile.update(camera);
169
- }, 200);
123
+ }, 25);
170
124
  }
171
125
  startInterval();
172
-
173
126
 
174
127
  scene.add(ogc3DTile)
175
128
  return ogc3DTile;
@@ -178,9 +131,9 @@ function initTileset(scene) {
178
131
  function initController(camera, dom) {
179
132
  const controller = new OrbitControls(camera, dom);
180
133
 
181
- controller.target.set(-20, -20, 35);
134
+ controller.target.set(-11.50895,0.058452500000001, 3.1369285);
182
135
  controller.minDistance = 1;
183
- controller.maxDistance = 500;
136
+ controller.maxDistance = 5000;
184
137
  controller.update();
185
138
  return controller;
186
139
  }
@@ -191,7 +144,7 @@ function animate() {
191
144
 
192
145
  camera.updateMatrixWorld();
193
146
  renderer.render(scene, camera);
194
-
147
+ tilesetStats.update();
195
148
  stats.update();
196
149
 
197
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
 
@@ -51,11 +71,10 @@ class OGC3DTile extends THREE.Object3D {
51
71
  this.json; // the json corresponding to this tile
52
72
  this.materialVisibility = false;
53
73
  this.inFrustum = true;
54
- this.level = properties.level? properties.level : 0;
74
+ this.level = properties.level ? properties.level : 0;
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,54 +194,40 @@ 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
215
 
206
- }
207
- }));
208
- for (let i = 0; i < this.childrenTiles.length; i++) {
209
-
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
 
223
- update(camera){
230
+ update(camera) {
224
231
  const frustum = new THREE.Frustum();
225
232
  frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
226
233
  this._update(camera, frustum);
@@ -228,19 +235,19 @@ 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
- updateTree(metric);
236
- self.childrenTiles.forEach(child => child._update(camera, frustum));
237
- updateNodeVisibility(metric);
238
- trimTree(metric);
243
+ updateNodeVisibility(self.metric);
244
+ updateTree(self.metric);
245
+ trimTree(self.metric);
239
246
 
240
247
 
241
248
  function updateTree(metric) {
242
249
  // If this tile does not have mesh content but it has children
243
- if(metric<0 && self.hasMeshContent) return;
250
+ if (metric < 0 && self.hasMeshContent) return;
244
251
  if (!self.hasMeshContent || (metric < self.geometricError && !!self.meshContent)) {
245
252
  if (!!self.json && !!self.json.children && self.childrenTiles.length != self.json.children.length) {
246
253
  loadJsonChildren();
@@ -255,7 +262,7 @@ class OGC3DTile extends THREE.Object3D {
255
262
  if (!self.hasMeshContent) return;
256
263
 
257
264
  // mesh content not yet loaded
258
- if(!self.meshContent) {
265
+ if (!self.meshContent) {
259
266
  return;
260
267
  }
261
268
 
@@ -264,7 +271,7 @@ class OGC3DTile extends THREE.Object3D {
264
271
  self.inFrustum = false;
265
272
  self.changeContentVisibility(!!self.loadOutsideView);
266
273
  return;
267
- }else{
274
+ } else {
268
275
  self.inFrustum = true;
269
276
  }
270
277
 
@@ -282,7 +289,7 @@ class OGC3DTile extends THREE.Object3D {
282
289
  // if children are visible and have been displayed, can be hidden
283
290
  var allChildrenReady = true;
284
291
  self.childrenTiles.every(child => {
285
-
292
+
286
293
  if (!child.isReady()) {
287
294
  allChildrenReady = false;
288
295
  return false;
@@ -291,8 +298,9 @@ class OGC3DTile extends THREE.Object3D {
291
298
  });
292
299
  if (allChildrenReady) {
293
300
  self.changeContentVisibility(false);
294
- }else{
295
- self.changeContentVisibility(true);
301
+ } else {
302
+ //self.changeContentVisibility(true);
303
+
296
304
  }
297
305
  }
298
306
  }
@@ -309,7 +317,7 @@ class OGC3DTile extends THREE.Object3D {
309
317
  return;
310
318
  }
311
319
  }
312
-
320
+
313
321
  }
314
322
 
315
323
  function loadJsonChildren() {
@@ -321,9 +329,9 @@ class OGC3DTile extends THREE.Object3D {
321
329
  json: childJSON,
322
330
  rootPath: self.rootPath,
323
331
  geometricErrorMultiplier: self.geometricErrorMultiplier,
324
- meshCallback: self.meshCallback,
325
332
  loadOutsideView: self.loadOutsideView,
326
- level:self.level+1
333
+ level: self.level + 1,
334
+ tileLoader: self.tileLoader
327
335
  });
328
336
  self.childrenTiles.push(childTile);
329
337
  self.add(childTile);
@@ -338,13 +346,13 @@ class OGC3DTile extends THREE.Object3D {
338
346
  */
339
347
  isReady() {
340
348
  // if outside frustum
341
- if(!this.inFrustum) return true;
349
+ if (!this.inFrustum) return true;
342
350
 
343
351
  // if json is not done loading
344
352
  if (this.hasUnloadedJSONContent) return false;
345
353
 
346
354
  // if this tile has no mesh content or if it's marked as visible false, look at children
347
- if ((!this.hasMeshContent || !this.meshContent || !this.materialVisibility) && this.childrenTiles.length>0) {
355
+ if ((!this.hasMeshContent || !this.meshContent || !this.materialVisibility) && this.childrenTiles.length > 0) {
348
356
  var allChildrenReady = true;
349
357
  this.childrenTiles.every(child => {
350
358
  if (!child.isReady()) {
@@ -357,11 +365,11 @@ class OGC3DTile extends THREE.Object3D {
357
365
  }
358
366
 
359
367
  // if this tile has no mesh content
360
- if(!this.hasMeshContent){
368
+ if (!this.hasMeshContent) {
361
369
  return true;
362
370
  }
363
371
  // if mesh content not yet loaded
364
- if(!this.meshContent) {
372
+ if (!this.meshContent) {
365
373
  return false;
366
374
  }
367
375
 
@@ -375,11 +383,12 @@ class OGC3DTile extends THREE.Object3D {
375
383
  return true;
376
384
  }
377
385
 
378
- return false;
379
-
386
+ return true;
387
+
380
388
  }
381
389
 
382
-
390
+
391
+
383
392
 
384
393
  changeContentVisibility(visibility) {
385
394
  const self = this;
@@ -437,8 +446,8 @@ class OGC3DTile extends THREE.Object3D {
437
446
  if (distance == 0) {
438
447
  return 0;
439
448
  }
440
- const scale = this.matrixWorld.getMaxScaleOnAxis ();
441
- return ((distance / 100) / this.geometricErrorMultiplier)/scale;
449
+ const scale = this.matrixWorld.getMaxScaleOnAxis();
450
+ return ((distance / 100) / this.geometricErrorMultiplier) / scale;
442
451
  } else if (this.boundingVolume instanceof THREE.Box3) {
443
452
  // Region
444
453
  // Region not supported
@@ -453,28 +462,4 @@ class OGC3DTile extends THREE.Object3D {
453
462
  this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
454
463
  }
455
464
  }
456
-
457
- /**
458
- *
459
- * @param {Integer} size a number of vertices
460
- */
461
- function createMeshCache(size = 5000000, meshCallback = ()=>{}){
462
- /* return new Cache(
463
- (url, self)=>{
464
- fetch(url, { signal: self.controller.signal }).then(result => {
465
- if (!result.ok) {
466
- throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
467
- }
468
- result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
469
- mesh.traverse((o) => {
470
- if (o.isMesh) {
471
- o.material.visible = false;
472
- }
473
- });
474
- return mesh;
475
- }).catch(error => { });
476
- }
477
- }
478
- ) */
479
- }
480
- 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;
package/webpack.config.js CHANGED
@@ -83,19 +83,13 @@ module.exports = {
83
83
  }]
84
84
  },
85
85
  {
86
- test: /\.(png|gif)$/,
87
- use: [{
88
- loader: "file-loader",
89
- options: {
90
- name: "images/[name].[contenthash].[ext]"
91
- }
92
- }]
93
- }
86
+ test: /\.(png|svg|jpg|jpeg|gif)$/i,
87
+ type: 'asset/resource',
88
+ },
94
89
  ],
95
90
  },
96
91
  optimization: {
97
92
  minimizer: [new TerserPlugin({
98
- cache: true,
99
93
  parallel: true,
100
94
  terserOptions: {
101
95
  ecma: undefined,
@@ -119,8 +113,14 @@ module.exports = {
119
113
  devServer: {
120
114
  hot: true,
121
115
  open: true,
122
- openPage: "",
123
116
  port: DEFAULT_WEBPACK_PORT
117
+ },
118
+ resolve: {
119
+ extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],// other stuff
120
+ fallback: {
121
+ "fs": false,
122
+ "path": require.resolve("path-browserify")
123
+ }
124
124
  }
125
125
 
126
126
  };
@@ -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};