@jdultra/threedtiles 5.0.0 → 5.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.
package/README.md CHANGED
@@ -20,7 +20,8 @@ import { OGC3DTile } from "./tileset/OGC3DTile";
20
20
  ...
21
21
 
22
22
  const ogc3DTile = new OGC3DTile({
23
- url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json"
23
+ url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
24
+ renderer: renderer
24
25
  });
25
26
 
26
27
  scene.add(ogc3DTile);
@@ -56,6 +57,7 @@ you may also set this value at initialization:
56
57
  ```
57
58
  const ogc3DTile = new OGC3DTile({
58
59
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
60
+ renderer: renderer,
59
61
  geometricErrorMultiplier: 2.0
60
62
  });
61
63
  ```
@@ -66,11 +68,12 @@ A value of 1.0 is the default.
66
68
 
67
69
  ### load tiles outside of view
68
70
  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.
69
- 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.
71
+ 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 from parts of the scene that are not in the view.
70
72
 
71
73
  ```
72
74
  const ogc3DTile = new OGC3DTile({
73
75
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
76
+ renderer: renderer,
74
77
  loadOutsideView: true
75
78
  });
76
79
  ```
@@ -84,6 +87,7 @@ This can be useful to position the tileset at a specific location when it is not
84
87
  ```
85
88
  const ogc3DTile = new OGC3DTile({
86
89
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
90
+ renderer: renderer,
87
91
  onLoadCallback: tilese => {
88
92
  console.log(tileset.boundingVolume);
89
93
  }
@@ -96,6 +100,7 @@ Add a callback on loaded tiles in order to set a material or do some logic on th
96
100
  ```
97
101
  const ogc3DTile = new OGC3DTile({
98
102
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
103
+ renderer: renderer,
99
104
  meshCallback: mesh => {
100
105
  mesh.material.wireframe = true;
101
106
  mesh.material.side = THREE.DoubleSide;
@@ -116,6 +121,7 @@ import { TileLoader } from "@jdultra/threedtiles/src/tileset/TileLoader";
116
121
 
117
122
  const ogc3DTile = new OGC3DTile({
118
123
  url: "https://storage.googleapis.com/ogc-3d-tiles/ayutthaya/tileset.json",
124
+ renderer: renderer,
119
125
  tileLoader: new TileLoader(mesh => {
120
126
  //// Insert code to be called on every newly decoded mesh e.g.:
121
127
  mesh.material.wireframe = false;
@@ -133,6 +139,7 @@ The OGC 3DTile object is a regular three.js Object3D so it can be transformed vi
133
139
  ```
134
140
  const ogc3DTile = new OGC3DTile({
135
141
  url: "https://ebeaufay.github.io/ThreedTilesViewer.github.io/momoyama/tileset.json"
142
+ renderer: renderer,
136
143
  });
137
144
 
138
145
  ogc3DTile.translateOnAxis(new THREE.Vector3(0,1,0), -450);
@@ -156,6 +163,7 @@ This service must be passed to every OGC3DTiles object like so:
156
163
  ```
157
164
  const ogc3DTile = new OGC3DTile({
158
165
  url: "path/to/tileset.json",
166
+ renderer: renderer,
159
167
  occlusionCullingService: occlusionCullingService
160
168
  });
161
169
  ```
@@ -201,11 +209,12 @@ const instancedTileLoader = new InstancedTileLoader(scene, mesh => {
201
209
  const instancedTilesets = [];
202
210
  for (let i = 0; i < 100; i++) {
203
211
  const tileset = new InstancedOGC3DTile({
204
- url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
205
- geometricErrorMultiplier: 1.0,
206
- loadOutsideView: false,
207
- tileLoader: instancedTileLoader,
208
- static: true // when static is set to true, don't forget to call InstancedOGC3DTile#updateMatrix manually
212
+ url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
213
+ renderer: renderer,
214
+ geometricErrorMultiplier: 1.0,
215
+ loadOutsideView: false,
216
+ tileLoader: instancedTileLoader,
217
+ static: true // when static is set to true, don't forget to call InstancedOGC3DTile#updateMatrix manually
209
218
  });
210
219
 
211
220
  tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * i);
@@ -238,6 +247,7 @@ This will skip recalculating the transformation matrix of every tile each frame
238
247
  ```
239
248
  const ogc3DTile = new OGC3DTile({
240
249
  url: "path/to/tileset.json",
250
+ renderer: renderer,
241
251
  static: true
242
252
  });
243
253
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jdultra/threedtiles",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "An OGC 3DTiles viewer for Three.js",
5
5
  "main": "tileset.js",
6
6
  "scripts": {
@@ -31,15 +31,15 @@
31
31
  "path-browserify": "^1.0.1",
32
32
  "regenerator-runtime": "^0.13.11",
33
33
  "set-interval-async": "^2.0.3",
34
- "three": "0.146.0",
34
+ "three": "0.148.0",
35
35
  "uuid": "^8.3.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@babel/core": "^7.20.2",
38
+ "@babel/core": "^7.20.12",
39
39
  "@babel/preset-env": "^7.20.2",
40
40
  "babel-loader": "^8.3.0",
41
41
  "copy-webpack-plugin": "^6.3.2",
42
- "core-js": "^3.26.1",
42
+ "core-js": "^3.27.1",
43
43
  "html-loader": "^1.3.2",
44
44
  "html-webpack-plugin": "^4.5.0",
45
45
  "mini-css-extract-plugin": "^1.6.2",
@@ -1,9 +1,11 @@
1
1
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
2
2
  import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
3
3
  import * as THREE from 'three';
4
+ import {FeatureTable, BatchTable} from './FeatureTable';
4
5
 
5
6
  const gltfLoader = new GLTFLoader();
6
7
  const dracoLoader = new DRACOLoader();
8
+ const tempMatrix = new THREE.Matrix4();
7
9
  dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.4.3/');
8
10
  gltfLoader.setDRACOLoader(dracoLoader);
9
11
  const dummy = new THREE.Object3D();
@@ -31,10 +33,10 @@ function parseB3DM(arrayBuffer, meshCallback) {
31
33
  const batchTableBinaryByteLength = dataView.getUint32(24, true);
32
34
 
33
35
  const featureTableStart = 28;
34
- //const featureTable = new FeatureTable( arrayBuffer, featureTableStart, featureTableJSONByteLength, featureTableBinaryByteLength );
36
+ const featureTable = new FeatureTable(arrayBuffer, featureTableStart, featureTableJSONByteLength, featureTableBinaryByteLength);
35
37
 
36
38
  const batchTableStart = featureTableStart + featureTableJSONByteLength + featureTableBinaryByteLength;
37
- //const batchTable = new BatchTable( arrayBuffer, featureTable.getData( 'BATCH_LENGTH' ), batchTableStart, batchTableJSONByteLength, batchTableBinaryByteLength );
39
+ const batchTable = new BatchTable(arrayBuffer, featureTable.getData('BATCH_LENGTH'), batchTableStart, batchTableJSONByteLength, batchTableBinaryByteLength);
38
40
 
39
41
  const glbStart = batchTableStart + batchTableJSONByteLength + batchTableBinaryByteLength;
40
42
  const glbBytes = new Uint8Array(arrayBuffer, glbStart, byteLength - glbStart);
@@ -48,6 +50,7 @@ function parseB3DM(arrayBuffer, meshCallback) {
48
50
  gltfLoader.parse(gltfBuffer, null, model => {
49
51
 
50
52
  ////TODO
53
+
51
54
  //model.batchTable = b3dm.batchTable;
52
55
  //model.featureTable = b3dm.featureTable;
53
56
 
@@ -55,6 +58,15 @@ function parseB3DM(arrayBuffer, meshCallback) {
55
58
  //model.scene.featureTable = b3dm.featureTable;
56
59
 
57
60
  //const scene = mergeColoredObject(model.scene);
61
+
62
+ //model.scene.applyMatrix4(ytozUpMatrix);
63
+
64
+ const rtcCenter = featureTable.getData('RTC_CENTER');
65
+ if (rtcCenter) {
66
+ tempMatrix.makeTranslation(rtcCenter[0], rtcCenter[2], -rtcCenter[1])
67
+ model.scene.applyMatrix4(tempMatrix);
68
+ }
69
+
58
70
  model.scene.traverse((o) => {
59
71
  if (o.isMesh) {
60
72
  if (!!meshCallback) {
@@ -76,11 +88,11 @@ const B3DMDecoder = {
76
88
 
77
89
  return parseB3DM(arrayBuffer, meshCallback).then(mesh => {
78
90
  let instancedMesh;
91
+ mesh.updateWorldMatrix(false, true)
79
92
  mesh.traverse(child => {
80
93
  if (child.isMesh) {
81
94
  instancedMesh = new THREE.InstancedMesh(child.geometry, child.material, maxCount);
82
- instancedMesh.baseMatrix = child.matrix;
83
- //console.log(child.matrix.elements[12])
95
+ instancedMesh.baseMatrix = child.matrixWorld;
84
96
  }
85
97
  });
86
98
  return instancedMesh;
@@ -0,0 +1,169 @@
1
+ /**
2
+ * This class is taken straight from NASA-AMMOS library.
3
+ * https://github.com/NASA-AMMOS/3DTilesRendererJS/blob/master/src/utilities/FeatureTable.js
4
+ */
5
+
6
+ const utf8decoder = new TextDecoder();
7
+ export class FeatureTable {
8
+
9
+ constructor(buffer, start, headerLength, binLength) {
10
+
11
+ this.buffer = buffer;
12
+ this.binOffset = start + headerLength;
13
+ this.binLength = binLength;
14
+
15
+ let header = null;
16
+ if (headerLength !== 0) {
17
+
18
+ try {
19
+ const headerData = new Uint8Array(buffer, start, headerLength);
20
+ header = JSON.parse(utf8decoder.decode(headerData));
21
+ } catch (e) {
22
+ header = {};
23
+ }
24
+
25
+ } else {
26
+
27
+ header = {};
28
+
29
+ }
30
+ this.header = header;
31
+
32
+ }
33
+
34
+ getKeys() {
35
+
36
+ return Object.keys(this.header);
37
+
38
+ }
39
+
40
+ getData(key, count, defaultComponentType = null, defaultType = null) {
41
+
42
+ const header = this.header;
43
+
44
+ if (!(key in header)) {
45
+
46
+ return null;
47
+
48
+ }
49
+
50
+ const feature = header[key];
51
+ if (!(feature instanceof Object)) {
52
+
53
+ return feature;
54
+
55
+ } else if (Array.isArray(feature)) {
56
+
57
+ return feature;
58
+
59
+ } else {
60
+
61
+ const { buffer, binOffset, binLength } = this;
62
+ const byteOffset = feature.byteOffset || 0;
63
+ const featureType = feature.type || defaultType;
64
+ const featureComponentType = feature.componentType || defaultComponentType;
65
+
66
+ if ('type' in feature && defaultType && feature.type !== defaultType) {
67
+
68
+ throw new Error('FeatureTable: Specified type does not match expected type.');
69
+
70
+ }
71
+
72
+ let stride;
73
+ switch (featureType) {
74
+
75
+ case 'SCALAR':
76
+ stride = 1;
77
+ break;
78
+
79
+ case 'VEC2':
80
+ stride = 2;
81
+ break;
82
+
83
+ case 'VEC3':
84
+ stride = 3;
85
+ break;
86
+
87
+ case 'VEC4':
88
+ stride = 4;
89
+ break;
90
+
91
+ default:
92
+ throw new Error(`FeatureTable : Feature type not provided for "${key}".`);
93
+
94
+ }
95
+
96
+ let data;
97
+ const arrayStart = binOffset + byteOffset;
98
+ const arrayLength = count * stride;
99
+
100
+ switch (featureComponentType) {
101
+
102
+ case 'BYTE':
103
+ data = new Int8Array(buffer, arrayStart, arrayLength);
104
+ break;
105
+
106
+ case 'UNSIGNED_BYTE':
107
+ data = new Uint8Array(buffer, arrayStart, arrayLength);
108
+ break;
109
+
110
+ case 'SHORT':
111
+ data = new Int16Array(buffer, arrayStart, arrayLength);
112
+ break;
113
+
114
+ case 'UNSIGNED_SHORT':
115
+ data = new Uint16Array(buffer, arrayStart, arrayLength);
116
+ break;
117
+
118
+ case 'INT':
119
+ data = new Int32Array(buffer, arrayStart, arrayLength);
120
+ break;
121
+
122
+ case 'UNSIGNED_INT':
123
+ data = new Uint32Array(buffer, arrayStart, arrayLength);
124
+ break;
125
+
126
+ case 'FLOAT':
127
+ data = new Float32Array(buffer, arrayStart, arrayLength);
128
+ break;
129
+
130
+ case 'DOUBLE':
131
+ data = new Float64Array(buffer, arrayStart, arrayLength);
132
+ break;
133
+
134
+ default:
135
+ throw new Error(`FeatureTable : Feature component type not provided for "${key}".`);
136
+
137
+ }
138
+
139
+ const dataEnd = arrayStart + arrayLength * data.BYTES_PER_ELEMENT;
140
+ if (dataEnd > binOffset + binLength) {
141
+
142
+ throw new Error('FeatureTable: Feature data read outside binary body length.');
143
+
144
+ }
145
+
146
+ return data;
147
+
148
+ }
149
+
150
+ }
151
+
152
+ }
153
+
154
+ export class BatchTable extends FeatureTable {
155
+
156
+ constructor(buffer, batchSize, start, headerLength, binLength) {
157
+
158
+ super(buffer, start, headerLength, binLength);
159
+ this.batchSize = batchSize;
160
+
161
+ }
162
+
163
+ getData(key, componentType = null, type = null) {
164
+
165
+ return super.getData(key, this.batchSize, componentType, type);
166
+
167
+ }
168
+
169
+ }
package/src/index.js CHANGED
@@ -14,7 +14,6 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
14
14
  import { InstancedOGC3DTile } from "./tileset/instanced/InstancedOGC3DTile.js"
15
15
  import { InstancedTileLoader } from "./tileset/instanced/InstancedTileLoader.js"
16
16
 
17
- import { B3DMDecoder } from "./decoder/B3DMDecoder";
18
17
 
19
18
  const occlusionCullingService = new OcclusionCullingService();
20
19
  occlusionCullingService.setSide(THREE.DoubleSide);
@@ -24,35 +23,16 @@ const domContainer = initDomContainer("screen");
24
23
  const camera = initCamera(domContainer.offsetWidth, domContainer.offsetHeight);
25
24
  const stats = initStats(domContainer);
26
25
  const renderer = initRenderer(camera, domContainer);
27
- //const ogc3DTiles = initTileset(scene);
26
+ const ogc3DTiles = initTileset(scene, 1.0);
28
27
 
29
-
30
- const instancedTileLoader = createInstancedTileLoader(scene);
31
- initInstancedTilesets(instancedTileLoader);
28
+ //const instancedTileLoader = createInstancedTileLoader(scene);
29
+ //initInstancedTilesets(instancedTileLoader);
32
30
 
33
31
  const controller = initController(camera, domContainer);
34
32
 
35
33
  const composer = initComposer(scene, camera, renderer);
36
34
 
37
35
 
38
- /* fetch("https://storage.googleapis.com/ogc-3d-tiles/droneship/1/2007.b3dm").then(result => {
39
-
40
- if (!result.ok) {
41
- console.error("could not load tile with path : " + path)
42
- throw new Error(`couldn't load "${path}". Request failed with status ${result.status} : ${result.statusText}`);
43
- }
44
- return result.arrayBuffer();
45
-
46
- })
47
- .then(resultArrayBuffer=>{
48
- return B3DMDecoder.parseB3DMInstanced(resultArrayBuffer, self.meshCallback, 1);
49
- })
50
- .then(mesh=>{
51
- scene.add(mesh)
52
-
53
- }) */
54
-
55
-
56
36
  animate();
57
37
 
58
38
 
@@ -70,10 +50,10 @@ function initScene() {
70
50
  const scene = new THREE.Scene();
71
51
  scene.matrixAutoUpdate = false;
72
52
  //scene.matrixWorldAutoUpdate = false;
73
- scene.background = new THREE.Color(0x404040);
53
+ scene.background = new THREE.Color(0xffffff);
74
54
  scene.add(new THREE.AmbientLight(0xFFFFFF, 1.0));
75
55
 
76
- /*const light = new THREE.PointLight(0xbbbbff, 2, 5000);
56
+ /* const light = new THREE.PointLight(0xbbbbff, 2, 5000);
77
57
  const sphere = new THREE.SphereGeometry(2, 16, 8);
78
58
  light.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({ color: 0xbbbbff })));
79
59
  scene.add(light);
@@ -84,9 +64,9 @@ function initScene() {
84
64
  const sphere2 = new THREE.SphereGeometry(2, 16, 8);
85
65
  light2.add(new THREE.Mesh(sphere2, new THREE.MeshBasicMaterial({ color: 0xffbbbb })));
86
66
  scene.add(light2);
87
- light2.position.set(200, 100, -100);*/
67
+ light2.position.set(200, 100, -100); */
68
+
88
69
 
89
-
90
70
  return scene;
91
71
  }
92
72
 
@@ -134,119 +114,83 @@ function initStats(dom) {
134
114
 
135
115
  function initCamera(width, height) {
136
116
  const camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
137
- camera.position.set(-400.060421028462592,-14.561785966685625,700.123058268059668);
138
-
117
+ camera.position.set(10000,0,0);
118
+ camera.lookAt(0,0,0);
119
+
139
120
  camera.matrixAutoUpdate = true;
140
121
  return camera;
141
122
  }
142
123
 
143
- function initTileset(scene) {
124
+ function initTileset(scene, gem) {
144
125
 
145
126
  const tileLoader = new TileLoader(mesh => {
146
127
  //// Insert code to be called on every newly decoded mesh e.g.:
147
128
  mesh.material.wireframe = false;
148
129
  mesh.material.side = THREE.DoubleSide;
149
130
  mesh.material.metalness = 0.0
150
- }, 1000)
131
+ }, 100)
151
132
  const ogc3DTile = new OGC3DTile({
152
- //url: "http://localhost:8080/tileset.json",
153
- //url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
154
- url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
155
- //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
156
- //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/an7opcnyije/tileset.json",
157
- //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ands2ty8orz/tileset.json",
158
- //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/a88b3sungng/tileset.json",
159
- geometricErrorMultiplier: 0.01,
133
+ //url: "https://sampledata.luciad.com/data/ogc3dtiles/LucerneAirborneMesh/tileset.json",
134
+ url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
135
+ //url: "https://storage.googleapis.com/ogc-3d-tiles/baltimore/tileset.json",
136
+ //url: "http://localhost:8082/tileset.json",
137
+ geometricErrorMultiplier: gem,
160
138
  loadOutsideView: false,
161
139
  tileLoader: tileLoader,
162
140
  //occlusionCullingService: occlusionCullingService,
163
141
  static: false,
142
+ centerModel:true,
164
143
  renderer: renderer
165
144
 
166
145
  });
146
+ setIntervalAsync(function () {
147
+ ogc3DTile.update(camera);
148
+ }, 20);
149
+ //ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
150
+ //ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 1)
151
+ /*
152
+ ogc3DTile.translateOnAxis(new THREE.Vector3(0, 0, 1), 10) // Z-UP to Y-UP
153
+ ogc3DTile.translateOnAxis(new THREE.Vector3(0, 1, 0), 18.5) // Z-UP to Y-UP */
154
+ scene.add(ogc3DTile);
167
155
 
168
-
169
-
170
-
171
- //// The OGC3DTile object is a threejs Object3D so you may do all the usual opperations like transformations e.g.:
172
- ogc3DTile.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
173
- ogc3DTile.scale.set(100.0,100.0,100.0)
174
- //// If the OGC3DTile object is marked as "static" (constructorParameter), these operations will not work.
175
-
176
-
177
-
178
- //// 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
179
-
180
-
181
-
182
- var interval;
183
- document.addEventListener('keyup', (e) => {
184
- console.log(camera.position)
185
- if (!!e.key && e.key !== "p") {
186
-
187
- if (!!interval) {
188
- clearInterval(interval);
189
- interval = null;
190
- } else {
191
- startInterval();
192
- }
193
- }
194
- if (!!e.key && e.key !== "l") {
195
-
196
- console.log("new THREE.Vector3(" + camera.position.x + "," + camera.position.y + "," + camera.position.z + ")");
197
- console.log("new THREE.Quaternion(" + camera.quaternion.x + "," + camera.quaternion.y + "," + camera.quaternion.z + "," + camera.quaternion.w + ")");
198
-
199
- }
200
-
201
- });
202
- function startInterval() {
203
- interval = setIntervalAsync(function () {
204
- ogc3DTile.update(camera);
205
-
206
- }, 20);
207
- }
208
- startInterval();
209
-
210
- scene.add(ogc3DTile)
211
156
  return ogc3DTile;
212
157
  }
213
158
 
159
+
214
160
  function createInstancedTileLoader(scene) {
215
161
  return new InstancedTileLoader(scene, mesh => {
216
162
  //// Insert code to be called on every newly decoded mesh e.g.:
217
163
  mesh.material.wireframe = false;
218
164
  mesh.material.side = THREE.DoubleSide;
219
- }, 1000, 3375);
165
+ mesh.material.metalness = 0.0;
166
+ }, 0, 1);
220
167
  }
221
168
  function initInstancedTilesets(instancedTileLoader) {
222
169
 
170
+ /*new GLTFLoader().load('http://localhost:8080/test.glb', function ( gltf ) {
171
+ scene.add(gltf.scene);
172
+ } );*/
173
+
223
174
  const instancedTilesets = [];
224
175
 
225
- for (let x = 0; x < 15; x++) {
226
- for (let y = 0; y < 15; y++) {
227
- for (let z = 0; z < 15; z++) {
228
- const tileset = new InstancedOGC3DTile({
229
- url: "https://storage.googleapis.com/ogc-3d-tiles/droneship/tileset.json",
230
- //url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
231
- //url: "http://localhost:8080/tileset.json",
232
- geometricErrorMultiplier: 1.0,
233
- loadOutsideView: true,
234
- tileLoader: instancedTileLoader,
235
- static: true,
236
- renderer: renderer
237
- });
238
- //tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
239
- tileset.translateOnAxis(new THREE.Vector3(1, 0, 0), 50 * x)
240
- tileset.translateOnAxis(new THREE.Vector3(0, 1, 0), 50 * y)
241
- tileset.translateOnAxis(new THREE.Vector3(0, 0, 1), 50 * z)
242
- tileset.updateMatrix()
243
- scene.add(tileset);
244
- instancedTilesets.push(tileset);
245
-
246
-
247
- }
248
- }
249
- }
176
+
177
+ const tileset = new InstancedOGC3DTile({
178
+ //url: "https://storage.googleapis.com/ogc-3d-tiles/berlinTileset/tileset.json",
179
+ //url: "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json",
180
+ //url: "https://s3.eu-central-2.wasabisys.com/construkted-assets-eu/ab13lasdc9i/tileset.json",
181
+ url: "http://localhost:8081/tileset.json",
182
+ geometricErrorMultiplier: 1.0,
183
+ loadOutsideView: true,
184
+ tileLoader: instancedTileLoader,
185
+ static: false,
186
+ centerModel:true,
187
+ renderer: renderer
188
+ });
189
+ //tileset.rotateOnAxis(new THREE.Vector3(1, 0, 0), Math.PI * -0.5) // Z-UP to Y-UP
190
+
191
+ tileset.updateMatrix()
192
+ scene.add(tileset);
193
+ instancedTilesets.push(tileset);
250
194
 
251
195
  scene.updateMatrixWorld(true)
252
196
  function now() {
@@ -255,14 +199,14 @@ function initInstancedTilesets(instancedTileLoader) {
255
199
  let updateIndex = 0;
256
200
  setInterval(() => {
257
201
  let startTime = now();
258
- do{
202
+ do {
259
203
  const frustum = new THREE.Frustum();
260
204
  frustum.setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
261
205
  instancedTilesets[updateIndex].update(camera, frustum);
262
- updateIndex= (updateIndex+1)%instancedTilesets.length;
263
- }while(updateIndex < instancedTilesets.length && now()-startTime<4);
264
- },40);
265
-
206
+ updateIndex = (updateIndex + 1) % instancedTilesets.length;
207
+ } while (updateIndex < instancedTilesets.length && now() - startTime < 4);
208
+ }, 40);
209
+
266
210
  //initLODMultiplierSlider(instancedTilesets);
267
211
  }
268
212
 
@@ -273,7 +217,7 @@ function initLODMultiplierSlider(instancedTilesets) {
273
217
 
274
218
  slider.oninput = () => {
275
219
  instancedTilesets.forEach(tileset => {
276
- tileset.setGeometricErrorMultiplier(slider.value*0.1)
220
+ tileset.setGeometricErrorMultiplier(slider.value * 0.1)
277
221
  })
278
222
  output.innerHTML = slider.value;
279
223
  }
@@ -283,7 +227,10 @@ function initController(camera, dom) {
283
227
  const controller = new OrbitControls(camera, dom);
284
228
 
285
229
  controller.target.set(0,0,0);
286
- controller.minDistance = 0.01;
230
+ //controller.target.set(0,0,0);
231
+
232
+
233
+ controller.minDistance = 0.1;
287
234
  controller.maxDistance = 100000;
288
235
  controller.update();
289
236
  return controller;
@@ -292,7 +239,7 @@ function initController(camera, dom) {
292
239
 
293
240
  function animate() {
294
241
  requestAnimationFrame(animate);
295
- instancedTileLoader.update();
242
+ //instancedTileLoader.update();
296
243
  composer.render();
297
244
  //occlusionCullingService.update(scene, renderer, camera)
298
245
  stats.update();
@@ -5,8 +5,12 @@ import { v4 as uuidv4 } from "uuid";
5
5
  import * as path from "path-browserify"
6
6
  import { clamp } from "three/src/math/MathUtils";
7
7
 
8
- const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
8
+ const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
9
+ const tempVec1 = new THREE.Vector3(0, 0, 0);
10
+ const tempVec2 = new THREE.Vector3(0, 0, 0);
11
+ const upVector = new THREE.Vector3(0, 1, 0);
9
12
  const rendererSize = new THREE.Vector2();
13
+ const tempQuaternion = new THREE.Quaternion();
10
14
 
11
15
  class OGC3DTile extends THREE.Object3D {
12
16
 
@@ -28,6 +32,7 @@ class OGC3DTile extends THREE.Object3D {
28
32
  * onLoadCallback: function,
29
33
  * occlusionCullingService: OcclusionCullingService,
30
34
  * static: Boolean,
35
+ * centerModel: Boolean
31
36
  * renderer: Renderer
32
37
  * } properties
33
38
  */
@@ -63,6 +68,7 @@ class OGC3DTile extends THREE.Object3D {
63
68
  if (this.static) {
64
69
  this.matrixAutoUpdate = false;
65
70
  }
71
+
66
72
  // declare properties specific to the tile for clarity
67
73
  this.childrenTiles = [];
68
74
  this.meshContent;
@@ -84,6 +90,7 @@ class OGC3DTile extends THREE.Object3D {
84
90
  if (!!properties.json) { // If this tile is created as a child of another tile, properties.json is not null
85
91
  self.setup(properties);
86
92
  if (properties.onLoadCallback) properties.onLoadCallback(self);
93
+
87
94
  } else if (properties.url) { // If only the url to the tileset.json is provided
88
95
  fetch(properties.url, { signal: self.abortController.signal }).then(result => {
89
96
  if (!result.ok) {
@@ -92,6 +99,34 @@ class OGC3DTile extends THREE.Object3D {
92
99
  result.json().then(json => {
93
100
  self.setup({ rootPath: path.dirname(properties.url), json: json });
94
101
  if (properties.onLoadCallback) properties.onLoadCallback(self);
102
+ if (!!properties.centerModel) {
103
+ const tempSphere = new THREE.Sphere();
104
+ if (self.boundingVolume instanceof OBB) {
105
+ // box
106
+ tempSphere.copy(self.boundingVolume.sphere);
107
+ } else if (self.boundingVolume instanceof THREE.Sphere) {
108
+ //sphere
109
+ tempSphere.copy(self.boundingVolume);
110
+ }
111
+
112
+ //tempSphere.applyMatrix4(self.matrixWorld);
113
+ if (!!this.json.boundingVolume.region) {
114
+ this.transformWGS84ToCartesian(
115
+ (this.json.boundingVolume.region[0] + this.json.boundingVolume.region[2]) * 0.5,
116
+ (this.json.boundingVolume.region[1] + this.json.boundingVolume.region[3]) * 0.5,
117
+ (this.json.boundingVolume.region[4] + this.json.boundingVolume.region[5]) * 0.5,
118
+ tempVec1);
119
+ tempVec2.set(tempVec1.x, tempVec1.z, -tempVec1.y);
120
+
121
+ tempQuaternion.setFromUnitVectors(tempVec2.normalize(), upVector.normalize());
122
+ self.applyQuaternion(tempQuaternion);
123
+ }
124
+
125
+ self.translateX(-tempSphere.center.x * self.scale.x);
126
+ self.translateY(-tempSphere.center.y * self.scale.y);
127
+ self.translateZ(-tempSphere.center.z * self.scale.z);
128
+
129
+ }
95
130
  });
96
131
  });
97
132
  }
@@ -133,7 +168,10 @@ class OGC3DTile extends THREE.Object3D {
133
168
  this.boundingVolume = new OBB(this.json.boundingVolume.box);
134
169
  } else if (!!this.json.boundingVolume.region) {
135
170
  const region = this.json.boundingVolume.region;
136
- this.boundingVolume = new THREE.Box3(new THREE.Vector3(region[0], region[2], region[4]), new THREE.Vector3(region[1], region[3], region[5]));
171
+ this.transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
172
+ this.transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
173
+ tempVec1.lerp(tempVec2, 0.5);
174
+ this.boundingVolume = new THREE.Sphere(new THREE.Vector3(tempVec1.x, tempVec1.z, -tempVec1.y), tempVec1.distanceTo(tempVec2));
137
175
  } else if (!!this.json.boundingVolume.sphere) {
138
176
  const sphere = this.json.boundingVolume.sphere;
139
177
  this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[2], -sphere[1]), sphere[3]);
@@ -518,39 +556,30 @@ class OGC3DTile extends THREE.Object3D {
518
556
  tempSphere.copy(this.boundingVolume);
519
557
  tempSphere.applyMatrix4(this.matrixWorld);
520
558
  if (!frustum.intersectsSphere(tempSphere)) return -1;
521
- } else if (this.boundingVolume instanceof THREE.Box3) {
522
- // Region
523
- // Region not supported
524
- //throw Error("Region bounding volume not supported");
525
- return -1;
559
+ } else {
560
+ console.error("unsupported shape");
561
+ return -1
562
+
526
563
  }
527
564
 
528
565
  /////// return metric based on geometric error and distance
529
- if (this.boundingVolume instanceof OBB || this.boundingVolume instanceof THREE.Sphere) {
530
- // box
531
- const distance = Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
532
- if (distance == 0) {
533
- return 0;
534
- }
535
- const scale = this.matrixWorld.getMaxScaleOnAxis();
536
- this.renderer.getDrawingBufferSize(rendererSize);
537
- let s = rendererSize.y;
538
- let fov = camera.fov;
539
- if(camera.aspect < 1){
540
- fov *= camera.aspect;
541
- s = rendererSize.x;
542
- }
543
-
544
- let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
545
-
546
- return (window.devicePixelRatio * 16 * lambda) / (s * scale);
547
566
 
548
- } else if (this.boundingVolume instanceof THREE.Box3) {
549
- // Region
550
- // Region not supported
551
- //throw Error("Region bounding volume not supported");
552
- return -1;
567
+ const distance = Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
568
+ if (distance == 0) {
569
+ return 0;
553
570
  }
571
+ const scale = this.matrixWorld.getMaxScaleOnAxis();
572
+ this.renderer.getDrawingBufferSize(rendererSize);
573
+ let s = rendererSize.y;
574
+ let fov = camera.fov;
575
+ if (camera.aspect < 1) {
576
+ fov *= camera.aspect;
577
+ s = rendererSize.x;
578
+ }
579
+
580
+ let lambda = 2.0 * Math.tan(0.5 * fov * 0.01745329251994329576923690768489) * distance;
581
+
582
+ return (window.devicePixelRatio * 16 * lambda) / (s * scale);
554
583
  }
555
584
 
556
585
  getSiblings() {
@@ -583,8 +612,8 @@ class OGC3DTile extends THREE.Object3D {
583
612
  tempSphere.applyMatrix4(this.matrixWorld);
584
613
  //if (!frustum.intersectsSphere(tempSphere)) return -1;
585
614
  }
586
- if (this.boundingVolume instanceof THREE.Box3) {
587
- return -1; // region not supported
615
+ else {
616
+ console.error("unsupported shape")
588
617
  }
589
618
  return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
590
619
  }
@@ -592,5 +621,21 @@ class OGC3DTile extends THREE.Object3D {
592
621
  this.geometricErrorMultiplier = geometricErrorMultiplier;
593
622
  this.childrenTiles.forEach(child => child.setGeometricErrorMultiplier(geometricErrorMultiplier));
594
623
  }
624
+
625
+ transformWGS84ToCartesian(lon, lat, h, sfct) {
626
+ const a = 6378137.0;
627
+ const e = 0.006694384442042;
628
+ const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
629
+ const cosLat = Math.cos(lat);
630
+ const cosLon = Math.cos(lon);
631
+ const sinLat = Math.sin(lat);
632
+ const sinLon = Math.sin(lon);
633
+ const nPh = (N + h);
634
+ const x = nPh * cosLat * cosLon;
635
+ const y = nPh * cosLat * sinLon;
636
+ const z = (0.993305615557957 * N + h) * sinLat;
637
+
638
+ sfct.set(x, y, z);
639
+ }
595
640
  }
596
641
  export { OGC3DTile };
@@ -4,8 +4,13 @@ import { v4 as uuidv4 } from "uuid";
4
4
  import * as path from "path-browserify";
5
5
  import * as _ from "lodash";
6
6
 
7
- const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0, 1));
7
+ const tempSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
8
+ const tempVec1 = new THREE.Vector3(0, 0, 0);
9
+ const tempVec2 = new THREE.Vector3(0, 0, 0);
10
+ const upVector = new THREE.Vector3(0, 1, 0);
8
11
  const rendererSize = new THREE.Vector2();
12
+ const tempQuaternion = new THREE.Quaternion();
13
+ const tempMatrix = new THREE.Matrix4();
9
14
 
10
15
  class InstancedTile extends THREE.Object3D {
11
16
 
@@ -23,7 +28,8 @@ class InstancedTile extends THREE.Object3D {
23
28
  * meshCallback: function,
24
29
  * cameraOnLoad: camera,
25
30
  * parentTile: OGC3DTile,
26
- * onLoadCallback: function
31
+ * onLoadCallback: function,
32
+ * centerModel: Boolean
27
33
  * } properties
28
34
  */
29
35
  constructor(properties) {
@@ -76,7 +82,36 @@ class InstancedTile extends THREE.Object3D {
76
82
  //json = JSON.parse(JSON.stringify(json))
77
83
  const p = path.dirname(url);
78
84
  self.setup({ rootPath: p, json: json });
85
+ if (!!properties.centerModel) {
86
+ const tempSphere = new THREE.Sphere();
87
+ if (self.boundingVolume instanceof OBB) {
88
+ // box
89
+ tempSphere.copy(self.boundingVolume.sphere);
90
+ } else if (self.boundingVolume instanceof THREE.Sphere) {
91
+ //sphere
92
+ tempSphere.copy(self.boundingVolume);
93
+ }
94
+
95
+ //tempSphere.applyMatrix4(self.matrixWorld);
96
+ if (!!this.json.boundingVolume.region) {
97
+ self.transformWGS84ToCartesian(
98
+ (self.json.boundingVolume.region[0] + self.json.boundingVolume.region[2]) * 0.5,
99
+ (self.json.boundingVolume.region[1] + self.json.boundingVolume.region[3]) * 0.5,
100
+ (self.json.boundingVolume.region[4] + self.json.boundingVolume.region[5]) * 0.5,
101
+ tempVec1);
102
+ tempVec2.set(tempVec1.x, tempVec1.z, -tempVec1.y);
103
+
104
+ tempQuaternion.setFromUnitVectors(tempVec2.normalize(), upVector.normalize());
105
+ self.master.applyQuaternion(tempQuaternion);
106
+ self.master.updateWorldMatrix(false, false)
107
+ }
108
+ tempMatrix.makeTranslation(-tempSphere.center.x * self.scale.x, -tempSphere.center.y * self.scale.y, -tempSphere.center.z * self.scale.z);
109
+ //self.master.applyMatrix4(tempMatrix);
110
+ self.master.matrix.multiply(tempMatrix);
111
+ self.master.matrix.decompose( self.master.position, self.master.quaternion, self.master.scale );
112
+ }
79
113
  if (properties.onLoadCallback) properties.onLoadCallback(self);
114
+
80
115
  }
81
116
  self.tileLoader.get(self.abortController, properties.url, self.uuid, self);
82
117
 
@@ -132,7 +167,10 @@ class InstancedTile extends THREE.Object3D {
132
167
  this.boundingVolume = new OBB(this.json.boundingVolume.box);
133
168
  } else if (!!this.json.boundingVolume.region) {
134
169
  const region = this.json.boundingVolume.region;
135
- this.boundingVolume = new THREE.Box3(new THREE.Vector3(region[0], region[2], region[4]), new THREE.Vector3(region[1], region[3], region[5]));
170
+ this.transformWGS84ToCartesian(region[0], region[1], region[4], tempVec1);
171
+ this.transformWGS84ToCartesian(region[2], region[3], region[5], tempVec2);
172
+ tempVec1.lerp(tempVec2, 0.5);
173
+ this.boundingVolume = new THREE.Sphere(new THREE.Vector3(tempVec1.x, tempVec1.z, -tempVec1.y), tempVec1.distanceTo(tempVec2));
136
174
  } else if (!!this.json.boundingVolume.sphere) {
137
175
  const sphere = this.json.boundingVolume.sphere;
138
176
  this.boundingVolume = new THREE.Sphere(new THREE.Vector3(sphere[0], sphere[2], -sphere[1]), sphere[3]);
@@ -188,6 +226,8 @@ class InstancedTile extends THREE.Object3D {
188
226
 
189
227
  }
190
228
  }
229
+ self.matrixWorldNeedsUpdate = true;
230
+ self.updateWorldMatrix(true,true)
191
231
  }
192
232
 
193
233
  loadMesh(mesh) {
@@ -440,11 +480,9 @@ class InstancedTile extends THREE.Object3D {
440
480
  tempSphere.copy(this.boundingVolume);
441
481
  tempSphere.applyMatrix4(this.master.matrixWorld);
442
482
  if (!frustum.intersectsSphere(tempSphere)) return -1;
443
- } else if (this.boundingVolume instanceof THREE.Box3) {
444
- // Region
445
- // Region not supported
446
- //throw Error("Region bounding volume not supported");
447
- return -1;
483
+ } else {
484
+ console.error("unsupported shape");
485
+ return -1
448
486
  }
449
487
 
450
488
  /////// return metric based on geometric error and distance
@@ -505,8 +543,8 @@ class InstancedTile extends THREE.Object3D {
505
543
  tempSphere.applyMatrix4(this.master.matrixWorld);
506
544
  //if (!frustum.intersectsSphere(tempSphere)) return -1;
507
545
  }
508
- if (this.boundingVolume instanceof THREE.Box3) {
509
- return -1; // region not supported
546
+ else {
547
+ console.error("unsupported shape")
510
548
  }
511
549
  return Math.max(0, camera.position.distanceTo(tempSphere.center) - tempSphere.radius);
512
550
  }
@@ -515,5 +553,21 @@ class InstancedTile extends THREE.Object3D {
515
553
  const self = this;
516
554
  return self.master.matrixWorld;
517
555
  }
556
+
557
+ transformWGS84ToCartesian(lon, lat, h, sfct) {
558
+ const a = 6378137.0;
559
+ const e = 0.006694384442042;
560
+ const N = a / (Math.sqrt(1.0 - (e * Math.pow(Math.sin(lat), 2))));
561
+ const cosLat = Math.cos(lat);
562
+ const cosLon = Math.cos(lon);
563
+ const sinLat = Math.sin(lat);
564
+ const sinLon = Math.sin(lon);
565
+ const nPh = (N + h);
566
+ const x = nPh * cosLat * cosLon;
567
+ const y = nPh * cosLat * sinLon;
568
+ const z = (0.993305615557957 * N + h) * sinLat;
569
+
570
+ sfct.set(x, y, z);
571
+ }
518
572
  }
519
573
  export { InstancedTile };
@@ -1,6 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import { InstancedMesh } from 'three';
3
3
 
4
+ const t = new THREE.Matrix4();
4
5
  class MeshTile{
5
6
  constructor(scene){
6
7
  const self = this;
@@ -60,7 +61,12 @@ class MeshTile{
60
61
  self.instancedTiles[i].meshContent = self.instancedMesh;
61
62
  if(self.instancedTiles[i].materialVisibility && !!self.instancedTiles[i].meshContent){
62
63
  self.instancedMesh.count++;
63
- self.instancedMesh.setMatrixAt(self.instancedMesh.count-1, self.reuseableMatrix.multiplyMatrices(self.instancedTiles[i].getWorldMatrix(), self.instancedMesh.baseMatrix) )
64
+ self.reuseableMatrix.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);
65
+ self.reuseableMatrix.multiply(self.instancedTiles[i].master.matrixWorld);
66
+ self.reuseableMatrix.multiply(self.instancedMesh.baseMatrix);
67
+ self.instancedMesh.setMatrixAt(self.instancedMesh.count-1, self.reuseableMatrix );
68
+ self.instancedMesh.getMatrixAt(0, t);
69
+ console.log()
64
70
  }
65
71
 
66
72
  }