@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 +44 -14
- package/index.html +1 -4
- package/package.json +1 -1
- package/src/index.js +40 -25
- package/src/tileset/OGC3DTile.js +32 -18
- package/src/tileset/TileLoader.js +167 -142
- package/src/draco/draco_decoder.js +0 -48
- package/src/draco/draco_decoder.wasm +0 -0
- package/src/draco/draco_encoder.js +0 -33
- package/src/draco/draco_wasm_wrapper.js +0 -104
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.
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
169
|
-
|
|
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
|
|
176
|
-
Contact:
|
|
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.
|
|
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,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(
|
|
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 = 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(
|
|
80
|
-
camera.position.set(
|
|
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
|
-
|
|
90
|
-
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,
|
|
91
97
|
loadOutsideView: false,
|
|
92
|
-
tileLoader:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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 (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
|
package/src/tileset/OGC3DTile.js
CHANGED
|
@@ -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
|
-
|
|
52
|
-
|
|
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.
|
|
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.
|
|
228
|
-
element.
|
|
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
|
-
|
|
464
|
+
|
|
465
|
+
self.meshContent.traverse((o) => {
|
|
466
|
+
if (o.isMesh) {
|
|
467
|
+
o.layers.enable(0);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
460
470
|
}else{
|
|
461
|
-
self.
|
|
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) {
|