@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 +34 -7
- package/index.html +1 -4
- package/package.json +1 -1
- package/src/index.js +33 -33
- package/src/tileset/OGC3DTile.js +6 -6
- package/src/tileset/TileLoader.js +29 -20
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# T H R E E D T I L E S : http://www.jdultra.com/
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
},
|
|
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
|
|
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
|
-
|
|
172
|
-
|
|
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:
|
|
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.
|
|
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
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(
|
|
30
|
-
scene.add(new THREE.AmbientLight(0xFFFFFF, 0.
|
|
31
|
-
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.
|
|
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 =
|
|
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(
|
|
81
|
-
camera.position.set(
|
|
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
|
-
|
|
96
|
-
geometricErrorMultiplier: 0.
|
|
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
|
-
|
|
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 (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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
|
+
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -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.
|
|
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.
|
|
232
|
-
element.
|
|
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
|
-
|
|
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()
|
|
199
|
-
|
|
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()
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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,
|