Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/google3d tiles #2124

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@

"Parser": [
"GeoJsonParser",
"GLTFParser",
"GpxParser",
"VectorTileParser",
"CameraCalibrationParser",
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/Fundamentals.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ with `{@link WFSSource}` and [Tile Map Service](https://wiki.osgeo.org/wiki/Tile

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
[Entwine](https://entwine.io/) (`{@link EntwinePointTileSource}`) 3D point clouds, [3DTiles](https://www.ogc.org/standards/3DTiles)
mesh (b3dm) and point clouds (pnts) from web servers (`{@link C3DTilesSource}`) and from Cesium ion `{@link C3DTilesIonSource}`,
mesh (b3dm) and point clouds (pnts) from web servers (`{@link C3DTilesSource}`), Cesium ion `{@link C3DTilesIonSource}` and from Google api `{@link C3DTilesGoogleSource}`,
[GeoJSON](https://geojson.org/) with `{@link FileSource}` and `{@link GeoJsonParser}`,
[KML](https://www.ogc.org/standards/kml) with `{@link FileSource}` and `{@link KMLParser}`, [GPX](https://www.topografix.com/gpx.asp)
with `{@link FileSource}` and `{@link GpxParser}` and oriented images with `{@link OrientedImageSource}`.
Expand All @@ -68,7 +68,7 @@ Several specific types of `Layers` exist, the use of which depends on the data t
- `{@link PointCloudLayer}` can be used to display 3D point clouds. Any point cloud formats are supported as long as the corresponding `Source` is provided.
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
`C3DTilesLayer`.
- `{@link C3DTilesLayer}` can be used to display 3D Tiles layer (only b3dm and pnts).
- `{@link C3DTilesLayer}` can be used to display 3D Tiles datasets in version 1.0 (b3dm, pnts and gltf tiles are supported).
- `{@link OrientedImageLayer}` can be used to display oriented images.


Expand Down
3 changes: 2 additions & 1 deletion src/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export { default as OrientedImageSource } from 'Source/OrientedImageSource';
export { default as PotreeSource } from 'Source/PotreeSource';
export { default as C3DTilesSource } from 'Source/C3DTilesSource';
export { default as C3DTilesIonSource } from 'Source/C3DTilesIonSource';
export { default as C3DTilesGoogleSource } from 'Source/C3DTilesGoogleSource';
export { default as EntwinePointTileSource } from 'Source/EntwinePointTileSource';

// Parsers provided by default in iTowns
Expand All @@ -91,7 +92,7 @@ export { default as LASParser } from 'Parser/LASParser';
export { default as ISGParser } from 'Parser/ISGParser';
export { default as GDFParser } from 'Parser/GDFParser';
export { default as GTXParser } from 'Parser/GTXParser';
export { enableDracoLoader, enableKtx2Loader, glTFLoader, legacyGLTFLoader } from 'Parser/B3dmParser';
export { default as GLTFParser, enableDracoLoader, enableKtx2Loader, glTFLoader, legacyGLTFLoader } from 'Parser/GLTFParser';

// 3D Tiles classes and extensions
// Exported to allow one to implement its own 3D Tiles extension which needs to
Expand Down
187 changes: 71 additions & 116 deletions src/Parser/B3dmParser.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import * as THREE from 'three';
import Capabilities from 'Core/System/Capabilities';
import { GLTFLoader } from 'ThreeExtended/loaders/GLTFLoader';
import { DRACOLoader } from 'ThreeExtended/loaders/DRACOLoader';
import { KTX2Loader } from 'ThreeExtended/loaders/KTX2Loader';
import LegacyGLTFLoader from 'Parser/deprecated/LegacyGLTFLoader';
import shaderUtils from 'Renderer/Shader/ShaderUtils';
import C3DTBatchTable from 'Core/3DTiles/C3DTBatchTable';
import ReferLayerProperties from 'Layer/ReferencingLayerProperties';
import Capabilities from 'Core/System/Capabilities';
import { MeshBasicMaterial } from 'three';
import disposeThreeMaterial from 'Utils/ThreeUtils';
import shaderUtils from 'Renderer/Shader/ShaderUtils';
import ReferLayerProperties from 'Layer/ReferencingLayerProperties';
import GLTFParser from './GLTFParser';

const matrixChangeUpVectorZtoY = (new THREE.Matrix4()).makeRotationX(Math.PI / 2);
// For gltf rotation
const matrixChangeUpVectorZtoX = (new THREE.Matrix4()).makeRotationZ(-Math.PI / 2);
const matrixChangeUpVectorYtoZInv = (new THREE.Matrix4()).makeRotationX(-Math.PI / 2);
const matrixChangeUpVectorXtoZ = (new THREE.Matrix4()).makeRotationZ(-Math.PI / 2);

const utf8Decoder = new TextDecoder();

export const glTFLoader = new GLTFLoader();

export const legacyGLTFLoader = new LegacyGLTFLoader();

/**
* 3D Tiles pre-1.0 contain not standardized and specific uniforms that we filter out to avoid shader compilation errors
* This method is passed to scene.traverse and applied to all 3D objects of the loaded gltf.
* @param {THREE.Object3D} obj - 3D object of the gltf hierarchy
*/
function filterUnsupportedSemantics(obj) {
// see GLTFLoader GLTFShader.prototype.update function
const supported = [
Expand All @@ -44,77 +41,45 @@ function filterUnsupportedSemantics(obj) {
}

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

/**
* Enable KTX2 decoding for gltf. This library is mandatory to load b3dm and gltf with KTX2 compression.
*
* The KTX2 library files are in folder itowns/examples/libs/basis/.
* You must indicate this path when you want to enable KTX2 decoding.
* For more information about KTX2, read /itowns/examples/libs/basis/README.md.
*
* @example <caption>Enable ktx2 decoder</caption>
* // if you copied /itowns/examples/libs/draco/ to the root folder of your project,you can set the path to './'.
* itowns.enableKtx2Loader('./', view.mainLoop.gfxEngine.renderer);
*
* @param {string} path path to KTX2 library folder.
* @param {THREE.WebGLRenderer} renderer the threejs renderer
* @module B3dmParser
*/
export function enableKtx2Loader(path, renderer) {
if (!path || !renderer) {
throw new Error('Path to ktx2 folder and renderer are mandatory');
}
const ktx2Loader = new KTX2Loader();
ktx2Loader.setTranscoderPath(path);
ktx2Loader.detectSupport(renderer);
glTFLoader.setKTX2Loader(ktx2Loader);
}

export default {
/** Parse b3dm buffer and extract THREE.Scene and batch table
* @param {ArrayBuffer} buffer - the b3dm buffer.
* @param {Object} options - additional properties.
* @param {string=} [options.gltfUpAxis='Y'] - embedded glTF model up axis.
* @param {string} options.urlBase - the base url of the b3dm file (used to fetch textures for the embedded glTF model).
* @param {boolean=} [options.doNotPatchMaterial='false'] - disable patching material with logarithmic depth buffer support.
* @param {boolean=} [options.doNotPatchMaterial=false] - disable patching material with logarithmic depth buffer support.
* @param {float} [options.opacity=1.0] - the b3dm opacity.
* @param {boolean|Material=} [options.overrideMaterials='false'] - override b3dm's embedded glTF materials. If
* @param {boolean=} [options.frustumCulled=false] - enable frustum culling.
* @param {boolean|Material=} [options.overrideMaterials=false] - override b3dm's embedded glTF materials. If
* true, a threejs [MeshBasicMaterial](https://threejs.org/docs/index.html?q=meshbasic#api/en/materials/MeshBasicMaterial)
* is set up. config.overrideMaterials can also be a threejs [Material](https://threejs.org/docs/index.html?q=material#api/en/materials/Material)
* in which case it will be the material used to override.
* @return {Promise} - a promise that resolves with an object containig a THREE.Scene (gltf) and a batch table (batchTable).
*
*/
parse(buffer, options) {
const gltfUpAxis = options.gltfUpAxis;
const urlBase = options.urlBase;
const frustumCulled = options.frustumCulled === true;

if (!buffer) {
throw new Error('No array buffer provided.');
}
Expand Down Expand Up @@ -186,66 +151,56 @@ export default {
const gltfBuffer = buffer.slice(posGltf);
const headerView = new DataView(gltfBuffer, 0, 20);

promises.push(new Promise((resolve/* , reject */) => {
const onload = (gltf) => {
for (const scene of gltf.scenes) {
scene.traverse(filterUnsupportedSemantics);
}
// Rotation managed
if (gltfUpAxis === undefined || gltfUpAxis === 'Y') {
gltf.scene.applyMatrix4(matrixChangeUpVectorZtoY);
} else if (gltfUpAxis === 'X') {
gltf.scene.applyMatrix4(matrixChangeUpVectorZtoX);
const init_mesh = function f_init(mesh) {
mesh.frustumCulled = frustumCulled;
Desplandis marked this conversation as resolved.
Show resolved Hide resolved
if (mesh.material) {
if (options.overrideMaterials) {
const oldMat = mesh.material;
// Set up new material
if (typeof (options.overrideMaterials) === 'object' &&
options.overrideMaterials.isMaterial) {
mesh.material = options.overrideMaterials;
} else {
mesh.material = new MeshBasicMaterial();
}
disposeThreeMaterial(oldMat);
} else if (Capabilities.isLogDepthBufferSupported()
&& mesh.material.isRawShaderMaterial
&& !options.doNotPatchMaterial) {
shaderUtils.patchMaterialForLogDepthSupport(mesh.material);
console.warn('glTF shader has been patched to add log depth buffer support');
}
ReferLayerProperties(mesh.material, options.layer);
}
};

// Apply relative center from Feature table.
gltf.scene.position.copy(FT_RTC);
promises.push(GLTFParser.parse(gltfBuffer, options).then((gltf) => {
for (const scene of gltf.scenes) {
scene.traverse(filterUnsupportedSemantics);
}

// Apply relative center from gltf json.
const contentArray = new Uint8Array(gltfBuffer, 20, headerView.getUint32(12, true));
const content = utf8Decoder.decode(new Uint8Array(contentArray));
const json = JSON.parse(content);
if (json.extensions && json.extensions.CESIUM_RTC) {
gltf.scene.position.fromArray(json.extensions.CESIUM_RTC.center);
gltf.scene.updateMatrixWorld(true);
}
applyDeprecatedGltfUpAxis(gltf.scene, options.gltfUpAxis);

const init_mesh = function f_init(mesh) {
mesh.frustumCulled = false;
if (mesh.material) {
if (options.overrideMaterials) {
const oldMat = mesh.material;
// Set up new material
if (typeof (options.overrideMaterials) === 'object' &&
options.overrideMaterials.isMaterial) {
mesh.material = options.overrideMaterials;
} else {
mesh.material = new MeshBasicMaterial();
}
disposeThreeMaterial(oldMat);
} else if (Capabilities.isLogDepthBufferSupported()
&& mesh.material.isRawShaderMaterial
&& !options.doNotPatchMaterial) {
shaderUtils.patchMaterialForLogDepthSupport(mesh.material);
console.warn('b3dm shader has been patched to add log depth buffer support');
}
ReferLayerProperties(mesh.material, options.layer);
}
};
const shouldBePatchedForLogDepthSupport = Capabilities.isLogDepthBufferSupported() && !options.doNotPatchMaterial;
if (options.frustumCulling === false || options.overrideMaterials || shouldBePatchedForLogDepthSupport || options.layer) {
gltf.scene.traverse(init_mesh);
}

resolve(gltf);
};

const version = headerView.getUint32(4, true);
// Apply relative center from Feature table.
gltf.scene.position.copy(FT_RTC);

if (version === 1) {
legacyGLTFLoader.parse(gltfBuffer, urlBase, onload);
} else {
glTFLoader.parse(gltfBuffer, urlBase, onload);
// Apply relative center from gltf json.
const contentArray = new Uint8Array(gltfBuffer, 20, headerView.getUint32(12, true));
const content = utf8Decoder.decode(new Uint8Array(contentArray));
const json = JSON.parse(content);
if (json.extensions && json.extensions.CESIUM_RTC) {
gltf.scene.position.fromArray(json.extensions.CESIUM_RTC.center);
gltf.scene.updateMatrixWorld(true);
}
}));
return Promise.all(promises).then(values => ({ gltf: values[1], batchTable: values[0] }));

return gltf;
}).catch((e) => { throw new Error(e); }));
return Promise.all(promises).then(values => ({ gltf: values[1], batchTable: values[0] })).catch((e) => { throw new Error(e); });
} else {
throw new Error('Invalid b3dm file.');
}
Expand Down
92 changes: 92 additions & 0 deletions src/Parser/GLTFParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as THREE from 'three';
import { GLTFLoader } from 'ThreeExtended/loaders/GLTFLoader';
import { DRACOLoader } from 'ThreeExtended/loaders/DRACOLoader';
import { KTX2Loader } from 'ThreeExtended/loaders/KTX2Loader';
import LegacyGLTFLoader from 'Parser/deprecated/LegacyGLTFLoader';

const matrixChangeUpVectorYtoZ = (new THREE.Matrix4()).makeRotationX(Math.PI / 2);

export const glTFLoader = new GLTFLoader();
export const legacyGLTFLoader = new LegacyGLTFLoader();

/**
* @module GLTFParser
jailln marked this conversation as resolved.
Show resolved Hide resolved
* @description Parses [glTF](https://www.khronos.org/gltf/) 1.0 and 2.0 files.
*
* Under the hood, glTF 2.0 files are parsed with THREE.GltfLoader() and GLTF 1.0 are parsed with the previous THREE
* GltfLoader (for 1.0 glTF) that has been kept and maintained in iTowns.
*/

/**
* Enable loading gltf files with [Draco](https://google.github.io/draco/) geometry extension.
*
* @param {String} path path to draco library folder containing the JS and WASM decoder libraries. They can be found in
* [itowns examples](https://github.com/iTowns/itowns/tree/master/examples/libs/draco).
* @param {Object} [config] optional configuration for Draco decoder (see threejs'
* [setDecoderConfig](https://threejs.org/docs/index.html?q=draco#examples/en/loaders/DRACOLoader.setDecoderConfig) that
* is called under the hood with this configuration for details.
*/
export function enableDracoLoader(path, config) {
if (!path) {
throw new Error('Path to draco folder is mandatory');
}
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(path);
if (config) {
dracoLoader.setDecoderConfig(config);
}
glTFLoader.setDRACOLoader(dracoLoader);
}

/**
* Enable loading gltf files with [KTX2](https://www.khronos.org/ktx/) texture extension.
*
* @param {String} path path to ktx2 library folder containing the JS and WASM decoder libraries. They can be found in
* [itowns examples](https://github.com/iTowns/itowns/tree/master/examples/libs/basis).
* @param {THREE.WebGLRenderer} renderer the threejs renderer
*/
export function enableKtx2Loader(path, renderer) {
if (!path || !renderer) {
throw new Error('Path to ktx2 folder and renderer are mandatory');
}
const ktx2Loader = new KTX2Loader();
ktx2Loader.setTranscoderPath(path);
ktx2Loader.detectSupport(renderer);
glTFLoader.setKTX2Loader(ktx2Loader);
}

export default {
/** Parses a gltf buffer to an object with threejs structures and applies a y-up to z-up conversion to align with
* itowns convention. Essentially calls THREE.GltfLoader.parse() for glTF 2.0 files and the legacy threejs parser
* for gtTF 1.0 files.
* @param {ArrayBuffer} buffer - the glTF asset to parse, as an ArrayBuffer, JSON string or object.
* @param {String} path - the base path from which to find subsequent glTF resources such as textures and .bin data files.
* @return {Promise} - a promise that resolves with an object containing an Object that contains loaded parts:
* .scene, .scenes, .cameras, .animations, and .asset.
*/
parse(buffer, path) {
return new Promise((resolve, reject) => {
if (!buffer || !path) {
reject(new Error('[GLTFParser]: Buffer and path are mandatory to parse a glTF.'));
return;
}

// Apply y-up (gltf convention) to z-up (itowns convention) conversion
const onload = (gltf) => {
gltf.scene.applyMatrix4(matrixChangeUpVectorYtoZ);
resolve(gltf);
};
const onError = (e) => {
reject(new Error(`[GLTFParser]: Failed to parse gltf with error: ${e}`));
};
const headerView = new DataView(buffer, 0, 20);
const version = headerView.getUint32(4, true);

if (version === 1) {
legacyGLTFLoader.parse(buffer, path, onload, onError);
} else {
glTFLoader.parse(buffer, path, onload, onError);
}
});
},
};
Loading
Loading