-
Notifications
You must be signed in to change notification settings - Fork 303
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(3dtiles): add new OGC3DTilesLayer using 3d-tiles-renderer-js
Deprecate C3DTilesLayer (replaced by OGC3DTilesLayer). Add new iGLTFLoader that loads gltf 1.0 and 2.0 files.
- Loading branch information
Showing
29 changed files
with
2,883 additions
and
1,676 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
<html> | ||
<head> | ||
<title>Itowns - 3D Tiles loader</title> | ||
|
||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
|
||
<link rel="stylesheet" type="text/css" href="css/example.css"> | ||
<link rel="stylesheet" type="text/css" href="css/LoadingScreen.css"> | ||
|
||
<style type="text/css"> | ||
#description { | ||
z-index: 2; | ||
right: 10px; | ||
} | ||
</style> | ||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script> | ||
</head> | ||
<body> | ||
<div id="viewerDiv"></div> | ||
<div id="description">Specify the URL of a tileset to load: | ||
<input type="text" id="url" /> | ||
<button onclick="setURL(document.getElementById('url').value)"> | ||
Load | ||
</button> | ||
<hr /> | ||
<p><b>Feature Information:</b></p> | ||
<div id="featureInfo"></div> | ||
</div> | ||
|
||
<script src="js/GUI/GuiTools.js"></script> | ||
<script src="../dist/itowns.js"></script> | ||
<script src="js/GUI/LoadingScreen.js"></script> | ||
<script src="../dist/debug.js"></script> | ||
|
||
<script type="importmap"> | ||
{ | ||
"imports": { | ||
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js", | ||
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/" | ||
} | ||
} | ||
</script> | ||
|
||
<script type="module"> | ||
import { AmbientLight } from 'three'; | ||
import { | ||
zoomToLayer, | ||
fillHTMLWithPickingInfo, | ||
} from './jsm/OGC3DTilesHelper.js'; | ||
|
||
const { | ||
TMSSource, WMTSSource, OGC3DTilesSource, | ||
ColorLayer, ElevationLayer, OGC3DTilesLayer, | ||
GlobeView, Coordinates, Fetcher, | ||
} = itowns; | ||
|
||
const uri = new URL(location); | ||
const state = { | ||
// URL to tileset JSON | ||
tileset: uri.searchParams.get('tileset'), | ||
// Cesium ION / | ||
assetId: uri.searchParams.get('assetId'), | ||
}; | ||
|
||
function setURL(url) { | ||
if (!url) return; | ||
|
||
uri.searchParams.set('tileset', url); | ||
history.pushState(null, '', `?${uri.searchParams.toString()}`); | ||
|
||
location.reload(); | ||
} | ||
|
||
// ---- CREATE A GlobeView FOR SUPPORTING DATA VISUALIZATION ---- | ||
|
||
// Define camera initial position | ||
const placement = { | ||
coord: new Coordinates('EPSG:4326', 2.351323, 48.856712), | ||
range: 12500000, | ||
}; | ||
|
||
// `viewerDiv` will contain iTowns' rendering area (`<canvas>`) | ||
const viewerDiv = document.getElementById('viewerDiv'); | ||
|
||
// Create a GlobeView | ||
const view = new GlobeView(viewerDiv, placement, {}); | ||
|
||
// Add ambient light to globally illuminates all objects | ||
const light = new AmbientLight(0x404040, 15); | ||
view.scene.add(light); | ||
|
||
// Setup loading screen | ||
setupLoadingScreen(viewerDiv, view); | ||
|
||
// Setup debug menu | ||
const menuGlobe = new GuiTools('menuDiv', view, 300); | ||
debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer); | ||
|
||
|
||
// ---- ADD A BASEMAP ---- | ||
|
||
// Add one imagery layer to the scene. This layer's properties are | ||
// defined in a json file, but it cou ld be defined as a plain js | ||
// object. See `Layer` documentation for more info. | ||
Fetcher.json('./layers/JSONLayers/OPENSM.json').then((config) => { | ||
const layer = new ColorLayer('Ortho', { | ||
...config, | ||
source: new TMSSource(config.source), | ||
}); | ||
view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); | ||
}); | ||
|
||
// ---- ADD 3D TILES TILESET ---- | ||
|
||
// Enable various compression support for 3D Tiles tileset: | ||
// - `KHR_draco_mesh_compression` mesh compression extension | ||
// - `KHR_texture_basisu` texture compresion extension | ||
itowns.enableDracoLoader('./libs/draco/'); | ||
itowns.enableKtx2Loader('./lib/basis/', view.renderer); | ||
|
||
if (state.tileset) { | ||
const source = new OGC3DTilesSource({ url: state.tileset }); | ||
const layer = new OGC3DTilesLayer('3DTiles', { | ||
source, | ||
}); | ||
|
||
// Add an event for picking the 3D Tiles layer and displaying | ||
// information about the picked feature in an html div | ||
const pickingArgs = { | ||
htmlDiv: document.getElementById('featureInfo'), | ||
view, | ||
layer, | ||
}; | ||
|
||
// Add the layer to our view | ||
view.addLayer(layer).then((layer) => { | ||
zoomToLayer(view, layer); | ||
window.addEventListener('click', | ||
(event) => fillHTMLWithPickingInfo(event, pickingArgs), false); | ||
}); | ||
|
||
debug.createOGC3DTilesDebugUI(menuGlobe.gui, view, layer); | ||
} | ||
|
||
window.setURL = setURL; | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
module.exports = { | ||
extends: [ | ||
'eslint-config-airbnb-base', | ||
'eslint-config-airbnb-base/rules/strict', | ||
'../.eslintrc.cjs', | ||
], | ||
parserOptions: { | ||
ecmaVersion: 13, | ||
sourceType: 'module', | ||
ecmaFeatures: { | ||
impliedStrict: true, | ||
}, | ||
}, | ||
env: { | ||
browser: true, | ||
es6: true, | ||
amd: true, | ||
commonjs: true, | ||
}, | ||
globals: { | ||
itowns: true, | ||
}, | ||
rules: { | ||
'prefer-arrow-callback': 'off', | ||
'object-shorthand': 'off', | ||
'no-param-reassign': ['error', { props: false }], | ||
'no-mixed-operators': ['error', { allowSamePrecedence: true }], | ||
'prefer-template': 'off', | ||
'prefer-rest-params': 'off', | ||
'arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }], | ||
|
||
// deactivated rules for `examples/` | ||
'no-console': 'off', | ||
// TODO reactivate all the following rules | ||
'no-underscore-dangle': 'off', | ||
|
||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { MathUtils, Vector3 } from 'three'; | ||
|
||
const { Coordinates, Extent, CameraUtils } = itowns; | ||
|
||
/** | ||
* Function allowing picking on a given 3D tiles layer and filling an html div | ||
* with information on the picked feature. | ||
* @param {MouseEvent} event | ||
* @param {Object} pickingArg | ||
* @param {HTMLDivElement} pickingArg.htmlDiv - div element which contains the | ||
* picked information | ||
* @param {GlobeView} picking.view - iTowns view where the picking must be done | ||
* @param {OGC3DTilesLayer} pickingArg.layer - the layer on which the picking | ||
* must be done | ||
*/ | ||
export function fillHTMLWithPickingInfo(event, pickingArg) { | ||
const { htmlDiv, view, layer } = pickingArg; | ||
|
||
// Remove content already in html div | ||
while (htmlDiv.firstChild) { | ||
htmlDiv.removeChild(htmlDiv.firstChild); | ||
} | ||
|
||
// Get intersected objects | ||
const intersects = view.pickObjectsAt(event, 5, layer); | ||
|
||
// Get information from intersected objects (from the batch table and | ||
// eventually the 3D Tiles extensions | ||
const closestC3DTileFeature = | ||
layer.getC3DTileFeatureFromIntersectsArray(intersects); | ||
|
||
if (closestC3DTileFeature) { | ||
// eslint-disable-next-line | ||
htmlDiv.appendChild(createHTMLListFromObject(closestC3DTileFeature)); | ||
} | ||
} | ||
|
||
function zoomToSphere(view, tile, sphere) { | ||
const transform = tile.cached.transform; | ||
|
||
const center = new Vector3().fromArray(sphere).applyMatrix4(transform); | ||
const radius = sphere[3] * transform.getMaxScaleOnAxis(); | ||
|
||
// Get the distance to sphere where the diameter cover the whole screen | ||
// This is similar to SSE computation where sse = screen height. | ||
const fov = view.camera3D.fov * MathUtils.DEG2RAD; | ||
const distance = radius * Math.tan(fov * 2); | ||
|
||
return { | ||
coord: new Coordinates('EPSG:4978', center), | ||
range: distance + radius, | ||
}; | ||
} | ||
|
||
function zoomToBox(view, tile, box) { | ||
const radius = Math.max( | ||
new Vector3().fromArray(box, 3).length(), | ||
new Vector3().fromArray(box, 6).length(), | ||
new Vector3().fromArray(box, 9).length(), | ||
); | ||
|
||
// Approximate zoomToBox with sphere | ||
const sphere = [box[0], box[1], box[2], radius]; | ||
return zoomToSphere(view, tile, sphere); | ||
} | ||
|
||
function zoomToRegion(view, region) { | ||
const extent = new Extent('EPSG:4326', | ||
region[0] * MathUtils.RAD2DEG, // west | ||
region[2] * MathUtils.RAD2DEG, // east | ||
region[1] * MathUtils.RAD2DEG, // south | ||
region[3] * MathUtils.RAD2DEG, // north | ||
); | ||
|
||
return CameraUtils.getCameraTransformOptionsFromExtent( | ||
view, | ||
view.camera3D, | ||
extent, | ||
); | ||
} | ||
|
||
function zoomToTile(view, tile) { | ||
const { region, box, sphere } = tile.boundingVolume; | ||
|
||
let cameraTransform; | ||
if (region) { | ||
cameraTransform = zoomToRegion(view, region); | ||
} else if (box) { | ||
cameraTransform = zoomToBox(view, tile, box); | ||
} else { | ||
cameraTransform = zoomToSphere(view, tile, sphere); | ||
} | ||
|
||
view.controls.lookAtCoordinate({ | ||
coord: cameraTransform.coord, | ||
range: 1.25 * cameraTransform.range, // zoom out a little bit | ||
tilt: 60, | ||
}); | ||
} | ||
|
||
export function zoomToLayer(view, layer) { | ||
const root = layer.tilesRenderer.root; | ||
|
||
zoomToTile(view, root); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.