@jdultra/threedtiles 3.3.1 → 3.3.2

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
@@ -1,6 +1,8 @@
1
- # threedtiles
1
+ # T H R E E D T I L E S : http://www.jdultra.com/
2
2
 
3
- 3DTiles viewer for three.js
3
+
4
+
5
+ The fastest 3DTiles viewer for three.js
4
6
 
5
7
  Photogrametry : https://ebeaufay.github.io/ThreedTilesViewer.github.io/
6
8
 
@@ -26,7 +28,7 @@ It's up to the user to call updates on the tileset. You might call them whenever
26
28
  ```
27
29
  setInterval(function () {
28
30
  ogc3DTile.update(camera);
29
- }, 200);
31
+ }, 20);
30
32
  ```
31
33
 
32
34
  Currently, the library is limmited to B3DM files.
@@ -71,6 +73,21 @@ const ogc3DTile = new OGC3DTile({
71
73
  ```
72
74
 
73
75
  ### Callback
76
+
77
+ #### onLoadCallback
78
+ Add a callback that is called once when the first tile is loaded and geometry is available.
79
+ This can be useful to position the tileset at a specific location when it is not centered on origin for example.
80
+
81
+ ```
82
+ const ogc3DTile = new OGC3DTile({
83
+ url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
84
+ onLoadCallback: tilese => {
85
+ console.log(tileset.boundingVolume);
86
+ }
87
+ });
88
+ ```
89
+
90
+ #### Mesh callback
74
91
  Add a callback on loaded tiles in order to set a material or do some logic on the meshes.
75
92
 
76
93
  ```
@@ -157,7 +174,7 @@ occlusionCullingService.setSide(THREE.DoubleSide);
157
174
  ```
158
175
 
159
176
 
160
- ### static tilesets (Performance tip)
177
+ ### static tilesets and other performance tips
161
178
  When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
162
179
  This will skip recalculating the transformation matrix of every tile each frame and give a few extra frames per second.
163
180
 
@@ -168,12 +185,22 @@ const ogc3DTile = new OGC3DTile({
168
185
  });
169
186
  ```
170
187
 
171
- # Displaying meshes on a globe
172
- I'm working on this project in parallel https://github.com/ebeaufay/UltraGlobe which allows displaying a globe with multi resolution imagery, elevation and 3DTiles.
188
+ Either way, it's advised to set the autoUpdate property of the scene to false and update the matrices manually whenever you move things around.
189
+
190
+ ```
191
+ scene.autoUpdate = false;
192
+ ```
193
+
194
+ # Projects that use this library
195
+ https://github.com/ebeaufay/UltraGlobe allows displaying a globe with multi resolution imagery, elevation and 3DTiles.
196
+
197
+ Don't hesitate to tell me if you have a project that stems from this code. I'd love to link to it here and I'm always open to implementing extra features.
198
+ Contact: emeric.beaufays@jdultra.com
199
+
173
200
 
174
201
  # Mesh to 3DTiles Converter
175
202
 
176
203
  I also have code to convert meshes to 3DTiles with no limit to the size of the dataset relative to faces or textures.
177
204
  It works for all types of meshes: photogrametry, BIM, colored or textured meshes with a single texture atlas or many individual textures.
178
205
  I'm keeping the code private for now but feel free to contact me about it.
179
- Contact: emericbeaufays@gmail.com
206
+ Contact: emeric.beaufays@jdultra.com
package/index.html CHANGED
@@ -48,12 +48,9 @@
48
48
  <body>
49
49
  <div id="screen"></div>
50
50
  <div style="position: absolute; top: 1%; z-index: 100; right:1%; ">
51
- <input type="range" min="0.1" max="2" value="1.0", step="0.001" class="slider" id="lodMultiplier" >
51
+ <input type="range" min="0.0" max="0.3" value="0.1", step="0.001" class="slider" id="lodMultiplier" >
52
52
  <p style="color: #0439aa;">LOD multiplier: <span id="multiplierValue"></span></p>
53
53
  </div>
54
- <div style="position: absolute; bottom: 1%; z-index: 100;">
55
- <a href="https://skfb.ly/6UoNJ">ORIGINAL MODEL</a>
56
- </div>
57
54
  </body>
58
55
 
59
56
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "3.3.1",
3
+ "version": "3.3.2",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -26,13 +26,13 @@ animate();
26
26
  function initScene() {
27
27
  const scene = new THREE.Scene();
28
28
  scene.matrixAutoUpdate = false;
29
- scene.background = new THREE.Color(0xaaffcc);
30
- scene.add(new THREE.AmbientLight(0xFFFFFF, 0.8));
31
- const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.2 );
29
+ scene.background = new THREE.Color(0xaaccee);
30
+ scene.add(new THREE.AmbientLight(0xFFFFFF, 0.6));
31
+ const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.4 );
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
+ scene.autoUpdate = true;
36
36
  return scene;
37
37
  }
38
38
 
@@ -77,8 +77,8 @@ function initStats(dom) {
77
77
 
78
78
 
79
79
  function initCamera() {
80
- const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 0.1, 1000);
81
- camera.position.set(-10, 5, 20);
80
+ const camera = new THREE.PerspectiveCamera(40, window.offsetWidth / window.offsetHeight, 0.1, 6000);
81
+ camera.position.set(1400,100,1400);
82
82
  camera.matrixAutoUpdate = true;
83
83
  return camera;
84
84
  }
@@ -91,32 +91,25 @@ function initTileset(scene) {
91
91
  mesh.material.side = THREE.DoubleSide;
92
92
  }, 1000)
93
93
  const ogc3DTile = new OGC3DTile({
94
- url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
95
- //url: "http://localhost:8080/tileset.json",
96
- geometricErrorMultiplier: 0.5,
94
+ //url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
95
+ url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
96
+ geometricErrorMultiplier: 0.02,
97
97
  loadOutsideView: false,
98
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
99
+ //occlusionCullingService: occlusionCullingService,
100
+ static: false,
101
+
111
102
  });
103
+
112
104
 
113
105
 
114
106
 
115
107
  //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
116
- //ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
108
+ ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
117
109
  //// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
118
110
 
119
111
 
112
+
120
113
  //// 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
121
114
 
122
115
 
@@ -124,24 +117,32 @@ function initTileset(scene) {
124
117
  var interval;
125
118
  document.addEventListener('keyup', (e) => {
126
119
  console.log(camera.position)
127
- if (!e.key || e.key !== "p") return;
128
- if (!!interval) {
129
- clearInterval(interval);
130
- interval = null;
131
- } else {
132
- startInterval();
120
+ if (!!e.key && e.key !== "p"){
121
+
122
+ if (!!interval) {
123
+ clearInterval(interval);
124
+ interval = null;
125
+ } else {
126
+ startInterval();
127
+ }
128
+ }
129
+ if (!!e.key && e.key !== "l"){
130
+
131
+ console.log("new THREE.Vector3(" + camera.position.x + "," + camera.position.y + "," + camera.position.z + ")");
132
+ console.log("new THREE.Quaternion(" + camera.quaternion.x + "," + camera.quaternion.y + "," + camera.quaternion.z + "," + camera.quaternion.w + ")");
133
+
133
134
  }
135
+
134
136
  });
135
137
  function startInterval() {
136
138
  interval = setIntervalAsync(function () {
137
139
  ogc3DTile.update(camera);
138
- ogc3DTile2.update(camera);
140
+
139
141
  }, 20);
140
142
  }
141
143
  startInterval();
142
144
 
143
145
  scene.add(ogc3DTile)
144
- scene.add(ogc3DTile2)
145
146
  return ogc3DTile;
146
147
  }
147
148
 
@@ -170,12 +171,11 @@ function initController(camera, dom) {
170
171
  function animate() {
171
172
  requestAnimationFrame(animate);
172
173
  renderer.render(scene, camera);
173
- occlusionCullingService.update(scene, renderer, camera)
174
+ //occlusionCullingService.update(scene, renderer, camera)
174
175
  stats.update();
175
-
176
-
177
176
  }
178
177
 
179
178
 
180
179
 
181
180
 
181
+
@@ -76,14 +76,14 @@ class OGC3DTile extends THREE.Object3D {
76
76
  this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
77
77
  this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
78
78
 
79
+ this.abortController = new AbortController();
79
80
  this.layers.disable(0);
80
81
 
81
82
  if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
82
83
  self.setup(properties);
83
84
  if (properties.onLoadCallback) properties.onLoadCallback(self);
84
85
  } else if (properties.url) { // If only the url to the tileset.json is provided
85
- self.controller = new AbortController();
86
- fetch(properties.url, { signal: self.controller.signal }).then(result => {
86
+ fetch(properties.url, { signal: self.abortController.signal }).then(result => {
87
87
  if (!result.ok) {
88
88
  throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
89
89
  }
@@ -176,7 +176,7 @@ class OGC3DTile extends THREE.Object3D {
176
176
  if (!!url) {
177
177
  if (url.includes(".b3dm")) {
178
178
  self.contentURL = url;
179
- self.tileLoader.get(this.uuid, url, mesh => {
179
+ self.tileLoader.get(self.abortController,this.uuid, url, mesh => {
180
180
  if (!!self.deleted) return;
181
181
  mesh.traverse((o) => {
182
182
  if (o.isMesh) {
@@ -204,7 +204,7 @@ class OGC3DTile extends THREE.Object3D {
204
204
  return self.calculateDistanceToCamera(self.cameraOnLoad);
205
205
  }, () => self.getSiblings(), self.level);
206
206
  } else if (url.includes(".json")) {
207
- self.tileLoader.get(this.uuid, url, json => {
207
+ self.tileLoader.get(self.abortController,this.uuid, url, json => {
208
208
  if (!!self.deleted) return;
209
209
  if (!self.json.children) self.json.children = [];
210
210
  json.rootPath = path.dirname(url);
@@ -228,8 +228,8 @@ class OGC3DTile extends THREE.Object3D {
228
228
  if (!!element.contentURL) {
229
229
  self.tileLoader.invalidate(element.contentURL, element.uuid);
230
230
  }
231
- if (!!element.controller) { // abort tile request
232
- element.controller.abort();
231
+ if (!!element.abortController) { // abort tile request
232
+ element.abortController.abort();
233
233
  }
234
234
 
235
235
  });
@@ -76,11 +76,7 @@ class TileLoader {
76
76
  const register = data[1];
77
77
  const key = data[2];
78
78
  const mesh = cache.get(key);
79
- if(mesh instanceof THREE.InstancedMesh){
80
- console.log("instanced");
81
- }else{
82
- console.log(" not instanced");
83
- }
79
+
84
80
  if (!!mesh && !!register[key]) {
85
81
  Object.keys(register[key]).forEach(tile => {
86
82
  const callback = register[key][tile];
@@ -164,10 +160,16 @@ class TileLoader {
164
160
  }
165
161
 
166
162
 
167
- get(tileIdentifier, path, callback, distanceFunction, getSiblings, level) {
163
+ get(abortController, tileIdentifier, path, callback, distanceFunction, getSiblings, level) {
168
164
  const self = this;
169
165
  const key = simplifyPath(path);
170
166
 
167
+ const realAbortController = new AbortController();
168
+ abortController.signal.addEventListener("abort", ()=>{
169
+ if(!self.register[key] || Object.keys(self.register[key]).length == 0){
170
+ realAbortController.abort();
171
+ }
172
+ })
171
173
 
172
174
  if (!path.includes(".b3dm") && !path.includes(".json")) {
173
175
  console.error("the 3DTiles cache can only be used to load B3DM and json data");
@@ -189,40 +191,47 @@ class TileLoader {
189
191
  if (path.includes(".b3dm")) {
190
192
  downloadFunction = () => {
191
193
  concurentDownloads++;
192
- fetch(path).then(result => {
194
+ fetch(path, {signal: realAbortController.signal}).then(result => {
193
195
  concurentDownloads--;
194
196
  if (!result.ok) {
195
197
  console.error("could not load tile with path : " + path)
196
198
  throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
197
199
  }
198
- result.arrayBuffer().then(buffer => B3DMDecoder.parseB3DM(buffer, self.meshCallback)).then(mesh => {
199
- self.cache.put(key, mesh);
200
+ return result.arrayBuffer();
201
+
202
+ })
203
+ .then(resultArrayBuffer=>{
204
+ return B3DMDecoder.parseB3DM(resultArrayBuffer, self.meshCallback);
205
+ })
206
+ .then(mesh=>{
207
+ self.cache.put(key, mesh);
200
208
  self.checkSize();
201
209
  this.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
202
- });
203
-
204
- });
210
+ })
211
+ .catch(()=>{});;
205
212
  }
206
213
  } else if (path.includes(".json")) {
207
214
  downloadFunction = () => {
208
215
  concurentDownloads++;
209
- fetch(path).then(result => {
216
+ fetch(path, {signal: realAbortController.signal}).then(result => {
210
217
  concurentDownloads--;
211
218
  if (!result.ok) {
212
219
  console.error("could not load tile with path : " + path)
213
220
  throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
214
221
  }
215
- result.json().then(json => {
216
- self.cache.put(key, json);
217
- self.checkSize();
218
- this.meshReceived(self.cache, self.register, key);
219
- });
220
- });
222
+ return result.json();
223
+
224
+ }).then(json => {
225
+ self.cache.put(key, json);
226
+ self.checkSize();
227
+ this.meshReceived(self.cache, self.register, key);
228
+ })
229
+ .catch(e=>console.error("tile download aborted"));
221
230
  }
222
231
  }
223
232
  this.scheduleDownload({
224
233
  "shouldDoDownload": () => {
225
- return !!self.register[key] && Object.keys(self.register[key]).length > 0;
234
+ return !abortController.signal.aborted && !!self.register[key] && Object.keys(self.register[key]).length > 0;
226
235
  },
227
236
  "doDownload": downloadFunction,
228
237
  "distanceFunction": distanceFunction,