Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e6eb4cf

Browse files
jogarnierjailln
authored andcommitted
feat(gltf): add a GLTFParser to parse gltf 1.0 and 2.0 files
1 parent 3163f5f commit e6eb4cf

File tree

11 files changed

+281
-281
lines changed

11 files changed

+281
-281
lines changed

docs/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282

8383
"Parser": [
8484
"GeoJsonParser",
85+
"GLTFParser",
8586
"GpxParser",
8687
"VectorTileParser",
8788
"CameraCalibrationParser",

docs/tutorials/Fundamentals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ with `{@link WFSSource}` and [Tile Map Service](https://wiki.osgeo.org/wiki/Tile
4747

4848
iTowns also has sources for many data formats: [vector tile](https://docs.mapbox.com/help/glossary/vector-tiles/) resources from [MapBox](https://www.mapbox.com/) with `{@link VectorTilesSource}`, [Potree](https://github.com/potree/potree) (`{@link PotreeSource}`) and
4949
[Entwine](https://entwine.io/) (`{@link EntwinePointTileSource}`) 3D point clouds, [3DTiles](https://www.ogc.org/standards/3DTiles)
50-
mesh (b3dm) and point clouds (pnts) from web servers (`{@link C3DTilesSource}`) and from Cesium ion `{@link C3DTilesIonSource}`,
50+
mesh (b3dm) and point clouds (pnts) from web servers (`{@link C3DTilesSource}`), Cesium ion `{@link C3DTilesIonSource}` and from Google api `{@link C3DTilesGoogleSource}`,
5151
[GeoJSON](https://geojson.org/) with `{@link FileSource}` and `{@link GeoJsonParser}`,
5252
[KML](https://www.ogc.org/standards/kml) with `{@link FileSource}` and `{@link KMLParser}`, [GPX](https://www.topografix.com/gpx.asp)
5353
with `{@link FileSource}` and `{@link GpxParser}` and oriented images with `{@link OrientedImageSource}`.
@@ -68,7 +68,7 @@ Several specific types of `Layers` exist, the use of which depends on the data t
6868
- `{@link PointCloudLayer}` can be used to display 3D point clouds. Any point cloud formats are supported as long as the corresponding `Source` is provided.
6969
Some point clouds formats such as Potree, Las and Entwine already have parsers defined in itowns that you can use. For 3D Tiles point clouds (pnts), use
7070
`C3DTilesLayer`.
71-
- `{@link C3DTilesLayer}` can be used to display 3D Tiles layer (only b3dm and pnts).
71+
- `{@link C3DTilesLayer}` can be used to display 3D Tiles datasets in version 1.0 (b3dm, pnts and gltf tiles are supported).
7272
- `{@link OrientedImageLayer}` can be used to display oriented images.
7373

7474

src/Main.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export { default as OrientedImageSource } from 'Source/OrientedImageSource';
7777
export { default as PotreeSource } from 'Source/PotreeSource';
7878
export { default as C3DTilesSource } from 'Source/C3DTilesSource';
7979
export { default as C3DTilesIonSource } from 'Source/C3DTilesIonSource';
80+
export { default as C3DTilesGoogleSource } from 'Source/C3DTilesGoogleSource';
8081
export { default as EntwinePointTileSource } from 'Source/EntwinePointTileSource';
8182

8283
// Parsers provided by default in iTowns
@@ -91,7 +92,7 @@ export { default as LASParser } from 'Parser/LASParser';
9192
export { default as ISGParser } from 'Parser/ISGParser';
9293
export { default as GDFParser } from 'Parser/GDFParser';
9394
export { default as GTXParser } from 'Parser/GTXParser';
94-
export { enableDracoLoader, enableKtx2Loader, glTFLoader, legacyGLTFLoader } from 'Parser/B3dmParser';
95+
export { default as GLTFParser, enableDracoLoader, enableKtx2Loader, glTFLoader, legacyGLTFLoader } from 'Parser/GLTFParser';
9596

9697
// 3D Tiles classes and extensions
9798
// Exported to allow one to implement its own 3D Tiles extension which needs to

src/Parser/B3dmParser.js

Lines changed: 71 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
import * as THREE from 'three';
2-
import Capabilities from 'Core/System/Capabilities';
3-
import { GLTFLoader } from 'ThreeExtended/loaders/GLTFLoader';
4-
import { DRACOLoader } from 'ThreeExtended/loaders/DRACOLoader';
5-
import { KTX2Loader } from 'ThreeExtended/loaders/KTX2Loader';
6-
import LegacyGLTFLoader from 'Parser/deprecated/LegacyGLTFLoader';
7-
import shaderUtils from 'Renderer/Shader/ShaderUtils';
82
import C3DTBatchTable from 'Core/3DTiles/C3DTBatchTable';
9-
import ReferLayerProperties from 'Layer/ReferencingLayerProperties';
3+
import Capabilities from 'Core/System/Capabilities';
104
import { MeshBasicMaterial } from 'three';
115
import disposeThreeMaterial from 'Utils/ThreeUtils';
6+
import shaderUtils from 'Renderer/Shader/ShaderUtils';
7+
import ReferLayerProperties from 'Layer/ReferencingLayerProperties';
8+
import GLTFParser from './GLTFParser';
129

13-
const matrixChangeUpVectorZtoY = (new THREE.Matrix4()).makeRotationX(Math.PI / 2);
14-
// For gltf rotation
15-
const matrixChangeUpVectorZtoX = (new THREE.Matrix4()).makeRotationZ(-Math.PI / 2);
10+
const matrixChangeUpVectorYtoZInv = (new THREE.Matrix4()).makeRotationX(-Math.PI / 2);
11+
const matrixChangeUpVectorXtoZ = (new THREE.Matrix4()).makeRotationZ(-Math.PI / 2);
1612

1713
const utf8Decoder = new TextDecoder();
1814

19-
export const glTFLoader = new GLTFLoader();
20-
21-
export const legacyGLTFLoader = new LegacyGLTFLoader();
22-
15+
/**
16+
* 3D Tiles pre-1.0 contain not standardized and specific uniforms that we filter out to avoid shader compilation errors
17+
* This method is passed to scene.traverse and applied to all 3D objects of the loaded gltf.
18+
* @param {THREE.Object3D} obj - 3D object of the gltf hierarchy
19+
*/
2320
function filterUnsupportedSemantics(obj) {
2421
// see GLTFLoader GLTFShader.prototype.update function
2522
const supported = [
@@ -44,77 +41,45 @@ function filterUnsupportedSemantics(obj) {
4441
}
4542

4643
/**
47-
* @module B3dmParser
44+
* 3D Tiles pre-1.0 had a gltfUpAxis parameter that defined the up vector of the gltf file that might be different from
45+
* the standard y-up for gltf. Manage the case when this gltfUpAxis is defined (i.e. apply the correct rotation to the
46+
* gltf file to have it z-up in the end).
47+
* @param {THREE.Object3D} gltfScene - the parsed glTF scene
48+
* @param {String} gltfUpAxis - the gltfUpAxis parameter
4849
*/
49-
/**
50-
* Enable Draco decoding for gltf.
51-
*
52-
* The Draco library files are in folder itowns/examples/libs/draco/.
53-
* You must indicate this path when you want to enable Draco Decoding.
54-
* For more information on Draco, read /itowns/examples/libs/draco/README.md.
55-
*
56-
* @example <caption>Enable draco decoder</caption>
57-
* // if you copied /itowns/examples/libs/draco/ to the root folder of your project,you can set the path to './'.
58-
* itowns.enableDracoLoader('./');
59-
*
60-
* @param {string} path path to draco library folder.
61-
* This library is mandatory to load b3dm and gltf with Draco compression.
62-
* @param {object} config optional configuration for Draco compression.
63-
*/
64-
export function enableDracoLoader(path, config) {
65-
if (!path) {
66-
throw new Error('Path to draco folder is mandatory');
67-
}
68-
const dracoLoader = new DRACOLoader();
69-
dracoLoader.setDecoderPath(path);
70-
if (config) {
71-
dracoLoader.setDecoderConfig(config);
50+
function applyDeprecatedGltfUpAxis(gltfScene, gltfUpAxis) {
51+
if (gltfUpAxis === 'Z') {
52+
// If gltf up was already z-up, apply the inverse transform matrix that was applied in the glTFParser
53+
gltfScene.applyMatrix4(matrixChangeUpVectorYtoZInv);
54+
} else if (gltfUpAxis === 'X') {
55+
gltfScene.applyMatrix4(matrixChangeUpVectorYtoZInv);
56+
gltfScene.applyMatrix4(matrixChangeUpVectorXtoZ);
7257
}
73-
glTFLoader.setDRACOLoader(dracoLoader);
7458
}
7559

7660
/**
77-
* Enable KTX2 decoding for gltf. This library is mandatory to load b3dm and gltf with KTX2 compression.
78-
*
79-
* The KTX2 library files are in folder itowns/examples/libs/basis/.
80-
* You must indicate this path when you want to enable KTX2 decoding.
81-
* For more information about KTX2, read /itowns/examples/libs/basis/README.md.
82-
*
83-
* @example <caption>Enable ktx2 decoder</caption>
84-
* // if you copied /itowns/examples/libs/draco/ to the root folder of your project,you can set the path to './'.
85-
* itowns.enableKtx2Loader('./', view.mainLoop.gfxEngine.renderer);
86-
*
87-
* @param {string} path path to KTX2 library folder.
88-
* @param {THREE.WebGLRenderer} renderer the threejs renderer
61+
* @module B3dmParser
8962
*/
90-
export function enableKtx2Loader(path, renderer) {
91-
if (!path || !renderer) {
92-
throw new Error('Path to ktx2 folder and renderer are mandatory');
93-
}
94-
const ktx2Loader = new KTX2Loader();
95-
ktx2Loader.setTranscoderPath(path);
96-
ktx2Loader.detectSupport(renderer);
97-
glTFLoader.setKTX2Loader(ktx2Loader);
98-
}
9963

10064
export default {
10165
/** Parse b3dm buffer and extract THREE.Scene and batch table
10266
* @param {ArrayBuffer} buffer - the b3dm buffer.
10367
* @param {Object} options - additional properties.
10468
* @param {string=} [options.gltfUpAxis='Y'] - embedded glTF model up axis.
10569
* @param {string} options.urlBase - the base url of the b3dm file (used to fetch textures for the embedded glTF model).
106-
* @param {boolean=} [options.doNotPatchMaterial='false'] - disable patching material with logarithmic depth buffer support.
70+
* @param {boolean=} [options.doNotPatchMaterial=false] - disable patching material with logarithmic depth buffer support.
10771
* @param {float} [options.opacity=1.0] - the b3dm opacity.
108-
* @param {boolean|Material=} [options.overrideMaterials='false'] - override b3dm's embedded glTF materials. If
72+
* @param {boolean=} [options.frustumCulled=false] - enable frustum culling.
73+
* @param {boolean|Material=} [options.overrideMaterials=false] - override b3dm's embedded glTF materials. If
10974
* true, a threejs [MeshBasicMaterial](https://threejs.org/docs/index.html?q=meshbasic#api/en/materials/MeshBasicMaterial)
11075
* is set up. config.overrideMaterials can also be a threejs [Material](https://threejs.org/docs/index.html?q=material#api/en/materials/Material)
11176
* in which case it will be the material used to override.
11277
* @return {Promise} - a promise that resolves with an object containig a THREE.Scene (gltf) and a batch table (batchTable).
11378
*
11479
*/
11580
parse(buffer, options) {
116-
const gltfUpAxis = options.gltfUpAxis;
117-
const urlBase = options.urlBase;
81+
const frustumCulled = options.frustumCulled === true;
82+
11883
if (!buffer) {
11984
throw new Error('No array buffer provided.');
12085
}
@@ -186,66 +151,56 @@ export default {
186151
const gltfBuffer = buffer.slice(posGltf);
187152
const headerView = new DataView(gltfBuffer, 0, 20);
188153

189-
promises.push(new Promise((resolve/* , reject */) => {
190-
const onload = (gltf) => {
191-
for (const scene of gltf.scenes) {
192-
scene.traverse(filterUnsupportedSemantics);
193-
}
194-
// Rotation managed
195-
if (gltfUpAxis === undefined || gltfUpAxis === 'Y') {
196-
gltf.scene.applyMatrix4(matrixChangeUpVectorZtoY);
197-
} else if (gltfUpAxis === 'X') {
198-
gltf.scene.applyMatrix4(matrixChangeUpVectorZtoX);
154+
const init_mesh = function f_init(mesh) {
155+
mesh.frustumCulled = frustumCulled;
156+
if (mesh.material) {
157+
if (options.overrideMaterials) {
158+
const oldMat = mesh.material;
159+
// Set up new material
160+
if (typeof (options.overrideMaterials) === 'object' &&
161+
options.overrideMaterials.isMaterial) {
162+
mesh.material = options.overrideMaterials;
163+
} else {
164+
mesh.material = new MeshBasicMaterial();
165+
}
166+
disposeThreeMaterial(oldMat);
167+
} else if (Capabilities.isLogDepthBufferSupported()
168+
&& mesh.material.isRawShaderMaterial
169+
&& !options.doNotPatchMaterial) {
170+
shaderUtils.patchMaterialForLogDepthSupport(mesh.material);
171+
console.warn('glTF shader has been patched to add log depth buffer support');
199172
}
173+
ReferLayerProperties(mesh.material, options.layer);
174+
}
175+
};
200176

201-
// Apply relative center from Feature table.
202-
gltf.scene.position.copy(FT_RTC);
177+
promises.push(GLTFParser.parse(gltfBuffer, options).then((gltf) => {
178+
for (const scene of gltf.scenes) {
179+
scene.traverse(filterUnsupportedSemantics);
180+
}
203181

204-
// Apply relative center from gltf json.
205-
const contentArray = new Uint8Array(gltfBuffer, 20, headerView.getUint32(12, true));
206-
const content = utf8Decoder.decode(new Uint8Array(contentArray));
207-
const json = JSON.parse(content);
208-
if (json.extensions && json.extensions.CESIUM_RTC) {
209-
gltf.scene.position.fromArray(json.extensions.CESIUM_RTC.center);
210-
gltf.scene.updateMatrixWorld(true);
211-
}
182+
applyDeprecatedGltfUpAxis(gltf.scene, options.gltfUpAxis);
212183

213-
const init_mesh = function f_init(mesh) {
214-
mesh.frustumCulled = false;
215-
if (mesh.material) {
216-
if (options.overrideMaterials) {
217-
const oldMat = mesh.material;
218-
// Set up new material
219-
if (typeof (options.overrideMaterials) === 'object' &&
220-
options.overrideMaterials.isMaterial) {
221-
mesh.material = options.overrideMaterials;
222-
} else {
223-
mesh.material = new MeshBasicMaterial();
224-
}
225-
disposeThreeMaterial(oldMat);
226-
} else if (Capabilities.isLogDepthBufferSupported()
227-
&& mesh.material.isRawShaderMaterial
228-
&& !options.doNotPatchMaterial) {
229-
shaderUtils.patchMaterialForLogDepthSupport(mesh.material);
230-
console.warn('b3dm shader has been patched to add log depth buffer support');
231-
}
232-
ReferLayerProperties(mesh.material, options.layer);
233-
}
234-
};
184+
const shouldBePatchedForLogDepthSupport = Capabilities.isLogDepthBufferSupported() && !options.doNotPatchMaterial;
185+
if (options.frustumCulling === false || options.overrideMaterials || shouldBePatchedForLogDepthSupport || options.layer) {
235186
gltf.scene.traverse(init_mesh);
187+
}
236188

237-
resolve(gltf);
238-
};
239-
240-
const version = headerView.getUint32(4, true);
189+
// Apply relative center from Feature table.
190+
gltf.scene.position.copy(FT_RTC);
241191

242-
if (version === 1) {
243-
legacyGLTFLoader.parse(gltfBuffer, urlBase, onload);
244-
} else {
245-
glTFLoader.parse(gltfBuffer, urlBase, onload);
192+
// Apply relative center from gltf json.
193+
const contentArray = new Uint8Array(gltfBuffer, 20, headerView.getUint32(12, true));
194+
const content = utf8Decoder.decode(new Uint8Array(contentArray));
195+
const json = JSON.parse(content);
196+
if (json.extensions && json.extensions.CESIUM_RTC) {
197+
gltf.scene.position.fromArray(json.extensions.CESIUM_RTC.center);
198+
gltf.scene.updateMatrixWorld(true);
246199
}
247-
}));
248-
return Promise.all(promises).then(values => ({ gltf: values[1], batchTable: values[0] }));
200+
201+
return gltf;
202+
}).catch((e) => { throw new Error(e); }));
203+
return Promise.all(promises).then(values => ({ gltf: values[1], batchTable: values[0] })).catch((e) => { throw new Error(e); });
249204
} else {
250205
throw new Error('Invalid b3dm file.');
251206
}

src/Parser/GLTFParser.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import * as THREE from 'three';
2+
import { GLTFLoader } from 'ThreeExtended/loaders/GLTFLoader';
3+
import { DRACOLoader } from 'ThreeExtended/loaders/DRACOLoader';
4+
import { KTX2Loader } from 'ThreeExtended/loaders/KTX2Loader';
5+
import LegacyGLTFLoader from 'Parser/deprecated/LegacyGLTFLoader';
6+
7+
const matrixChangeUpVectorYtoZ = (new THREE.Matrix4()).makeRotationX(Math.PI / 2);
8+
9+
export const glTFLoader = new GLTFLoader();
10+
export const legacyGLTFLoader = new LegacyGLTFLoader();
11+
12+
/**
13+
* @module GLTFParser
14+
* @description Parses [glTF](https://www.khronos.org/gltf/) 1.0 and 2.0 files.
15+
*
16+
* Under the hood, glTF 2.0 files are parsed with THREE.GltfLoader() and GLTF 1.0 are parsed with the previous THREE
17+
* GltfLoader (for 1.0 glTF) that has been kept and maintained in iTowns.
18+
*/
19+
20+
/**
21+
* Enable loading gltf files with [Draco](https://google.github.io/draco/) geometry extension.
22+
*
23+
* @param {String} path path to draco library folder containing the JS and WASM decoder libraries. They can be found in
24+
* [itowns examples](https://github.com/iTowns/itowns/tree/master/examples/libs/draco).
25+
* @param {Object} [config] optional configuration for Draco decoder (see threejs'
26+
* [setDecoderConfig](https://threejs.org/docs/index.html?q=draco#examples/en/loaders/DRACOLoader.setDecoderConfig) that
27+
* is called under the hood with this configuration for details.
28+
*/
29+
export function enableDracoLoader(path, config) {
30+
if (!path) {
31+
throw new Error('Path to draco folder is mandatory');
32+
}
33+
const dracoLoader = new DRACOLoader();
34+
dracoLoader.setDecoderPath(path);
35+
if (config) {
36+
dracoLoader.setDecoderConfig(config);
37+
}
38+
glTFLoader.setDRACOLoader(dracoLoader);
39+
}
40+
41+
/**
42+
* Enable loading gltf files with [KTX2](https://www.khronos.org/ktx/) texture extension.
43+
*
44+
* @param {String} path path to ktx2 library folder containing the JS and WASM decoder libraries. They can be found in
45+
* [itowns examples](https://github.com/iTowns/itowns/tree/master/examples/libs/basis).
46+
* @param {THREE.WebGLRenderer} renderer the threejs renderer
47+
*/
48+
export function enableKtx2Loader(path, renderer) {
49+
if (!path || !renderer) {
50+
throw new Error('Path to ktx2 folder and renderer are mandatory');
51+
}
52+
const ktx2Loader = new KTX2Loader();
53+
ktx2Loader.setTranscoderPath(path);
54+
ktx2Loader.detectSupport(renderer);
55+
glTFLoader.setKTX2Loader(ktx2Loader);
56+
}
57+
58+
export default {
59+
/** Parses a gltf buffer to an object with threejs structures and applies a y-up to z-up conversion to align with
60+
* itowns convention. Essentially calls THREE.GltfLoader.parse() for glTF 2.0 files and the legacy threejs parser
61+
* for gtTF 1.0 files.
62+
* @param {ArrayBuffer} buffer - the glTF asset to parse, as an ArrayBuffer, JSON string or object.
63+
* @param {String} path - the base path from which to find subsequent glTF resources such as textures and .bin data files.
64+
* @return {Promise} - a promise that resolves with an object containing an Object that contains loaded parts:
65+
* .scene, .scenes, .cameras, .animations, and .asset.
66+
*/
67+
parse(buffer, path) {
68+
return new Promise((resolve, reject) => {
69+
if (!buffer || !path) {
70+
reject(new Error('[GLTFParser]: Buffer and path are mandatory to parse a glTF.'));
71+
return;
72+
}
73+
74+
// Apply y-up (gltf convention) to z-up (itowns convention) conversion
75+
const onload = (gltf) => {
76+
gltf.scene.applyMatrix4(matrixChangeUpVectorYtoZ);
77+
resolve(gltf);
78+
};
79+
const onError = (e) => {
80+
reject(new Error(`[GLTFParser]: Failed to parse gltf with error: ${e}`));
81+
};
82+
const headerView = new DataView(buffer, 0, 20);
83+
const version = headerView.getUint32(4, true);
84+
85+
if (version === 1) {
86+
legacyGLTFLoader.parse(buffer, path, onload, onError);
87+
} else {
88+
glTFLoader.parse(buffer, path, onload, onError);
89+
}
90+
});
91+
},
92+
};

0 commit comments

Comments
 (0)