diff --git a/examples/3dtiles_25d.html b/examples/3dtiles_25d.html deleted file mode 100644 index 41cc65ad15..0000000000 --- a/examples/3dtiles_25d.html +++ /dev/null @@ -1,120 +0,0 @@ - - - Itowns - 3D Tiles on 2.5D map example - - - - - - - - -
- - - - - - - - diff --git a/examples/3dtiles_basic.html b/examples/3dtiles_basic.html deleted file mode 100644 index 9db050b8a3..0000000000 --- a/examples/3dtiles_basic.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - Itowns - 3d-tiles example - - - - - - - - -
-
-

Feature Information:

-
-
- - - - - - - - diff --git a/examples/3dtiles_batch_table.html b/examples/3dtiles_batch_table.html deleted file mode 100644 index 4471f98eb4..0000000000 --- a/examples/3dtiles_batch_table.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - Itowns - 3d-tiles hierarchy example - - - - - - - - -
-
-

Feature Information:

-
-
- - - - - - - - diff --git a/examples/3dtiles_ion.html b/examples/3dtiles_ion.html deleted file mode 100644 index 3ef10b4227..0000000000 --- a/examples/3dtiles_ion.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - Itowns - 3d-tiles from Cesium ion example - - - - - - - - - - - - - -
-

This example displays a dataset representing extruded OSM buildings from Cesium ion - with Cesium default access token. Zoom to any place in the world to see the buildings.
- Buildings may appear to "fly" above the ground in some places, this is due to the combination - of precision errors of this dataset and of the 3D terrain we use in this example.
-

-
-
-
- - - - - - - - - diff --git a/examples/3dtiles_loader.html b/examples/3dtiles_loader.html index 3d701f7f60..66ac75c62f 100644 --- a/examples/3dtiles_loader.html +++ b/examples/3dtiles_loader.html @@ -11,7 +11,15 @@ @@ -21,12 +29,17 @@
Specify the URL of a tileset to load: - + +
+ + + +
+

+

Display feature information by clicking on it

Feature Information:

-
+
@@ -52,28 +65,17 @@ const { TMSSource, WMTSSource, OGC3DTilesSource, - ColorLayer, ElevationLayer, OGC3DTilesLayer, + ColorLayer, ElevationLayer, OGC3DTilesLayer, PNTS_SIZE_MODE, 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; + window.layer = null; // 3D Tiles layer - uri.searchParams.set('tileset', url); - history.pushState(null, '', `?${uri.searchParams.toString()}`); + const uri = new URL(location); - location.reload(); - } + window.gui = new dat.GUI(); - // ---- CREATE A GlobeView FOR SUPPORTING DATA VISUALIZATION ---- + // ---- Create a GlobeView ---- // Define camera initial position const placement = { @@ -85,7 +87,17 @@ const viewerDiv = document.getElementById('viewerDiv'); // Create a GlobeView - const view = new GlobeView(viewerDiv, placement, {}); + const view = new GlobeView(viewerDiv, placement, { + controls: { + minDistance: 100, + } + }); + + // 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); // Add ambient light to globally illuminates all objects const light = new AmbientLight(0x404040, 15); @@ -94,36 +106,68 @@ // 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 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', { + const colorLayer = new ColorLayer('Ortho', { ...config, source: new TMSSource(config.source), }); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); + view.addLayer(colorLayer); }); - // ---- ADD 3D TILES TILESET ---- + // ---- Add 3D terrain ---- - // 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); + // Add two elevation layers: world terrain and a more precise terrain for france + // These will deform iTowns globe geometry to represent terrain elevation. + function addElevationLayerFromConfig(config) { + config.source = new itowns.WMTSSource(config.source); + var elevationLayer = new itowns.ElevationLayer(config.id, config); + view.addLayer(elevationLayer); + } + itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); + itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + + // ---------- 3D Tiles loading + + function readURL() { + const url = document.getElementById('url').value; + + if (url) { + setUrl(url); + } + } + + function setUrl(url) { + if (!url) return; + + const input_url = document.getElementById('url'); + if (!input_url) return; + + uri.searchParams.set('copc', url); + history.replaceState(null, null, `?${uri.searchParams.toString()}`); + + input_url.value = url; + load(url); + } + + + function load(url) { + const source = new OGC3DTilesSource({ url }); - if (state.tileset) { - const source = new OGC3DTilesSource({ url: state.tileset }); - const layer = new OGC3DTilesLayer('3DTiles', { + if (window.layer) { + gui.removeFolder('Layer 3DTiles'); + view.removeLayer('3DTiles'); + view.notifyChange(); + window.layer.delete(); + } + + window.layer = new OGC3DTilesLayer('3DTiles', { source, + pntsSizeMode: PNTS_SIZE_MODE.ATTENUATED, }); // Add an event for picking the 3D Tiles layer and displaying @@ -131,20 +175,37 @@ const pickingArgs = { htmlDiv: document.getElementById('featureInfo'), view, - layer, + layer: window.layer, }; // Add the layer to our view - view.addLayer(layer).then((layer) => { + view.addLayer(window.layer).then((layer) => { zoomToLayer(view, layer); window.addEventListener('click', (event) => fillHTMLWithPickingInfo(event, pickingArgs), false); }); - debug.createOGC3DTilesDebugUI(menuGlobe.gui, view, layer); + window.layer.whenReady + .then(() => debug.createOGC3DTilesDebugUI(gui, view, window.layer)); + } + + function loadLyon() { + setUrl("https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/refs/heads/master/3DTiles/lyon1-4978/tileset.json"); + } + + function loadSete() { + setUrl("https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/pnts-sete-2021-0756_6256/tileset.json"); } - window.setURL = setURL; + function loadLille() { + setUrl("https://webimaging.lillemetropole.fr/externe/maillage/2020_mel_5cm/tileset.json"); + } + + window.readURL = readURL; + window.loadLyon = loadLyon; + window.loadSete = loadSete; + window.loadLille = loadLille; + diff --git a/examples/3dtiles_pointcloud.html b/examples/3dtiles_pointcloud.html deleted file mode 100644 index 25300f0727..0000000000 --- a/examples/3dtiles_pointcloud.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - Itowns - Pointcloud classification - - - - - - - - -
- Pointcloud classification - -
-
- - - - - - - - diff --git a/examples/config.json b/examples/config.json index 2fdeaa2c60..318b70e726 100644 --- a/examples/config.json +++ b/examples/config.json @@ -14,16 +14,8 @@ "geoid_geoidLayer": "Display geoid heights" }, - "3d Tiles": { - "3dtiles_basic": "on 3D map", - "3dtiles_25d": "On 2.5D map", - "3dtiles_batch_table": "Batch table Hierarchy extension", - "3dtiles_ion": "From Cesium ion", - "3dtiles_pointcloud": "Pointcloud classification" - }, - - "3D Tiles (new)": { - "3dtiles_loader": "3D Tiles tileset loader" + "3D Tiles": { + "3dtiles_loader": "3D Tiles loader" }, "Pointcloud": { diff --git a/src/Layer/OGC3DTilesLayer.js b/src/Layer/OGC3DTilesLayer.js index 1a75f9514a..c284bcee5b 100644 --- a/src/Layer/OGC3DTilesLayer.js +++ b/src/Layer/OGC3DTilesLayer.js @@ -126,8 +126,8 @@ class OGC3DTilesLayer extends GeometryLayer { * @param {String} [config.pntsSizeMode= PNTS_SIZE_MODE.VALUE] {@link PointsMaterial} Point cloud size mode (passed to {@link PointsMaterial}). * Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance * from point to camera. - * @param {Number} [config.pntsMinAttenuatedSize=1] Minimum scale used by 'ATTENUATED' size mode. - * @param {Number} [config.pntsMaxAttenuatedSize=7] Maximum scale used by 'ATTENUATED' size mode. + * @param {Number} [config.pntsMinAttenuatedSize=3] Minimum scale used by 'ATTENUATED' size mode. + * @param {Number} [config.pntsMaxAttenuatedSize=10] Maximum scale used by 'ATTENUATED' size mode. */ constructor(id, config) { super(id, new THREE.Group(), { source: config.source }); @@ -186,8 +186,8 @@ class OGC3DTilesLayer extends GeometryLayer { this.pntsShape = config.pntsShape ?? PNTS_SHAPE.CIRCLE; this.classification = config.classification ?? ClassificationScheme.DEFAULT; this.pntsSizeMode = config.pntsSizeMode ?? PNTS_SIZE_MODE.VALUE; - this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 1; - this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 7; + this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 3; + this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 10; } /** diff --git a/src/Renderer/PointsMaterial.js b/src/Renderer/PointsMaterial.js index 1a478b14ea..e6bc3fc194 100644 --- a/src/Renderer/PointsMaterial.js +++ b/src/Renderer/PointsMaterial.js @@ -156,7 +156,7 @@ class PointsMaterial extends THREE.ShaderMaterial { /** * @class PointsMaterial * @param {object} [options={}] The options - * @param {number} [options.size=0] size point + * @param {number} [options.size=1] point size * @param {number} [options.mode=PNTS_MODE.COLOR] display mode. * @param {number} [options.shape=PNTS_SHAPE.CIRCLE] rendered points shape. * @param {THREE.Vector4} [options.overlayColor=new THREE.Vector4(0, 0, 0, 0)] overlay color. diff --git a/test/functional/3dtiles_25d.js b/test/functional/3dtiles_25d.js deleted file mode 100644 index 533b3c6ca3..0000000000 --- a/test/functional/3dtiles_25d.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; - -describe('3dtiles_25d', function _() { - let result; - before(async () => { - result = await loadExample( - 'examples/3dtiles_25d.html', - this.fullTitle(), - ); - }); - - it('should run', async () => { - assert.ok(result); - }); - - it('should pick the planar layer', async () => { - const layers = await page.evaluate( - () => view.pickObjectsAt({ - x: 194, - y: 100, - }) - .map(p => p.layer.id), - ); - - assert.ok(layers.indexOf('planar') >= 0); - }); -}); diff --git a/test/functional/3dtiles_basic.js b/test/functional/3dtiles_basic.js deleted file mode 100644 index 6ffa74bc58..0000000000 --- a/test/functional/3dtiles_basic.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert'; - -describe('3dtiles_basic', function _() { - let result; - before(async () => { - result = await loadExample('examples/3dtiles_basic.html', this.fullTitle()); - }); - - it('should run', async () => { - assert.ok(result); - }); - - it('should return the dragon and the globe', async () => { - const layers = await page.evaluate( - () => view.pickObjectsAt({ x: 195, y: 146 }).map(p => p.layer.id), - ); - - assert.ok(layers.indexOf('globe') >= 0); - assert.ok(layers.indexOf('3d-tiles-discrete-lod') >= 0); - assert.equal(layers.indexOf('3d-tiles-request-volume'), -1); - }); - - it('should return points', async function __() { - // click on the 'goto pointcloud' button - await page.evaluate(() => d.zoom()); - - await waitUntilItownsIsIdle(this.test.fullTitle()); - - const pickingCount = await page.evaluate(() => view.pickObjectsAt( - { x: 200, y: 150 }, - 1, - '3d-tiles-request-volume', - ).length); - assert.ok(pickingCount > 0); - }); - - it('should remove GeometryLayer', async () => { - const countGeometryLayerStart = await page.evaluate(() => view.getLayers(l => l.isGeometryLayer).length); - const attachedGeometricLayerCount = await page.evaluate(() => { - const attachedLayers = view.getLayerById('3d-tiles-discrete-lod').attachedLayers.filter(l => l.isGeometryLayer); - return attachedLayers.length; - }); - await page.evaluate(() => view.removeLayer('3d-tiles-discrete-lod')); - const countGeometryLayerEnd = await page.evaluate(() => view.getLayers(l => l.isGeometryLayer).length); - // the attached GeometryLayers are, also, removed. - assert.equal(countGeometryLayerStart - countGeometryLayerEnd, 1 + attachedGeometricLayerCount); - }); -}); diff --git a/test/functional/3dtiles_batch_table.js b/test/functional/3dtiles_batch_table.js deleted file mode 100644 index 662a082de4..0000000000 --- a/test/functional/3dtiles_batch_table.js +++ /dev/null @@ -1,77 +0,0 @@ -import assert from 'assert'; - -describe('3dtiles_batch_table', function _() { - let result; - before(async () => { - result = await loadExample( - 'examples/3dtiles_batch_table.html', - this.fullTitle(), - ); - }); - - it('should run', async () => { - assert.ok(result); - }); - - it('should return the globe and 3d tiles layer', async () => { - const layers = await page.evaluate( - () => view.pickObjectsAt({ - x: 218, - y: 90, - }) - .map(p => p.layer.id), - ); - - assert.ok(layers.indexOf('globe') >= 0); - assert.ok(layers.indexOf('3d-tiles-bt-hierarchy') >= 0); - }); - - // Verifies that the batch id, batch table and batch table hierarchy - // extension picked information are correct for object at { x: 218, y: 90 } - it('should return the batch table and batch hierarchy picked information', - async function _it() { - // Picks the object at (218,90) and gets its closest c3DTileFeature - const pickResult = await page.evaluate( - () => { - const intersects = view.pickObjectsAt({ - x: 218, - y: 90, - }); - const layer = view.getLayerById('3d-tiles-bt-hierarchy'); - const c3DTileFeaturePicked = layer.getC3DTileFeatureFromIntersectsArray(intersects); - return { - info: c3DTileFeaturePicked.getInfo(), - batchId: c3DTileFeaturePicked.batchId, - }; - }, - ); - - // Create the expected object - const expectedPickingInfo = { - batchTable: { - height: 10, - area: 20, - }, - extensions: { - '3DTILES_batch_table_hierarchy': { - wall: { - wall_name: 'wall2', - wall_paint: 'blue', - wall_windows: 4, - }, - building: { - building_name: 'building2', - building_area: 39.3, - }, - zone: { - zone_name: 'zone0', - zone_buildings: 3, - }, - }, - }, - }; - - assert.equal(pickResult.batchId, 29); - assert.deepStrictEqual(pickResult.info, expectedPickingInfo); - }); -}); diff --git a/test/functional/3dtiles_ion.js b/test/functional/3dtiles_ion.js deleted file mode 100644 index d402d22442..0000000000 --- a/test/functional/3dtiles_ion.js +++ /dev/null @@ -1,40 +0,0 @@ -import assert from 'assert'; - -describe('3dtiles_ion', function _() { - let result; - before(async () => { - result = await loadExample( - 'examples/3dtiles_ion.html', - this.fullTitle(), - ); - }); - - it('should run', async () => { - assert.ok(result); - }); - - it('should pick the globe', async () => { - const layers = await page.evaluate( - () => view.pickObjectsAt({ x: 195, y: 146 }).map(p => p.layer.id), - ); - - assert.ok(layers.indexOf('globe') >= 0); - }); - - it('should pick a building from 3D', async () => { - await page.evaluate(() => { - const lyonExtent = new itowns.Extent('EPSG:4326', 4.85, 4.9, 45.75, 45.77); - itowns.CameraUtils.transformCameraToLookAtTarget(view, view.camera3D, lyonExtent); - }); - await waitUntilItownsIsIdle(this.fullTitle()); - const layers = await page.evaluate( - () => view.pickObjectsAt({ - x: 166, - y: 65, - }) - .map(p => p.layer.id), - ); - - assert.ok(layers.indexOf('3d-tiles-cesium-ion') >= 0); - }); -}); diff --git a/test/functional/3dtiles_pointcloud.js b/test/functional/3dtiles_pointcloud.js deleted file mode 100644 index 4c88639e8d..0000000000 --- a/test/functional/3dtiles_pointcloud.js +++ /dev/null @@ -1,49 +0,0 @@ -import assert from 'assert'; - -describe('3dtiles_pointcloud', function _() { - let result; - before(async () => { - result = await loadExample( - 'examples/3dtiles_pointcloud.html', - this.fullTitle(), - ); - }); - - it('should run', async () => { - assert.ok(result); - }); - - it('should load 3d tile layer', async () => { - const exist = await page.evaluate( - () => view.getLayerById('3d-tiles-sete') != null, - ); - - assert.ok(exist); - }); - - it('should load points', async () => { - const objects = await page.evaluate( - () => { - const res = []; - if (view.scene) { - const objects3d = view.scene; - objects3d.traverse((obj) => { - if (obj.isPoints) { - if (obj.layer) { - res.push(obj.layer.id); - } - } - }); - } - return res; - }); - assert.ok(objects.indexOf('3d-tiles-sete') >= 0); - }); - - it('Add 3dtiles layer with batch table', async () => { - const batchLength = await page.evaluate( - () => view.getLayerById('3d-tiles-sete').root.batchTable.batchLength, - ); - assert.equal(batchLength, 1858); - }); -}); diff --git a/utils/debug/OGC3DTilesDebug.js b/utils/debug/OGC3DTilesDebug.js index 8a9e8c086e..7963f4314c 100644 --- a/utils/debug/OGC3DTilesDebug.js +++ b/utils/debug/OGC3DTilesDebug.js @@ -59,7 +59,6 @@ export default function createOGC3DTilesDebugUI(datDebugTool, view, _3dTileslaye gui.add(_3dTileslayer, 'pntsSizeMode', PNTS_SIZE_MODE).name('Pnts size mode').onChange(() => { view.notifyChange(view.camera.camera3D); }); - gui.add(_3dTileslayer, 'pntsMinAttenuatedSize', 0, 15).name('Min attenuated size').onChange(() => { view.notifyChange(view.camera.camera3D); });