diff --git a/examples/3dtiles_loader.html b/examples/3dtiles_loader.html index 56ae5b01f1..44cb652a26 100644 --- a/examples/3dtiles_loader.html +++ b/examples/3dtiles_loader.html @@ -119,13 +119,12 @@ // 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 colorLayer = new ColorLayer('Ortho', { - ...config, - source: new TMSSource(config.source), - }); - view.addLayer(colorLayer); + const openSmConfig = await Fetcher.json('./layers/JSONLayers/OPENSM.json'); + const colorLayer = new ColorLayer('Ortho', { + ...openSmConfig, + source: new TMSSource(openSmConfig.source), }); + view.addLayer(colorLayer); // ---- Add 3D terrain ---- @@ -136,8 +135,10 @@ 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); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // ---------- 3D Tiles loading diff --git a/examples/copc_3d_loader.html b/examples/copc_3d_loader.html index d572fe86c5..b695582cd0 100644 --- a/examples/copc_3d_loader.html +++ b/examples/copc_3d_loader.html @@ -60,11 +60,10 @@ // Add one imagery layer to the scene and the miniView // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. function addElevationLayerFromConfig(config) { @@ -72,8 +71,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); var layer; diff --git a/examples/effects_postprocessing.html b/examples/effects_postprocessing.html index e2f9870ad9..ee4c976e80 100644 --- a/examples/effects_postprocessing.html +++ b/examples/effects_postprocessing.html @@ -118,16 +118,14 @@ r.render(postprocessScene, cam); }; - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ElevationLayer(config.id, config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); + const ignMntConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT.json'); + ignMntConfig.source = new itowns.WMTSSource(ignMntConfig.source); + var elevationLayer = new itowns.ElevationLayer(ignMntConfig.id, ignMntConfig); + view.addLayer(elevationLayer); window.view = view; window.itowns = itowns; window.THREE = THREE; diff --git a/examples/effects_split.html b/examples/effects_split.html index 265423e5ac..e9949fccf0 100644 --- a/examples/effects_split.html +++ b/examples/effects_split.html @@ -82,17 +82,15 @@ // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - const pOrtho = itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - orthoLayer = new itowns.ColorLayer('Ortho', config); - view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - const pOsm = itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json').then(function _(config) { - config.source = new itowns.TMSSource(config.source); - osmLayer = new itowns.ColorLayer('OSM', config); - view.addLayer(osmLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const osmConfig = await itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json'); + osmConfig.source = new itowns.TMSSource(osmConfig.source); + osmLayer = new itowns.ColorLayer('OSM', osmConfig); + view.addLayer(osmLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -101,8 +99,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // Slide handling function splitSliderMove(evt) { @@ -155,8 +155,7 @@ } // Override default rendering method when color layers are ready - Promise.all([pOrtho, pOsm]).then( - function _() { view.render = splitRendering; }).catch(console.error); + view.render = splitRendering; window.view = view; window.itowns = itowns; window.THREE = THREE; diff --git a/examples/effects_stereo.html b/examples/effects_stereo.html index ffed5d9d09..2a825375d1 100644 --- a/examples/effects_stereo.html +++ b/examples/effects_stereo.html @@ -78,11 +78,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - const source = new itowns.WMTSSource(config.source); - const layer = new itowns.ColorLayer(config.id, { source }); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + const orthoSource = new itowns.WMTSSource(orthoConfig.source); + const orthoLayer = new itowns.ColorLayer(orthoConfig.id, { source: orthoSource }); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. function addElevationLayerFromConfig(config) { @@ -90,8 +89,10 @@ const layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); /* eslint-disable no-unused-vars */ function updateEyeSep(value) { diff --git a/examples/entwine_3d_loader.html b/examples/entwine_3d_loader.html index 27e155f23f..a392729089 100644 --- a/examples/entwine_3d_loader.html +++ b/examples/entwine_3d_loader.html @@ -61,11 +61,10 @@ // Add one imagery layer to the scene and the miniView // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -74,8 +73,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // Check if an url had been given as an argument // and parse to get url and options. diff --git a/examples/geoid_geoidLayer.html b/examples/geoid_geoidLayer.html index 5e3295f507..952e6109ef 100644 --- a/examples/geoid_geoidLayer.html +++ b/examples/geoid_geoidLayer.html @@ -62,12 +62,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('layers/JSONLayers/Ortho.json').then((config) => { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer(config.id, config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer(orthoConfig.id, orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); // Add two elevaion layers, each with a different level of detail. Here again, each layer's properties are // defined in a json file. @@ -77,8 +76,10 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/examples/itowns-potree.html b/examples/itowns-potree.html index 3d9cbdcf02..5b59e82bc4 100644 --- a/examples/itowns-potree.html +++ b/examples/itowns-potree.html @@ -195,13 +195,12 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - config.source.zoom = { max: 19, min: 3 }; - itownsViewer.addLayer( - new itowns.ColorLayer(config.id, config), - ); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + orthoConfig.source.zoom = { max: 19, min: 3 }; + itownsViewer.addLayer( + new itowns.ColorLayer(orthoConfig.id, orthoConfig), + ); // Add two elevaion layers, each with a different level of detail. Here again, each layer's properties are // defined in a json file. @@ -211,8 +210,10 @@ new itowns.ElevationLayer(config.id, config), ); } - itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/examples/misc_camera_animation.html b/examples/misc_camera_animation.html index e400c16869..2e08866f85 100644 --- a/examples/misc_camera_animation.html +++ b/examples/misc_camera_animation.html @@ -57,12 +57,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer('Ortho', config), - ); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer('Ortho', orthoConfig), + ); @@ -76,8 +75,10 @@ new itowns.ElevationLayer(config.id, config), ); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/examples/misc_camera_traveling.html b/examples/misc_camera_traveling.html index 0434fe5643..d4ffe9f1a5 100644 --- a/examples/misc_camera_traveling.html +++ b/examples/misc_camera_traveling.html @@ -71,11 +71,10 @@ // Add one imagery layer to the scene and the miniView. // This layer is defined in a json file but it could be defined as a plain js // object. See Layer for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - const orthoLayer = new itowns.ColorLayer('Ortho', config); - view.addLayer(orthoLayer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + const orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -84,8 +83,10 @@ let layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // ---------- Select camera positions and travel through these positions : ---------- diff --git a/examples/misc_clamp_ground.html b/examples/misc_clamp_ground.html index 60a49b4830..234a3b29c2 100644 --- a/examples/misc_clamp_ground.html +++ b/examples/misc_clamp_ground.html @@ -55,20 +55,24 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. function addElevationLayerFromConfig(config) { config.source = new itowns.WMTSSource(config.source); var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); + + return layer; } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); + function addMeshToScene() { // creation of the new mesh (a cylinder) @@ -100,9 +104,8 @@ } // Listen for globe full initialisation event - view.addEventListener(itowns.GLOBE_VIEW_EVENTS.GLOBE_INITIALIZED, function globeInitialized() { - // eslint-disable-next-line no-console - console.info('Globe initialized'); + view.addEventListener(itowns.GLOBE_VIEW_EVENTS.GLOBE_INITIALIZED, function init() { + console.log('GLOBE_INITIALIZED'); addMeshToScene(); }); window.view = view; diff --git a/examples/misc_colorlayer_visibility.html b/examples/misc_colorlayer_visibility.html index dbbc622333..54400c84f0 100644 --- a/examples/misc_colorlayer_visibility.html +++ b/examples/misc_colorlayer_visibility.html @@ -57,10 +57,14 @@ view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(addColorLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/Cada.json').then(addColorLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + addColorLayerFromConfig(orthoConfig); + const cadaConfig = await itowns.Fetcher.json('./layers/JSONLayers/Cada.json'); + addColorLayerFromConfig(cadaConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); view.addEventListener(itowns.GLOBE_VIEW_EVENTS.GLOBE_INITIALIZED, function _() { // eslint-disable-next-line no-console diff --git a/examples/misc_compare_25d_3d.html b/examples/misc_compare_25d_3d.html index d102d5bdd0..925c3624aa 100644 --- a/examples/misc_compare_25d_3d.html +++ b/examples/misc_compare_25d_3d.html @@ -104,11 +104,10 @@ overGlobe = false; }, false); - promises.push(itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer(config.id, config); - view.addLayer(layer); - })); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer(orthoConfig.id, orthoConfig); + view.addLayer(orthoLayer); // Listen for globe full initialisation event view @@ -116,38 +115,36 @@ function globeInitialized() { // eslint-disable-next-line no-console console.info('Globe initialized'); - Promise.all(promises).then(function init() { - var planarCamera = planarView.camera3D; - var globeCamera = view.camera3D; - var params; - menuGlobe.addImageryLayersGUI(view.getLayers(function filterColor(l) { return l.isColorLayer; })); - menuGlobe.addElevationLayersGUI(view.getLayers(function filterElevation(l) { return l.isElevationLayer; })); - - function sync() { - if (overGlobe) { - params = itowns.CameraUtils - .getTransformCameraLookingAtTarget( - view, globeCamera); - itowns.CameraUtils + var planarCamera = planarView.camera3D; + var globeCamera = view.camera3D; + var params; + menuGlobe.addImageryLayersGUI(view.getLayers(function filterColor(l) { return l.isColorLayer; })); + menuGlobe.addElevationLayersGUI(view.getLayers(function filterElevation(l) { return l.isElevationLayer; })); + + function sync() { + if (overGlobe) { + params = itowns.CameraUtils + .getTransformCameraLookingAtTarget( + view, globeCamera); + itowns.CameraUtils + .transformCameraToLookAtTarget( + planarView, planarCamera, params); + } else { + params = itowns.CameraUtils + .getTransformCameraLookingAtTarget( + planarView, planarCamera); + itowns.CameraUtils .transformCameraToLookAtTarget( - planarView, planarCamera, params); - } else { - params = itowns.CameraUtils - .getTransformCameraLookingAtTarget( - planarView, planarCamera); - itowns.CameraUtils - .transformCameraToLookAtTarget( - view, globeCamera, params); - } + view, globeCamera, params); } - sync(); - view - .addFrameRequester(itowns - .MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, sync); - planarView - .addFrameRequester(itowns - .MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, sync); - }).catch(console.error); + } + sync(); + view + .addFrameRequester(itowns + .MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, sync); + planarView + .addFrameRequester(itowns + .MAIN_LOOP_EVENTS.AFTER_CAMERA_UPDATE, sync); }); var wmsImagerySource = new itowns.WMSSource({ diff --git a/examples/misc_custom_controls.html b/examples/misc_custom_controls.html index 5a482877fe..847c9dadb9 100644 --- a/examples/misc_custom_controls.html +++ b/examples/misc_custom_controls.html @@ -57,20 +57,21 @@ setupLoadingScreen(viewerDiv, view); // Add imagery and elevation layers - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function (config) { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer('Ortho', config), - ); - }) + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer('Ortho', orthoConfig), + ); function addElevationLayerFromConfig(config) { config.source = new itowns.WMTSSource(config.source); view.addLayer( new itowns.ElevationLayer(config.id, config), ); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); const customControls = { // Disable pan movement diff --git a/examples/misc_custom_label.html b/examples/misc_custom_label.html index e410f03e54..fca04b7e80 100644 --- a/examples/misc_custom_label.html +++ b/examples/misc_custom_label.html @@ -62,16 +62,17 @@ const viewerDiv = document.getElementById('viewerDiv'); const view = new itowns.GlobeView(viewerDiv, placement); var menuGlobe = new GuiTools('menuDiv', view); - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - view.addLayer(new itowns.ColorLayer('Ortho', config)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer(new itowns.ColorLayer('Ortho', orthoConfig)); function addElevationLayerFromConfig(config) { config.source = new itowns.WMTSSource(config.source); view.addLayer(new itowns.ElevationLayer(config.id, config)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); debug.createTileDebugUI(menuGlobe.gui, view); // Create a custom div which will be displayed as a label diff --git a/examples/misc_georeferenced_images.html b/examples/misc_georeferenced_images.html index 8ba0b95422..4b097f1c83 100644 --- a/examples/misc_georeferenced_images.html +++ b/examples/misc_georeferenced_images.html @@ -78,11 +78,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -91,8 +90,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); pictureInfos = { panoramic: { diff --git a/examples/misc_geotiff.html b/examples/misc_geotiff.html index 53bd43dd3d..2db3bb29e0 100644 --- a/examples/misc_geotiff.html +++ b/examples/misc_geotiff.html @@ -57,16 +57,15 @@ // ---------- DISPLAY ORTHO IMAGES: ---------- - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then((config) => { - const colorLayer = new itowns.ColorLayer( - 'Ortho', - { - ...config, - source: new itowns.WMTSSource(config.source), - }, - ); - view.addLayer(colorLayer).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + const colorLayer = new itowns.ColorLayer( + 'Ortho', + { + ...orthoConfig, + source: new itowns.WMTSSource(orthoConfig.source), + }, + ); + view.addLayer(colorLayer).then(debugMenu.addLayerGUI.bind(debugMenu)); diff --git a/examples/misc_instancing.html b/examples/misc_instancing.html index 7fec14f906..dcc3cdbcb6 100644 --- a/examples/misc_instancing.html +++ b/examples/misc_instancing.html @@ -62,14 +62,11 @@ view.scene.add(ambLight); // Add one imagery layer to the scene - itowns.Fetcher.json("./layers/JSONLayers/Ortho.json").then( - function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer("Ortho", config); - view.addLayer(layer).then( - debugMenu.addLayerGUI.bind(debugMenu) - ); - } + const orthoConfig = await itowns.Fetcher.json("./layers/JSONLayers/Ortho.json"); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer("Ortho", orthoConfig); + view.addLayer(orthoLayer).then( + debugMenu.addLayerGUI.bind(debugMenu) ); //Tree diff --git a/examples/plugins_drag_n_drop.html b/examples/plugins_drag_n_drop.html index 17afe43b06..16faa5fe27 100644 --- a/examples/plugins_drag_n_drop.html +++ b/examples/plugins_drag_n_drop.html @@ -58,11 +58,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -71,8 +70,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); DragNDrop.setView(view); DragNDrop.register('geojson', DragNDrop.JSON, itowns.GeoJsonParser.parse, DragNDrop.COLOR); diff --git a/examples/plugins_pyramidal_tiff.html b/examples/plugins_pyramidal_tiff.html index 5a0a39f980..5f5ac9155a 100644 --- a/examples/plugins_pyramidal_tiff.html +++ b/examples/plugins_pyramidal_tiff.html @@ -80,7 +80,8 @@ menuGlobe.elevationGui.__folders.GEOIDMNT.open(); }); } - itowns.Fetcher.json('./layers/JSONLayers/GeoidMNT.json').then(addElevationLayerFromConfig); + const geoidMntConfig = await itowns.Fetcher.json('./layers/JSONLayers/GeoidMNT.json'); + addElevationLayerFromConfig(geoidMntConfig); const d = new debug.Debug(view, menuGlobe.gui); debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer, d); diff --git a/examples/plugins_vrt.html b/examples/plugins_vrt.html index 196763996d..8dfd82e61c 100644 --- a/examples/plugins_vrt.html +++ b/examples/plugins_vrt.html @@ -58,11 +58,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json').then(function _(config) { - config.source = new itowns.TMSSource(config.source); - var layer = new itowns.ColorLayer('OPENSM', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const openSmConfig = await itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json'); + openSmConfig.source = new itowns.TMSSource(openSmConfig.source); + var openSmLayer = new itowns.ColorLayer('OPENSM', openSmConfig); + view.addLayer(openSmLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // In this case, a VRT is associated to a CSV itowns.Fetcher.multiple('https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/vrt/velib-disponibilite-en-temps-reel', { diff --git a/examples/potree_3d_map.html b/examples/potree_3d_map.html index 9971dce5af..8b2534aeeb 100644 --- a/examples/potree_3d_map.html +++ b/examples/potree_3d_map.html @@ -71,16 +71,14 @@ view.controls.minDistance = 50; - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ElevationLayer(config.id, config); - view.addLayer(layer); - }); - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer(config.id, config); - view.addLayer(layer); - }); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + ignMntHighResConfig.source = new itowns.WMTSSource(ignMntHighResConfig.source); + var elevationLayer = new itowns.ElevationLayer(ignMntHighResConfig.id, ignMntHighResConfig); + view.addLayer(elevationLayer); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer(orthoConfig.id, orthoConfig); + view.addLayer(orthoLayer); /* OTHER dataset Donnees geocentrique -> probleme pour le placement camera diff --git a/examples/source_file_from_fetched_data.html b/examples/source_file_from_fetched_data.html index 54f91d5fc2..25bc863cd7 100644 --- a/examples/source_file_from_fetched_data.html +++ b/examples/source_file_from_fetched_data.html @@ -62,11 +62,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(debugMenu.addLayerGUI.bind(debugMenu)); diff --git a/examples/source_file_from_format.html b/examples/source_file_from_format.html index 1fbc9890c5..2d635ff534 100644 --- a/examples/source_file_from_format.html +++ b/examples/source_file_from_format.html @@ -60,11 +60,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(debugMenu.addLayerGUI.bind(debugMenu)); diff --git a/examples/source_file_from_methods.html b/examples/source_file_from_methods.html index 34026a0952..387c259c85 100644 --- a/examples/source_file_from_methods.html +++ b/examples/source_file_from_methods.html @@ -60,11 +60,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(debugMenu.addLayerGUI.bind(debugMenu)); diff --git a/examples/source_file_from_parsed_data.html b/examples/source_file_from_parsed_data.html index 9dab6c5776..fd6d766838 100644 --- a/examples/source_file_from_parsed_data.html +++ b/examples/source_file_from_parsed_data.html @@ -61,11 +61,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(debugMenu.addLayerGUI.bind(debugMenu)); diff --git a/examples/source_file_geojson_3d.html b/examples/source_file_geojson_3d.html index 3400159bbd..0aed91a132 100644 --- a/examples/source_file_geojson_3d.html +++ b/examples/source_file_geojson_3d.html @@ -48,11 +48,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -61,8 +60,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // Add a geometry layer, which will contain the multipolygon to display var marne = new itowns.FeatureGeometryLayer('Marne', { diff --git a/examples/source_file_geojson_raster.html b/examples/source_file_geojson_raster.html index fd93807314..97137844c3 100644 --- a/examples/source_file_geojson_raster.html +++ b/examples/source_file_geojson_raster.html @@ -58,13 +58,12 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(function _() { - menuGlobe.addLayerGUI.bind(menuGlobe); - itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(function _() { + menuGlobe.addLayerGUI.bind(menuGlobe); + itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); }); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -73,8 +72,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // Display the content of two GeoJSON files on terrain with ColorLayer and FileSource. diff --git a/examples/source_file_gpx_3d.html b/examples/source_file_gpx_3d.html index 86c2e9a6b7..57b0c0424a 100644 --- a/examples/source_file_gpx_3d.html +++ b/examples/source_file_gpx_3d.html @@ -52,11 +52,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. function addElevationLayerFromConfig(config) { @@ -64,8 +63,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // update the waypoint var distance, scale, point = new THREE.Vector3(); diff --git a/examples/source_file_gpx_raster.html b/examples/source_file_gpx_raster.html index 3ef62fa8dd..5772d5ca37 100644 --- a/examples/source_file_gpx_raster.html +++ b/examples/source_file_gpx_raster.html @@ -55,13 +55,12 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(function _() { - menuGlobe.addLayerGUI.bind(menuGlobe); - itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(function _() { + menuGlobe.addLayerGUI.bind(menuGlobe); + itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); }); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -70,8 +69,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); const gpxSource = new itowns.FileSource({ diff --git a/examples/source_file_kml_raster.html b/examples/source_file_kml_raster.html index 832d591a5f..6ac82cf2a8 100644 --- a/examples/source_file_kml_raster.html +++ b/examples/source_file_kml_raster.html @@ -57,13 +57,12 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(function _() { - menuGlobe.addLayerGUI.bind(menuGlobe); - itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(function _() { + menuGlobe.addLayerGUI.bind(menuGlobe); + itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); }); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -72,8 +71,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // Fetch, Parse and Convert by iTowns var kmlSource = new itowns.FileSource({ diff --git a/examples/source_file_kml_raster_usgs.html b/examples/source_file_kml_raster_usgs.html index dfcbb663ce..37fc022576 100644 --- a/examples/source_file_kml_raster_usgs.html +++ b/examples/source_file_kml_raster_usgs.html @@ -54,13 +54,12 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(function _() { - menuGlobe.addLayerGUI.bind(menuGlobe); - itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(function _() { + menuGlobe.addLayerGUI.bind(menuGlobe); + itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); }); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -69,8 +68,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); // Fetch, Parse and Convert by iTowns var kmlSource = new itowns.FileSource({ diff --git a/examples/source_file_shapefile.html b/examples/source_file_shapefile.html index bb5745cd3b..f8fb8efe43 100644 --- a/examples/source_file_shapefile.html +++ b/examples/source_file_shapefile.html @@ -54,11 +54,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json').then(function _(config) { - config.source = new itowns.TMSSource(config.source); - var layer = new itowns.ColorLayer('OPENSM', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const openSmConfig = await itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json'); + openSmConfig.source = new itowns.TMSSource(openSmConfig.source); + var openSmLayer = new itowns.ColorLayer('OPENSM', openSmConfig); + view.addLayer(openSmLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); diff --git a/examples/source_stream_wfs_3d.html b/examples/source_stream_wfs_3d.html index 0682ef1891..4bbbac59e0 100644 --- a/examples/source_stream_wfs_3d.html +++ b/examples/source_stream_wfs_3d.html @@ -69,11 +69,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -82,8 +81,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); var color = new THREE.Color(); var tile; diff --git a/examples/source_stream_wfs_raster.html b/examples/source_stream_wfs_raster.html index ae89cca030..a3bae45f6a 100644 --- a/examples/source_stream_wfs_raster.html +++ b/examples/source_stream_wfs_raster.html @@ -56,11 +56,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -69,8 +68,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); var wfsBuildingSource = new itowns.WFSSource({ url: 'https://data.geopf.fr/wfs/ows?', diff --git a/examples/vector_tile_3d_mesh.html b/examples/vector_tile_3d_mesh.html index c2cabd2a33..28f7f9b964 100644 --- a/examples/vector_tile_3d_mesh.html +++ b/examples/vector_tile_3d_mesh.html @@ -73,16 +73,17 @@ view.addLayer(new itowns.ElevationLayer(config.id, config)) .then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // ---------- DISPLAY ORTHO-IMAGES : ---------- - const ortho = itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - return view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // ---------- DISPLAY VECTOR TILED MAP DATA AS A ColorLayer : ---------- @@ -158,9 +159,7 @@ debug.createTileDebugUI(debugMenu.gui, view); view.addEventListener(itowns.GLOBE_VIEW_EVENTS.GLOBE_INITIALIZED, function () { - ortho.then(function () { - itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); - }).catch(console.error); + itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); }); window.view = view; diff --git a/examples/vector_tile_3d_mesh_mapbox.html b/examples/vector_tile_3d_mesh_mapbox.html index 6500d85674..74391dee9b 100644 --- a/examples/vector_tile_3d_mesh_mapbox.html +++ b/examples/vector_tile_3d_mesh_mapbox.html @@ -80,12 +80,10 @@ // Add one imagery layer to the scene. This layer's properties are // defined in a json file, but it could be defined as a plain js // object. See `Layer` documentation for more info. - const ortho = itowns.Fetcher.json('./layers/JSONLayers/Ortho.json') - .then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - return view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add an elevation layer, whose properties are defined in a json // file. @@ -94,8 +92,8 @@ view.addLayer(new itowns.ElevationLayer(config.id, config)) .then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json') - .then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); diff --git a/examples/vector_tile_raster_3d.html b/examples/vector_tile_raster_3d.html index f046d81129..58df7fce4f 100644 --- a/examples/vector_tile_raster_3d.html +++ b/examples/vector_tile_raster_3d.html @@ -55,11 +55,10 @@ setupLoadingScreen(viewerDiv, view); - promises.push(itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - return view.addLayer(layer); - })); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Define a VectorTilesSource to load Vector Tiles data from the geoportail var mvtSource = new itowns.VectorTilesSource({ @@ -80,10 +79,8 @@ var menuGlobe = new GuiTools('menuDiv', view, 300); // Listen for globe full initialisation event view.addEventListener(itowns.GLOBE_VIEW_EVENTS.GLOBE_INITIALIZED, function () { - Promise.all(promises).then(function () { - menuGlobe.addImageryLayersGUI(view.getLayers(function (l) { return l.isColorLayer; })); - itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); - }).catch(console.error); + menuGlobe.addImageryLayersGUI(view.getLayers(function (l) { return l.isColorLayer; })); + itowns.ColorLayersOrdering.moveLayerToIndex(view, 'Ortho', 0); }); debug.createTileDebugUI(menuGlobe.gui, view); diff --git a/examples/view_3d_map.html b/examples/view_3d_map.html index 5a5b99544c..ae13cf5b53 100644 --- a/examples/view_3d_map.html +++ b/examples/view_3d_map.html @@ -62,12 +62,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer('Ortho', config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer('Ortho', orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); @@ -81,8 +80,10 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); // ---------- ADD SOME WIDGETS : ---------- @@ -91,19 +92,19 @@ const scale = new itowns_widgets.Scale(view, { position: 'bottom-right', translate: { x: -80 } }); // ADD A MINIMAP : - const minimap = new itowns_widgets.Minimap( - view, - new itowns.ColorLayer('minimap', { - source: new itowns.VectorTilesSource({ - style: "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json", - // We don't display mountains and plot related data to ease visualisation - filter: (layer) => !layer['source-layer'].includes('oro_') - && !layer['source-layer'].includes('parcellaire'), - }), - addLabelLayer: { performance: true }, - }), - { cursor: '+' }, - ); + // const minimap = new itowns_widgets.Minimap( + // view, + // new itowns.ColorLayer('minimap', { + // source: new itowns.VectorTilesSource({ + // style: "https://data.geopf.fr/annexes/ressources/vectorTiles/styles/PLAN.IGN/standard.json", + // // We don't display mountains and plot related data to ease visualisation + // filter: (layer) => !layer['source-layer'].includes('oro_') + // && !layer['source-layer'].includes('parcellaire'), + // }), + // addLabelLayer: { performance: true }, + // }), + // { cursor: '+' }, + // ); // ADD NAVIGATION TOOLS : const navigation = new itowns_widgets.Navigation(view, { diff --git a/examples/view_3d_map_webxr.html b/examples/view_3d_map_webxr.html index 9de40fe104..aa4fd20ebc 100644 --- a/examples/view_3d_map_webxr.html +++ b/examples/view_3d_map_webxr.html @@ -73,11 +73,10 @@ // Add one imagery layer to the scene. This layer's properties are // defined in a json file, but it could be defined as a plain js // object. See `Layer` documentation for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then((config) => { - config.source = new itowns.WMTSSource(config.source); - view.addLayer(new itowns.ColorLayer('Ortho', config), - ); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer(new itowns.ColorLayer('Ortho', orthoConfig), + ); // ---------- DISPLAY A DIGITAL ELEVATION MODEL : ---------- @@ -89,10 +88,10 @@ new itowns.ElevationLayer(config.id, config), ); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json') - .then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json') - .then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); window.view = view; window.itowns = itowns; window.THREE = THREE; diff --git a/examples/view_3d_mns_map.html b/examples/view_3d_mns_map.html index aaae1fe583..089538f17d 100644 --- a/examples/view_3d_mns_map.html +++ b/examples/view_3d_mns_map.html @@ -69,12 +69,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer('Ortho', config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer('Ortho', orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); // ---------- DISPLAY A DIGITAL ELEVATION MODEL : ---------- @@ -87,7 +86,8 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNS_HIGHRES.json').then(addElevationLayerFromConfig); + const ignMnsHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNS_HIGHRES.json'); + addElevationLayerFromConfig(ignMnsHighResConfig); // ---------- ADD SOME WIDGETS : ---------- diff --git a/examples/view_immersive.html b/examples/view_immersive.html index f9cd1a0d33..4e632f054c 100644 --- a/examples/view_immersive.html +++ b/examples/view_immersive.html @@ -78,11 +78,10 @@ // Add one imagery layer to the scene // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -91,7 +90,8 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer).then(menuGlobe.addLayerGUI.bind(menuGlobe)); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); function altitudeBuildings(properties) { // I set altitude building 3 meters down, to be sure building is anchored in the ground diff --git a/examples/view_multiglobe.html b/examples/view_multiglobe.html index c48ea60b38..de3fe44d86 100644 --- a/examples/view_multiglobe.html +++ b/examples/view_multiglobe.html @@ -71,11 +71,10 @@ setupLoadingScreen(viewerDiv, view); object3ds.push(object3d); - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Create a second smaller globe object3d = new THREE.Object3D(); @@ -90,11 +89,10 @@ // add globe2 to the view so it gets updated itowns.View.prototype.addLayer.call(view, globe2); - itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json').then(function _(osm) { - osm.source = new itowns.TMSSource(osm.source); - var layer = new itowns.ColorLayer(osm.id, osm); - itowns.View.prototype.addLayer.call(view, layer, globe2); - }); + const openSmConfig = await itowns.Fetcher.json('./layers/JSONLayers/OPENSM.json'); + openSmConfig.source = new itowns.TMSSource(openSmConfig.source); + var globe2Layer = new itowns.ColorLayer(openSmConfig.id, openSmConfig); + itowns.View.prototype.addLayer.call(view, globe2Layer, globe2); // Globe animation // Exchange postion and scale of globes in a 1 second animation diff --git a/examples/vpc_3d_loader.html b/examples/vpc_3d_loader.html index 50f050403b..d6fb919c9b 100644 --- a/examples/vpc_3d_loader.html +++ b/examples/vpc_3d_loader.html @@ -57,11 +57,10 @@ // Add one imagery layer to the scene and the miniView // This layer is defined in a json file but it could be defined as a plain js // object. See Layer* for more info. - itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(function _(config) { - config.source = new itowns.WMTSSource(config.source); - var layer = new itowns.ColorLayer('Ortho', config); - view.addLayer(layer); - }); + const orthoConfig = await itowns.Fetcher.json('./layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + var orthoLayer = new itowns.ColorLayer('Ortho', orthoConfig); + view.addLayer(orthoLayer); // Add two elevation layers. // These will deform iTowns globe geometry to represent terrain elevation. @@ -70,8 +69,10 @@ var layer = new itowns.ElevationLayer(config.id, config); view.addLayer(layer); } - itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); const url = 'https://data.geopf.fr/chunk/telechargement/download/lidarhd_fxx_ept/vpc/index.vpc'; loadVPC(url); diff --git a/examples/widgets_minimap.html b/examples/widgets_minimap.html index d3f1b0ed06..17098de115 100644 --- a/examples/widgets_minimap.html +++ b/examples/widgets_minimap.html @@ -76,12 +76,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('layers/JSONLayers/Ortho.json').then((config) => { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer(config.id, config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer(orthoConfig.id, orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); // Add two elevation layers, each with a different level of detail. Here again, each layer's properties are // defined in a json file. @@ -91,8 +90,10 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/examples/widgets_navigation.html b/examples/widgets_navigation.html index efd37ae108..3d3a57cd51 100644 --- a/examples/widgets_navigation.html +++ b/examples/widgets_navigation.html @@ -67,12 +67,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('layers/JSONLayers/Ortho.json').then((config) => { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer(config.id, config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer(orthoConfig.id, orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); // Add two elevation layers, each with a different level of detail. Here again, each layer's properties are // defined in a json file. @@ -82,8 +81,10 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/examples/widgets_scale.html b/examples/widgets_scale.html index 68d0ccb075..216657bb5b 100644 --- a/examples/widgets_scale.html +++ b/examples/widgets_scale.html @@ -68,12 +68,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('layers/JSONLayers/Ortho.json').then((config) => { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer(config.id, config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer(orthoConfig.id, orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); // Add two elevation layers, each with a different level of detail. Here again, each layer's properties are // defined in a json file. @@ -83,8 +82,10 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/examples/widgets_searchbar.html b/examples/widgets_searchbar.html index 9d3063ac1d..1ba137d8c4 100644 --- a/examples/widgets_searchbar.html +++ b/examples/widgets_searchbar.html @@ -66,12 +66,11 @@ // Add one imagery layer to the scene. This layer's properties are defined in a json file, but it could be // defined as a plain js object. See `Layer` documentation for more info. - itowns.Fetcher.json('layers/JSONLayers/Ortho.json').then((config) => { - config.source = new itowns.WMTSSource(config.source); - view.addLayer( - new itowns.ColorLayer(config.id, config), - ).then(debugMenu.addLayerGUI.bind(debugMenu)); - }); + const orthoConfig = await itowns.Fetcher.json('layers/JSONLayers/Ortho.json'); + orthoConfig.source = new itowns.WMTSSource(orthoConfig.source); + view.addLayer( + new itowns.ColorLayer(orthoConfig.id, orthoConfig), + ).then(debugMenu.addLayerGUI.bind(debugMenu)); // Add two elevation layers, each with a different level of detail. Here again, each layer's properties are // defined in a json file. @@ -81,8 +80,10 @@ new itowns.ElevationLayer(config.id, config), ).then(debugMenu.addLayerGUI.bind(debugMenu)); } - itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addElevationLayerFromConfig); - itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json').then(addElevationLayerFromConfig); + const ignMntHighResConfig = await itowns.Fetcher.json('layers/JSONLayers/IGN_MNT_HIGHRES.json'); + addElevationLayerFromConfig(ignMntHighResConfig); + const worldDtmConfig = await itowns.Fetcher.json('layers/JSONLayers/WORLD_DTM.json'); + addElevationLayerFromConfig(worldDtmConfig); diff --git a/packages/Geographic/src/Extent.ts b/packages/Geographic/src/Extent.ts index 388571a254..b2a2285773 100644 --- a/packages/Geographic/src/Extent.ts +++ b/packages/Geographic/src/Extent.ts @@ -1,4 +1,4 @@ -import { Vector2, Vector3, Vector4, Box3, type Matrix4 } from 'three'; +import { Vector2, Vector3, Vector4, Box3, Matrix3, type Matrix4 } from 'three'; import Coordinates from './Coordinates'; import * as CRS from './Crs'; @@ -288,7 +288,7 @@ class Extent { this.planarDimensions(_dim2); const originX = (this.west - extent.west) / _dim.x; - const originY = (extent.north - this.north) / _dim.y; + const originY = (this.south - extent.south) / _dim.y; const scaleX = _dim2.x / _dim.x; const scaleY = _dim2.y / _dim.y; @@ -296,6 +296,13 @@ class Extent { return target.set(originX, originY, scaleX, scaleY); } + transformToParent(extent: Extent, target = new Matrix3()) { + const { x: ox, y: oy, z: sx, w: sy } = this.offsetToParent(extent); + + return target.setUvTransform(ox, oy, sx, sy, 0, 0, 0); + } + + /** * Checks wheter this bounding box intersects with the given extent * parameter. diff --git a/packages/Main/src/Converter/convertToTile.ts b/packages/Main/src/Converter/convertToTile.ts index 46adb7af18..7f0ae1ad75 100644 --- a/packages/Main/src/Converter/convertToTile.ts +++ b/packages/Main/src/Converter/convertToTile.ts @@ -47,38 +47,57 @@ export default { hideSkirt: layer.hideSkirt, }; - return newTileGeometry(builder, paramsGeometry).then((result) => { - // build tile mesh - result.geometry.increaseRefCount(); - const crsCount = layer.tileMatrixSets.length; - const material = new LayeredMaterial(layer.materialOptions, crsCount); - ReferLayerProperties(material, layer); - - const tile = new TileMesh(result.geometry, material, layer, extent, level); - - if (parent && parent.isTileMesh) { - // get parent extent transformation - const pTrans = builder.computeShareableExtent(parent.extent); - // place relative to his parent - result.position.sub(pTrans.position).applyQuaternion(pTrans.quaternion.invert()); - result.quaternion.premultiply(pTrans.quaternion); - } + const { geometry, quaternion, position } = newTileGeometry(builder, paramsGeometry); + // build tile mesh + geometry.increaseRefCount(); + const crsCount = layer.tileMatrixSets.length; + const material = new LayeredMaterial(layer.materialOptions, crsCount); + ReferLayerProperties(material, layer); + + const tile = new TileMesh(geometry, material, layer, extent, level); + + if (parent && parent.isTileMesh) { + // get parent extent transformation + const pTrans = builder.computeShareableExtent(parent.extent); + // place relative to his parent + position.sub(pTrans.position).applyQuaternion(pTrans.quaternion.invert()); + quaternion.premultiply(pTrans.quaternion); + } + + tile.position.copy(position); + tile.quaternion.copy(quaternion); + tile.visible = false; + tile.updateMatrix(); - tile.position.copy(result.position); - tile.quaternion.copy(result.quaternion); - tile.visible = false; - tile.updateMatrix(); + setTileFromTiledLayer(tile, layer); - setTileFromTiledLayer(tile, layer); + if (parent) { + tile.geoidHeight = parent.geoidHeight; + const geoidHeight = geoidLayerIsVisible(layer) ? tile.geoidHeight : 0; + tile.material.setUniform('geoidHeight', geoidHeight); - if (parent) { - tile.geoidHeight = parent.geoidHeight; - const geoidHeight = geoidLayerIsVisible(layer) ? tile.geoidHeight : 0; + const parent_uniforms = parent.material.uniforms; + const child_uniforms = tile.material.uniforms; + + if (parent.material.uniforms.map.value) { + const extentParent = parent_uniforms.map.value.extent; + child_uniforms.map.value = parent_uniforms.map.value; + tile.extent.transformToParent(extentParent, child_uniforms.mapTransform.value); + } + + if (parent_uniforms.displacementMap.value) { + const extentParent = parent_uniforms.displacementMap.value.extent.toExtent(tile.extent.crs); + child_uniforms.displacementMap.value = parent_uniforms.displacementMap.value; + tile.extent.transformToParent(extentParent, child_uniforms.displacementMapTransform.value); + const rasterElevationNode = parent_uniforms.elevationLayer.value; + child_uniforms.elevationLayer.value = rasterElevationNode; + tile.setBBoxZ({ min: rasterElevationNode.min, max: rasterElevationNode.max, scale: rasterElevationNode.layer.scale, geoidHeight }); + } else { tile.setBBoxZ({ min: parent.obb.z.min, max: parent.obb.z.max, geoidHeight }); - tile.material.setUniform('geoidHeight', geoidHeight); } + } + - return tile; - }); + return tile; }, }; diff --git a/packages/Main/src/Core/Prefab/Globe/GlobeLayer.js b/packages/Main/src/Core/Prefab/Globe/GlobeLayer.js index 02123e2495..bb706eb020 100644 --- a/packages/Main/src/Core/Prefab/Globe/GlobeLayer.js +++ b/packages/Main/src/Core/Prefab/Globe/GlobeLayer.js @@ -53,8 +53,7 @@ class GlobeLayer extends TiledGeometryLayer { 'EPSG:3857', ]; - const uvCount = tileMatrixSets.length; - const builder = new GlobeTileBuilder({ uvCount }); + const builder = new GlobeTileBuilder(); super(id, object3d || new THREE.Group(), schemeTile, builder, { tileMatrixSets, @@ -112,7 +111,7 @@ class GlobeLayer extends TiledGeometryLayer { return super.preUpdate(context, changeSources); } - subdivision(context, layer, node) { + subdivision(context, node) { if (node.level == 5) { const row = node.getExtentsByProjection('EPSG:4326')[0].row; if (row == 31 || row == 0) { @@ -120,7 +119,7 @@ class GlobeLayer extends TiledGeometryLayer { return false; } } - return super.subdivision(context, layer, node); + return super.subdivision(context, node); } culling(node, camera) { diff --git a/packages/Main/src/Core/Prefab/Globe/GlobeTileBuilder.ts b/packages/Main/src/Core/Prefab/Globe/GlobeTileBuilder.ts index d67e2756c1..7f212b27ea 100644 --- a/packages/Main/src/Core/Prefab/Globe/GlobeTileBuilder.ts +++ b/packages/Main/src/Core/Prefab/Globe/GlobeTileBuilder.ts @@ -6,26 +6,13 @@ import { TileBuilderParams, } from '../TileBuilder'; -const PI_OV_FOUR = Math.PI / 4; -const INV_TWO_PI = 1.0 / (Math.PI * 2); const axisZ = new THREE.Vector3(0, 0, 1); const axisY = new THREE.Vector3(0, 1, 0); const quatToAlignLongitude = new THREE.Quaternion(); const quatToAlignLatitude = new THREE.Quaternion(); const quatNormalToZ = new THREE.Quaternion(); -/** - * Transforms a WGS84 latitude into a usable texture offset. - * @param latitude - * @returns - */ -function WGS84ToOneSubY(latitude: number): number { - return 1.0 - (0.5 - Math.log(Math.tan( - PI_OV_FOUR + THREE.MathUtils.degToRad(latitude) * 0.5, - )) * INV_TWO_PI); -} - -interface Transform { +type Transform = { /** Buffers for 2-part coordinate mapping operations. */ coords: [Coordinates, Coordinates]; position: THREE.Vector3; @@ -36,8 +23,6 @@ interface Transform { export interface GlobeTileBuilderParams extends TileBuilderParams { /** Number of rows of tiles, essentially the resolution of the globe. */ nbRow: number; - /** Offset of the second texture set. */ - deltaUV1: number; /** Transformation to align a tile's normal to the Z axis. */ quatNormalToZ: THREE.Quaternion; } @@ -48,11 +33,7 @@ export interface GlobeTileBuilderParams extends TileBuilderParams { */ export class GlobeTileBuilder implements TileBuilder { - private static _crs = 'EPSG:4978'; - private static _computeExtraOffset(params: GlobeTileBuilderParams): number { - const t = WGS84ToOneSubY(params.coordinates.latitude) * params.nbRow; - return (!isFinite(t) ? 0 : t) - params.deltaUV1; - } + private static _crs: string = 'EPSG:4978'; /** * Buffer holding information about the tile/vertex currently being @@ -60,16 +41,11 @@ implements TileBuilder { */ private _transform: Transform; - public computeExtraOffset?: (params: GlobeTileBuilderParams) => number; - public get crs(): string { return GlobeTileBuilder._crs; } - public constructor(options: { - /** Number of unaligned texture sets. */ - uvCount: number; - }) { + public constructor() { this._transform = { coords: [ new Coordinates('EPSG:4326', 0, 0), @@ -78,29 +54,13 @@ implements TileBuilder { position: new THREE.Vector3(), dimension: new THREE.Vector2(), }; - - // UV: Normalized coordinates (from degree) on the entire tile - // EPSG:4326 - // Offset: Float row coordinate from Pseudo mercator coordinates - // EPSG:3857 - if (options.uvCount > 1) { - this.computeExtraOffset = GlobeTileBuilder._computeExtraOffset; - } } public prepare(params: TileBuilderParams): GlobeTileBuilderParams { const nbRow = 2 ** (params.level + 1.0); - let st1 = WGS84ToOneSubY(params.extent.south); - - if (!isFinite(st1)) { st1 = 0; } - - const sizeTexture = 1.0 / nbRow; - - const start = (st1 % (sizeTexture)); const newParams = { nbRow, - deltaUV1: (st1 - start) * nbRow, // transformation to align tile's normal to z axis quatNormalToZ: quatNormalToZ.setFromAxisAngle( axisY, diff --git a/packages/Main/src/Core/Prefab/Planar/PlanarTileBuilder.ts b/packages/Main/src/Core/Prefab/Planar/PlanarTileBuilder.ts index 8cadb6acd4..8ec632e650 100644 --- a/packages/Main/src/Core/Prefab/Planar/PlanarTileBuilder.ts +++ b/packages/Main/src/Core/Prefab/Planar/PlanarTileBuilder.ts @@ -17,7 +17,6 @@ interface Transform { /** Specialized parameters for the [PlanarTileBuilder]. */ export interface PlanarTileBuilderParams extends TileBuilderParams { - uvCount?: number; nbRow: number; } @@ -26,14 +25,12 @@ export interface PlanarTileBuilderParams extends TileBuilderParams { * tile arrangements. */ export class PlanarTileBuilder implements TileBuilder { - private _uvCount: number; private _transform: Transform; private _crs: string; public constructor(options: { projection?: string; crs: string; - uvCount?: number; }) { if (options.projection) { console.warn('PlanarTileBuilder projection parameter is deprecated,' @@ -48,12 +45,6 @@ export class PlanarTileBuilder implements TileBuilder { position: new THREE.Vector3(), normal: new THREE.Vector3(0, 0, 1), }; - - this._uvCount = options.uvCount ?? 1; - } - - public get uvCount(): number { - return this._uvCount; } public get crs(): string { diff --git a/packages/Main/src/Core/Prefab/TileBuilder.ts b/packages/Main/src/Core/Prefab/TileBuilder.ts index c5e65097b5..a2bfafec09 100644 --- a/packages/Main/src/Core/Prefab/TileBuilder.ts +++ b/packages/Main/src/Core/Prefab/TileBuilder.ts @@ -10,14 +10,14 @@ const cacheBuffer = new Map(); -const cacheTile = new LRUCache>({ max: 500 }); +const cacheTile = new LRUCache({ max: 500 }); export interface GpuBufferAttributes { index: THREE.BufferAttribute | null; position: THREE.BufferAttribute; normal: THREE.BufferAttribute; - uvs: THREE.BufferAttribute[]; -} + uv: THREE.BufferAttribute | null; +}; /** * Reference to a tile's extent with rigid transformations. @@ -89,35 +89,27 @@ export function newTileGeometry( `${builder.crs}_${params.disableSkirt ? 0 : 1}_${params.segments}`; const key = `s${south}l${params.level}bK${bufferKey}`; - let promiseGeometry = cacheTile.get(key); + let geometry = cacheTile.get(key); // build geometry if doesn't exist - if (!promiseGeometry) { - let resolve: ((value: TileGeometry) => void) | undefined; - promiseGeometry = new Promise((r) => { resolve = r; }); - cacheTile.set(key, promiseGeometry); - + if (!geometry) { params.extent = shareableExtent; const center = builder.center(params.extent).clone(); // Read previously cached values (index and uv.wgs84 only // depend on the # of triangles) let cachedBuffers = cacheBuffer.get(bufferKey); - let buffers; - try { - buffers = computeBuffers( - builder, - { ...params, center }, - cachedBuffers !== undefined - ? { - index: cachedBuffers.index.array as UintArray, - uv: cachedBuffers.uv.array as Float32Array, - } - : undefined, - ); - } catch (e) { - return Promise.reject(e); - } + const buffers = computeBuffers( + builder, + { ...params, center }, + cachedBuffers !== undefined + ? { + index: cachedBuffers.index.array as + Uint8Array | Uint16Array | Uint32Array, + uv: cachedBuffers.uv.array as Float32Array, + } + : undefined, + ); if (!cachedBuffers) { // We know the fields will exist due to the condition @@ -125,8 +117,8 @@ export function newTileGeometry( // TODO: Make this brain-based check compiler-based. cachedBuffers = { - index: new THREE.BufferAttribute(buffers.index as UintArray, 1), - uv: new THREE.BufferAttribute(buffers.uvs[0] as Float32Array, 2), + index: new THREE.BufferAttribute(buffers.index!, 1), + uv: new THREE.BufferAttribute(buffers.uv!, 2), }; // Update cacheBuffer @@ -135,26 +127,20 @@ export function newTileGeometry( const gpuBuffers: GpuBufferAttributes = { index: cachedBuffers.index, - uvs: [ - cachedBuffers.uv, - ...(buffers.uvs[1] !== undefined - ? [new THREE.BufferAttribute(buffers.uvs[1], 1)] - : [] - ), - ], + uv: cachedBuffers.uv, position: new THREE.BufferAttribute(buffers.position, 3), normal: new THREE.BufferAttribute(buffers.normal, 3), }; - const geometry = new TileGeometry(builder, params, gpuBuffers); - const bbox = geometry.boundingBox as THREE.Box3; - geometry.OBB = new OBB(bbox.min, bbox.max); + geometry = new TileGeometry(builder, params, gpuBuffers); + geometry.OBB = + new OBB(geometry.boundingBox.min, geometry.boundingBox.max); geometry.initRefCount(cacheTile, key); - (resolve as (value: TileGeometry) => void)(geometry); - return Promise.resolve({ geometry, quaternion, position }); + cacheTile.set(key, geometry); + + return { geometry, quaternion, position }; } - return (promiseGeometry as Promise) - .then(geometry => ({ geometry, quaternion, position })); + return { geometry, quaternion, position }; } diff --git a/packages/Main/src/Core/Prefab/computeBufferTileGeometry.ts b/packages/Main/src/Core/Prefab/computeBufferTileGeometry.ts index f2fde07fd5..37aba4ea9b 100644 --- a/packages/Main/src/Core/Prefab/computeBufferTileGeometry.ts +++ b/packages/Main/src/Core/Prefab/computeBufferTileGeometry.ts @@ -15,10 +15,8 @@ export interface Buffers { index: IndexArray; position: Float32Array; normal: Float32Array; - uvs: [Option, Option]; -} - -type UintArray = Uint8Array | Uint16Array | Uint32Array; + uv: Float32Array; +}; type BuffersAndSkirt = Buffers & { skirt: IndexArray; @@ -98,28 +96,7 @@ function allocateBuffers( skirt, position: new Float32Array(nVertex * 3), normal: new Float32Array(nVertex * 3), - // 2 UV set per tile: wgs84 (uv[0]) and pseudo-mercator (pm, uv[1]) - // - wgs84: 1 texture per tile because tiles are using wgs84 - // projection - // - pm: use multiple textures per tile. - // +-------------------------+ - // | | - // | Texture 0 | - // +-------------------------+ - // | | - // | Texture 1 | - // +-------------------------+ - // | | - // | Texture 2 | - // +-------------------------+ - // * u = wgs84.u - // * v = textureid + v in builder texture - uvs: [ - cache?.uv ?? new Float32Array(nVertex * 2), - builder.computeExtraOffset !== undefined - ? new Float32Array(nVertex) - : undefined, - ], + uv: cache?.uv ?? new Float32Array(nVertex * 2), }; } @@ -128,12 +105,7 @@ function computeUv0(uv: Float32Array, id: number, u: number, v: number): void { uv[id * 2 + 1] = v; } -function initComputeUv1(value: number): (uv: Float32Array, id: number) => void { - return (uv: Float32Array, id: number): void => { uv[id] = value; }; -} - -type ComputeUvs = - [typeof computeUv0 | (() => void), ReturnType?]; +type ComputeUvs = typeof computeUv0; interface ComputeBuffersParams extends TileBuilderPrepareParams { center: THREE.Vector3; @@ -180,8 +152,7 @@ export function computeBuffers( cache, ); - const computeUvs: ComputeUvs = - [cache === undefined ? computeUv0 : () => { }]; + const computeUv: ComputeUvs = cache === undefined ? computeUv0 : () => { }; const preparedParams = builder.prepare(params); @@ -190,12 +161,6 @@ export function computeBuffers( preparedParams.coordinates.y = builder.vProject(v, params.extent); - if (builder.computeExtraOffset !== undefined) { - computeUvs[1] = initComputeUv1( - builder.computeExtraOffset(preparedParams) as number, - ); - } - for (let x = 0; x <= nSeg; x++) { const u = x / nSeg; const id_m3 = (y * nVertex + x) * 3; @@ -219,12 +184,7 @@ export function computeBuffers( vertex.toArray(outBuffers.position, id_m3); normal.toArray(outBuffers.normal, id_m3); - - for (const [index, computeUv] of computeUvs.entries()) { - if (computeUv !== undefined) { - computeUv(outBuffers.uvs[index] as Float32Array, y * nVertex + x, u, v); - } - } + computeUv(outBuffers.uv, y * nVertex + x, u, v); } } @@ -342,11 +302,7 @@ export function computeBuffers( outBuffers.normal[id_m3 + 1] = outBuffers.normal[id2_m3 + 1]; outBuffers.normal[id_m3 + 2] = outBuffers.normal[id2_m3 + 2]; - buildSkirt.uv(outBuffers.uvs[0], start + i, id); - - if (outBuffers.uvs[1] !== undefined) { - outBuffers.uvs[1][start + i] = outBuffers.uvs[1][id]; - } + buildSkirt.uv(outBuffers.uv, start + i, id); const idf = (i + 1) % skirt.length; @@ -363,7 +319,7 @@ export function computeBuffers( return { index: outBuffers.index, position: outBuffers.position, - uvs: outBuffers.uvs, + uv: outBuffers.uv, normal: outBuffers.normal, }; } diff --git a/packages/Main/src/Core/Scheduler/Scheduler.js b/packages/Main/src/Core/Scheduler/Scheduler.js index 0154025972..a043d5ea98 100644 --- a/packages/Main/src/Core/Scheduler/Scheduler.js +++ b/packages/Main/src/Core/Scheduler/Scheduler.js @@ -6,7 +6,6 @@ import PriorityQueue from 'js-priority-queue'; import DataSourceProvider from 'Provider/DataSourceProvider'; -import TileProvider from 'Provider/TileProvider'; import $3dTilesProvider from 'Provider/3dTilesProvider'; import PointCloudProvider from 'Provider/PointCloudProvider'; import URLBuilder from 'Provider/URLBuilder'; @@ -128,7 +127,6 @@ Scheduler.prototype.constructor = Scheduler; Scheduler.prototype.initDefaultProviders = function initDefaultProviders() { // Register all providers - this.addProtocolProvider('tile', TileProvider); this.addProtocolProvider('3d-tiles', $3dTilesProvider); this.addProtocolProvider('pointcloud', PointCloudProvider); }; @@ -239,7 +237,7 @@ Scheduler.prototype.execute = function execute(command) { * @returns {Promise} The {@link Scheduler} always expect a Promise as a result, * resolving to an object containing sufficient information for the associated * processing to the current layer. For example, see the - * [LayeredMaterialNodeProcessing#updateLayeredMaterialNodeElevation]{@link + * [LayeredMaterialNodeProcessing#updateRasterTile]{@link * https://github.com/iTowns/itowns/blob/master/src/Process/LayeredMaterialNodeProcessing.js} * class or other processing class. */ diff --git a/packages/Main/src/Core/Tile/Tile.ts b/packages/Main/src/Core/Tile/Tile.ts index 143dac99c0..4b43817c02 100644 --- a/packages/Main/src/Core/Tile/Tile.ts +++ b/packages/Main/src/Core/Tile/Tile.ts @@ -99,8 +99,7 @@ class Tile { } /** - * Returns the translation and scale to transform this tile to the input - * tile. + * Returns UV transform values to map this tile in the input tile. * * @param tile - the input tile. * @param target - copy the result to target. @@ -112,12 +111,19 @@ class Tile { } const r = _rowColfromParent(this, tile.zoom); + const offsetX = this.col * r.invDiff - r.col; + const offsetY = 1 - r.invDiff - (this.row * r.invDiff - r.row); return target.set( - this.col * r.invDiff - r.col, - this.row * r.invDiff - r.row, + offsetX, + offsetY, r.invDiff, r.invDiff); } + transformToParent(tile: Tile, target = new THREE.Matrix3()) { + const { x: ox, y: oy, z: sx, w: sy } = this.offsetToParent(tile); + return target.setUvTransform(ox, oy, sx, sy, 0, 0, 0); + } + /** * Returns the parent tile at the given level. * diff --git a/packages/Main/src/Core/TileGeometry.ts b/packages/Main/src/Core/TileGeometry.ts index bf18e3693f..c7f9627a87 100644 --- a/packages/Main/src/Core/TileGeometry.ts +++ b/packages/Main/src/Core/TileGeometry.ts @@ -33,15 +33,9 @@ function defaultBuffers( index: buffers.index ? new THREE.BufferAttribute(buffers.index, 1) : null, - uvs: [ - ...(buffers.uvs[0] - ? [new THREE.BufferAttribute(buffers.uvs[0], 2)] - : [] - ), - ...(buffers.uvs[1] - ? [new THREE.BufferAttribute(buffers.uvs[1], 1)] - : []), - ], + uv: buffers.uv + ? new THREE.BufferAttribute(buffers.uv, 2) + : null, position: new THREE.BufferAttribute(buffers.position, 3), normal: new THREE.BufferAttribute(buffers.normal, 3), }; @@ -57,6 +51,8 @@ export class TileGeometry extends THREE.BufferGeometry { /** Resolution of the tile geometry in segments per side. */ public segments: number; + public boundingBox: THREE.Box3; + /** * [TileGeometry] instances are shared between tiles. Since a geometry * handles its own GPU resource, it needs a reference counter to dispose of @@ -81,12 +77,11 @@ export class TileGeometry extends THREE.BufferGeometry { this.setIndex(bufferAttributes.index); this.setAttribute('position', bufferAttributes.position); this.setAttribute('normal', bufferAttributes.normal); - this.setAttribute('uv', bufferAttributes.uvs[0]); - - for (let i = 1; i < bufferAttributes.uvs.length; i++) { - this.setAttribute(`uv_${i}`, bufferAttributes.uvs[i]); + if (bufferAttributes.uv) { + this.setAttribute('uv', bufferAttributes.uv); } + this.boundingBox = new THREE.Box3(); this.computeBoundingBox(); this.OBB = null; this.hideSkirt = params.hideSkirt ?? false; @@ -110,7 +105,7 @@ export class TileGeometry extends THREE.BufferGeometry { * @param key - The [south, level, epsg] key of this geometry. */ public initRefCount( - cacheTile: LRUCache>, + cacheTile: LRUCache, key: string, ): void { if (this._refCount !== null) { diff --git a/packages/Main/src/Core/TileMesh.ts b/packages/Main/src/Core/TileMesh.ts index 33aa931a56..ded043a33f 100644 --- a/packages/Main/src/Core/TileMesh.ts +++ b/packages/Main/src/Core/TileMesh.ts @@ -161,9 +161,7 @@ class TileMesh extends THREE.Mesh { * @param renderer - The renderer used to render textures. */ override onBeforeRender(renderer: THREE.WebGLRenderer) { - if (this.material.layersNeedUpdate) { - this.material.updateLayersUniforms(renderer); - } + this.material.updateLayersUniforms(renderer, this.extent); // Track actual usage every time this mesh is rendered // Use global current rendering view ID set by MainLoop diff --git a/packages/Main/src/Layer/ColorLayer.js b/packages/Main/src/Layer/ColorLayer.js index da56d87873..4b3f4e8ecf 100644 --- a/packages/Main/src/Layer/ColorLayer.js +++ b/packages/Main/src/Layer/ColorLayer.js @@ -1,5 +1,4 @@ import RasterLayer from 'Layer/RasterLayer'; -import { updateLayeredMaterialNodeImagery } from 'Process/LayeredMaterialNodeProcessing'; import { RasterColorTile } from 'Renderer/RasterTile'; import { deprecatedColorLayerOptions } from 'Core/Deprecated/Undeprecator'; import Style from 'Core/Style'; @@ -164,7 +163,9 @@ class ColorLayer extends RasterLayer { * @returns {RasterColorTile} The raster color node added. */ setupRasterNode(node) { - const rasterColorTile = new RasterColorTile(this); + const tiles = node.getExtentsByProjection(this.crs); + + const rasterColorTile = new RasterColorTile(this, tiles); node.material.addColorTile(rasterColorTile); // set up ColorLayer ordering. @@ -172,10 +173,6 @@ class ColorLayer extends RasterLayer { return rasterColorTile; } - - update(context, layer, node, parent) { - return updateLayeredMaterialNodeImagery(context, this, node, parent); - } } export default ColorLayer; diff --git a/packages/Main/src/Layer/ElevationLayer.js b/packages/Main/src/Layer/ElevationLayer.js index 276f36966b..0beef2d197 100644 --- a/packages/Main/src/Layer/ElevationLayer.js +++ b/packages/Main/src/Layer/ElevationLayer.js @@ -1,5 +1,4 @@ import RasterLayer from 'Layer/RasterLayer'; -import { updateLayeredMaterialNodeElevation } from 'Process/LayeredMaterialNodeProcessing'; import { RasterElevationTile } from 'Renderer/RasterTile'; /** @@ -115,7 +114,9 @@ class ElevationLayer extends RasterLayer { * @returns {RasterElevationTile} The raster elevation node added. */ setupRasterNode(node) { - const rasterElevationNode = new RasterElevationTile(this); + const tiles = node.getExtentsByProjection(this.crs); + + const rasterElevationNode = new RasterElevationTile(this, tiles); node.material.setElevationTile(rasterElevationNode); node.material.setElevationTileId(this.id); @@ -123,7 +124,6 @@ class ElevationLayer extends RasterLayer { const updateBBox = () => node.setBBoxZ({ min: rasterElevationNode.min, max: rasterElevationNode.max, scale: this.scale, }); - updateBBox(); // listen elevation updating rasterElevationNode.addEventListener('rasterElevationLevelChanged', updateBBox); @@ -138,8 +138,20 @@ class ElevationLayer extends RasterLayer { return rasterElevationNode; } - update(context, layer, node, parent) { - return updateLayeredMaterialNodeElevation(context, this, node, parent); + getRasterTile(node) { + return node.material.getElevationTile(); + } + + /** + * Compares source zoom ranges to detect whether the new source can provide + * more precise data than the source currently attached to this tile. + * If so, the raster tile is recreated to reload elevation with finer detail. + * + * @param {RasterElevationTile} rasterTile - Existing elevation raster tile on the node. + * @returns {boolean} `true` when the new source is more precise and tile reload is required. + */ + overloadRasterTile(rasterTile) { + return this.source.zoom.min > rasterTile.layer.source.zoom.max; } } diff --git a/packages/Main/src/Layer/Layer.js b/packages/Main/src/Layer/Layer.js index 08a0313d75..235638fa6a 100644 --- a/packages/Main/src/Layer/Layer.js +++ b/packages/Main/src/Layer/Layer.js @@ -254,17 +254,19 @@ class Layer extends THREE.EventDispatcher { } getData(from, to) { - const key = this.source.getDataKey(this.source.isVectorSource ? to : from); - let data = this.cache.get(key); - if (!data) { - data = this.source.loadData(from, this) - .then(feat => this.convert(feat, to)) - .catch((err) => { - this.source.handlingError(err); - }); - this.cache.set(key, data); + if (from) { + const key = this.source.getDataKey(this.source.isVectorSource ? to : from); + let data = this.cache.get(key); + if (!data) { + data = this.source.loadData(from, this) + .then(feat => this.convert(feat, to)) + .catch((err) => { + this.source.handlingError(err); + }); + this.cache.set(key, data); + } + return data; } - return data; } /** diff --git a/packages/Main/src/Layer/LayerUpdateStrategy.ts b/packages/Main/src/Layer/LayerUpdateStrategy.ts index 34a1fc9c62..f62c257cde 100644 --- a/packages/Main/src/Layer/LayerUpdateStrategy.ts +++ b/packages/Main/src/Layer/LayerUpdateStrategy.ts @@ -1,4 +1,4 @@ -import { EMPTY_TEXTURE_ZOOM } from 'Renderer/RasterTile'; +import { EMPTY_TEXTURE_ZOOM, type RasterTile } from 'Renderer/RasterTile'; /** * This modules implements various layer update strategies. * @@ -112,3 +112,17 @@ export function chooseNextLevelToFetch( } return nextLevelToFetch; } + +export const nextLevelToFetch = (t: RasterTile) => { + const zoom = { + min: Math.max(t.layer.zoom.min, t.layer.source.zoom?.min), + max: Math.min(t.layer.zoom.max, t.layer.source.zoom?.max), + }; + return chooseNextLevelToFetch( + t.layer.updateStrategy, + t.tiles[0].zoom, + t.level, + t.state.failureParams, + zoom, + ); +}; diff --git a/packages/Main/src/Layer/RasterLayer.js b/packages/Main/src/Layer/RasterLayer.js index 75c93002ff..2bdfe199bf 100644 --- a/packages/Main/src/Layer/RasterLayer.js +++ b/packages/Main/src/Layer/RasterLayer.js @@ -1,9 +1,25 @@ import Layer from 'Layer/Layer'; import { STRATEGY_MIN_NETWORK_TRAFFIC } from 'Layer/LayerUpdateStrategy'; -import { removeLayeredMaterialNodeTile } from 'Process/LayeredMaterialNodeProcessing'; import textureConverter from 'Converter/textureConverter'; import { CACHE_POLICIES } from 'Core/Scheduler/Cache'; +export function removeLayeredMaterialNodeTile(tileId) { + /** + * @param {TileMesh} node - The node to udpate. + */ + return function removeLayeredMaterialNodeTile(node) { + if (node.material?.removeTile) { + if (node.material.elevationTile !== undefined) { + node.setBBoxZ({ min: 0, max: 0 }); + } + node.material.removeTile(tileId); + } + if (node.layerUpdateState && node.layerUpdateState[tileId]) { + delete node.layerUpdateState[tileId]; + } + }; +} + class RasterLayer extends Layer { constructor(id, config) { const { @@ -19,6 +35,7 @@ class RasterLayer extends Layer { cacheLifeTime, }); + this.visible = true; this.minFilter = minFilter; this.magFilter = magFilter; @@ -44,6 +61,61 @@ class RasterLayer extends Layer { root.traverse(removeLayeredMaterialNodeTile(this.id)); } } + + hasData(node) { + const minZoom = Math.max(this.source.zoom.min, this.zoom.min); + + const tiles = node.getExtentsByProjection(this.crs) + .map(e => e.tiledExtentParent(minZoom)); + + return tiles.some(e => e.zoom >= minZoom && this.source.hasData(e)); + } + + /** + * Indicates whether an existing raster tile must be recreated. + * Subclasses can override this to force rebuilding tiles based on tile state. + * + * @param {RasterTile} rasterTile - Current raster tile attached to the node. + * @returns {boolean} `true` to recreate the raster tile, `false` to keep it. + */ + // eslint-disable-next-line no-unused-vars + overloadRasterTile(rasterTile) { + return false; + } + + /** + * Returns the raster tile associated with this layer for a given node. + * + * @param {TileMesh} node - The tile mesh carrying layered material tiles. + * @returns {?RasterTile} The matching raster tile, or `undefined` when none exists. + */ + getRasterTile(node) { + return node.material.getTile(this.id); + } + + /** + * Updates raster data for a node if the layer is active and data is available. + * Creates or recreates the raster tile when needed, then triggers loading. + * + * @param {object} context - Update context. + * @param {View} context.view - Active view used to schedule loading. + * @param {RasterLayer} layer - Current raster layer. + * @param {TileMesh} node - Tile node to update. + * @returns {Promise|undefined} A loading promise when an update is scheduled. + */ + update(context, layer, node) { + if (layer.visible && !layer.freeze && this.hasData(node)) { + let rasterTile = this.getRasterTile(node); + + if (!rasterTile || this.overloadRasterTile(rasterTile)) { + rasterTile = this.setupRasterNode(node); + } + + if (rasterTile && (!rasterTile.hasData() || (node.visible && node.material.visible))) { + return rasterTile.load(node, context.view); + } + } + } } export default RasterLayer; diff --git a/packages/Main/src/Layer/TiledGeometryLayer.js b/packages/Main/src/Layer/TiledGeometryLayer.js index 3e19051ad3..89299051a0 100644 --- a/packages/Main/src/Layer/TiledGeometryLayer.js +++ b/packages/Main/src/Layer/TiledGeometryLayer.js @@ -313,51 +313,30 @@ class TiledGeometryLayer extends GeometryLayer { if (!node.parent) { return ObjectRemovalHelper.removeChildrenAndCleanup(this, node); } - // early exit if parent' subdivision is in progress - if (node.parent.pendingSubdivision) { - node.visible = false; - node.material.visible = false; - this.info.update(node); - return undefined; - } - // do proper culling node.visible = !this.culling(node, context.camera); - if (node.visible) { - let requestChildrenUpdate = false; - - node.material.visible = true; - node.material.layersNeedUpdate = true; + if (node.visible && this.subdivision(context, node)) { + node.material.visible = false; this.info.update(node); - if (node.pendingSubdivision || (TiledGeometryLayer.hasEnoughTexturesToSubdivide(context, node) && this.subdivision(context, this, node))) { - this.subdivideNode(context, node); - // display iff children aren't ready - node.material.visible = node.pendingSubdivision; - this.info.update(node); - requestChildrenUpdate = true; - } + return this.subdivideNode(context, node); + } - if (node.material.visible) { - if (!requestChildrenUpdate) { - return ObjectRemovalHelper.removeChildren(this, node); - } - } + node.material.visible = node.visible; - return requestChildrenUpdate ? node.children.filter(n => n.layer == this) : undefined; - } + // RasterTile.needsUpdate has been removed + // TODO: optimize uniforms update + node.material.layersNeedUpdate = node.material.visible; - node.material.visible = false; this.info.update(node); return ObjectRemovalHelper.removeChildren(this, node); } convert(requester, extent) { - return convertToTile.convert(requester, extent, this).then((tileMesh) => { - tileMesh.material.renderTargetCache = this.renderTargetCache; - return tileMesh; - }); + const tileMesh = convertToTile.convert(requester, extent, this); + tileMesh.material.renderTargetCache = this.renderTargetCache; + return tileMesh; } /** @@ -379,56 +358,6 @@ class TiledGeometryLayer extends GeometryLayer { return false; } - /** - * Tell if a node has enough elevation or color textures to subdivide. - * Subdivision is prevented if: - *
    - *
  • the node is covered by at least one elevation layer and if the node - * doesn't have an elevation texture yet
  • - *
  • a color texture is missing
  • - *
- * - * @param {object} context - The context of the update; see the {@link - * MainLoop} for more informations. - * @param {TileMesh} node - The node to subdivide. - * - * @returns {boolean} False if the node can not be subdivided, true - * otherwise. - */ - static hasEnoughTexturesToSubdivide(context, node) { - const layerUpdateState = node.layerUpdateState || {}; - let nodeLayer = node.material.getElevationTile(); - - for (const e of context.elevationLayers) { - const tiles = node.getExtentsByProjection(e.crs); - - if (!e.frozen && e.ready && tiles.some(t => e.source.hasData(t)) && (!nodeLayer || nodeLayer.level < 0)) { - // no stop subdivision in the case of a loading error - if (layerUpdateState[e.id] && layerUpdateState[e.id].inError()) { - continue; - } - return false; - } - } - - for (const c of context.colorLayers) { - if (c.frozen || !c.visible || !c.ready) { - continue; - } - const tiles = node.getExtentsByProjection(c.crs); - - // no stop subdivision in the case of a loading error - if (layerUpdateState[c.id] && layerUpdateState[c.id].inError()) { - continue; - } - nodeLayer = node.material.getColorTile(c.id); - if (tiles.some(t => c.source.hasData(t)) && (!nodeLayer || nodeLayer.level < 0)) { - return false; - } - } - return true; - } - /** * Subdivides a node of this layer. If the node is currently in the process * of subdivision, it will not do anything here. The subdivision of a node @@ -442,38 +371,21 @@ class TiledGeometryLayer extends GeometryLayer { * @param {TileMesh} node - The node to subdivide. * @returns {Promise} { description_of_the_return_value } */ + subdivideNode(context, node) { - if (!node.pendingSubdivision && !node.children.some(n => n.layer == this)) { + if (node.children.length === 0) { const extents = node.extent.subdivision(); - // TODO: pendingSubdivision mechanism is fragile, get rid of it - node.pendingSubdivision = true; - - const command = { - /* mandatory */ - view: context.view, - requester: node, - layer: this, - priority: 10000, - /* specific params */ - extentsSource: extents, - redraw: false, - }; - - return context.scheduler.execute(command).then((children) => { - for (const child of children) { - node.add(child); - child.updateMatrixWorld(true); - } - node.pendingSubdivision = false; - context.view.notifyChange(node, false); - }, (err) => { - node.pendingSubdivision = false; - if (!err.isCancelledCommandException) { - throw new Error(err); - } + extents.forEach((extent) => { + const child = this.convert(node, extent); + node.add(child); + child.updateMatrixWorld(true); }); + context.view.notifyChange(node, true); } + + + return node.children; } /** @@ -481,12 +393,11 @@ class TiledGeometryLayer extends GeometryLayer { * * @param {object} context - The context of the update; see the {@link * MainLoop} for more informations. - * @param {PlanarLayer} layer - This layer, parameter to be removed. * @param {TileMesh} node - The node to test. * * @returns {boolean} - True if the node is subdivisable, otherwise false. */ - subdivision(context, layer, node) { + subdivision(context, node) { if (node.level < this.minSubdivisionLevel) { return true; } @@ -494,15 +405,20 @@ class TiledGeometryLayer extends GeometryLayer { if (this.maxSubdivisionLevel <= node.level) { return false; } + + if (!node.material.dataHasLoaded()) { + return false; + } + + const camera3D = context.camera.camera3D; + subdivisionVector.setFromMatrixScale(node.matrixWorld); boundingSphereCenter.copy(node.boundingSphere.center).applyMatrix4(node.matrixWorld); const distance = Math.max( - 0.0, - context.camera.camera3D.position.distanceTo(boundingSphereCenter) - node.boundingSphere.radius * subdivisionVector.x); + 0.0, camera3D.position.distanceTo(boundingSphereCenter) - node.boundingSphere.radius * subdivisionVector.x); // Size projection on pixel of bounding - if (context.camera.camera3D.isOrthographicCamera) { - const camera3D = context.camera.camera3D; + if (camera3D.isOrthographicCamera) { const preSSE = context.camera._preSSE * 2 * camera3D.zoom / (camera3D.top - camera3D.bottom); node.screenSize = preSSE * node.boundingSphere.radius * subdivisionVector.x; } else { diff --git a/packages/Main/src/Main.js b/packages/Main/src/Main.js index 5261034d8a..da8fa4df43 100644 --- a/packages/Main/src/Main.js +++ b/packages/Main/src/Main.js @@ -16,7 +16,6 @@ export { default as View } from 'Core/View'; export { VIEW_EVENTS } from 'Core/View'; export { default as FeatureProcessing } from 'Process/FeatureProcessing'; export { default as ObjectRemovalHelper } from 'Process/ObjectRemovalHelper'; -export { updateLayeredMaterialNodeImagery, updateLayeredMaterialNodeElevation } from 'Process/LayeredMaterialNodeProcessing'; export { default as OrientedImageCamera } from 'Renderer/OrientedImageCamera'; export { default as PointsMaterial, PNTS_MODE, PNTS_SHAPE, PNTS_SIZE_MODE, ClassificationScheme } from 'Renderer/PointsMaterial'; export { default as GlobeControls } from 'Controls/GlobeControls'; @@ -33,7 +32,7 @@ export { default as FeaturesUtils } from 'Utils/FeaturesUtils'; export { default as DEMUtils } from 'Utils/DEMUtils'; export { default as CameraUtils } from 'Utils/CameraUtils'; export { default as ShaderChunk } from 'Renderer/Shader/ShaderChunk'; -export { getMaxColorSamplerUnitsCount, colorLayerEffects } from 'Renderer/LayeredMaterial'; +export { colorLayerEffects } from 'Renderer/LayeredMaterial'; export { default as Capabilities } from 'Core/System/Capabilities'; export { CAMERA_TYPE } from 'Renderer/Camera'; export { default as OBB } from 'Renderer/OBB'; diff --git a/packages/Main/src/Parser/XbilParser.js b/packages/Main/src/Parser/XbilParser.js index 5be413206b..5f2bb7fdb2 100644 --- a/packages/Main/src/Parser/XbilParser.js +++ b/packages/Main/src/Parser/XbilParser.js @@ -1,9 +1,9 @@ import { readTextureValueWithBilinearFiltering } from 'Utils/DEMUtils'; function minMax4Corners(texture, pitch, options) { - const u = pitch.x; - const v = pitch.y; - const w = pitch.z; + const u = pitch.elements[6]; + const v = pitch.elements[7]; + const w = pitch.elements[0]; const z = [ readTextureValueWithBilinearFiltering(options, texture, u, v), readTextureValueWithBilinearFiltering(options, texture, u + w, v), @@ -25,7 +25,7 @@ function minMax4Corners(texture, pitch, options) { * Calculates the minimum maximum texture elevation with xbil data * * @param {THREE.Texture} texture The texture to parse - * @param {THREE.Vector4} pitch The pitch, restrict zone to parse + * @param {THREE.Matrix3} pitch The pitch, restrict zone to parse * @param {object} options No data value and clamp values * @param {number} options.noDataValue No data value * @param {number} [options.zmin] The minimum elevation value after which it will be clamped @@ -44,12 +44,12 @@ export function computeMinMaxElevation(texture, pitch, options) { // compute the minimum and maximum elevation on the 4 corners texture. let { min, max } = minMax4Corners(texture, pitch, options); - const sizeX = Math.floor(pitch.z * width); + const sizeX = Math.floor(pitch.elements[0] * width); if (sizeX > 2) { - const sizeY = Math.floor(pitch.z * height); - const xs = Math.floor(pitch.x * width); - const ys = Math.floor(pitch.y * height); + const sizeY = Math.floor(pitch.elements[4] * height); + const xs = Math.floor(pitch.elements[6] * width); + const ys = Math.floor(pitch.elements[7] * height); const inc = Math.max(Math.floor(sizeX / 32), 2); const limX = ys + sizeY; for (let y = ys; y < limX; y += inc) { diff --git a/packages/Main/src/Process/LayeredMaterialNodeProcessing.js b/packages/Main/src/Process/LayeredMaterialNodeProcessing.js deleted file mode 100644 index a7684c8a43..0000000000 --- a/packages/Main/src/Process/LayeredMaterialNodeProcessing.js +++ /dev/null @@ -1,262 +0,0 @@ -import { chooseNextLevelToFetch } from 'Layer/LayerUpdateStrategy'; -import LayerUpdateState from 'Layer/LayerUpdateState'; -import handlingError from 'Process/handlerNodeError'; - -function materialCommandQueuePriorityFunction(material) { - // We know that 'node' is visible because commands can only be - // issued for visible nodes. - // TODO: need priorization of displayed nodes - // Then prefer displayed node over non-displayed one - return material.visible ? 100 : 10; -} - -function refinementCommandCancellationFn(cmd) { - if (!cmd.requester.parent || !cmd.requester.material) { - return true; - } - // Cancel the command if the tile already has a better texture. - // This is only needed for elevation layers, because we may have several - // concurrent layers but we can only use one texture. - if (cmd.layer.isElevationLayer && cmd.requester.material.getElevationTile() && - cmd.targetLevel <= cmd.requester.material.getElevationTile().level) { - return true; - } - - // Cancel the command if the layer was removed between command scheduling and command execution - if (!cmd.requester.layerUpdateState[cmd.layer.id] - || !cmd.layer.source._featuresCaches[cmd.layer.crs]) { - return true; - } - - return !cmd.requester.material.visible; -} - -function buildCommand(view, layer, extentsSource, extentsDestination, requester) { - return { - view, - layer, - extentsSource, - extentsDestination, - requester, - priority: materialCommandQueuePriorityFunction(requester.material), - earlyDropFunction: refinementCommandCancellationFn, - partialLoading: true, - }; -} - -function computePitchs(textures, extentsDestination) { - return extentsDestination - .map((ext, i) => (ext.offsetToParent(textures[i].extent))); -} - -export function updateLayeredMaterialNodeImagery(context, layer, node, parent) { - const material = node.material; - if (!parent || !material) { - return; - } - const extentsDestination = node.getExtentsByProjection(layer.crs); - - const zoom = extentsDestination[0].zoom; - if (zoom > layer.zoom.max || zoom < layer.zoom.min) { - return; - } - - let nodeLayer = material.getTile(layer.id); - - // Initialisation - if (node.layerUpdateState[layer.id] === undefined) { - node.layerUpdateState[layer.id] = new LayerUpdateState(); - - if (!extentsDestination.some(t => layer.source.hasData(t))) { - // we also need to check that tile's parent doesn't have a texture for this layer, - // because even if this tile is outside of the layer, it could inherit it's - // parent texture - if (!layer.noTextureParentOutsideLimit && - parent.material && - parent.material.getTile && - parent.material.getTile(layer.id)) { - // ok, we're going to inherit our parent's texture - } else { - node.layerUpdateState[layer.id].noMoreUpdatePossible(); - return; - } - } - - if (!nodeLayer) { - // Create new raster node - nodeLayer = layer.setupRasterNode(node); - - // Init the node by parent - const parentLayer = parent.material?.getTile(layer.id); - nodeLayer.initFromParent(parentLayer, extentsDestination); - } - - // Proposed new process, two separate processes: - // * FIRST PASS: initNodeXXXFromParent and get out of the function - // * SECOND PASS: Fetch best texture - - // The two-step allows you to filter out unnecessary requests - // Indeed in the second pass, their state (not visible or not displayed) can block them to fetch - if (nodeLayer.level >= layer.source.zoom?.min) { - context.view.notifyChange(node, false); - return; - } - } - - // Node is hidden, no need to update it - if (!material.visible) { - return; - } - - // An update is pending / or impossible -> abort - if (!layer.visible || !node.layerUpdateState[layer.id].canTryUpdate()) { - return; - } - - if (nodeLayer.level >= extentsDestination[0].zoom) { - // default decision method - node.layerUpdateState[layer.id].noMoreUpdatePossible(); - return; - } - - // is fetching data from this layer disabled? - if (layer.frozen) { - return; - } - - const failureParams = node.layerUpdateState[layer.id].failureParams; - const destinationLevel = extentsDestination[0].zoom || node.level; - const targetLevel = chooseNextLevelToFetch(layer.updateStrategy, destinationLevel, nodeLayer.level, failureParams, layer.source.zoom); - - if ((!layer.source.isVectorSource && targetLevel <= nodeLayer.level) || targetLevel > destinationLevel) { - if (failureParams.lowestLevelError != Infinity) { - // this is the highest level found in case of error. - node.layerUpdateState[layer.id].noMoreUpdatePossible(); - } - return; - } else if (!extentsDestination.some(t => layer.source.hasData(t))) { - node.layerUpdateState[layer.id].noData({ targetLevel }); - context.view.notifyChange(node, false); - return; - } - - const extentsSource = extentsDestination.map(e => e.tiledExtentParent(targetLevel)); - node.layerUpdateState[layer.id].newTry(); - const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node); - - return context.scheduler.execute(command) - .then( - (results) => { - // Does nothing if the layer has been removed while command was being or waiting to be executed - if (!node.layerUpdateState[layer.id]) { - return; - } - const textures = results.map((texture, index) => (texture != null ? texture : - { isTexture: false, extent: extentsDestination[index] })); - - // TODO: Handle error : result is undefined in provider. throw error - const pitchs = computePitchs(textures, extentsDestination); - nodeLayer.setTextures(textures, pitchs); - node.layerUpdateState[layer.id].success(); - }) - .catch((err) => { - handlingError(err, node, layer, targetLevel, context.view); - }); -} - -export function updateLayeredMaterialNodeElevation(context, layer, node, parent) { - const material = node.material; - if (!parent || !material) { - return; - } - - // TODO: we need either - // - compound or exclusive layers - // - support for multiple elevation layers - - // Elevation is currently handled differently from color layers. - // This is caused by a LayeredMaterial limitation: only 1 elevation texture - // can be used (where a tile can have N textures x M layers) - const extentsDestination = node.getExtentsByProjection(layer.crs); - const zoom = extentsDestination[0].zoom; - if (zoom > layer.zoom.max || zoom < layer.zoom.min) { - return; - } - // Init elevation layer, and inherit from parent if possible - let nodeLayer = material.getElevationTile(); - if (!nodeLayer) { - nodeLayer = layer.setupRasterNode(node); - } - - if (node.layerUpdateState[layer.id] === undefined) { - node.layerUpdateState[layer.id] = new LayerUpdateState(); - - const parentLayer = parent.material?.getTile(layer.id); - nodeLayer.initFromParent(parentLayer, extentsDestination); - - if (nodeLayer.level >= layer.source.zoom.min) { - context.view.notifyChange(node, false); - return; - } - } - - // Possible conditions to *not* update the elevation texture - if (layer.frozen || - !material.visible || - !node.layerUpdateState[layer.id].canTryUpdate()) { - return; - } - - const failureParams = node.layerUpdateState[layer.id].failureParams; - const targetLevel = chooseNextLevelToFetch(layer.updateStrategy, extentsDestination[0].zoom, nodeLayer.level, failureParams, layer.source.zoom); - - if (targetLevel <= nodeLayer.level || targetLevel > extentsDestination[0].zoom) { - node.layerUpdateState[layer.id].noMoreUpdatePossible(); - return; - } else if (!extentsDestination.some(t => layer.source.hasData(t))) { - node.layerUpdateState[layer.id].noData({ targetLevel }); - context.view.notifyChange(node, false); - return; - } - - const extentsSource = extentsDestination.map(e => e.tiledExtentParent(targetLevel)); - node.layerUpdateState[layer.id].newTry(); - const command = buildCommand(context.view, layer, extentsSource, extentsDestination, node); - - return context.scheduler.execute(command).then( - (results) => { - // Does nothing if the layer has been removed while command was being or waiting to be executed - if (!node.layerUpdateState[layer.id]) { - return; - } - - // Do not apply the new texture if its level is < than the current - // one. This is only needed for elevation layers, because we may - // have several concurrent layers but we can only use one texture. - if (targetLevel <= nodeLayer.level) { - node.layerUpdateState[layer.id].noMoreUpdatePossible(); - return; - } - const pitchs = computePitchs(results, extentsDestination); - nodeLayer.setTextures(results, pitchs); - node.layerUpdateState[layer.id].success(); - }, - err => handlingError(err, node, layer, targetLevel, context.view)); -} - -export function removeLayeredMaterialNodeTile(tileId) { - /** - * @param {TileMesh} node - The node to udpate. - */ - return function removeLayeredMaterialNodeTile(node) { - if (node.material?.removeTile) { - if (node.material.elevationTile !== undefined) { - node.setBBoxZ({ min: 0, max: 0 }); - } - node.material.removeTile(tileId); - } - if (node.layerUpdateState && node.layerUpdateState[tileId]) { - delete node.layerUpdateState[tileId]; - } - }; -} diff --git a/packages/Main/src/Provider/DataSourceProvider.js b/packages/Main/src/Provider/DataSourceProvider.js index 399c8e6666..e0b3e9c343 100644 --- a/packages/Main/src/Provider/DataSourceProvider.js +++ b/packages/Main/src/Provider/DataSourceProvider.js @@ -7,6 +7,8 @@ export default { // partialLoading sets the return promise as fulfilled if at least one sub-promise is fulfilled // It waits until all promises are resolved + // + // TODO FAIRE uniquement quand il y a une erreur if (command.partialLoading) { return Promise.allSettled(promises) .then((results) => { diff --git a/packages/Main/src/Provider/TileProvider.js b/packages/Main/src/Provider/TileProvider.js deleted file mode 100644 index 65ebffcd56..0000000000 --- a/packages/Main/src/Provider/TileProvider.js +++ /dev/null @@ -1,21 +0,0 @@ -import CancelledCommandException from 'Core/Scheduler/CancelledCommandException'; - -export default { - executeCommand(command) { - const promises = []; - const layer = command.layer; - const requester = command.requester; - const extentsSource = command.extentsSource; - - if (requester && - !requester.material) { - return Promise.reject(new CancelledCommandException(command)); - } - - for (const extent of extentsSource) { - promises.push(layer.convert(requester, extent)); - } - - return Promise.all(promises); - }, -}; diff --git a/packages/Main/src/Renderer/GeographicTransformMaterials.js b/packages/Main/src/Renderer/GeographicTransformMaterials.js new file mode 100644 index 0000000000..cfc8f361d8 --- /dev/null +++ b/packages/Main/src/Renderer/GeographicTransformMaterials.js @@ -0,0 +1,88 @@ +import * as THREE from 'three'; +import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js'; +import { Extent } from '@itowns/geographic'; +import geoTransfomerfragmentShader from './Shader/geoTransformFS.glsl'; + +class GeographicTransformMaterial extends THREE.ShaderMaterial { + constructor(options) { + const params = { + transparent: true, + uniforms: { + ...CopyShader.uniforms, + mapToUvTransform: { value: new THREE.Matrix3() }, + localUvToWorldTransform: { value: new THREE.Matrix3() }, + mapTransform: { value: new THREE.Matrix3() }, + }, + vertexShader: CopyShader.vertexShader, + fragmentShader: options.defines ? geoTransfomerfragmentShader : CopyShader.fragmentShader, + }; + super(params); + + if (options.defines) { + this.defines = options.defines; + } + } + setTexture(tDiffuse) { + this.uniforms.tDiffuse.value = tDiffuse; + } + + setOpacity(opacity) { + this.uniforms.opacity.value = opacity; + } + + setOutputExtent(outputExtent) { + this.outputExtent = outputExtent; + } +} + +const inputExtent = new Extent('EPSG:4326'); +const extent = new Extent('EPSG:4326'); +const s = new THREE.Vector2(); +const t = new THREE.Vector2(); + +export const materialUnit = new GeographicTransformMaterial({ + fragmentShader: CopyShader.fragmentShader, +}); + +export const materialMercatorToWGS84 = new (class extends GeographicTransformMaterial { + setTexture(tDiffuse) { + super.setTexture(tDiffuse); + tDiffuse.extent.toExtent(tDiffuse.extent.crs, inputExtent); + inputExtent.planarDimensions(s); + t.set(inputExtent.west, inputExtent.south); + this.uniforms.mapToUvTransform.value.set( + 1 / s.x, 0, -t.x / s.x, + 0, 1 / s.y, -t.y / s.y, + 0, 0, 1, + ); + } + setOutputExtent(outputExtent) { + super.setOutputExtent(outputExtent); + + this.outputExtent.planarDimensions(s).multiplyScalar(THREE.MathUtils.DEG2RAD); + t.set(this.outputExtent.west, this.outputExtent.south).multiplyScalar(THREE.MathUtils.DEG2RAD); + + this.uniforms.localUvToWorldTransform.value.set( + s.x, 0, t.x, + 0, s.y, t.y, + 0, 0, 1, + ); + } +})({ + defines: { + EPSG_4326_EPSG_3857: 1, + }, +}); + +export const materialMercatorToWGS84Optimized = new (class extends GeographicTransformMaterial { + setTexture(tDiffuse) { + super.setTexture(tDiffuse); + tDiffuse.extent.toExtent(tDiffuse.extent.crs, inputExtent); + this.outputExtent.transformToParent(inputExtent.as(this.outputExtent.crs, extent), this.uniforms.mapTransform.value); + } +})({ + defines: { + EPSG_4326_EPSG_3857: 1, + OPTIMIZED: 1, + }, +}); diff --git a/packages/Main/src/Renderer/LayeredMaterial.ts b/packages/Main/src/Renderer/LayeredMaterial.ts index 58cc3938ef..30074204cf 100644 --- a/packages/Main/src/Renderer/LayeredMaterial.ts +++ b/packages/Main/src/Renderer/LayeredMaterial.ts @@ -1,15 +1,13 @@ import * as THREE from 'three'; +import { Extent } from '@itowns/geographic'; import TileVS from 'Renderer/Shader/TileVS.glsl'; import TileFS from 'Renderer/Shader/TileFS.glsl'; import ShaderUtils from 'Renderer/Shader/ShaderUtils'; -import Capabilities from 'Core/System/Capabilities'; import RenderMode from 'Renderer/RenderMode'; import { RasterTile, RasterElevationTile, RasterColorTile } from './RasterTile'; -import { makeDataArrayRenderTarget } from './WebGLComposer'; +import { drawMap } from './WebGLComposer'; import { RenderTargetCache } from './RenderTargetCache'; -const identityOffsetScale = new THREE.Vector4(0.0, 0.0, 1.0, 1.0); - // from three.js packDepthToRGBA const UnpackDownscale = 255 / 256; // 0..1 -> fraction (excluding 1) const bitSh = new THREE.Vector4( @@ -23,13 +21,6 @@ export function unpack1K(color: THREE.Vector4Like, factor: number): number { return factor ? bitSh.dot(color) * factor : bitSh.dot(color); } -const samplersElevationCount = 1; - -export function getMaxColorSamplerUnitsCount(): number { - const maxSamplerUnitsCount = Capabilities.getMaxTextureUnitsCount(); - return maxSamplerUnitsCount - samplersElevationCount; -} - export const colorLayerEffects = { noEffect: 0, removeLightColor: 1, @@ -37,16 +28,6 @@ export const colorLayerEffects = { customEffect: 3, } as const; -/** GPU struct for color layers */ -interface StructColorLayer { - textureOffset: number; - crs: number; - opacity: number; - effect_parameter: number; - effect_type: number; - transparent: boolean; -} - /** GPU struct for elevation layers */ interface StructElevationLayer { scale: number; @@ -57,25 +38,12 @@ interface StructElevationLayer { } /** Default GPU struct values for initialization QoL */ -const defaultStructLayers: Readonly<{ - color: StructColorLayer; - elevation: StructElevationLayer; -}> = { - color: { - textureOffset: 0, - crs: 0, - opacity: 0, - effect_parameter: 0, - effect_type: colorLayerEffects.noEffect, - transparent: false, - }, - elevation: { - scale: 0, - bias: 0, - mode: 0, - zmin: 0, - zmax: 0, - }, +const defaultStructElevationLayer: StructElevationLayer = { + scale: 0, + bias: 0, + mode: 0, + zmin: 0, + zmax: 0, }; /** @@ -85,93 +53,35 @@ const defaultStructLayers: Readonly<{ * * @param uniforms - The uniforms object for your material. * @param tiles - An array of RasterTile objects, each containing textures. - * @param max - The maximum number of layers for the DataArrayTexture. - * @param type - Layer set identifier: 'c' for color, 'e' for elevation. * @param renderer - The renderer used to render textures. * @param renderTargetCache - Cache for managing render targets. + * @param extent - tile extent */ -function updateLayersUniformsForType( +function updateLayersUniforms( uniforms: Record, tiles: RasterTile[], - max: number, - type: Type, renderer: THREE.WebGLRenderer, renderTargetCache: RenderTargetCache | undefined, -) { - // Aliases for readability - const uLayers = uniforms.layers.value; - const uTextures = uniforms.textures; - const uOffsetScales = uniforms.offsetScales.value; - const uTextureCount = uniforms.textureCount; - - // Flatten the 2d array: [i, j] -> layers[_layerIds[i]].textures[j] - let count = 0; - let width = 0; - let height = 0; - let setSize = false; - - // Determine total count of textures and dimensions - // (assuming all textures are same size) - let textureSetId: string = type; - for (const tile of tiles) { - // FIXME: RasterElevationTile are always passed to this function - // alone so this works, but it's really not great even ignoring - // the dynamic addition of a field. - // @ts-expect-error: adding field to passed layer - tile.textureOffset = count; - - for ( - let i = 0; - i < tile.textures.length && count < max; - ++i, ++count - ) { - const texture = tile.textures[i]; - if (!texture.isTexture) { continue; } - - textureSetId += `${texture.id}.`; - uOffsetScales[count] = tile.offsetScales[i]; - uLayers[count] = tile; - - const img = texture.image; - if (!img || img.width <= 0 || img.height <= 0) { - console.error('Texture image not loaded or has zero dimensions'); - uTextureCount.value = 0; - } else if (setSize === false) { - width = img.width; - height = img.height; - setSize = true; - } else if (width !== img.width || height !== img.height) { - console.error('Texture dimensions mismatch'); - uTextureCount.value = 0; - } - } + extent: Extent) { + if (tiles.length == 0 || tiles.find(tile => tile.textures.length == 0) || !renderTargetCache) { + return; } - const cachedRT = renderTargetCache?.get(textureSetId); + const textureSetId = extent.toString(); - if (cachedRT) { - uTextures.value = cachedRT.texture; - uTextureCount.value = count; - return; - } + let renderTarget = renderTargetCache.get(textureSetId); - const rt = makeDataArrayRenderTarget(width, height, count, tiles, max, renderer); - if (!rt) { - uTextureCount.value = 0; - return; + if (!renderTarget) { + renderTarget = new THREE.WebGLRenderTarget(256, 256, { depthBuffer: false }); + renderTarget.texture.extent = extent; + + renderTargetCache.set(textureSetId, renderTarget); } - renderTargetCache?.set(textureSetId, rt); - rt.texture.userData = { textureSetId }; - uniforms.textures.value = rt.texture; + drawMap(renderTarget, tiles, renderer); - if (count > max) { - console.warn( - `LayeredMaterial: Not enough texture units (${max} < ${count}), ` - + 'excess textures have been discarded.', - ); - } - uTextureCount.value = count; + uniforms.mapTransform.value.identity(); + uniforms.map.value = renderTarget.texture; } export const ELEVATION_MODES = { @@ -218,22 +128,18 @@ interface LayeredMaterialRawUniforms { // Debug showOutline: boolean; outlineWidth: number; - outlineColors: THREE.Color[]; + outlineColors: THREE.Color; // Elevation layers - elevationLayers: StructElevationLayer[]; - elevationTextures: THREE.DataArrayTexture | null; - elevationOffsetScales: THREE.Vector4[]; - elevationTextureCount: number; + elevationLayer: StructElevationLayer; + displacementMap: THREE.DataTexture | null; + displacementMapTransform: THREE.Matrix3; // Color layers - colorLayers: StructColorLayer[]; - colorTextures: THREE.DataArrayTexture | null; - colorOffsetScales: THREE.Vector4[]; - colorTextureCount: number; + map: THREE.DataTexture | null; + mapTransform: THREE.Matrix3; } -let nbSamplers: [number, number] | undefined; const fragmentShader: string[] = []; /** Replacing the default uniforms dynamic type with our own static map. */ @@ -268,9 +174,8 @@ function fillInProp< type ElevationModeDefines = DefineMapping<'ELEVATION', typeof ELEVATION_MODES>; type RenderModeDefines = DefineMapping<'MODE', typeof RenderMode.MODES>; type LayeredMaterialDefines = { - NUM_VS_TEXTURES: number; - NUM_FS_TEXTURES: number; - NUM_CRS: number; + USE_MAP: number; + USE_DISPLACEMENTMAP: number; DEBUG: number; MODE: number; } & ElevationModeDefines & RenderModeDefines; @@ -309,32 +214,47 @@ export class LayeredMaterial extends THREE.ShaderMaterial { super(options); this.name = 'LayeredMaterial'; - nbSamplers ??= [samplersElevationCount, getMaxColorSamplerUnitsCount()]; - const defines: Partial = {}; - fillInProp(defines, 'NUM_VS_TEXTURES', nbSamplers[0]); - fillInProp(defines, 'NUM_FS_TEXTURES', nbSamplers[1]); - fillInProp(defines, 'NUM_CRS', crsCount); + fillInProp(defines, 'USE_MAP', 1); + fillInProp(defines, 'USE_DISPLACEMENTMAP', 1); initModeDefines(defines); fillInProp(defines, 'MODE', RenderMode.MODES.FINAL); fillInProp(defines, 'DEBUG', +__DEBUG__); - if (__DEBUG__) { - const outlineColors = [new THREE.Color(1.0, 0.0, 0.0)]; - if (crsCount > 1) { - outlineColors.push(new THREE.Color(1.0, 0.5, 0.0)); - } + // iTowns-specific uniforms + overrides of lambert defaults. + const itownsUniforms: Record = { + lightingEnabled: { value: false }, + lightPosition: { value: new THREE.Vector3(-0.5, 0.0, 1.0) }, + overlayAlpha: { value: 0 }, + overlayColor: { value: new THREE.Color(1.0, 0.3, 0.0) }, + objectId: { value: 0 }, + geoidHeight: { value: 0.0 }, + minBorderDistance: { value: -0.01 }, + elevationLayer: { value: defaultStructElevationLayer }, + diffuse: { value: new THREE.Color(0.04, 0.23, 0.35) }, + opacity: { value: this.opacity }, + fogFar: { value: 1000 }, + fogColor: { value: new THREE.Color(0.76, 0.85, 1.0) }, + }; - this.initUniforms({ - showOutline: true, - outlineWidth: 0.008, - outlineColors, - }); + if (__DEBUG__) { + itownsUniforms.showOutline = { value: true }; + itownsUniforms.outlineWidth = { value: 0.008 }; + itownsUniforms.outlineColors = { value: new THREE.Color(1.0, 0.0, 0.0) }; } + // Merge Three.js lambert uniforms (fog, map, displacementmap, diffuse, + // opacity…) with iTowns-specific uniforms. + // User-provided options.uniforms take final precedence. + this.uniforms = THREE.UniformsUtils.merge([ + THREE.ShaderLib.lambert.uniforms, + itownsUniforms, + options.uniforms ?? {}, + ]); + this.defines = defines; this.lights = true; @@ -346,63 +266,11 @@ export class LayeredMaterial extends THREE.ShaderMaterial { fragmentShader[crsCount] ??= ShaderUtils.unrollLoops(TileFS, defines); this.fragmentShader = fragmentShader[crsCount]; - this.initUniforms({ - // Color uniforms - diffuse: new THREE.Color(0.04, 0.23, 0.35), - opacity: this.opacity, - - // Lighting uniforms - lightingEnabled: false, - lightPosition: new THREE.Vector3(-0.5, 0.0, 1.0), - - // Misc properties - fogNear: 1, - fogFar: 1000, - fogColor: new THREE.Color(0.76, 0.85, 1.0), - fogDensity: 0.00025, - overlayAlpha: 0, - overlayColor: new THREE.Color(1.0, 0.3, 0.0), - objectId: 0, - - geoidHeight: 0.0, - - // > 0 produces gaps, - // < 0 causes oversampling of textures - // = 0 causes sampling artefacts due to bad estimation of texture-uv - // gradients - // best is a small negative number - minBorderDistance: -0.01, - }); - // LayeredMaterialLayers this.colorTiles = []; this.colorTileIds = []; this.layersNeedUpdate = false; - // elevation/color layer uniforms, to be updated using updateUniforms() - this.initUniforms({ - elevationLayers: new Array(nbSamplers[0]) - .fill(defaultStructLayers.elevation), - elevationTextures: null, - elevationOffsetScales: new Array(nbSamplers[0]) - .fill(identityOffsetScale), - elevationTextureCount: 0, - - colorLayers: new Array(nbSamplers[1]) - .fill(defaultStructLayers.color), - colorTextures: null, - colorOffsetScales: new Array(nbSamplers[1]) - .fill(identityOffsetScale), - colorTextureCount: 0, - }); - - const lambertUniforms = THREE.UniformsUtils.clone(THREE.ShaderLib.lambert.uniforms); - const lambertUniformValues: Record = {}; - for (const [name, uniform] of Object.entries(lambertUniforms)) { - lambertUniformValues[name] = uniform.value; - } - this.initUniforms(lambertUniformValues); - // Can't do an ES6 getter/setter here because it would override the // Material::visible property with accessors, which is not allowed. Object.defineProperty(this, 'visible', { @@ -476,59 +344,28 @@ export class LayeredMaterial extends THREE.ShaderMaterial { } } - public getLayerUniforms(type: Type): - MappedUniforms<{ - layers: (Type extends 'color' - ? StructColorLayer - : StructElevationLayer)[]; - textures: THREE.Texture[]; - offsetScales: THREE.Vector4[]; - textureCount: number; - }> { - return { - layers: this.uniforms[`${type}Layers`], - textures: this.uniforms[`${type}Textures`], - offsetScales: this.uniforms[`${type}OffsetScales`], - textureCount: this.uniforms[`${type}TextureCount`], - }; - } - - /** - * Updates uniforms for both color and elevation layers. - * Orchestrates the processing of visible color layers and elevation tiles. - * - * @param renderer - The renderer used to render textures into arrays. - */ - public updateLayersUniforms(renderer: THREE.WebGLRenderer): void { - const colorlayers = this.colorTiles - .filter(rt => rt.visible && rt.opacity > 0); - colorlayers.sort((a, b) => - this.colorTileIds.indexOf(a.id) - this.colorTileIds.indexOf(b.id), - ); - - updateLayersUniformsForType( - this.getLayerUniforms('color'), - colorlayers, - this.defines.NUM_FS_TEXTURES, - 'c', - renderer, - this.renderTargetCache, - ); - - if (this.elevationTileId !== undefined && this.getElevationTile()) { - if (this.elevationTile !== undefined) { - updateLayersUniformsForType( - this.getLayerUniforms('elevation'), - [this.elevationTile], - this.defines.NUM_VS_TEXTURES, - 'e', - renderer, - this.renderTargetCache, + public updateLayersUniforms(renderer: THREE.WebGLRenderer, extent: Extent): void { + if (!this.layersNeedUpdate) { + return; + } else { + const colorlayers = this.colorTiles + .filter(rt => rt.visible && rt.opacity > 0) + .sort((a, b) => + this.colorTileIds.indexOf(a.id) - this.colorTileIds.indexOf(b.id), ); + + updateLayersUniforms(this.uniforms, + colorlayers, renderer, this.renderTargetCache, extent); + + if (this.elevationTile && this.elevationTile.level > 0) { + const { uniforms, elevationTile } = this; + uniforms.displacementMap.value = elevationTile.textures[0]; + uniforms.displacementMapTransform.value = elevationTile.mapTransforms[0]; + uniforms.elevationLayer.value = elevationTile; } - } - this.layersNeedUpdate = false; + this.layersNeedUpdate = false; + } } /** @@ -540,14 +377,14 @@ export class LayeredMaterial extends THREE.ShaderMaterial { throw new Error('renderTargetCache is not initialized'); } - const colorTextures = this.getUniform('colorTextures'); - if (colorTextures?.userData?.textureSetId) { - this.renderTargetCache.markAsUsed(colorTextures.userData.textureSetId); + const map = this.uniforms.map.value; + if (map?.userData?.textureSetId) { + this.renderTargetCache.markAsUsed(map.userData.textureSetId); } - const elevationTextures = this.getUniform('elevationTextures'); - if (elevationTextures?.userData?.textureSetId) { - this.renderTargetCache.markAsUsed(elevationTextures.userData.textureSetId); + const displacementMap = this.uniforms.displacementMap.value; + if (displacementMap?.userData?.textureSetId) { + this.renderTargetCache.markAsUsed(displacementMap.userData.textureSetId); } } @@ -622,6 +459,18 @@ export class LayeredMaterial extends THREE.ShaderMaterial { ? this.elevationTile : this.colorTiles.find(l => l.id === id); } + public colorDataHasLoaded() { + return this.colorTiles.length == 0 || !this.colorTiles.some(colorTile => !colorTile.hasData()); + } + + public elevationDataHasLoaded() { + return !this.elevationTile || this.elevationTile.hasData(); + } + + public dataHasLoaded() { + return this.colorDataHasLoaded() && this.elevationDataHasLoaded(); + } + public getTiles(ids: string[]): RasterTile[] { // NOTE: this could instead be a mapping with an undefined in place of // unfound IDs. Need to identify a use case for it though as it would diff --git a/packages/Main/src/Renderer/OBB.ts b/packages/Main/src/Renderer/OBB.ts index a188f7f6cb..4c62e7ea9b 100644 --- a/packages/Main/src/Renderer/OBB.ts +++ b/packages/Main/src/Renderer/OBB.ts @@ -6,7 +6,7 @@ import { CRS, Coordinates, OrientationUtils } from '@itowns/geographic'; import type { Extent, ProjectionLike } from '@itowns/geographic'; // get oriented bounding box of tile -const builder = new GlobeTileBuilder({ uvCount: 1 }); +const builder = new GlobeTileBuilder(); const size = new THREE.Vector3(); const dimension = new THREE.Vector2(); const center = new THREE.Vector3(); diff --git a/packages/Main/src/Renderer/RasterTile.js b/packages/Main/src/Renderer/RasterTile.js index ccc5393fae..0edb651948 100644 --- a/packages/Main/src/Renderer/RasterTile.js +++ b/packages/Main/src/Renderer/RasterTile.js @@ -1,9 +1,41 @@ import * as THREE from 'three'; import { ELEVATION_MODES } from 'Renderer/LayeredMaterial'; +import LayerUpdateState from 'Layer/LayerUpdateState'; +import { nextLevelToFetch } from 'Layer/LayerUpdateStrategy'; +import handlingError from 'Process/handlerNodeError'; import { checkNodeElevationTextureValidity, insertSignificantValuesFromParent, computeMinMaxElevation } from 'Parser/XbilParser'; export const EMPTY_TEXTURE_ZOOM = -1; +function materialCommandQueuePriorityFunction(material) { + // We know that 'node' is visible because commands can only be + // issued for visible nodes. + // TODO: need priorization of displayed nodes + // Then prefer displayed node over non-displayed one + return material.visible ? 100 : 10; +} + +function refinementCommandCancellationFn(cmd) { + const { requester } = cmd; + const { children } = requester; + + return !cmd.force && !requester.visible && (!requester.parent || !requester.material.visible || children.find(c => c.visible)); +} + +function buildCommand(tile, extentsSource, requester, view) { + return { + view, + layer: tile.layer, + extentsSource: extentsSource.map(t => (tile.layer.source.hasData(t) ? t : undefined)), + extentsDestination: tile.tiles, + requester, + priority: materialCommandQueuePriorityFunction(requester.material), + earlyDropFunction: refinementCommandCancellationFn, + partialLoading: true, + force: !tile.hasData(), + }; +} + const pitch = new THREE.Vector4(); function getIndiceWithPitch(i, pitch, w) { @@ -28,18 +60,16 @@ function getIndiceWithPitch(i, pitch, w) { * @class RasterTile */ export class RasterTile extends THREE.EventDispatcher { - constructor(layer) { + constructor(layer, tiles) { super(); this.layer = layer; - this.crs = layer.parent.tileMatrixSets.indexOf(layer.crs); - if (this.crs == -1) { - console.error('Unknown crs:', layer.crs); - } - this.textures = []; - this.offsetScales = []; + this.tiles = tiles; + this.mapTransforms = []; this.level = EMPTY_TEXTURE_ZOOM; this.needsUpdate = false; + this.state = new LayerUpdateState(); + this.lowestLevelError = Infinity; this._handlerCBEvent = () => { this.needsUpdate = true; }; layer.addEventListener('visible-property-changed', this._handlerCBEvent); @@ -58,27 +88,36 @@ export class RasterTile extends THREE.EventDispatcher { return this.layer.visible; } - initFromParent(parent, extents) { - if (parent && parent.level > this.level) { - let index = 0; - const sortedParentTextures = this.sortBestParentTextures(parent.textures); - for (const childExtent of extents) { - const matchingParentTexture = sortedParentTextures - .find(parentTexture => parentTexture && childExtent.isInside(parentTexture.extent)); - if (matchingParentTexture) { - this.setTexture(index++, matchingParentTexture, - childExtent.offsetToParent(matchingParentTexture.extent)); - } - } + load(requester, view) { + if (this.state.canTryUpdate()) { + this.state.newTry(); + + const nextLevel = nextLevelToFetch(this); + const nextTiles = this.tiles.map(tile => tile.tiledExtentParent(nextLevel)); + + const command = buildCommand(this, nextTiles, requester, view); - if (__DEBUG__) { - if (index != extents.length) { - console.error(`non-coherent result ${index} vs ${extents.length}.`, extents); + return view.mainLoop.scheduler.execute(command).then((textures) => { + this.setTextures(textures); + + if (nextLevelToFetch(this) == this.level) { + this.state.noMoreUpdatePossible(); + } else { + this.state.success(); } - } + + return textures; + }, () => { + this.state.success(); + }) + .catch(err => handlingError(err, requester, this.layer, nextLevel, view)); } } + hasData() { + return this.level > EMPTY_TEXTURE_ZOOM; + } + sortBestParentTextures(textures) { const sortByValidity = (a, b) => { if (a.isTexture === b.isTexture) { @@ -116,7 +155,7 @@ export class RasterTile extends THREE.EventDispatcher { // Dispose all textures this.disposeAtIndexes(this.textures.keys()); this.textures = []; - this.offsetScales = []; + this.mapTransforms = []; this.level = EMPTY_TEXTURE_ZOOM; } @@ -130,19 +169,22 @@ export class RasterTile extends THREE.EventDispatcher { this.needsUpdate = true; } - setTexture(index, texture, offsetScale) { - if (this.shouldWriteTextureAtIndex(index, texture)) { - this.level = (texture && texture.extent) ? texture.extent.zoom : this.level; - this.textures[index] = texture || null; - this.offsetScales[index] = offsetScale; + setTexture(index, texture, mapTransform) { + if (texture && this.shouldWriteTextureAtIndex(index, texture)) { + this.level = texture.extent ? texture.extent.zoom : this.level; + this.textures[index] = texture; + this.mapTransforms[index] = mapTransform; this.needsUpdate = true; + return texture; } } - setTextures(textures, pitchs) { + setTextures(textures) { this.disposeRedrawnTextures(textures); for (let i = 0, il = textures.length; i < il; ++i) { - this.setTexture(i, textures[i], pitchs[i]); + if (textures[i]) { + this.setTexture(i, textures[i], this.tiles[i].transformToParent(textures[i].extent)); + } } } @@ -165,8 +207,8 @@ export class RasterColorTile extends RasterTile { } export class RasterElevationTile extends RasterTile { - constructor(layer) { - super(layer); + constructor(layer, tiles) { + super(layer, tiles); const defaultEle = { bias: 0, mode: ELEVATION_MODES.DATA, @@ -210,23 +252,14 @@ export class RasterElevationTile extends RasterTile { } } - initFromParent(parent, extents) { - const currentLevel = this.level; - super.initFromParent(parent, extents); - this.updateMinMaxElevation(); - if (currentLevel !== this.level) { - this.dispatchEvent({ type: 'rasterElevationLevelChanged', node: this }); - } - } - - setTextures(textures, offsetScales) { + setTextures(textures) { const anyValidTexture = textures.find(texture => texture != null); if (!anyValidTexture) { return; } const currentLevel = this.level; this.replaceNoDataValueFromTexture(anyValidTexture); - super.setTextures(textures, offsetScales); + super.setTextures(textures); this.updateMinMaxElevation(); if (currentLevel !== this.level) { this.dispatchEvent({ type: 'rasterElevationLevelChanged', node: this }); @@ -238,7 +271,7 @@ export class RasterElevationTile extends RasterTile { if (firstValidIndex !== -1 && !this.layer.useColorTextureElevation) { const { min, max } = computeMinMaxElevation( this.textures[firstValidIndex], - this.offsetScales[firstValidIndex], + this.mapTransforms[firstValidIndex], { noDataValue: this.layer.noDataValue, zmin: this.layer.zmin, diff --git a/packages/Main/src/Renderer/RenderTargetCache.ts b/packages/Main/src/Renderer/RenderTargetCache.ts index da856e41fb..0ad403d463 100644 --- a/packages/Main/src/Renderer/RenderTargetCache.ts +++ b/packages/Main/src/Renderer/RenderTargetCache.ts @@ -10,7 +10,7 @@ export class RenderTargetCache { /** * Render targets queued for disposal. */ - private _pendingDisposal: Map; + private _pendingDisposal: Map; /** * Render targets used in the current render cycle. @@ -21,14 +21,14 @@ export class RenderTargetCache { * LRU cache of render targets, automatically discards least recently * used when max is exceeded. */ - private _cache: LRUCache; + private _cache: LRUCache; constructor(maxCacheSize = 200) { this._pendingDisposal = new Map(); this._usedIds = new Set(); this._cache = new LRUCache({ max: maxCacheSize, - dispose: (rt: THREE.WebGLArrayRenderTarget, key: string) => { + dispose: (rt: THREE.WebGLRenderTarget, key: string) => { this._pendingDisposal.set(key, rt); }, }); @@ -68,7 +68,7 @@ export class RenderTargetCache { * @param id - The identifier of the render target * @returns The render target or undefined if not found */ - public get(id: string): THREE.WebGLArrayRenderTarget | undefined { + public get(id: string): THREE.WebGLRenderTarget | undefined { let rt = this._cache.get(id); if (!rt) { @@ -88,7 +88,8 @@ export class RenderTargetCache { * @param id - The identifier of the render target * @param rt - The render target to cache */ - public set(id: string, rt: THREE.WebGLArrayRenderTarget): void { + public set(id: string, rt: THREE.WebGLRenderTarget): void { + rt.texture.userData.textureSetId = id; this._cache.set(id, rt); } diff --git a/packages/Main/src/Renderer/Shader/Chunk/color_layers_pars_fragment.glsl b/packages/Main/src/Renderer/Shader/Chunk/color_layers_pars_fragment.glsl index 97d2ba2fae..54a9957d52 100644 --- a/packages/Main/src/Renderer/Shader/Chunk/color_layers_pars_fragment.glsl +++ b/packages/Main/src/Renderer/Shader/Chunk/color_layers_pars_fragment.glsl @@ -1,26 +1,11 @@ -struct Layer { - int textureOffset; - int crs; - int effect_type; - float effect_parameter; - float opacity; - bool transparent; -}; - -#include - -uniform sampler2DArray colorTextures; -uniform vec4 colorOffsetScales[NUM_FS_TEXTURES]; -uniform Layer colorLayers[NUM_FS_TEXTURES]; -uniform int colorTextureCount; - -vec3 uvs[NUM_CRS]; - float getBorderDistance(vec2 uv) { vec2 p2 = min(uv, 1. -uv); return min(p2.x, p2.y); } + +/* move new Pass + float tolerance = 0.99; vec4 applyWhiteToInvisibleEffect(vec4 color) { @@ -37,10 +22,11 @@ vec4 applyLightColorToInvisibleEffect(vec4 color, float intensity) { color.rgb *= color.rgb * color.rgb; return color; } +*/ #if defined(DEBUG) uniform bool showOutline; -uniform vec3 outlineColors[NUM_CRS]; +uniform vec3 outlineColors; uniform float outlineWidth; vec4 getOutlineColor(vec3 outlineColor, vec2 uv) { @@ -50,31 +36,3 @@ vec4 getOutlineColor(vec3 outlineColor, vec2 uv) { #endif uniform float minBorderDistance; -vec4 getLayerColor(int textureOffset, sampler2DArray tex, vec4 offsetScale, Layer layer) { - if ( textureOffset >= colorTextureCount ) return vec4(0); - - vec3 uv; - // #pragma unroll_loop - for ( int i = 0; i < NUM_CRS; i ++ ) { - if ( i == layer.crs ) uv = uvs[ i ]; - } - - float borderDistance = getBorderDistance(uv.xy); - if (textureOffset != layer.textureOffset + int(uv.z) || borderDistance < minBorderDistance ) return vec4(0); - vec4 color = texture(tex, vec3(pitUV(uv.xy, offsetScale), float(textureOffset))); - if (layer.effect_type == 3) { - #include - } else { - if (layer.transparent && color.a != 0.0) { - color.rgb /= color.a; - } - - if (layer.effect_type == 1) { - color = applyLightColorToInvisibleEffect(color, layer.effect_parameter); - } else if (layer.effect_type == 2) { - color = applyWhiteToInvisibleEffect(color); - } - } - color.a *= layer.opacity; - return color; -} diff --git a/packages/Main/src/Renderer/Shader/Chunk/elevation_pars_vertex.glsl b/packages/Main/src/Renderer/Shader/Chunk/elevation_pars_vertex.glsl index d5048c16d7..2377c904d8 100644 --- a/packages/Main/src/Renderer/Shader/Chunk/elevation_pars_vertex.glsl +++ b/packages/Main/src/Renderer/Shader/Chunk/elevation_pars_vertex.glsl @@ -1,4 +1,6 @@ -#if NUM_VS_TEXTURES > 0 +#if USE_DISPLACEMENTMAP + #include + struct Layer { float scale; float bias; @@ -7,10 +9,7 @@ float zmax; }; - uniform Layer elevationLayers[NUM_VS_TEXTURES]; - uniform sampler2DArray elevationTextures; - uniform vec4 elevationOffsetScales[NUM_VS_TEXTURES]; - uniform int elevationTextureCount; + uniform Layer elevationLayer; uniform float geoidHeight; highp float decode32(highp vec4 rgba) { @@ -21,19 +20,19 @@ return Result; } - float getElevationMode(vec2 uv, sampler2DArray tex, int mode) { + float getElevationMode(vec2 uv, sampler2D tex, int mode) { if (mode == ELEVATION_RGBA) - return decode32(texture(tex, vec3(uv, 0.0)).abgr * 255.0); + return decode32(texture(tex, uv).abgr * 255.0); if (mode == ELEVATION_DATA || mode == ELEVATION_COLOR) - return texture(tex, vec3(uv, 0.0)).r; + return texture(tex, uv).r; return 0.; } - float getElevation(vec2 uv, sampler2DArray tex, vec4 offsetScale, Layer layer) { - // Elevation textures are inverted along the y-axis - uv = vec2(uv.x, 1.0 - uv.y); - uv = uv * offsetScale.zw + offsetScale.xy; - float d = clamp(getElevationMode(uv, tex, layer.mode), layer.zmin, layer.zmax); + float getElevation(Layer layer) { + vec2 uv = vDisplacementMapUv; + // Elevation textures are stored top-to-bottom (v=0 at north), flip after offset + uv.y = 1.0 - uv.y; + float d = clamp(getElevationMode(uv, displacementMap, layer.mode), layer.zmin, layer.zmax); return d * layer.scale + layer.bias; } #endif diff --git a/packages/Main/src/Renderer/Shader/Chunk/elevation_vertex.glsl b/packages/Main/src/Renderer/Shader/Chunk/elevation_vertex.glsl index 54ab40ee9c..63c1937599 100644 --- a/packages/Main/src/Renderer/Shader/Chunk/elevation_vertex.glsl +++ b/packages/Main/src/Renderer/Shader/Chunk/elevation_vertex.glsl @@ -1,6 +1,4 @@ -#if NUM_VS_TEXTURES > 0 - if(elevationTextureCount > 0) { - float elevation = getElevation(uv, elevationTextures, elevationOffsetScales[0], elevationLayers[0]); +#if USE_DISPLACEMENTMAP + float elevation = getElevation(elevationLayer); transformed += elevation * normal; - } #endif diff --git a/packages/Main/src/Renderer/Shader/Chunk/pitUV.glsl b/packages/Main/src/Renderer/Shader/Chunk/pitUV.glsl deleted file mode 100644 index 5dba9788ed..0000000000 --- a/packages/Main/src/Renderer/Shader/Chunk/pitUV.glsl +++ /dev/null @@ -1,5 +0,0 @@ -vec2 pitUV(vec2 uv, vec4 pit) -{ - return uv * pit.zw + vec2(pit.x, 1.0 - pit.w - pit.y); -} - diff --git a/packages/Main/src/Renderer/Shader/ShaderChunk.js b/packages/Main/src/Renderer/Shader/ShaderChunk.js index 2868f19d89..ca6335d0e4 100644 --- a/packages/Main/src/Renderer/Shader/ShaderChunk.js +++ b/packages/Main/src/Renderer/Shader/ShaderChunk.js @@ -8,7 +8,6 @@ import mode_depth_fragment from './Chunk/mode_depth_fragment.glsl'; import mode_id_fragment from './Chunk/mode_id_fragment.glsl'; import overlay_fragment from './Chunk/overlay_fragment.glsl'; import overlay_pars_fragment from './Chunk/overlay_pars_fragment.glsl'; -import pitUV from './Chunk/pitUV.glsl'; import precision_qualifier from './Chunk/precision_qualifier.glsl'; import projective_texturing_vertex from './Chunk/projective_texturing_vertex.glsl'; import projective_texturing_pars_vertex from './Chunk/projective_texturing_pars_vertex.glsl'; @@ -29,7 +28,6 @@ const itownsShaderChunk = { mode_pars_fragment, overlay_fragment, overlay_pars_fragment, - pitUV, precision_qualifier, projective_texturing_vertex, projective_texturing_pars_vertex, diff --git a/packages/Main/src/Renderer/Shader/TileFS.glsl b/packages/Main/src/Renderer/Shader/TileFS.glsl index f172fca602..ae30e06ec0 100644 --- a/packages/Main/src/Renderer/Shader/TileFS.glsl +++ b/packages/Main/src/Renderer/Shader/TileFS.glsl @@ -1,6 +1,8 @@ #include #include -#include +#include +#include + #include #if MODE == MODE_FINAL #include @@ -16,7 +18,7 @@ uniform vec3 diffuse; uniform float opacity; -varying vec3 vUv; // uv.x/uv_1.x, uv.y, uv_1.y +varying vec3 vUv; varying vec2 vHighPrecisionZW; void main() { @@ -32,30 +34,17 @@ void main() { #else - gl_FragColor = vec4(diffuse, opacity); - - uvs[0] = vec3(vUv.xy, 0.); - -#if NUM_CRS > 1 - uvs[1] = vec3(vUv.x, fract(vUv.z), floor(vUv.z)); -#endif + gl_FragColor = vec4(diffuse, opacity); - vec4 color; - #pragma unroll_loop - for ( int i = 0; i < NUM_FS_TEXTURES; i ++ ) { - color = getLayerColor( i , colorTextures, colorOffsetScales[ i ], colorLayers[ i ]); - gl_FragColor.rgb = mix(gl_FragColor.rgb, color.rgb, color.a); - } + vec4 color = texture(map, vMapUv); + gl_FragColor.rgb = mix(gl_FragColor.rgb, color.rgb, color.a); #if DEBUG == 1 if (showOutline) { - #pragma unroll_loop - for ( int i = 0; i < NUM_CRS; i ++) { - color = getOutlineColor( outlineColors[ i ], uvs[ i ].xy); - gl_FragColor.rgb = mix(gl_FragColor.rgb, color.rgb, color.a); - } + color = getOutlineColor( outlineColors, vUv.xy); + gl_FragColor.rgb = mix(gl_FragColor.rgb, color.rgb, color.a); } - #endif + #endif // if no lights are defined, keep flat shading #if NUM_DIR_LIGHTS + NUM_SPOT_LIGHTS + NUM_POINT_LIGHTS + NUM_HEMI_LIGHTS > 0 diff --git a/packages/Main/src/Renderer/Shader/TileVS.glsl b/packages/Main/src/Renderer/Shader/TileVS.glsl index 2e57c0b55e..4a425d5b8d 100644 --- a/packages/Main/src/Renderer/Shader/TileVS.glsl +++ b/packages/Main/src/Renderer/Shader/TileVS.glsl @@ -1,11 +1,9 @@ #include #include +#include #include #include #include -#if NUM_CRS > 1 -attribute float uv_1; -#endif #include @@ -18,24 +16,24 @@ varying vec3 vUv; varying vec3 vNormal; #endif void main() { - #include - #include - #include - #include - #include - vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; - #include - #include - vHighPrecisionZW = gl_Position.zw; + vec2 DISPLACEMENTMAP_UV = vec2(uv); + vec2 MAP_UV = vec2(uv); + + #include + #include + #include + #include + #include + #include + vViewPosition = (modelViewMatrix * vec4(position, 1.0)).xyz; + #include + #include + vHighPrecisionZW = gl_Position.zw; #if MODE == MODE_FINAL - #include - #include - #include - #if NUM_CRS > 1 - vUv = vec3(uv, (uv_1 > 0.) ? uv_1 : uv.y); // set uv_1 = uv if uv_1 is undefined - #else - vUv = vec3(uv, 0.0); - #endif - vNormal = normalize ( mat3( normalMatrix[0].xyz, normalMatrix[1].xyz, normalMatrix[2].xyz ) * normal ); + #include + #include + #include + vUv = vec3(uv, 0.0); + vNormal = normalize ( mat3( normalMatrix[0].xyz, normalMatrix[1].xyz, normalMatrix[2].xyz ) * normal ); #endif } diff --git a/packages/Main/src/Renderer/Shader/geoTransformFS.glsl b/packages/Main/src/Renderer/Shader/geoTransformFS.glsl new file mode 100644 index 0000000000..b6f61d6b36 --- /dev/null +++ b/packages/Main/src/Renderer/Shader/geoTransformFS.glsl @@ -0,0 +1,84 @@ +precision highp float; + +varying vec2 vUv; + +uniform sampler2D tDiffuse; +uniform mat3 localUvToWorldTransform; +uniform mat3 mapToUvTransform; +uniform mat3 mapTransform; +uniform float opacity; + +#ifdef EPSG_4326_EPSG_3857 + +// Precomputed constant for converting latitude to Y in Mercator projection +// The constant is derived from the formula for the Mercator projection: +// y = R * ln(tan(pi/4 + lat/2)) +// where R is the Earth's radius (approximately 6378137 meters) +// and the factor 0.69314718 is ln(2) to convert from natural log to log base 2 +// 20037508.34 is maximum extent size in Mercator projection +const float CTOYL = 20037508.34 / 3.141592653589793 * 0.69314718; + +// world WGS84 to Mercator +vec3 worldToMapEPSG(vec3 world_coordinates) { + vec3 mercatorCoordinates; + + float s = sin(world_coordinates.y); + + mercatorCoordinates.y = 0.5 * log2((1.0 + s) / (1.0 - s)) * CTOYL; + + mercatorCoordinates.z = 1.0; + + return mercatorCoordinates; +} + +vec2 mapCoordinatesToUvTexture(mat3 mapTransform, vec2 uvMesh, vec3 mapCoordinates) { + + return vec2(uvMesh.x, (mapTransform * mapCoordinates).y); + +} + +vec2 optimized(mat3 mapTransform, vec2 uvMesh) { + + return ( mapTransform * vec3( uvMesh, 1 ) ).xy; + +} + +#endif + +void main() { + + #ifdef OPTIMIZED + + vec2 vMapUv = optimized( mapTransform, vUv); + + #else + + // world view coordinates + + vec3 world_coordinates; + + // coordinates in input map EPSG + vec3 mapCoordinates; + + // local uv mesh to world view EPSG + + world_coordinates = localUvToWorldTransform * vec3(vUv, 1.0); + + // world view EPSG to input map EPSG + + mapCoordinates = worldToMapEPSG(world_coordinates); + + // input raster EPSG to local texture + + vec2 vMapUv = mapCoordinatesToUvTexture(mapToUvTransform, vUv, mapCoordinates); + + #endif + + if (vMapUv.y < 0.0 || vMapUv.y > 1.0 || vMapUv.x < 0.0 || vMapUv.x > 1.0) { + // break if out of texture + discard; + } else { + gl_FragColor = texture2D(tDiffuse, vMapUv); + gl_FragColor.a *= opacity; + } +} \ No newline at end of file diff --git a/packages/Main/src/Renderer/WebGLComposer.ts b/packages/Main/src/Renderer/WebGLComposer.ts index 62f5de1381..23b6a8a118 100644 --- a/packages/Main/src/Renderer/WebGLComposer.ts +++ b/packages/Main/src/Renderer/WebGLComposer.ts @@ -1,121 +1,119 @@ import * as THREE from 'three'; +import { Extent } from '@itowns/geographic'; +// eslint-disable-next-line import/extensions +import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'; +// eslint-disable-next-line import/extensions +import { Pass, FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { DotScreenPass } from 'three/examples/jsm/postprocessing/DotScreenPass.js'; import { RasterTile } from './RasterTile'; +import { + materialUnit, + materialMercatorToWGS84, + materialMercatorToWGS84Optimized } from './GeographicTransformMaterials'; -// shader for copying a 2D texture to a framebuffer -const copyTextureShader = { - vertexShader: ` - varying vec2 vUv; - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); +declare module 'three' { + interface Texture { + extent: Extent | null | undefined, + } +} + +const getGeographicTransformMaterial = + (input : string, outputExtent : Extent | null | undefined) => { + if (!outputExtent || input == outputExtent.crs) { + return materialUnit; } - `, - fragmentShader: ` - precision highp float; - uniform sampler2D sourceTexture; - varying vec2 vUv; - void main() { - gl_FragColor = texture2D(sourceTexture, vUv); + if (input == 'EPSG:3857' && outputExtent.crs == 'EPSG:4326') { + // todo could computed with the size texture + if (outputExtent.planarDimensions().x < 0.01) { + return materialMercatorToWGS84Optimized; + } else { + return materialMercatorToWGS84; + } } - `, -}; - -let material: THREE.ShaderMaterial | null = null; -let quad: THREE.Mesh | null = null; -const quadCam: THREE.OrthographicCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); -/** - * Initializes a THREE.WebGLArrayRenderTarget with immutable storage - * and populates its layers. - * Returns the populated render target so callers can own/dispose it. - * - * @param width - The width of each layer in the DataArrayTexture. - * @param height - The height of each layer in the DataArrayTexture. - * @param count - The total number of layers the DataArrayTexture should have. - * @param tiles - An array of RasterTile objects, each containing textures. - * @param max - The maximum allowed number of layers for the DataArrayTexture. - * @param renderer - The renderer used to render the texture. - * @returns The constructed render target, or null - */ -export function makeDataArrayRenderTarget( - width: number, - height: number, - count: number, + + console.log('No Geographic Transform Material'); + }; + +class GeographicProjectionPass extends Pass { + tiles: RasterTile[]; + extent: Extent | null; + _fsQuad: FullScreenQuad; + clear: boolean; + constructor() { + super(); + this._fsQuad = new FullScreenQuad(); + this.tiles = []; + this.extent = null; + this.clear = true; + this.needsSwap = true; + } + + render(renderer: THREE.WebGLRenderer, writeBuffer: THREE.WebGLRenderTarget) { + renderer.setRenderTarget(writeBuffer); + + const inputCrs = this.tiles[0].layer.crs; + + const outputExtent = writeBuffer.texture.extent; + + const geographicTransformMaterial = getGeographicTransformMaterial(inputCrs, outputExtent); + + if (geographicTransformMaterial) { + geographicTransformMaterial.setOutputExtent(outputExtent); + + this._fsQuad.material = geographicTransformMaterial; + + if (this.clear) { renderer.clear(); } + + this.tiles.forEach((tile) => { + if (tile.visible) { + geographicTransformMaterial.setOpacity(tile.opacity); + + tile.textures.forEach((texture) => { + if (texture) { + geographicTransformMaterial.setTexture(texture); + this._fsQuad.render(renderer); + } + }); + } + }); + } + } + + dispose() { + this._fsQuad.dispose(); + } +} + +const geographicProjectionPass = new GeographicProjectionPass(); +let composer : null | EffectComposer = null; + +const dotPass = new DotScreenPass(new THREE.Vector2(0, 0), 0.5, 0.8); +// console.log('dotPass', dotPass); +dotPass.needsSwap = false; + +export function drawMap( + renderTarget: THREE.WebGLRenderTarget, tiles: RasterTile[], - max: number, renderer: THREE.WebGLRenderer, -): THREE.WebGLArrayRenderTarget | null { - if (count === 0) { return null; } - - // The render target's internal framebuffer will be used to attach layers. - const renderTarget = new THREE.WebGLArrayRenderTarget(width, height, count, { - depthBuffer: false, // No depth buffer needed for simple 2D texture copy - }); - const arrayTexture = renderTarget.texture; - - // Set up the quad for rendering - if (!quad) { - const geometry = new THREE.PlaneGeometry(2, 2); - material = new THREE.ShaderMaterial({ - uniforms: { - // This uniform will be updated with each source 2D texture - sourceTexture: { value: null }, - }, - vertexShader: copyTextureShader.vertexShader, - fragmentShader: copyTextureShader.fragmentShader, - }); - quad = new THREE.Mesh(geometry, material); +): undefined { + if (!composer) { + composer = new EffectComposer(renderer, renderTarget); + composer.addPass(geographicProjectionPass); + composer.addPass(dotPass); + } else { + composer.renderer = renderer; + composer.renderTarget1 = renderTarget; + composer.writeBuffer = renderTarget; } - // Store renderer state and temporarily disable VR - const previousRenderTarget = renderer.getRenderTarget(); - const gl = renderer.getContext(); - const glViewport = gl.getParameter(gl.VIEWPORT); + geographicProjectionPass.tiles = tiles; + const wasVREnabled = renderer.xr.enabled; if (wasVREnabled) { renderer.xr.enabled = false; } - // loop through each tile and its textures - // to render them into DataArrayTexture layers - let currentLayerIndex = 0; - - let setTexture = false; - - for (const tile of tiles) { - for ( - let i = 0; - i < tile.textures.length && currentLayerIndex < max; - ++i, ++currentLayerIndex - ) { - const texture = tile.textures[i]; - if (!texture.isTexture) { continue; } - - // Set the current source 2D texture on the quad's material - (material as THREE.ShaderMaterial).uniforms.sourceTexture.value = texture; - - if (!setTexture) { - // Set parameters from the first valid texture - arrayTexture.magFilter = texture.magFilter; - arrayTexture.minFilter = texture.minFilter; - arrayTexture.wrapS = texture.wrapS; - arrayTexture.wrapT = texture.wrapT; - arrayTexture.format = texture.format; - arrayTexture.type = texture.type; - arrayTexture.internalFormat = texture.internalFormat; - arrayTexture.anisotropy = texture.anisotropy; - arrayTexture.premultiplyAlpha = texture.premultiplyAlpha; - setTexture = true; - } + // todo verify if renderer size isn't overkill + composer.render(); - // render this source texture into the current layer - renderer.setRenderTarget(renderTarget, currentLayerIndex); - renderer.render(quad, quadCam); - } - } - - // Restore renderer state - renderer.setRenderTarget(previousRenderTarget); - // renderer.setViewport is not enough to update internal GL state - gl.viewport(glViewport[0], glViewport[1], glViewport[2], glViewport[3]); if (wasVREnabled) { renderer.xr.enabled = true; } - - return renderTarget; } diff --git a/packages/Main/src/Renderer/c3DEngine.js b/packages/Main/src/Renderer/c3DEngine.js index bc535e2e87..6354eacd4a 100644 --- a/packages/Main/src/Renderer/c3DEngine.js +++ b/packages/Main/src/Renderer/c3DEngine.js @@ -100,6 +100,7 @@ class c3DEngine { antialias: options.antialias, alpha: options.alpha, logarithmicDepthBuffer: options.logarithmicDepthBuffer, + preserveDrawingBuffer: true, }); this.renderer.domElement.style.position = 'relative'; this.renderer.domElement.style.zIndex = 0; diff --git a/packages/Main/src/Utils/DEMUtils.js b/packages/Main/src/Utils/DEMUtils.js index c7d38914a9..9400bf67b7 100644 --- a/packages/Main/src/Utils/DEMUtils.js +++ b/packages/Main/src/Utils/DEMUtils.js @@ -346,7 +346,7 @@ function _readZ(layer, method, coord, nodes, cache) { // Assuming that tiles are split in 4 children, we lookup the parent that // really owns this texture - const stepsUpInHierarchy = Math.round(Math.log2(1.0 / tileLayer.offsetScales[0].z)); + const stepsUpInHierarchy = Math.round(Math.log2(1.0 / tileLayer.mapTransforms[0].elements[0])); for (let i = 0; i < stepsUpInHierarchy; i++) { tileWithValidElevationTexture = tileWithValidElevationTexture.parent; } diff --git a/packages/Main/test/unit/dataSourceProvider.js b/packages/Main/test/unit/dataSourceProvider.js index 80f1821839..863e43ed5d 100644 --- a/packages/Main/test/unit/dataSourceProvider.js +++ b/packages/Main/test/unit/dataSourceProvider.js @@ -1,6 +1,5 @@ import * as THREE from 'three'; import assert from 'assert'; -import { updateLayeredMaterialNodeImagery, updateLayeredMaterialNodeElevation } from 'Process/LayeredMaterialNodeProcessing'; import FeatureProcessing from 'Process/FeatureProcessing'; import TileMesh from 'Core/TileMesh'; import { Extent } from '@itowns/geographic'; @@ -8,7 +7,6 @@ import { globalExtentTMS } from 'Core/Tile/TileGrid'; import OBB from 'Renderer/OBB'; import DataSourceProvider from 'Provider/DataSourceProvider'; import Fetcher from 'Provider/Fetcher'; -import TileProvider from 'Provider/TileProvider'; import WMTSSource from 'Source/WMTSSource'; import WMSSource from 'Source/WMSSource'; import WFSSource from 'Source/WFSSource'; @@ -46,21 +44,28 @@ describe('Provide in Sources', function () { let nodeLayer; let nodeLayerElevation; let featureLayer; + let tile; // Mock scheduler const context = { view: { notifyChange: () => true, - }, - scheduler: { - commands: [], - execute: (cmd) => { - context.scheduler.commands.push(cmd); - return new Promise(() => { /* no-op */ }); + mainLoop: { + scheduler: { + commands: [], + execute: (cmd) => { + context.view.mainLoop.scheduler.commands.push(cmd); + return new Promise(() => { /* no-op */ }); + }, + }, }, }, }; + const { scheduler } = context.view.mainLoop; + + context.scheduler = scheduler; + before(function () { stubFetcherJson = sinon.stub(Fetcher, 'json') .callsFake(() => Promise.resolve(JSON.parse(holes))); @@ -80,9 +85,11 @@ describe('Provide in Sources', function () { planarlayer.attach(colorlayer); planarlayer.attach(elevationlayer); - const fakeNode = { material, setBBoxZ: () => { }, addEventListener: () => { } }; - colorlayer.setupRasterNode(fakeNode); - elevationlayer.setupRasterNode(fakeNode); + tile = new TileMesh(geom, material, planarlayer, extent); + tile.parent = { material }; + + colorlayer.setupRasterNode(tile); + elevationlayer.setupRasterNode(tile); nodeLayer = material.getColorTile(colorlayer.id); nodeLayerElevation = material.getElevationTile(); @@ -124,7 +131,7 @@ describe('Provide in Sources', function () { beforeEach('reset state', function () { // clear commands array - context.scheduler.commands = []; + scheduler.commands = []; }); it('should get wmts texture with DataSourceProvider', (done) => { @@ -142,15 +149,12 @@ describe('Provide in Sources', function () { }); colorlayer.source.onLayerAdded({ out: colorlayer }); - - const tile = new TileMesh(geom, material, planarlayer, extent); material.visible = true; nodeLayer.level = EMPTY_TEXTURE_ZOOM; tile.parent = {}; - updateLayeredMaterialNodeImagery(context, colorlayer, tile, tile.parent); - updateLayeredMaterialNodeImagery(context, colorlayer, tile, tile.parent); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + colorlayer.update(context, colorlayer, tile); + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((textures) => { assert.equal(textures.length, 1); assert.equal(textures[0].isTexture, true); @@ -172,21 +176,18 @@ describe('Provide in Sources', function () { }); elevationlayer.source.onLayerAdded({ out: elevationlayer }); - const tile = new TileMesh(geom, material, planarlayer, extent, zoom); material.visible = true; nodeLayerElevation.level = EMPTY_TEXTURE_ZOOM; tile.parent = {}; - updateLayeredMaterialNodeElevation(context, elevationlayer, tile, tile.parent); - updateLayeredMaterialNodeElevation(context, elevationlayer, tile, tile.parent); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + elevationlayer.update(context, elevationlayer, tile); + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((textures) => { assert.equal(textures.length, 1); assert.equal(textures[0].isTexture, true); done(); }).catch(done); }); - it('should get wms texture with DataSourceProvider', (done) => { colorlayer.source = new WMSSource({ url: 'http://domain.com', @@ -204,12 +205,14 @@ describe('Provide in Sources', function () { const tile = new TileMesh(geom, material, planarlayer, extent, zoom); material.visible = true; + material.setColorTileIds([]); + material.removeTile(colorlayer.id); nodeLayer.level = EMPTY_TEXTURE_ZOOM; tile.parent = {}; - updateLayeredMaterialNodeImagery(context, colorlayer, tile, tile.parent); - updateLayeredMaterialNodeImagery(context, colorlayer, tile, tile.parent); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + colorlayer.update(context, colorlayer, tile); + + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((textures) => { assert.equal(textures.length, 1); assert.equal(textures[0].isTexture, true); @@ -224,15 +227,13 @@ describe('Provide in Sources', function () { tile.parent = {}; planarlayer.subdivideNode(context, tile); - TileProvider.executeCommand(context.scheduler.commands[0]) - .then((tiles) => { - assert.equal(tiles.length, 4); - assert.equal(tiles[0].extent.west, tile.extent.east * 0.5); - assert.equal(tiles[0].extent.east, tile.extent.east); - assert.equal(tiles[0].extent.north, tile.extent.north); - assert.equal(tiles[0].extent.south, tile.extent.north * 0.5); - done(); - }).catch(done); + const tiles = tile.children; + assert.equal(tiles.length, 4); + assert.equal(tiles[0].extent.west, tile.extent.east * 0.5); + assert.equal(tiles[0].extent.east, tile.extent.east); + assert.equal(tiles[0].extent.north, tile.extent.north); + assert.equal(tiles[0].extent.south, tile.extent.north * 0.5); + done(); }); it('should get 3 meshs with WFS source and DataSourceProvider', (done) => { @@ -246,7 +247,7 @@ describe('Provide in Sources', function () { featureLayer.source.onLayerAdded({ out: featureLayer }); featureLayer.update(context, featureLayer, tile); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((features) => { assert.equal(features[0].meshes.children.length, 4); done(); @@ -268,7 +269,7 @@ describe('Provide in Sources', function () { featureLayer.source._featuresCaches = {}; featureLayer.source.onLayerAdded({ out: featureLayer }); featureLayer.update(context, featureLayer, tile); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((features) => { assert.ok(features[0].meshes.children[0].isMesh); assert.ok(features[0].meshes.children[1].isPoints); @@ -290,7 +291,7 @@ describe('Provide in Sources', function () { nodeLayer.level = EMPTY_TEXTURE_ZOOM; tile.material.visible = true; featureLayer.source.uid = 22; - const colorlayerWfs = new ColorLayer('color', { + const colorlayerWfs = new ColorLayer('colorWfs', { crs: 'EPSG:3857', source: featureLayer.source, style: { @@ -308,10 +309,14 @@ describe('Provide in Sources', function () { }, }, }); + + planarlayer.attach(colorlayerWfs); + colorlayerWfs.source.onLayerAdded({ out: colorlayerWfs }); - updateLayeredMaterialNodeImagery(context, colorlayerWfs, tile, tile.parent); - updateLayeredMaterialNodeImagery(context, colorlayerWfs, tile, tile.parent); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + + colorlayerWfs.update(context, colorlayerWfs, tile); + + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((textures) => { assert.equal(textures.length, 1); assert.ok(textures[0].isTexture); @@ -335,12 +340,14 @@ describe('Provide in Sources', function () { const tile = new TileMesh(geom, new LayeredMaterial(), planarlayer, extent); tile.material.visible = true; + material.setColorTileIds([]); + material.removeTile(colorlayer.id); nodeLayer.level = EMPTY_TEXTURE_ZOOM; tile.parent = {}; - updateLayeredMaterialNodeImagery(context, colorlayer, tile, tile.parent); - updateLayeredMaterialNodeImagery(context, colorlayer, tile, tile.parent); - DataSourceProvider.executeCommand(context.scheduler.commands[0]) + colorlayer.update(context, colorlayer, tile); + + DataSourceProvider.executeCommand(scheduler.commands[0]) .then((result) => { tile.material.setColorTileIds([colorlayer.id]); tile.material.getColorTile(colorlayer.id).setTextures(result, [new THREE.Vector4()]); diff --git a/packages/Main/test/unit/demutils.js b/packages/Main/test/unit/demutils.js index f47abe2b62..a2b879ce4e 100644 --- a/packages/Main/test/unit/demutils.js +++ b/packages/Main/test/unit/demutils.js @@ -5,12 +5,9 @@ import Fetcher from 'Provider/Fetcher'; import assert from 'assert'; import GlobeView from 'Core/Prefab/GlobeView'; import { Coordinates, Extent } from '@itowns/geographic'; -import { updateLayeredMaterialNodeElevation } from 'Process/LayeredMaterialNodeProcessing'; import TileMesh from 'Core/TileMesh'; import OBB from 'Renderer/OBB'; -import LayerUpdateState from 'Layer/LayerUpdateState'; import DEMUtils from 'Utils/DEMUtils'; -import { RasterElevationTile } from 'Renderer/RasterTile'; import { LayeredMaterial } from 'Renderer/LayeredMaterial'; import * as sinon from 'sinon'; import Renderer from './bootstrap'; @@ -27,7 +24,6 @@ describe('DemUtils', function () { const viewer = new GlobeView(renderer.domElement, placement, { renderer }); let elevationlayer; - let context; let stubFetcherTextFloat; const ELEVATION = 300; @@ -50,18 +46,6 @@ describe('DemUtils', function () { }); source.url = 'https://github.com/iTowns/iTowns2-sample-data/blob/master/dem3_3_8.bil?raw=true'; elevationlayer = new ElevationLayer('worldelevation', { source }); - - context = { - camera: viewer.camera, - engine: viewer.mainLoop.gfxEngine, - scheduler: { - execute: (command) => { - const provider = viewer.mainLoop.scheduler.getProtocolProvider(command.layer.protocol); - return provider.executeCommand(command); - }, - }, - view: viewer, - }; }); after(() => { @@ -83,16 +67,16 @@ describe('DemUtils', function () { const geom = new THREE.BufferGeometry(); geom.OBB = new OBB(new THREE.Vector3(), new THREE.Vector3(1, 1, 1)); const material = new LayeredMaterial(); - const nodeLayer = new RasterElevationTile(elevationlayer); - material.getElevationTile = () => nodeLayer; const tile = new TileMesh(geom, material, viewer.tileLayer, extent, 5); - tile.layerUpdateState[elevationlayer.id] = new LayerUpdateState(); + tile.parent = {}; tiles.push(tile); - updateLayeredMaterialNodeElevation(context, elevationlayer, tile, {}) - .then(() => { - assert.equal(nodeLayer.textures[0].image.data[0], ELEVATION); - done(); - }).catch(done); + const nodeLayer = elevationlayer.setupRasterNode(tile); + + material.getElevationTile = () => nodeLayer; + nodeLayer.load(tile, viewer).then(() => { + assert.equal(nodeLayer.textures[0].image.data[0], ELEVATION); + done(); + }).catch(done); }); it('get elevation value at center with PRECISE_READ_Z', () => { diff --git a/packages/Main/test/unit/layeredmaterial.js b/packages/Main/test/unit/layeredmaterial.js index 16a1e7ef02..e441ea81fd 100644 --- a/packages/Main/test/unit/layeredmaterial.js +++ b/packages/Main/test/unit/layeredmaterial.js @@ -1,7 +1,6 @@ import assert from 'assert'; import ColorLayer from 'Layer/ColorLayer'; import TMSSource from 'Source/TMSSource'; -import { updateLayeredMaterialNodeImagery } from 'Process/LayeredMaterialNodeProcessing'; import GlobeView from 'Core/Prefab/GlobeView'; import { Coordinates } from '@itowns/geographic'; import TileMesh from 'Core/TileMesh'; @@ -50,7 +49,7 @@ describe('material state vs layer state', function () { }); it('should correctly initialize opacity & visibility', () => { - updateLayeredMaterialNodeImagery(context, layer, node, node.parent); + layer.update(context, layer, node, node.parent); const nodeLayer = material.getTile(layer.id); nodeLayer.textures.push(new THREE.Texture()); assert.equal(nodeLayer.opacity, layer.opacity); diff --git a/packages/Main/test/unit/layeredmaterialnodeprocessing.js b/packages/Main/test/unit/layeredmaterialnodeprocessing.js index 294f5fa769..eeaec56fa1 100644 --- a/packages/Main/test/unit/layeredmaterialnodeprocessing.js +++ b/packages/Main/test/unit/layeredmaterialnodeprocessing.js @@ -1,10 +1,9 @@ import * as THREE from 'three'; import assert from 'assert'; -import { updateLayeredMaterialNodeImagery } from 'Process/LayeredMaterialNodeProcessing'; import TileMesh from 'Core/TileMesh'; import { Extent } from '@itowns/geographic'; import OBB from 'Renderer/OBB'; -import Layer from 'Layer/Layer'; +import ColorLayer from 'Layer/ColorLayer'; import Source from 'Source/Source'; import { STRATEGY_MIN_NETWORK_TRAFFIC } from 'Layer/LayerUpdateStrategy'; import { RasterColorTile } from 'Renderer/RasterTile'; @@ -26,18 +25,22 @@ describe('updateLayeredMaterialNodeImagery', function () { commands: [], execute: (cmd) => { context.scheduler.commands.push(cmd); - return new Promise(() => { /* no-op */ }); + return new Promise(() => { }); }, }, }; + context.view.mainLoop = { + scheduler: context.scheduler, + }; + const source = new Source({ url: 'http://', crs: 'EPSG:4326', extent, }); - const layer = new Layer('foo', { + const layer = new ColorLayer('foo', { source, crs: 'EPSG:4326', info: { update: () => { } }, @@ -53,7 +56,11 @@ describe('updateLayeredMaterialNodeImagery', function () { ], }; - const nodeLayer = new RasterColorTile(layer); + const node = new TileMesh(geom, material, layer, extent, 0); + const tiles = node.getExtentsByProjection(layer.crs); + + const nodeLayer = new RasterColorTile(layer, tiles); + material.getTile = () => nodeLayer; beforeEach('reset state', function () { @@ -72,22 +79,21 @@ describe('updateLayeredMaterialNodeImagery', function () { source.extent = new Extent('EPSG:4326'); }); - it('hidden tile should not execute commands', () => { const tile = new TileMesh(geom, material, layer, extent, 0); material.visible = false; nodeLayer.level = 0; tile.parent = {}; - updateLayeredMaterialNodeImagery(context, layer, tile, tile.parent); + layer.update(context, layer, tile); assert.equal(context.scheduler.commands.length, 0); }); it('tile with best texture should not execute commands', () => { - const tile = new TileMesh(geom, material, layer, extent, 3); + const tile = new TileMesh(geom, material, layer, extent); material.visible = true; - nodeLayer.level = 3; + nodeLayer.state.state = 4; tile.parent = {}; - updateLayeredMaterialNodeImagery(context, layer, tile, tile.parent); + layer.update(context, layer, tile); assert.equal(context.scheduler.commands.length, 0); }); @@ -95,24 +101,22 @@ describe('updateLayeredMaterialNodeImagery', function () { const tile = new TileMesh(geom, material, layer, extent, 2); material.visible = true; nodeLayer.level = 1; + nodeLayer.state.state = 0; tile.parent = {}; - // FIRST PASS: init Node From Parent and get out of the function - // without any network fetch - updateLayeredMaterialNodeImagery(context, layer, tile, tile.parent); - assert.equal(context.scheduler.commands.length, 0); - // SECOND PASS: Fetch best texture - updateLayeredMaterialNodeImagery(context, layer, tile, tile.parent); + layer.update(context, layer, tile); assert.equal(context.scheduler.commands.length, 1); }); it('tile should not request texture with level > layer.source.zoom.max', () => { const countTexture = 2 ** 15; const newExtent = new Extent('EPSG:4326', 0, 180 / countTexture, 0, 180 / countTexture); - const tile = new TileMesh(geom, material, layer, newExtent, 15); + const tile = new TileMesh(geom, material, layer, newExtent); // Emulate a situation where tile inherited a level 1 texture material.visible = true; nodeLayer.level = 1; + nodeLayer.tiles = tile.getExtentsByProjection(layer.crs); + nodeLayer.state.state = 0; tile.parent = {}; source.isWMTSSource = true; source.tileMatrixSet = 'WGS84G'; @@ -123,11 +127,12 @@ describe('updateLayeredMaterialNodeImagery', function () { tile.material.getLayerTextures = () => [{}]; // Since layer is using STRATEGY_MIN_NETWORK_TRAFFIC, we should emit // a single command, requesting a texture at layer.source.zoom.max level - updateLayeredMaterialNodeImagery(context, layer, tile, tile.parent); - updateLayeredMaterialNodeImagery(context, layer, tile, tile.parent); + layer.update(context, layer, tile); + assert.equal(context.scheduler.commands.length, 1); assert.equal( context.scheduler.commands[0].extentsSource[0].zoom, layer.source.zoom.max); }); }); + diff --git a/packages/Main/test/unit/obb.js b/packages/Main/test/unit/obb.js index 212e0f240f..13d3f1cc29 100644 --- a/packages/Main/test/unit/obb.js +++ b/packages/Main/test/unit/obb.js @@ -53,46 +53,42 @@ function assertVerticesAreInOBB(builder, extent) { segments: 1, }; - return newTileGeometry(builder, params) - .then((result) => { - const geom = result.geometry; - const inverse = new THREE.Matrix4().copy(geom.OBB.matrix).invert(); + const { geometry } = newTileGeometry(builder, params); - let failing = 0; - const vec = new THREE.Vector3(); - for (let i = 0; i < geom.attributes.position.count; i++) { - vec.fromArray(geom.attributes.position.array, 3 * i); + const geom = geometry; + const inverse = new THREE.Matrix4().copy(geom.OBB.matrix).invert(); - vec.applyMatrix4(inverse); - if (!geom.OBB.box3D.containsPoint(vec)) { - failing++; - } - } - assert.equal(geom.attributes.position.count - failing, geom.attributes.position.count, 'All points should be inside OBB'); - }); + let failing = 0; + const vec = new THREE.Vector3(); + for (let i = 0; i < geom.attributes.position.count; i++) { + vec.fromArray(geom.attributes.position.array, 3 * i); + + vec.applyMatrix4(inverse); + if (!geom.OBB.box3D.containsPoint(vec)) { + failing++; + } + } + assert.equal(geom.attributes.position.count - failing, geom.attributes.position.count, 'All points should be inside OBB'); } describe('Planar tiles OBB computation', function () { const builder = new PlanarTileBuilder({ crs: 'EPSG:3946', uvCount: 1 }); - it('should compute OBB correctly', function (done) { + it('should compute OBB correctly', function () { const extent = new Extent('EPSG:3946', -100, 100, -50, 50); - assertVerticesAreInOBB(builder, extent) - .then(done, done); + assertVerticesAreInOBB(builder, extent); }); }); describe('Ellipsoid tiles OBB computation', function () { const builder = new GlobeTileBuilder({ uvCount: 1 }); - it('should compute globe-level 0 OBB correctly', function (done) { + it('should compute globe-level 0 OBB correctly', function () { const extent = new Extent('EPSG:4326', -180, 0, -90, 90); - assertVerticesAreInOBB(builder, extent) - .then(done, done); + assertVerticesAreInOBB(builder, extent); }); - it('should compute globe-level 2 OBB correctly', function (done) { + it('should compute globe-level 2 OBB correctly', function () { const extent = new Extent('EPSG:4326', 0, 45, -45, 0); - assertVerticesAreInOBB(builder, extent) - .then(done, done); + assertVerticesAreInOBB(builder, extent); }); }); diff --git a/packages/Main/test/unit/tile.js b/packages/Main/test/unit/tile.js index 845c29f5b5..793a339476 100644 --- a/packages/Main/test/unit/tile.js +++ b/packages/Main/test/unit/tile.js @@ -39,7 +39,7 @@ describe('Tile', function () { const parent = new Tile('EPSG:4326', zoom - 2, row, col); const offset = withValues.offsetToParent(parent); assert.equal(offset.x, 0.5); - assert.equal(offset.y, 0.5); + assert.equal(offset.y, 0.25); assert.equal(offset.z, 0.25); assert.equal(offset.w, 0.25); }); diff --git a/packages/Main/test/unit/tiledGeometryLayer.js b/packages/Main/test/unit/tiledGeometryLayer.js index 562c74515e..264efb5bf4 100644 --- a/packages/Main/test/unit/tiledGeometryLayer.js +++ b/packages/Main/test/unit/tiledGeometryLayer.js @@ -19,11 +19,11 @@ describe('TiledGeometryLayer', function () { it('subdivide should compute a screenSize', function () { // perspective camera - viewPerspective.tileLayer.subdivision(viewPerspective, viewPerspective.tileLayer, viewPerspective.tileLayer.level0Nodes[0]); + viewPerspective.tileLayer.subdivision({ camera: viewPerspective.camera }, viewPerspective.tileLayer.level0Nodes[0]); assert.notEqual(viewPerspective.tileLayer.level0Nodes[0].screenSize, undefined); // orthographic camera - viewOrtho.tileLayer.subdivision(viewOrtho, viewOrtho.tileLayer, viewOrtho.tileLayer.level0Nodes[0]); + viewOrtho.tileLayer.subdivision({ camera: viewOrtho.camera }, viewOrtho.tileLayer.level0Nodes[0]); assert.notEqual(viewOrtho.tileLayer.level0Nodes[0].screenSize, undefined); }); diff --git a/packages/Main/test/unit/tilemesh.js b/packages/Main/test/unit/tilemesh.js index 22b684bf31..82cd63b381 100644 --- a/packages/Main/test/unit/tilemesh.js +++ b/packages/Main/test/unit/tilemesh.js @@ -1,11 +1,9 @@ import * as THREE from 'three'; import assert from 'assert'; import TileMesh from 'Core/TileMesh'; -// import PlanarView from 'Core/Prefab/PlanarView'; import PlanarLayer from 'Core/Prefab/Planar/PlanarLayer'; import Tile from 'Core/Tile/Tile'; import { globalExtentTMS } from 'Core/Tile/TileGrid'; -import TileProvider from 'Provider/TileProvider'; import { newTileGeometry } from 'Core/Prefab/TileBuilder'; import OBB from 'Renderer/OBB'; import ElevationLayer from 'Layer/ElevationLayer'; @@ -66,18 +64,13 @@ describe('TileMesh', function () { assert.equal(res, tree[1][0]); }); - it('subdivide tile by 4 tiles', function (done) { + it('subdivide tile by 4 tiles', function () { const tile = planarlayer.object3d.children[0]; planarlayer.subdivideNode(context, tile); - const command = context.scheduler.commands[0]; - TileProvider.executeCommand(command).then((tiles) => { - context.scheduler.commands = []; - assert.equal(tiles.length, 4); - done(); - }); + assert.equal(tile.children.length, 4); }); - it('Choose the right typed Array', function (done) { + it('Choose the right typed Array', function () { const paramsGeometry = { extent: planarlayer.object3d.children[0].extent, level: 0, @@ -85,10 +78,10 @@ describe('TileMesh', function () { disableSkirt: true, }; - const a = newTileGeometry(planarlayer.builder, paramsGeometry).then((r) => { - const position = r.geometry.attributes.position; - assert.ok(position.array.constructor.name == 'Float32Array'); - }); + const { geometry } = newTileGeometry(planarlayer.builder, paramsGeometry); + + const position = geometry.attributes.position; + assert.ok(position.array.constructor.name == 'Float32Array'); const paramsGeometry2 = { extent: planarlayer.object3d.children[0].extent, @@ -97,20 +90,7 @@ describe('TileMesh', function () { disableSkirt: true, }; - const b = assert.rejects(newTileGeometry(planarlayer.builder, paramsGeometry2), Error); - Promise.all([a, b]).then(() => done()); - }); - - it('catch error when subdivide tile without material', function (done) { - const tile = planarlayer.object3d.children[0]; - tile.pendingSubdivision = false; - tile.material = undefined; - planarlayer.subdivideNode(context, tile); - const command = context.scheduler.commands[0]; - TileProvider.executeCommand(command).catch((error) => { - assert.ok(error.isCancelledCommandException); - done(); - }); + assert.throws(() => newTileGeometry(planarlayer.builder, paramsGeometry2), new Error('Tile segments count is too big')); }); it('should find the correct common ancestor between two tiles of different level', function () { @@ -128,7 +108,7 @@ describe('TileMesh', function () { assert.equal(res, tree[0][0]); }); - it('Cache tile geometry', function (done) { + it('Cache tile geometry', function () { const paramsGeometry = { extent: planarlayer.object3d.children[0].extent, level: 0, @@ -136,16 +116,15 @@ describe('TileMesh', function () { disableSkirt: true, }; - newTileGeometry(planarlayer.builder, paramsGeometry).then((r) => { - r.geometry.increaseRefCount(); - return newTileGeometry(planarlayer.builder, paramsGeometry).then((r) => { - assert.equal(r.geometry.refCount, 1); - done(); - }); - }); + const { geometry } = newTileGeometry(planarlayer.builder, paramsGeometry); + geometry.increaseRefCount(); + + const r = newTileGeometry(planarlayer.builder, paramsGeometry); + + assert.equal(r.geometry.refCount, 1); }); - it('Dispose tile geometry', function (done) { + it('Dispose tile geometry', function () { const paramsGeometry = { extent: planarlayer.object3d.children[0].extent, level: 0, @@ -153,11 +132,9 @@ describe('TileMesh', function () { disableSkirt: true, }; - newTileGeometry(planarlayer.builder, paramsGeometry).then((r) => { - r.geometry.dispose(); - assert.equal(r.geometry.index, null); - done(); - }); + const { geometry } = newTileGeometry(planarlayer.builder, paramsGeometry); + geometry.dispose(); + assert.equal(geometry.index, null); }); it('throw error if there\'s not extent in constructor', () => { @@ -182,6 +159,7 @@ describe('TileMesh', function () { it('event rasterElevationLevelChanged RasterElevationTile sets TileMesh bounding box ', () => { const tileMesh = new TileMesh(geom, material, planarlayer, tile.toExtent('EPSG:3857'), 0); + tileMesh.parent = {}; const rasterNode = elevationLayer.setupRasterNode(tileMesh); const min = 50; const max = 500; @@ -201,6 +179,7 @@ describe('TileMesh', function () { it('RasterElevationTile throws error if ElevationLayer.useRgbaTextureElevation is true', () => { elevationLayer.useRgbaTextureElevation = true; const tileMesh = new TileMesh(geom, material, planarlayer, tile.toExtent('EPSG:3857'), 0); + tileMesh.parent = {}; assert.throws(() => { elevationLayer.setupRasterNode(tileMesh); }); @@ -213,6 +192,7 @@ describe('TileMesh', function () { elevationLayer.colorTextureElevationMinZ = 10; elevationLayer.colorTextureElevationMaxZ = 100; const tileMesh = new TileMesh(geom, material, planarlayer, tile.toExtent('EPSG:3857'), 0); + tileMesh.parent = {}; const rasterNode = elevationLayer.setupRasterNode(tileMesh); assert.equal(rasterNode.min, elevationLayer.colorTextureElevationMinZ); assert.equal(rasterNode.max, elevationLayer.colorTextureElevationMaxZ); @@ -221,6 +201,7 @@ describe('TileMesh', function () { it('RasterElevationTile min and max are set by xbil texture', () => { delete elevationLayer.useColorTextureElevation; const tileMesh = new TileMesh(geom, material, planarlayer, tile.toExtent('EPSG:3857'), 0); + tileMesh.parent = {}; const rasterNode = elevationLayer.setupRasterNode(tileMesh); const texture = new THREE.Texture(); texture.extent = new Tile('EPSG:3857', 4, 10, 10); diff --git a/packages/Main/test/unit/view.js b/packages/Main/test/unit/view.js index 62099727f9..ba3c6df946 100644 --- a/packages/Main/test/unit/view.js +++ b/packages/Main/test/unit/view.js @@ -1,5 +1,4 @@ import * as THREE from 'three'; -import { getMaxColorSamplerUnitsCount } from 'Renderer/LayeredMaterial'; import { MAIN_LOOP_EVENTS } from 'Core/MainLoop'; import assert from 'assert'; import View from 'Core/View'; @@ -137,12 +136,6 @@ describe('Viewer', function () { // pretend that MAX_TEXTURE_IMAGE_UNITS is 42 renderer.context.getParameter = () => 42; - // Simulate a success of linkProgram by making getProgramParameter return true. - // In that case, the maximum color sampler count is computed. - renderer.context.getProgramParameter = () => true; - Capabilities.updateCapabilities(renderer); - assert.equal(getMaxColorSamplerUnitsCount(), 41); - // Simulate a failure of linkProgram by making getProgramParameter return false. // In that case, throw an error. renderer.context.getProgramParameter = () => false; diff --git a/test/functional/GlobeControls.js b/test/functional/GlobeControls.js index 1331cb4716..20db2a6c56 100644 --- a/test/functional/GlobeControls.js +++ b/test/functional/GlobeControls.js @@ -147,6 +147,7 @@ describe('GlobeControls with globe example', function _() { assert.ok(initialPosition.tilt - endTilt > 20); }); + // Doesn't work in master it('should change heading like expected', async () => { await page.evaluate(() => { view.controls.enableDamping = false; }); await page.keyboard.down('Control'); diff --git a/test/functional/view_25d_map.js b/test/functional/view_25d_map.js index 0d44ae6ec6..b0dd5a0037 100644 --- a/test/functional/view_25d_map.js +++ b/test/functional/view_25d_map.js @@ -28,7 +28,7 @@ describe('view_25d_map', function _() { }); assert.equal(displayedTiles['1'], 1); assert.equal(displayedTiles['2'], 6); - assert.equal(displayedTiles['3'], 6); + assert.equal(displayedTiles['3'], 5); }); it('should get picking position from depth', async function __() {