@jdultra/threedtiles 5.1.3 → 6.0.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.
package/README.md CHANGED
@@ -47,6 +47,11 @@ Currently, the library is limmited to B3DM files.
47
47
  - Occlusion culling
48
48
  - Instanced tilesets
49
49
  - Center tileset and re-orient geolocated data
50
+ - gltf/glb tiles (OGC3DTiles 1.1)
51
+ - point clouds (only through gltf/glb tiles)
52
+
53
+ Support for OGC3DTiles 1.1 is underway. gltf/glb tiles are already supported but not yet implicit tiling.
54
+ There is no plan to support .pnts or .i3dm tiles as points and instanced meshes are already supported through gltf tiles.
50
55
 
51
56
  ### geometric Error Multiplier
52
57
  The geometric error multiplier allows you to multiply the geometric error by a factor.
@@ -109,6 +114,20 @@ const ogc3DTile = new OGC3DTile({
109
114
  }
110
115
  });
111
116
  ```
117
+
118
+ #### Points callback
119
+ Add a callback on loaded point tiles in order to set a material or do some logic on the points.
120
+
121
+ ```
122
+ const ogc3DTile = new OGC3DTile({
123
+ url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
124
+ renderer: renderer,
125
+ pointsCallback: points => {
126
+ points.material.size = 0.1;
127
+ points.material.sizeAttenuation = true;
128
+ }
129
+ });
130
+ ```
112
131
  If using a shared cache between tilesets, check out the next section.
113
132
 
114
133
  ### Cache
@@ -124,12 +143,15 @@ import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
124
143
  const ogc3DTile = new OGC3DTile({
125
144
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
126
145
  renderer: renderer,
127
- tileLoader: new TileLoader(mesh => {
146
+ tileLoader: new TileLoader(2000, mesh => {
128
147
  //// Insert code to be called on every newly decoded mesh e.g.:
129
148
  mesh.material.wireframe = false;
130
149
  mesh.material.side = THREE.DoubleSide;
131
150
  },
132
- 2000
151
+ points=>{
152
+ points.material.size = 0.1;
153
+ points.material.sizeAttenuation = true;
154
+ }
133
155
  ),
134
156
  meshCallback: mesh => { mesh.material.wireframe = true;} // This callback will not be used as the callback provided to the TileLoader takes priority
135
157
  });
@@ -198,13 +220,15 @@ higher performance when displaying the same Tileset many times.
198
220
 
199
221
  ```
200
222
  // First create the InstancedTileLoader that will manage caching
201
- const instancedTileLoader = new InstancedTileLoader(scene, mesh => {
202
- //// Insert code to be called on every newly decoded mesh e.g.:
203
- mesh.material.wireframe = false;
204
- mesh.material.side = THREE.DoubleSide;
205
- },
206
- 1000, // cache size as in the number of tiles cached in memory
207
- 100, // max number of tilesets from the same source
223
+ const instancedTileLoader = new InstancedTileLoader(
224
+ scene,
225
+ 100, // cache size as in the number of tiles cached in memory
226
+ 1, // max number of tilesets from the same source
227
+ mesh => {
228
+ //// Insert code to be called on every newly decoded mesh e.g.:
229
+ mesh.material.wireframe = false;
230
+ mesh.material.side = THREE.DoubleSide;
231
+ }
208
232
  );
209
233
 
210
234
  // then create some tilesets
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "5.1.3",
3
+ "version": "6.0.0",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
@@ -5,9 +5,9 @@ import {FeatureTable, BatchTable} from './FeatureTable';
5
5
 
6
6
  const gltfLoader = new GLTFLoader();
7
7
  const dracoLoader = new DRACOLoader();
8
- const tempMatrix = new THREE.Matrix4();
9
8
  dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
10
9
  gltfLoader.setDRACOLoader(dracoLoader);
10
+ const tempMatrix = new THREE.Matrix4();
11
11
  const zUpToYUpMatrix = new THREE.Matrix4();
12
12
  zUpToYUpMatrix.set(1,0,0,0,
13
13
  0,0,-1,0,
@@ -16,7 +16,7 @@ zUpToYUpMatrix.set(1,0,0,0,
16
16
 
17
17
  //const legacyGLTFLoader = new LegacyGLTFLoader();
18
18
 
19
- function parseB3DM(arrayBuffer, meshCallback, zUpToYUp) {
19
+ function parseB3DM(arrayBuffer, meshCallback, geometricError, zUpToYUp) {
20
20
  const dataView = new DataView(arrayBuffer);
21
21
 
22
22
  const magic =
@@ -75,6 +75,7 @@ function parseB3DM(arrayBuffer, meshCallback, zUpToYUp) {
75
75
  model.scene.traverse((o) => {
76
76
 
77
77
  if (o.isMesh) {
78
+ o.geometricError = geometricError
78
79
  if(zUpToYUp){
79
80
  o.applyMatrix4(zUpToYUpMatrix);
80
81
  }
@@ -96,6 +97,7 @@ const B3DMDecoder = {
96
97
  parseB3DMInstanced: (arrayBuffer, meshCallback, maxCount, zUpToYUp) => { // expects GLTF with one node level
97
98
 
98
99
  return parseB3DM(arrayBuffer, meshCallback, zUpToYUp).then(mesh => {
100
+ // todo several meshes in a single gltf
99
101
  let instancedMesh;
100
102
  mesh.updateWorldMatrix(false, true)
101
103
  mesh.traverse(child => {
package/src/index.js CHANGED
@@ -23,7 +23,7 @@ const domContainer = initDomContainer("screen");
23
23
  const camera = initCamera(domContainer.offsetWidth, domContainer.offsetHeight);
24
24
  const stats = initStats(domContainer);
25
25
  const renderer = initRenderer(camera, domContainer);
26
- const ogc3DTiles = initTileset(scene, 4.0);
26
+ const ogc3DTiles = initTileset(scene, 1.0);
27
27
 
28
28
  //const instancedTileLoader = createInstancedTileLoader(scene);
29
29
  //initInstancedTilesets(instancedTileLoader);
@@ -49,9 +49,9 @@ function initScene() {
49
49
  const scene = new THREE.Scene();
50
50
  scene.matrixAutoUpdate = false;
51
51
  //scene.matrixWorldAutoUpdate = false;
52
- scene.background = new THREE.Color(0xffffff);
52
+ scene.background = new THREE.Color(0x888888);
53
53
  scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
54
-
54
+
55
55
  /* const light = new THREE.PointLight(0xbbbbff, 2, 5000);
56
56
  const sphere = new THREE.SphereGeometry(2, 16, 8);
57
57
  light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
@@ -112,8 +112,8 @@ function initStats(dom) {
112
112
 
113
113
 
114
114
  function initCamera(width, height) {
115
- const camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
116
- camera.position.set(10000,0,0);
115
+ const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
116
+ camera.position.set(-0.5,-0.7,10);
117
117
  camera.lookAt(0,0,0);
118
118
 
119
119
  camera.matrixAutoUpdate = true;
@@ -122,37 +122,32 @@ function initCamera(width, height) {
122
122
 
123
123
  function initTileset(scene, gem) {
124
124
 
125
- const tileLoader = new TileLoader(mesh => {
125
+ const tileLoader = new TileLoader(100, mesh => {
126
126
  //// Insert code to be called on every newly decoded mesh e.g.:
127
127
  mesh.material.wireframe = false;
128
128
  mesh.material.side = THREE.DoubleSide;
129
129
  mesh.material.metalness = 0.0
130
- }, 100);
130
+ }, points=>{
131
+ points.material.size = Math.min(1.0,0.5*Math.sqrt(points.geometricError));
132
+ points.material.sizeAttenuation = true;
133
+ });
131
134
 
132
135
  const ogc3DTile = new OGC3DTile({
133
- //url: "https://sampledata.luciad.com/data/ogc3dtiles/LucerneAirborneMesh/tileset.json",
134
- url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
135
136
  //url: "https://storage.googleapis.com/ogc-3d-tiles/baltimore/tileset.json",
137
+ url: "http://localhost:8080/tileset.json",
136
138
  //url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
137
- geometricErrorMultiplier: gem,
139
+ geometricErrorMultiplier: 1,
138
140
  loadOutsideView: false,
139
141
  tileLoader: tileLoader,
140
142
  //occlusionCullingService: occlusionCullingService,
141
143
  static: false,
142
144
  centerModel:true,
143
145
  renderer: renderer,
144
- /* onLoadCallback: (tile)=>{
145
- if (!!tile.json.boundingVolume.region) {
146
- const halfHeight = (tile.json.boundingVolume.region[5] - tile.json.boundingVolume.region[4]) * 0.5;
147
- ogc3DTile.translateOnAxis(new THREE.Vector3(0, 1, 0), halfHeight);
148
- //ogc3DTile.updateWorldMatrix(true, true);
149
- }
150
- } */
151
-
146
+
152
147
  });
153
148
  setIntervalAsync(function () {
154
149
  ogc3DTile.update(camera);
155
- }, 20);
150
+ }, 10);
156
151
 
157
152
 
158
153
 
@@ -168,12 +163,13 @@ function initTileset(scene, gem) {
168
163
 
169
164
 
170
165
  function createInstancedTileLoader(scene) {
171
- return new InstancedTileLoader(scene, mesh => {
172
- //// Insert code to be called on every newly decoded mesh e.g.:
173
- mesh.material.wireframe = false;
174
- mesh.material.side = THREE.DoubleSide;
175
- mesh.material.metalness = 0.0;
176
- }, 0, 1);
166
+ return new InstancedTileLoader(scene, 100, 1,
167
+ mesh => {
168
+ //// Insert code to be called on every newly decoded mesh e.g.:
169
+ mesh.material.wireframe = false;
170
+ mesh.material.side = THREE.DoubleSide;
171
+ mesh.material.metalness = 0.0;
172
+ });
177
173
  }
178
174
  function initInstancedTilesets(instancedTileLoader) {
179
175
 
@@ -185,11 +181,9 @@ function initInstancedTilesets(instancedTileLoader) {
185
181
 
186
182
 
187
183
  const tileset = new InstancedOGC3DTile({
188
- //url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
189
- url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
190
- //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ab13lasdc9i/tileset.json",
191
- //url: "http://localhost:8081/tileset.json",
192
- geometricErrorMultiplier: 1.0,
184
+ url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
185
+ //url: "http://localhost:8080/tileset.json",
186
+ geometricErrorMultiplier: 0.1,
193
187
  loadOutsideView: true,
194
188
  tileLoader: instancedTileLoader,
195
189
  static: false,
@@ -241,7 +235,7 @@ function initController(camera, dom) {
241
235
 
242
236
 
243
237
  controller.minDistance = 0.1;
244
- controller.maxDistance = 100000;
238
+ controller.maxDistance = 1000;
245
239
  controller.update();
246
240
  return controller;
247
241
  }
@@ -28,6 +28,7 @@ class OGC3DTile extends THREE.Object3D {
28
28
  * loadOutsideView: Boolean,
29
29
  * tileLoader : TileLoader,
30
30
  * meshCallback: function,
31
+ * pointsCallback: function,
31
32
  * cameraOnLoad: camera,
32
33
  * parentTile: OGC3DTile,
33
34
  * onLoadCallback: function,
@@ -45,11 +46,16 @@ class OGC3DTile extends THREE.Object3D {
45
46
  if (!!properties.tileLoader) {
46
47
  this.tileLoader = properties.tileLoader;
47
48
  } else {
48
- this.tileLoader = new TileLoader(!properties.meshCallback ?
49
- mesh => {
49
+ this.tileLoader = new TileLoader(
50
+ 200,
51
+ !properties.meshCallback ? mesh => {
50
52
  mesh.material.wireframe = false;
51
53
  mesh.material.side = THREE.DoubleSide;
52
- } : properties.meshCallback);
54
+ } : properties.meshCallback,
55
+ !properties.pointsCallback ? points => {
56
+ points.material.size = 0.1;
57
+ points.material.sizeAttenuation = true;
58
+ } : properties.pointsCallback);
53
59
  }
54
60
  // set properties general to the entire tileset
55
61
  this.geometricErrorMultiplier = !!properties.geometricErrorMultiplier ? properties.geometricErrorMultiplier : 1.0;
@@ -214,7 +220,7 @@ class OGC3DTile extends THREE.Object3D {
214
220
  }
215
221
 
216
222
  if (!!url) {
217
- if (url.includes(".b3dm")) {
223
+ if (url.includes(".b3dm") || url.includes(".glb") || url.includes(".gltf")) {
218
224
  self.contentURL = url;
219
225
  if(!!self.json.boundingVolume.region){
220
226
  //self.applyMatrix4(zUpToYUp);
@@ -248,7 +254,8 @@ class OGC3DTile extends THREE.Object3D {
248
254
  return self.calculateDistanceToCamera(self.cameraOnLoad);
249
255
  }, () => self.getSiblings(),
250
256
  self.level,
251
- !!this.json.boundingVolume.region
257
+ !!self.json.boundingVolume.region,
258
+ self.geometricError
252
259
  );
253
260
  } else if (url.includes(".json")) {
254
261
  self.tileLoader.get(self.abortController, this.uuid, url, json => {
@@ -3,12 +3,24 @@ import { B3DMDecoder } from "../decoder/B3DMDecoder";
3
3
  import { setIntervalAsync } from 'set-interval-async/dynamic';
4
4
  import { initial } from 'lodash';
5
5
  import * as THREE from 'three';
6
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
7
+ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
6
8
 
7
9
  let concurentDownloads = 0;
10
+ const zUpToYUpMatrix = new THREE.Matrix4();
11
+ zUpToYUpMatrix.set(1, 0, 0, 0,
12
+ 0, 0, -1, 0,
13
+ 0, 1, 0, 0,
14
+ 0, 0, 0, 1);
15
+ const gltfLoader = new GLTFLoader();
16
+ const dracoLoader = new DRACOLoader();
17
+ dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
18
+ gltfLoader.setDRACOLoader(dracoLoader);
8
19
 
9
20
  class TileLoader {
10
- constructor(meshCallback, maxCachedItems) {
21
+ constructor(maxCachedItems, meshCallback, pointsCallback) {
11
22
  this.meshCallback = meshCallback;
23
+ this.pointsCallback = pointsCallback;
12
24
  this.cache = new LinkedHashMap();
13
25
  this.maxCachedItems = !!maxCachedItems ? maxCachedItems : 100;
14
26
  this.register = {};
@@ -21,8 +33,8 @@ class TileLoader {
21
33
  this.init();
22
34
  }
23
35
 
24
- init(){
25
-
36
+ init() {
37
+
26
38
  const self = this;
27
39
  setIntervalAsync(() => {
28
40
  self.download();
@@ -33,7 +45,7 @@ class TileLoader {
33
45
  do {
34
46
  loaded = self.loadBatch();
35
47
  } while (loaded > 0 && (Date.now() - start) <= 0)
36
-
48
+
37
49
  }, 10);
38
50
  }
39
51
 
@@ -67,7 +79,7 @@ class TileLoader {
67
79
  const register = data[1];
68
80
  const key = data[2];
69
81
  const mesh = cache.get(key);
70
-
82
+
71
83
  if (!!mesh && !!register[key]) {
72
84
  Object.keys(register[key]).forEach(tile => {
73
85
  const callback = register[key][tile];
@@ -79,7 +91,7 @@ class TileLoader {
79
91
  }
80
92
  return 1;
81
93
  }
82
-
94
+
83
95
  getNextDownloads() {
84
96
  let smallestDistance = Number.MAX_VALUE;
85
97
  let closest = -1;
@@ -94,7 +106,7 @@ class TileLoader {
94
106
  }
95
107
  if (this.nextDownloads.length > 0) return;
96
108
  for (let i = this.downloads.length - 1; i >= 0; i--) {
97
- const dist = this.downloads[i].distanceFunction()*this.downloads[i].level;
109
+ const dist = this.downloads[i].distanceFunction() * this.downloads[i].level;
98
110
  if (dist < smallestDistance) {
99
111
  smallestDistance = dist;
100
112
  closest = i;
@@ -111,12 +123,12 @@ class TileLoader {
111
123
  }
112
124
  }
113
125
  }
114
-
126
+
115
127
  getNextReady() {
116
128
  let smallestDistance = Number.MAX_VALUE;
117
129
  let closest = -1;
118
130
  for (let i = this.ready.length - 1; i >= 0; i--) {
119
-
131
+
120
132
  if (!this.ready[i][3]) {// if no distance function, must be a json, give absolute priority!
121
133
  this.nextReady.push(this.ready.splice(i, 1)[0]);
122
134
  }
@@ -142,19 +154,19 @@ class TileLoader {
142
154
  }
143
155
 
144
156
 
145
- get(abortController, tileIdentifier, path, callback, distanceFunction, getSiblings, level, zUpToYUp) {
157
+ get(abortController, tileIdentifier, path, callback, distanceFunction, getSiblings, level, zUpToYUp, geometricError) {
146
158
  const self = this;
147
159
  const key = simplifyPath(path);
148
160
 
149
161
  const realAbortController = new AbortController();
150
- abortController.signal.addEventListener("abort", ()=>{
151
- if(!self.register[key] || Object.keys(self.register[key]).length == 0){
162
+ abortController.signal.addEventListener("abort", () => {
163
+ if (!self.register[key] || Object.keys(self.register[key]).length == 0) {
152
164
  realAbortController.abort();
153
165
  }
154
166
  })
155
167
 
156
- if (!path.includes(".b3dm") && !path.includes(".json")) {
157
- console.error("the 3DTiles cache can only be used to load B3DM and json data");
168
+ if (!path.includes(".b3dm") && !path.includes(".json") && !path.includes(".gltf") && !path.includes(".glb")) {
169
+ console.error("the 3DTiles cache can only be used to load B3DM, gltf and json data");
158
170
  return;
159
171
  }
160
172
  if (!self.register[key]) {
@@ -173,7 +185,7 @@ class TileLoader {
173
185
  if (path.includes(".b3dm")) {
174
186
  downloadFunction = () => {
175
187
  concurentDownloads++;
176
- fetch(path, {signal: realAbortController.signal}).then(result => {
188
+ fetch(path, { signal: realAbortController.signal }).then(result => {
177
189
  concurentDownloads--;
178
190
  if (!result.ok) {
179
191
  console.error("could not load tile with path : " + path)
@@ -182,33 +194,62 @@ class TileLoader {
182
194
  return result.arrayBuffer();
183
195
 
184
196
  })
185
- .then(resultArrayBuffer=>{
186
- return B3DMDecoder.parseB3DM(resultArrayBuffer, self.meshCallback, zUpToYUp);
187
- })
188
- .then(mesh=>{
189
- self.cache.put(key, mesh);
197
+ .then(resultArrayBuffer => {
198
+ return B3DMDecoder.parseB3DM(resultArrayBuffer, self.meshCallback, geometricError, zUpToYUp);
199
+ })
200
+ .then(mesh => {
201
+ self.cache.put(key, mesh);
190
202
  self.checkSize();
191
203
  this.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
192
- })
193
- .catch(()=>{});;
204
+ })
205
+ .catch(() => { });;
206
+ }
207
+ } else if (path.includes(".glb") || path.includes(".gltf")) {
208
+ downloadFunction = () => {
209
+ concurentDownloads++;
210
+ gltfLoader.load(path, gltf => {
211
+ gltf.scene.traverse((o) => {
212
+ o.geometricError = geometricError;
213
+ if (o.isMesh) {
214
+ if (zUpToYUp) {
215
+ o.applyMatrix4(zUpToYUpMatrix);
216
+ }
217
+ if (!!self.meshCallback) {
218
+ self.meshCallback(o);
219
+ }
220
+ }
221
+ if (o.isPoints) {
222
+ if (zUpToYUp) {
223
+ o.applyMatrix4(zUpToYUpMatrix);
224
+ }
225
+ if (!!self.pointsCallback) {
226
+ self.pointsCallback(o);
227
+ }
228
+ }
229
+ });
230
+ self.cache.put(key, gltf.scene);
231
+ self.checkSize();
232
+ self.meshReceived(self.cache, self.register, key, distanceFunction, getSiblings, level, tileIdentifier);
233
+ });
234
+
194
235
  }
195
236
  } else if (path.includes(".json")) {
196
237
  downloadFunction = () => {
197
238
  concurentDownloads++;
198
- fetch(path, {signal: realAbortController.signal}).then(result => {
239
+ fetch(path, { signal: realAbortController.signal }).then(result => {
199
240
  concurentDownloads--;
200
241
  if (!result.ok) {
201
242
  console.error("could not load tile with path : " + path)
202
243
  throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
203
244
  }
204
245
  return result.json();
205
-
246
+
206
247
  }).then(json => {
207
248
  self.cache.put(key, json);
208
249
  self.checkSize();
209
- this.meshReceived(self.cache, self.register, key);
250
+ self.meshReceived(self.cache, self.register, key);
210
251
  })
211
- .catch(e=>console.error("tile download aborted"));
252
+ .catch(e => console.error("tile download aborted"));
212
253
  }
213
254
  }
214
255
  this.scheduleDownload({
@@ -228,7 +269,9 @@ class TileLoader {
228
269
 
229
270
  invalidate(path, tileIdentifier) {
230
271
  const key = simplifyPath(path);
231
- delete this.register[key][tileIdentifier];
272
+ if (!!this.register[key]) {
273
+ delete this.register[key][tileIdentifier];
274
+ }
232
275
  }
233
276
 
234
277
  checkSize() {
@@ -212,14 +212,15 @@ class InstancedTile extends THREE.Object3D {
212
212
  }
213
213
 
214
214
  if (!!url) {
215
- if (url.includes(".b3dm")) {
215
+ if (url.includes(".b3dm")|| url.includes(".glb") || url.includes(".gltf")) {
216
216
  self.contentURL = url;
217
217
 
218
218
  self.tileLoader.get(self.abortController, url, self.uuid, self, !self.cameraOnLoad ? () => 0 : () => {
219
219
  return self.calculateDistanceToCamera(self.cameraOnLoad);
220
220
  }, () => self.getSiblings(),
221
221
  self.level,
222
- !!self.json.boundingVolume.region);
222
+ !!self.json.boundingVolume.region,
223
+ self.geometricError);
223
224
  } else if (url.includes(".json")) {
224
225
  self.tileLoader.get(self.abortController, url, self.uuid, self);
225
226
 
@@ -4,11 +4,23 @@ import { setIntervalAsync } from 'set-interval-async/dynamic';
4
4
  import * as THREE from 'three';
5
5
  import { MeshTile } from './MeshTile';
6
6
  import { JsonTile } from './JsonTile';
7
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
8
+ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
9
+
7
10
 
8
11
  let concurentDownloads = 0;
12
+ const gltfLoader = new GLTFLoader();
13
+ const dracoLoader = new DRACOLoader();
14
+ dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
15
+ gltfLoader.setDRACOLoader(dracoLoader);
16
+ const zUpToYUpMatrix = new THREE.Matrix4();
17
+ zUpToYUpMatrix.set(1, 0, 0, 0,
18
+ 0, 0, -1, 0,
19
+ 0, 1, 0, 0,
20
+ 0, 0, 0, 1);
9
21
 
10
22
  class InstancedTileLoader {
11
- constructor(scene, meshCallback, maxCachedItems, maxInstances) {
23
+ constructor(scene, maxCachedItems, maxInstances, meshCallback) {
12
24
  this.meshCallback = meshCallback;
13
25
  this.maxInstances = maxInstances;
14
26
  this.cache = new LinkedHashMap();
@@ -76,6 +88,43 @@ class InstancedTileLoader {
76
88
 
77
89
  })
78
90
  .catch(e=>console.error(e));
91
+ }if(nextDownload.path.includes(".glb") || (nextDownload.path.includes(".gltf"))){
92
+ gltfLoader.load(nextDownload.path, gltf => {
93
+ gltf.scene.traverse((o) => {
94
+ o.geometricError = nextDownload.geometricError;
95
+ if (o.isMesh) {
96
+ if (nextDownload.zUpToYUp) {
97
+ o.applyMatrix4(zUpToYUpMatrix);
98
+ }
99
+ if (!!self.meshCallback) {
100
+ self.meshCallback(o);
101
+ }
102
+ }
103
+ if (o.isPoints) {
104
+ console.error("instanced point cloud is not supported");
105
+ }
106
+ });
107
+ let instancedMesh;
108
+ gltf.scene.updateWorldMatrix(false, true)
109
+ gltf.scene.traverse(child => {
110
+ //TODO several meshes in a single gltf
111
+ if (child.isMesh) {
112
+ instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, self.maxInstances);
113
+ instancedMesh.baseMatrix = child.matrixWorld;
114
+ }
115
+
116
+ });
117
+ self.ready.unshift(nextDownload);
118
+ if(!instancedMesh){
119
+ gltf.scene.traverse(c=>{
120
+ if(c.dispose) c.dispose();
121
+ if(c.material) c.material.dispose();
122
+ });
123
+ }else{
124
+ nextDownload.tile.setObject(instancedMesh);
125
+ }
126
+ });
127
+
79
128
  }else if(nextDownload.path.includes(".json")){
80
129
  concurentDownloads++;
81
130
  fetch(nextDownload.path, {signal: nextDownload.abortController.signal}).then(result => {
@@ -138,12 +187,12 @@ class InstancedTileLoader {
138
187
  }
139
188
  }
140
189
 
141
- get(abortController, path, uuid, instancedOGC3DTile, distanceFunction, getSiblings, level, zUpToYUp) {
190
+ get(abortController, path, uuid, instancedOGC3DTile, distanceFunction, getSiblings, level, zUpToYUp, geometricError) {
142
191
  const self = this;
143
192
  const key = simplifyPath(path);
144
193
 
145
- if (!path.includes(".b3dm") && !path.includes(".json")) {
146
- console.error("the 3DTiles cache can only be used to load B3DM and json data");
194
+ if (!path.includes(".b3dm") && !path.includes(".json") && !path.includes(".glb") && !path.includes(".gltf")) {
195
+ console.error("the 3DTiles cache can only be used to load B3DM, gltf and json data");
147
196
  return;
148
197
  }
149
198
 
@@ -153,7 +202,7 @@ class InstancedTileLoader {
153
202
  return;
154
203
  } else {
155
204
 
156
- if (path.includes(".b3dm")) {
205
+ if (path.includes(".b3dm") || path.includes(".glb") || path.includes(".gltf")) {
157
206
  const tile = new MeshTile(self.scene);
158
207
  tile.addInstance(instancedOGC3DTile);
159
208
 
@@ -175,6 +224,7 @@ class InstancedTileLoader {
175
224
  level: level,
176
225
  uuid: uuid,
177
226
  zUpToYUp: zUpToYUp,
227
+ geometricError: geometricError,
178
228
  shouldDoDownload: () => {
179
229
  return true;
180
230
  },