@jdultra/threedtiles 3.3.0 → 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.
@@ -39,7 +41,7 @@ Currently, the library is limmited to B3DM files.
39
41
  - Optionally load low detail tiles outside of view frustum for correct shadows and basic mesh present when the camera moves quickly.
40
42
  - Share a cache between tileset instances
41
43
  - Optimal tile load order
42
- - Occlusion culling (demo)
44
+ - Occlusion culling
43
45
 
44
46
  ### geometric Error Multiplier
45
47
  The geometric error multiplier allows you to multiply the geometric error by a factor.
@@ -60,8 +62,8 @@ A value of 1.0 is the default.
60
62
 
61
63
 
62
64
  ### 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.
65
+ 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.
66
+ 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
67
 
66
68
  ```
67
69
  const ogc3DTile = new OGC3DTile({
@@ -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
  ```
@@ -78,6 +95,7 @@ const ogc3DTile = new OGC3DTile({
78
95
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
79
96
  meshCallback: mesh => {
80
97
  mesh.material.wireframe = true;
98
+ mesh.material.side = THREE.DoubleSide;
81
99
  }
82
100
  });
83
101
  ```
@@ -116,12 +134,14 @@ const ogc3DTile = new OGC3DTile({
116
134
 
117
135
  ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
118
136
  ogc3DTile.rotateOnAxis(new THREE.Vector3(1,0,0), -Math.PI*0.5);
119
- ...
137
+ ```
120
138
 
121
139
 
122
140
  ### Occlusion culling
123
141
  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.
142
+
143
+ A word of warning: activating occlusion culling causes an extra render-pass and as such, has an impact on frame-rate.
144
+ 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
145
 
126
146
 
127
147
  First, instantiate an OcclusionCullingService:
@@ -146,7 +166,7 @@ function animate() {
146
166
  }
147
167
  ```
148
168
 
149
- Finally, you may want to set what side of the faces are drawn in the occlusion pass. By default, THREE.FrontSide is used:
169
+ 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
170
 
151
171
  ```
152
172
  const occlusionCullingService = new OcclusionCullingService();
@@ -154,7 +174,7 @@ occlusionCullingService.setSide(THREE.DoubleSide);
154
174
  ```
155
175
 
156
176
 
157
- ### static tilesets (Performance tip)
177
+ ### static tilesets and other performance tips
158
178
  When you know your tileset will be static, you can specify it in the OGC3DTile object constructor parameter.
159
179
  This will skip recalculating the transformation matrix of every tile each frame and give a few extra frames per second.
160
180
 
@@ -165,12 +185,22 @@ const ogc3DTile = new OGC3DTile({
165
185
  });
166
186
  ```
167
187
 
168
- # Displaying meshes on a globe
169
- 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
+
170
200
 
171
201
  # Mesh to 3DTiles Converter
172
202
 
173
203
  I also have code to convert meshes to 3DTiles with no limit to the size of the dataset relative to faces or textures.
174
204
  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.
176
- Contact: emericbeaufays@gmail.com
205
+ I'm keeping the code private for now but feel free to contact me about it.
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.0",
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,12 +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.2));
31
- const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
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 = true;
35
36
  return scene;
36
37
  }
37
38
 
@@ -76,32 +77,37 @@ function initStats(dom) {
76
77
 
77
78
 
78
79
  function initCamera() {
79
- const camera = new THREE.PerspectiveCamera(70, window.offsetWidth / window.offsetHeight, 0.1, 1000);
80
- 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);
81
82
  camera.matrixAutoUpdate = true;
82
83
  return camera;
83
84
  }
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
- url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tiledWithSkirts/tileset.json",
89
- //url: "http://localhost:8081/tileset.json",
90
- 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,
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
+
98
102
  });
103
+
99
104
 
100
105
 
101
106
 
102
107
  //// 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
- //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
109
+ //// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
110
+
105
111
 
106
112
 
107
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
@@ -111,17 +117,27 @@ function initTileset(scene) {
111
117
  var interval;
112
118
  document.addEventListener('keyup', (e) => {
113
119
  console.log(camera.position)
114
- if (!e.key || e.key !== "p") return;
115
- if (!!interval) {
116
- clearInterval(interval);
117
- interval = null;
118
- } else {
119
- 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
+
120
134
  }
135
+
121
136
  });
122
137
  function startInterval() {
123
138
  interval = setIntervalAsync(function () {
124
139
  ogc3DTile.update(camera);
140
+
125
141
  }, 20);
126
142
  }
127
143
  startInterval();
@@ -155,12 +171,11 @@ function initController(camera, dom) {
155
171
  function animate() {
156
172
  requestAnimationFrame(animate);
157
173
  renderer.render(scene, camera);
158
- occlusionCullingService.update(scene, renderer, camera)
174
+ //occlusionCullingService.update(scene, renderer, camera)
159
175
  stats.update();
160
-
161
-
162
176
  }
163
177
 
164
178
 
165
179
 
166
180
 
181
+
@@ -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;
@@ -77,14 +76,14 @@ class OGC3DTile extends THREE.Object3D {
77
76
  this.hasMeshContent = false; // true when the provided json has a content field pointing to a B3DM file
78
77
  this.hasUnloadedJSONContent = false; // true when the provided json has a content field pointing to a JSON file that is not yet loaded
79
78
 
79
+ this.abortController = new AbortController();
80
80
  this.layers.disable(0);
81
81
 
82
82
  if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
83
83
  self.setup(properties);
84
84
  if (properties.onLoadCallback) properties.onLoadCallback(self);
85
85
  } else if (properties.url) { // If only the url to the tileset.json is provided
86
- self.controller = new AbortController();
87
- fetch(properties.url, { signal: self.controller.signal }).then(result => {
86
+ fetch(properties.url, { signal: self.abortController.signal }).then(result => {
88
87
  if (!result.ok) {
89
88
  throw new Error(`couldn't load "${properties.url}". Request failed with status ${result.status} : ${result.statusText}`);
90
89
  }
@@ -177,10 +176,11 @@ class OGC3DTile extends THREE.Object3D {
177
176
  if (!!url) {
178
177
  if (url.includes(".b3dm")) {
179
178
  self.contentURL = url;
180
- self.tileLoader.get(this.uuid, url, mesh => {
179
+ self.tileLoader.get(self.abortController,this.uuid, url, mesh => {
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,18 +189,22 @@ 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);
201
205
  }, () => self.getSiblings(), self.level);
202
206
  } else if (url.includes(".json")) {
203
- self.tileLoader.get(this.uuid, url, json => {
207
+ self.tileLoader.get(self.abortController,this.uuid, url, json => {
204
208
  if (!!self.deleted) return;
205
209
  if (!self.json.children) self.json.children = [];
206
210
  json.rootPath = path.dirname(url);
@@ -224,8 +228,8 @@ class OGC3DTile extends THREE.Object3D {
224
228
  if (!!element.contentURL) {
225
229
  self.tileLoader.invalidate(element.contentURL, element.uuid);
226
230
  }
227
- if (!!element.controller) { // abort tile request
228
- element.controller.abort();
231
+ if (!!element.abortController) { // abort tile request
232
+ element.abortController.abort();
229
233
  }
230
234
 
231
235
  });
@@ -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) {