From d165c8508bee96a47f60fd2e5b3a388591712a70 Mon Sep 17 00:00:00 2001 From: Gene Payne Date: Tue, 25 Jan 2022 13:10:58 -0700 Subject: [PATCH] Merged master in --- apps/OpenSpace/ext/sgct | 2 +- apps/OpenSpace/main.cpp | 4 +- .../examples/temperature_land_highres.asset | 48 +- ...om_w1_sea_ice_concentration_temporal.asset | 22 +- .../colorlayers/aqua_modis_temporal.asset | 22 +- .../layers/colorlayers/esri_viirs_combo.asset | 22 +- ...sst_sea_surface_temperature_temporal.asset | 22 +- ...mur_sea_surface_temperature_temporal.asset | 22 +- .../modis_terra_chlorophyll_a_temporal.asset | 16 +- .../colorlayers/terra_modis_temporal.asset | 22 +- .../colorlayers/viirs_snpp_temporal.asset | 22 +- .../nightlayers/earth_at_night_temporal.asset | 22 +- .../planets/mars/default_layers.asset | 2 +- data/assets/util/webgui.asset | 2 +- ext/ghoul | 2 +- include/openspace/engine/windowdelegate.h | 3 +- .../openspace/navigation/orbitalnavigator.h | 1 + include/openspace/navigation/pathnavigator.h | 3 + include/openspace/rendering/renderengine.h | 2 + .../util/kameleonfieldlinehelper.h | 1 + modules/galaxy/galaxymodule.cpp | 7 + modules/galaxy/galaxymodule.h | 2 + modules/galaxy/rendering/renderablegalaxy.cpp | 9 +- modules/galaxy/rendering/renderablegalaxy.h | 2 + modules/globebrowsing/CMakeLists.txt | 22 +- modules/globebrowsing/globebrowsingmodule.cpp | 44 +- .../globebrowsing/globebrowsingmodule_lua.inl | 2 +- .../globebrowsing/scripts/layer_support.lua | 69 +- .../globebrowsing/shaders/interpolate_fs.glsl | 12 +- .../src/asynctiledataprovider.cpp | 2 - .../globebrowsing/src/asynctiledataprovider.h | 2 - modules/globebrowsing/src/basictypes.h | 6 +- modules/globebrowsing/src/layer.cpp | 20 +- modules/globebrowsing/src/layer.h | 9 +- modules/globebrowsing/src/layeradjustment.cpp | 2 +- modules/globebrowsing/src/layeradjustment.h | 2 - modules/globebrowsing/src/layergroup.cpp | 11 +- modules/globebrowsing/src/layergroup.h | 5 +- modules/globebrowsing/src/layermanager.cpp | 4 +- modules/globebrowsing/src/renderableglobe.cpp | 21 +- modules/globebrowsing/src/tileprovider.cpp | 2009 ----------------- modules/globebrowsing/src/tileprovider.h | 292 --- .../src/tileprovider/defaulttileprovider.cpp | 204 ++ .../src/tileprovider/defaulttileprovider.h | 61 + .../imagesequencetileprovider.cpp | 158 ++ .../tileprovider/imagesequencetileprovider.h | 60 + .../tileprovider/singleimagetileprovider.cpp | 100 + .../tileprovider/singleimagetileprovider.h | 53 + .../sizereferencetileprovider.cpp | 121 + .../tileprovider/sizereferencetileprovider.h | 49 + .../src/tileprovider/temporaltileprovider.cpp | 822 +++++++ .../src/tileprovider/temporaltileprovider.h | 130 ++ .../src/tileprovider/texttileprovider.cpp | 109 + .../src/tileprovider/texttileprovider.h | 60 + .../tileprovider/tileindextileprovider.cpp | 65 + .../src/tileprovider/tileindextileprovider.h | 27 +- .../src/tileprovider/tileprovider.cpp | 238 ++ .../src/tileprovider/tileprovider.h | 145 ++ .../src/tileprovider/tileproviderbyindex.cpp | 140 ++ .../src/tileprovider/tileproviderbyindex.h | 51 + .../src/tileprovider/tileproviderbylevel.cpp | 161 ++ .../src/tileprovider/tileproviderbylevel.h | 55 + .../rendering/renderablecrawlingline.cpp | 2 +- .../rendering/renderablefov.cpp | 2 +- .../rendering/renderablemodelprojection.cpp | 2 +- .../rendering/renderableplaneprojection.cpp | 5 + .../rendering/renderableplaneprojection.h | 2 + .../rendering/renderableplanetprojection.cpp | 2 +- .../rendering/renderableshadowcylinder.cpp | 2 +- .../spacecraftinstrumentsmodule.cpp | 7 +- .../util/projectioncomponent.cpp | 2 +- .../rendering/renderabletoyvolume.cpp | 2 +- src/navigation/orbitalnavigator.cpp | 35 +- src/navigation/path.cpp | 1 - src/navigation/pathnavigator.cpp | 42 +- src/navigation/pathnavigator_lua.inl | 14 +- src/rendering/renderengine.cpp | 15 +- support/coding/codegen | 2 +- tests/CMakeLists.txt | 1 - 79 files changed, 3196 insertions(+), 2567 deletions(-) delete mode 100644 modules/globebrowsing/src/tileprovider.cpp delete mode 100644 modules/globebrowsing/src/tileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/defaulttileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/defaulttileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/imagesequencetileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/imagesequencetileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/singleimagetileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/singleimagetileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/sizereferencetileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/sizereferencetileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/temporaltileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/temporaltileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/texttileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/texttileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/tileindextileprovider.cpp rename tests/test_temporaltileprovider.cpp => modules/globebrowsing/src/tileprovider/tileindextileprovider.h (71%) create mode 100644 modules/globebrowsing/src/tileprovider/tileprovider.cpp create mode 100644 modules/globebrowsing/src/tileprovider/tileprovider.h create mode 100644 modules/globebrowsing/src/tileprovider/tileproviderbyindex.cpp create mode 100644 modules/globebrowsing/src/tileprovider/tileproviderbyindex.h create mode 100644 modules/globebrowsing/src/tileprovider/tileproviderbylevel.cpp create mode 100644 modules/globebrowsing/src/tileprovider/tileproviderbylevel.h diff --git a/apps/OpenSpace/ext/sgct b/apps/OpenSpace/ext/sgct index f8659e216d..15c2a977e1 160000 --- a/apps/OpenSpace/ext/sgct +++ b/apps/OpenSpace/ext/sgct @@ -1 +1 @@ -Subproject commit f8659e216dfdc99b56f345bd9c24876543784acb +Subproject commit 15c2a977e16350f9c98a4ef74d6b35a43d35b0bb diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 234168f2a9..6d0084bc21 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -865,11 +865,11 @@ void setSgctDelegateFunctions() { currentWindow->viewports().front()->nonLinearProjection() ) != nullptr; }; - sgctDelegate.takeScreenshot = [](bool applyWarping) { + sgctDelegate.takeScreenshot = [](bool applyWarping, std::vector windowIds) { ZoneScoped Settings::instance().setCaptureFromBackBuffer(applyWarping); - Engine::instance().takeScreenshot(); + Engine::instance().takeScreenshot(std::move(windowIds)); return Engine::instance().screenShotNumber(); }; sgctDelegate.swapBuffer = []() { diff --git a/data/assets/examples/temperature_land_highres.asset b/data/assets/examples/temperature_land_highres.asset index 40b562e1f7..b34baebc90 100644 --- a/data/assets/examples/temperature_land_highres.asset +++ b/data/assets/examples/temperature_land_highres.asset @@ -7,28 +7,46 @@ local path = asset.syncedResource({ Version = 1 }) -local layer = { - Identifier = "ERA5_Land_HighRes_Monthly_2M_Temperature_Temporal", - Name = "ERA5 Land HighRes Monthly 2M Temperature (Temporal)", +local layer_prototype = { + Identifier = "ERA5_Land_HighRes_Monthly_2M_Temperature_Temporal_prototype", + Name = "ERA5 Land HighRes Monthly 2M Temperature (Temporal) [Prototype]", Type = "TemporalTileLayer", - FilePath = - "" .. - "1981-01-01" .. - "1990-10-01" .. - "1M" .. - "YYYY-MM-DD" .. - "" .. path .. "rainbow.png" .. - "linear" .. - "" .. path .. "${OpenSpaceTimeId}-land.png" .. - "", + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "1981-01-01", + End = "1990-10-01" + }, + TemporalResolution = "1M", + TimeFormat = "YYYY-MM-DD", + Prototype = path .. "${OpenSpaceTimeId}-land.png" + }, + Interpolation = true, + Colormap = path .. "rainbow.png", + Description = [[ Temporal coverage: 01 Jan 1981 - 31 Dec 2020.]] +} + +local layer_folder = { + Identifier = "ERA5_Land_HighRes_Monthly_2M_Temperature_Temporal_folder", + Name = "ERA5 Land HighRes Monthly 2M Temperature (Temporal) [Folder]", + Type = "TemporalTileLayer", + Mode = "Folder", + Folder = { + Folder = "C:/Development/sync/http/earth_textures_climate/1", + Format = "%Y-%m-%d-land.png" + }, + Interpolation = true, + Colormap = path .. "rainbow.png", Description = [[ Temporal coverage: 01 Jan 1981 - 31 Dec 2020.]] } asset.onInitialize(function () - openspace.globebrowsing.addLayer(globeIdentifier, "ColorLayers", layer) + openspace.globebrowsing.addLayer(globeIdentifier, "ColorLayers", layer_prototype) + openspace.globebrowsing.addLayer(globeIdentifier, "ColorLayers", layer_folder) end) -asset.export("layer", layer) +asset.export("layer", layer_prototype) + asset.meta = { Name = "Climate Earth Layers", Version = "1.0", diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/amsr2_gcom_w1_sea_ice_concentration_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/amsr2_gcom_w1_sea_ice_concentration_temporal.asset index 60af47fdec..98388e8e7b 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/amsr2_gcom_w1_sea_ice_concentration_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/amsr2_gcom_w1_sea_ice_concentration_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "AMSR2_GCOM_W1_Sea_Ice_Concentration_Temporal", Name = "AMSR2 GCOM W1 Sea Ice Concentration (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "AMSRU2_Sea_Ice_Concentration_12km", - "2012-05-08", - "Yesterday", - "1d", - "2km", - "png" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2012-05-08", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "AMSRU2_Sea_Ice_Concentration_12km", + "2km", + "png" + ) + }, Description = [[ Temporal coverage: 02 July 2012 - Present. The Advanced Microwave Scanning Radiometer-E/Advanced Microwave Scanning Radiometer-2 (AMSR-E/AMSR2) unified "Sea Ice Concentration (12 km)" layer displays the percent of sea ice diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/aqua_modis_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/aqua_modis_temporal.asset index 61b122ca64..4807d05cd1 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/aqua_modis_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/aqua_modis_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "Aqua_Modis_Temporal", Name = "Aqua Modis (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "MODIS_Aqua_CorrectedReflectance_TrueColor", - "2002-07-04", - "Yesterday", - "1d", - "250m", - "jpg" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2002-07-04", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "MODIS_Aqua_CorrectedReflectance_TrueColor", + "250m", + "jpg" + ) + }, Description = [[ Temporal coverage: 03 July 2002 - Present. True Color: Red = Band 1, Green = Band 4, Blue = Band 3. These images are called true-color or natural color because this combination of wavelengths is similar to what the human eye would see. diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/esri_viirs_combo.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/esri_viirs_combo.asset index 35ecc0b6e2..374d067466 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/esri_viirs_combo.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/esri_viirs_combo.asset @@ -13,14 +13,20 @@ local layer = { Identifier = "Temporal_VIIRS_SNPP", Name = "Temporal VIIRS SNPP", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "VIIRS_SNPP_CorrectedReflectance_TrueColor", - "2015-11-24", - "Today", - "1d", - "250m", - "jpg" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2015-11-24", + End = "Today" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "VIIRS_SNPP_CorrectedReflectance_TrueColor", + "250m", + "jpg" + ) + }, PadTiles = false } }, diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_g1sst_sea_surface_temperature_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_g1sst_sea_surface_temperature_temporal.asset index 876fd0199e..9bf43d88d2 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_g1sst_sea_surface_temperature_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_g1sst_sea_surface_temperature_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "GHRSST_L4_G1SST_Sea_Surface_Temperature_Temporal", Name = "GHRSST L4 G1SST Sea Surface Temperature (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "GHRSST_L4_G1SST_Sea_Surface_Temperature", - "2010-06-21", - "2019-12-08", - "1d", - "1km", - "png" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2010-06-21", + End = "2019-12-08" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "GHRSST_L4_G1SST_Sea_Surface_Temperature", + "1km", + "png" + ) + }, Description = [[ Temporal coverage: 21 June 2010 - 08 December 2019. The imagery resolution is 1 km, and the temporal resolution is daily.]], Author = "NASA EOSDIS Global Imagery Browse Services" diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_mur_sea_surface_temperature_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_mur_sea_surface_temperature_temporal.asset index 0225ddc0c0..4efb087717 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_mur_sea_surface_temperature_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/ghrsst_l4_mur_sea_surface_temperature_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "GHRSST_L4_MUR_Sea_Surface_Temperature_Temporal", Name = "GHRSST L4 MUR Sea Surface Temperature (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "GHRSST_L4_MUR_Sea_Surface_Temperature", - "2002-06-01", - "Yesterday", - "1d", - "1km", - "png" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2002-06-01", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "GHRSST_L4_MUR_Sea_Surface_Temperature", + "1km", + "png" + ) + }, Description = [[ Temporal coverage: 01 June 2002 - Present. The imagery resolution is 1 km, and the temporal resolution is daily.]] } diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/modis_terra_chlorophyll_a_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/modis_terra_chlorophyll_a_temporal.asset index 186d771ad0..c4377b380e 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/modis_terra_chlorophyll_a_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/modis_terra_chlorophyll_a_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "MODIS_Terra_Chlorophyll_A_Temporal", Name = "MODIS Terra Chlorophyll A (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2013-07-02", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( "MODIS_Terra_Chlorophyll_A", - "2013-07-02", - "Yesterday", - "1d", "1km", "png" - ), + ) + }, Description = [[ Temporal coverage: 02 July 2013 - Present. The imagery resolution is 1 km, and the temporal resolution is daily.]] } diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/terra_modis_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/terra_modis_temporal.asset index 2324d3a9ea..fa39b2eff3 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/terra_modis_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/terra_modis_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "Terra_Modis_Temporal", Name = "Terra Modis (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "MODIS_Terra_CorrectedReflectance_TrueColor", - "2000-02-24", - "Yesterday", - "1d", - "250m", - "jpg" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2000-02-24", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "MODIS_Terra_CorrectedReflectance_TrueColor", + "250m", + "jpg" + ) + }, Description = [[ Temporal coverage: 02 July 2013 - Present. The imagery resolution is 1 km, and the temporal resolution is daily.]] } diff --git a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/viirs_snpp_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/viirs_snpp_temporal.asset index b52e86a434..d1bb41c089 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/viirs_snpp_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/colorlayers/viirs_snpp_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "VIIRS_SNPP_Temporal", Name = "VIIRS SNPP (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "VIIRS_SNPP_CorrectedReflectance_TrueColor", - "2015-11-24", - "Yesterday", - "1d", - "250m", - "jpg" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2015-11-24", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "VIIRS_SNPP_CorrectedReflectance_TrueColor", + "250m", + "jpg" + ) + }, Description = [[ Temporal coverage: 11 November 2015 - Present. The imagery resolution is 0.25 km, and the temporal resolution is daily.]] } diff --git a/data/assets/scene/solarsystem/planets/earth/layers/nightlayers/earth_at_night_temporal.asset b/data/assets/scene/solarsystem/planets/earth/layers/nightlayers/earth_at_night_temporal.asset index fa0a192e23..bd3c7f6cfb 100644 --- a/data/assets/scene/solarsystem/planets/earth/layers/nightlayers/earth_at_night_temporal.asset +++ b/data/assets/scene/solarsystem/planets/earth/layers/nightlayers/earth_at_night_temporal.asset @@ -4,14 +4,20 @@ local layer = { Identifier = "Earth_at_Night_Temporal", Name = "Earth at Night (Temporal)", Type = "TemporalTileLayer", - FilePath = openspace.globebrowsing.createTemporalGibsGdalXml( - "VIIRS_SNPP_DayNightBand_ENCC", - "2012-05-08", - "Yesterday", - "1d", - "500m", - "png" - ), + Mode = "Prototyped", + Prototyped = { + Time = { + Start = "2012-05-08", + End = "Yesterday" + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml( + "VIIRS_SNPP_DayNightBand_ENCC", + "500m", + "png" + ) + }, Description = [[ The VIIRS Nighttime Imagery (Day/Night Band, Enhanced Near Constant Contrast) layer shows the Earth's surface and atmosphere using a sensor designed to capture low-light emission sources, under varying illumination conditions. It diff --git a/data/assets/scene/solarsystem/planets/mars/default_layers.asset b/data/assets/scene/solarsystem/planets/mars/default_layers.asset index f691513188..198fa783b5 100644 --- a/data/assets/scene/solarsystem/planets/mars/default_layers.asset +++ b/data/assets/scene/solarsystem/planets/mars/default_layers.asset @@ -20,7 +20,7 @@ asset.require("./layers/colorlayers/hirisels") -- Height layers asset.require("./layers/heightlayers/mola_sweden") asset.require("./layers/heightlayers/mola_utah") -local heightLayer = asset.require("./layers/heightlayers/mdem200m") +local heightLayer = asset.require("./layers/heightlayers/MDEM200M") asset.require("./layers/heightlayers/hirisels") -- Overlays diff --git a/data/assets/util/webgui.asset b/data/assets/util/webgui.asset index eca2e190a7..203b14ea9d 100644 --- a/data/assets/util/webgui.asset +++ b/data/assets/util/webgui.asset @@ -3,7 +3,7 @@ asset.require("./static_server") local guiCustomization = asset.require("customization/gui") -- Select which commit hashes to use for the frontend and backend -local frontendHash = "4fe18eea379c8493dbcb2cea6798d09a85819912" +local frontendHash = "7e513ba86b0bb989b72f22712ebf0bb5a626ba06" local dataProvider = "data.openspaceproject.com/files/webgui" local frontend = asset.syncedResource({ diff --git a/ext/ghoul b/ext/ghoul index 0edf5e2588..a94d2561aa 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 0edf5e2588e80f402b7449ad7384ceddadd09f03 +Subproject commit a94d2561aadf0b2423185c266b3f77cc8676da04 diff --git a/include/openspace/engine/windowdelegate.h b/include/openspace/engine/windowdelegate.h index 1c2138b2dc..2ebdb6853a 100644 --- a/include/openspace/engine/windowdelegate.h +++ b/include/openspace/engine/windowdelegate.h @@ -76,7 +76,8 @@ struct WindowDelegate { bool (*isFisheyeRendering)() = []() { return false; }; - unsigned int (*takeScreenshot)(bool applyWarping) = [](bool) { return 0u; }; + unsigned int (*takeScreenshot)(bool applyWarping, std::vector windowIds) = + [](bool, std::vector) { return 0u; }; void (*swapBuffer)() = []() {}; diff --git a/include/openspace/navigation/orbitalnavigator.h b/include/openspace/navigation/orbitalnavigator.h index f541e78788..6820a2cfd9 100644 --- a/include/openspace/navigation/orbitalnavigator.h +++ b/include/openspace/navigation/orbitalnavigator.h @@ -148,6 +148,7 @@ private: // Reset camera direction to the aim node. properties::TriggerProperty _retargetAim; + properties::BoolProperty _followAnchorNodeRotation; properties::FloatProperty _followAnchorNodeRotationDistance; properties::FloatProperty _minimumAllowedDistance; diff --git a/include/openspace/navigation/pathnavigator.h b/include/openspace/navigation/pathnavigator.h index dbf8940825..b993d086bc 100644 --- a/include/openspace/navigation/pathnavigator.h +++ b/include/openspace/navigation/pathnavigator.h @@ -81,6 +81,8 @@ public: static scripting::LuaLibrary luaLibrary(); private: + void handlePathEnd(); + /** * Populate list of nodes that are relevant for collision checks, etc */ @@ -90,6 +92,7 @@ private: std::unique_ptr _currentPath = nullptr; bool _isPlaying = false; + bool _startSimulationTimeOnFinish = false; properties::OptionProperty _defaultPathType; properties::BoolProperty _includeRoll; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index d0f0e419e2..24dde421ca 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -177,6 +178,7 @@ private: properties::BoolProperty _showVersionInfo; properties::BoolProperty _showCameraInfo; + properties::IntListProperty _screenshotWindowIds; properties::BoolProperty _applyWarping; properties::BoolProperty _screenshotUseDate; properties::BoolProperty _showFrameInformation; diff --git a/modules/fieldlinessequence/util/kameleonfieldlinehelper.h b/modules/fieldlinessequence/util/kameleonfieldlinehelper.h index 008137d153..c86c146bb0 100644 --- a/modules/fieldlinessequence/util/kameleonfieldlinehelper.h +++ b/modules/fieldlinessequence/util/kameleonfieldlinehelper.h @@ -27,6 +27,7 @@ #include #include +#include #include namespace openspace { diff --git a/modules/galaxy/galaxymodule.cpp b/modules/galaxy/galaxymodule.cpp index 06670632d4..0000784d55 100644 --- a/modules/galaxy/galaxymodule.cpp +++ b/modules/galaxy/galaxymodule.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -47,4 +48,10 @@ void GalaxyModule::internalInitialize(const ghoul::Dictionary&) { fTask->registerClass("MilkywayPointsConversionTask"); } +std::vector GalaxyModule::documentations() const { + return { + RenderableGalaxy::Documentation() + }; +} + } // namespace openspace diff --git a/modules/galaxy/galaxymodule.h b/modules/galaxy/galaxymodule.h index f34f294da5..8664ed84d7 100644 --- a/modules/galaxy/galaxymodule.h +++ b/modules/galaxy/galaxymodule.h @@ -35,6 +35,8 @@ public: GalaxyModule(); + std::vector documentations() const override; + private: void internalInitialize(const ghoul::Dictionary&) override; }; diff --git a/modules/galaxy/rendering/renderablegalaxy.cpp b/modules/galaxy/rendering/renderablegalaxy.cpp index ce1602f36d..5ce17025ff 100644 --- a/modules/galaxy/rendering/renderablegalaxy.cpp +++ b/modules/galaxy/rendering/renderablegalaxy.cpp @@ -55,7 +55,7 @@ namespace { constexpr int8_t CurrentCacheVersion = 1; - constexpr const char* _loggerCat = "Renderable Galaxy"; + constexpr const char* _loggerCat = "RenderableGalaxy"; enum StarRenderingMethod { Points, @@ -125,8 +125,7 @@ namespace { { "Downscale", "Downscale Factor Volume Rendering", - "This value set the downscaling factor" - " when rendering the current volume." + "This value sets the downscaling factor when rendering the current volume." }; constexpr openspace::properties::Property::PropertyInfo NumberOfRayCastingStepsInfo = @@ -229,6 +228,10 @@ namespace { namespace openspace { +documentation::Documentation RenderableGalaxy::Documentation() { + return codegen::doc("galaxy_renderablegalaxy"); +} + RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _volumeRenderingEnabled(VolumeRenderingEnabledInfo, true) diff --git a/modules/galaxy/rendering/renderablegalaxy.h b/modules/galaxy/rendering/renderablegalaxy.h index 16841fd097..6d7755d82a 100644 --- a/modules/galaxy/rendering/renderablegalaxy.h +++ b/modules/galaxy/rendering/renderablegalaxy.h @@ -54,6 +54,8 @@ public: void render(const RenderData& data, RendererTasks& tasks) override; void update(const UpdateData& data) override; + static documentation::Documentation Documentation(); + private: void renderPoints(const RenderData& data); void renderBillboards(const RenderData& data); diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index b36fb93297..26d8a1e221 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -57,9 +57,18 @@ set(HEADER_FILES src/skirtedgrid.h src/tileindex.h src/tileloadjob.h - src/tileprovider.h src/tiletextureinitdata.h src/timequantizer.h + src/tileprovider/defaulttileprovider.h + src/tileprovider/imagesequencetileprovider.h + src/tileprovider/singleimagetileprovider.h + src/tileprovider/sizereferencetileprovider.h + src/tileprovider/temporaltileprovider.h + src/tileprovider/texttileprovider.h + src/tileprovider/tileindextileprovider.h + src/tileprovider/tileprovider.h + src/tileprovider/tileproviderbyindex.h + src/tileprovider/tileproviderbylevel.h ) set(SOURCE_FILES @@ -87,9 +96,18 @@ set(SOURCE_FILES src/skirtedgrid.cpp src/tileindex.cpp src/tileloadjob.cpp - src/tileprovider.cpp src/tiletextureinitdata.cpp src/timequantizer.cpp + src/tileprovider/defaulttileprovider.cpp + src/tileprovider/imagesequencetileprovider.cpp + src/tileprovider/singleimagetileprovider.cpp + src/tileprovider/sizereferencetileprovider.cpp + src/tileprovider/temporaltileprovider.cpp + src/tileprovider/texttileprovider.cpp + src/tileprovider/tileindextileprovider.cpp + src/tileprovider/tileprovider.cpp + src/tileprovider/tileproviderbyindex.cpp + src/tileprovider/tileproviderbylevel.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index 56418d997d..baeef89d6d 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -35,7 +35,15 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -236,7 +244,7 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { _tileCache = std::make_unique(_tileCacheSizeMB); addPropertySubOwner(_tileCache.get()); - tileprovider::initializeDefaultTile(); + TileProvider::initializeDefaultTile(); // Convert from MB to Bytes GdalWrapper::create( @@ -249,10 +257,9 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { global::callback::deinitializeGL->emplace_back([]() { ZoneScopedN("GlobeBrowsingModule") - tileprovider::deinitializeDefaultTile(); + TileProvider::deinitializeDefaultTile(); }); - // Render global::callback::render->emplace_back([&]() { ZoneScopedN("GlobeBrowsingModule") @@ -279,46 +286,45 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { ghoul_assert(fRotation, "Rotation factory was not created"); fRotation->registerClass("GlobeRotation"); - auto fTileProvider = - std::make_unique>(); + auto fTileProvider = std::make_unique>(); ghoul_assert(fTileProvider, "TileProvider factory was not created"); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::DefaultTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::SingleImageTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::ImageSequenceTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::TemporalTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::TileIndexTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::SizeReferenceTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::ByLevelTileLayer )] ); - fTileProvider->registerClass( + fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::ByIndexTileLayer )] @@ -376,12 +382,14 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const { "moveLayer", &globebrowsing::luascriptfunctions::moveLayer, "string, string, number, number", - "Rearranges the order of a single layer in a scene graph node. The first " - "parameter specifies the scene graph node, the second parameter specifies " + "Rearranges the order of a single layer on a globe. The first parameter" + "is the identifier of the globe, the second parameter specifies " "the name of the layer group, the third parameter is the original position " "of the layer that should be moved and the last parameter is the new " - "position. The new position may be -1 to place the layer at the top or any " - "large number bigger than the number of layers to place it at the bottom." + "position in the list. The first position in the list has index 0, and the " + "last position is given by the number of layers minus one. The new position " + "may be -1 to place the layer at the top or any number bigger than the " + "number of layers to place it at the bottom." }, { "goToChunk", diff --git a/modules/globebrowsing/globebrowsingmodule_lua.inl b/modules/globebrowsing/globebrowsingmodule_lua.inl index 2cb2d26889..2dd7398a66 100644 --- a/modules/globebrowsing/globebrowsingmodule_lua.inl +++ b/modules/globebrowsing/globebrowsingmodule_lua.inl @@ -169,7 +169,7 @@ int moveLayer(lua_State* L) { } globebrowsing::LayerGroup& lg = globe->layerManager().layerGroup(group); - lg.moveLayers(oldPosition, newPosition); + lg.moveLayer(oldPosition, newPosition); return 0; } diff --git a/modules/globebrowsing/scripts/layer_support.lua b/modules/globebrowsing/scripts/layer_support.lua index 0d3b94cc85..8f2d50bc29 100644 --- a/modules/globebrowsing/scripts/layer_support.lua +++ b/modules/globebrowsing/scripts/layer_support.lua @@ -1,30 +1,11 @@ openspace.globebrowsing.documentation = { { Name = "createTemporalGibsGdalXml", - Arguments = "string, string, string, string, string, string, [string]", - Documentation = - "Creates an XML configuration for a temporal GIBS dataset." .. - "Arguments are: Name, Start date, end date, time resolution, time format," .. - "resolution, file format. The last parameter is the temporal format and " .. - "defaults to YYYY-MM-DD. For all specifications, see " .. - "https://wiki.earthdata.nasa.gov/display/GIBS/GIBS+Available+Imagery+Products" .. - "Usage:" .. - "openspace.globebrowsing.addLayer(" .. - "\"Earth\"," .. - "\"ColorLayers\"," .. - "{" .. - "Type = \"TemporalTileLayer\"," .. - "Name = \"MODIS_Terra_Chlorophyll_A\"," .. - "FilePath = openspace.globebrowsing.createTemporalGibsGdalXml(" .. - "\"MODIS_Terra_Chlorophyll_A\"," .. - "\"2013-07-02\"," .. - "\"Yesterday\"," .. - "\"1d\"," .. - "\"1km\"," .. - "\"png\"" .. - ")" .. - "}" .. - ")" + Arguments = "string, string, string", + Documentation = [[ + Creates an XML configuration for a temporal GIBS dataset to be used in + a TemporalTileprovider + ]] }, { Name = "createGibsGdalXml", @@ -40,7 +21,7 @@ openspace.globebrowsing.documentation = { "\"ColorLayers\"," .. "{" .. "Name = \"MODIS_Terra_Chlorophyll_A\"," .. - "FilePath = openspace.globebrowsing.createTemporalGibsGdalXml(" .. + "FilePath = openspace.globebrowsing.createGibsGdalXml(" .. "\"MODIS_Terra_Chlorophyll_A\"," .. "\"2013-07-02\"," .. "\"1km\"," .. @@ -111,21 +92,27 @@ openspace.globebrowsing.addGibsLayer = function(layer, resolution, format, start if endDate == 'Present' then endDate = '' end - local xml = openspace.globebrowsing.createTemporalGibsGdalXml(layer, startDate, endDate, '1d', resolution, format) - openspace.globebrowsing.addLayer('Earth', 'ColorLayers', { Identifier = layer, Type = "TemporalTileLayer", FilePath = xml }) -end -openspace.globebrowsing.createTemporalGibsGdalXml = function (layerName, startDate, endDate, timeResolution, resolution, format, temporalFormat) - temporalFormat = temporalFormat or 'YYYY-MM-DD' - local temporalTemplate = - "" .. - "" .. startDate .. "" .. - "" .. endDate .. "" .. - "" .. timeResolution .. "" .. - "" .. temporalFormat .. "" .. - openspace.globebrowsing.createGibsGdalXml(layerName, "${OpenSpaceTimeId}", resolution, format) .. - "" - return temporalTemplate + local layer = { + Identifier = layerName, + Type = "TemporalTileLayer", + Mode = "Prototyped", + Prototyped = { + Time = { + Start = startDate, + End = endDate + }, + TemporalResolution = "1d", + TimeFormat = "YYYY-MM-DD", + Prototype = openspace.globebrowsing.createTemporalGibsGdalXml(layerName, resolution, format) + } + } + + openspace.globebrowsing.addLayer( + 'Earth', + 'ColorLayers', + layer + ) end openspace.globebrowsing.createGibsGdalXml = function (layerName, date, resolution, format) @@ -196,6 +183,10 @@ openspace.globebrowsing.createGibsGdalXml = function (layerName, date, resolutio return gdalWmsTemplate end +openspace.globebrowsing.createTemporalGibsGdalXml = function (layerName, resolution, format) + return openspace.globebrowsing.createGibsGdalXml(layerName, "${OpenSpaceTimeId}", resolution, format) +end + openspace.globebrowsing.parseInfoFile = function (file) -- We are loading these values from an external info file and since we are switching -- to a strict Lua, we need to predefine these global variables diff --git a/modules/globebrowsing/shaders/interpolate_fs.glsl b/modules/globebrowsing/shaders/interpolate_fs.glsl index a6835c858d..7654a8ac79 100644 --- a/modules/globebrowsing/shaders/interpolate_fs.glsl +++ b/modules/globebrowsing/shaders/interpolate_fs.glsl @@ -26,25 +26,23 @@ uniform sampler2D prevTexture; uniform sampler2D nextTexture; -uniform sampler2D colormapTexture; +uniform sampler1D colormapTexture; uniform float blendFactor; in vec2 texCoord; Fragment getFragment() { - vec4 texel0 = texture2D(prevTexture, texCoord); - vec4 texel1 = texture2D(nextTexture, texCoord); + vec4 texel0 = texture(prevTexture, texCoord); + vec4 texel1 = texture(nextTexture, texCoord); vec4 mixedTexture = mix(texel0, texel1, blendFactor); Fragment frag; if (mixedTexture.r > 0.999) { - vec2 position = vec2(mixedTexture.r - 0.01, 0.5); - frag.color = texture2D(colormapTexture, position); + frag.color = texture(colormapTexture, mixedTexture.r - 0.01); } else { - vec2 position = vec2(mixedTexture.r , 0.5); - frag.color = texture2D(colormapTexture, position); + frag.color = texture(colormapTexture, mixedTexture.r); } frag.color.a = mixedTexture.a; diff --git a/modules/globebrowsing/src/asynctiledataprovider.cpp b/modules/globebrowsing/src/asynctiledataprovider.cpp index e4829851d9..264a3006e0 100644 --- a/modules/globebrowsing/src/asynctiledataprovider.cpp +++ b/modules/globebrowsing/src/asynctiledataprovider.cpp @@ -50,8 +50,6 @@ AsyncTileDataProvider::AsyncTileDataProvider(std::string name, performReset(ResetRawTileDataReader::No); } -AsyncTileDataProvider::~AsyncTileDataProvider() {} // NOLINT - const RawTileDataReader& AsyncTileDataProvider::rawTileDataReader() const { return *_rawTileDataReader; } diff --git a/modules/globebrowsing/src/asynctiledataprovider.h b/modules/globebrowsing/src/asynctiledataprovider.h index fb48af2ffa..34e4325c10 100644 --- a/modules/globebrowsing/src/asynctiledataprovider.h +++ b/modules/globebrowsing/src/asynctiledataprovider.h @@ -50,8 +50,6 @@ public: AsyncTileDataProvider(std::string name, std::unique_ptr rawTileDataReader); - ~AsyncTileDataProvider(); - /** * Creates a job which asynchronously loads a raw tile. This job is enqueued. */ diff --git a/modules/globebrowsing/src/basictypes.h b/modules/globebrowsing/src/basictypes.h index c73c531a0c..0feec1945a 100644 --- a/modules/globebrowsing/src/basictypes.h +++ b/modules/globebrowsing/src/basictypes.h @@ -115,9 +115,8 @@ struct TileMetaData { class Tile { public: /** - * Describe if this Tile is good for usage (OK) or otherwise - * the reason why it is not. - */ + * Describe if this Tile is good for usage (OK) or otherwise the reason why it is not. + */ enum class Status { /** * E.g when texture data is not currently in memory. @@ -168,7 +167,6 @@ struct ChunkTile { -//using ChunkTilePile = std::vector; // The ChunkTilePile either contains 1 or 3 ChunkTile, depending on if layer-blending is // enabled. If it is enabled, we need the two adjacent levels, if it is not enabled, only // the current layer is needed diff --git a/modules/globebrowsing/src/layer.cpp b/modules/globebrowsing/src/layer.cpp index 51a20a5c1e..f5b9070074 100644 --- a/modules/globebrowsing/src/layer.cpp +++ b/modules/globebrowsing/src/layer.cpp @@ -286,13 +286,13 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, _reset.onChange([&]() { if (_tileProvider) { - tileprovider::reset(*_tileProvider); + _tileProvider->reset(); } }); _remove.onChange([&]() { if (_tileProvider) { - tileprovider::reset(*_tileProvider); + _tileProvider->reset(); _parent.deleteLayer(identifier()); } }); @@ -360,13 +360,13 @@ void Layer::initialize() { ZoneScoped if (_tileProvider) { - tileprovider::initialize(*_tileProvider); + _tileProvider->initialize(); } } void Layer::deinitialize() { if (_tileProvider) { - tileprovider::deinitialize(*_tileProvider); + _tileProvider->deinitialize(); } } @@ -374,7 +374,7 @@ ChunkTilePile Layer::chunkTilePile(const TileIndex& tileIndex, int pileSize) con ZoneScoped if (_tileProvider) { - return tileprovider::chunkTilePile(*_tileProvider, tileIndex, pileSize); + return _tileProvider->chunkTilePile(tileIndex, pileSize); } else { ChunkTilePile chunkTilePile; @@ -390,7 +390,7 @@ ChunkTilePile Layer::chunkTilePile(const TileIndex& tileIndex, int pileSize) con Tile::Status Layer::tileStatus(const TileIndex& index) const { return _tileProvider ? - tileprovider::tileStatus(*_tileProvider, index) : + _tileProvider->tileStatus(index) : Tile::Status::Unavailable; } @@ -404,7 +404,7 @@ layergroupid::BlendModeID Layer::blendMode() const { TileDepthTransform Layer::depthTransform() const { return _tileProvider ? - tileprovider::depthTransform(*_tileProvider) : + _tileProvider->depthTransform() : TileDepthTransform{ 1.f, 0.f }; } @@ -416,7 +416,7 @@ bool Layer::enabled() const { return _enabled; } -tileprovider::TileProvider* Layer::tileProvider() const { +TileProvider* Layer::tileProvider() const { return _tileProvider.get(); } @@ -440,7 +440,7 @@ void Layer::update() { ZoneScoped if (_tileProvider) { - tileprovider::update(*_tileProvider); + _tileProvider->update(); } } @@ -483,7 +483,7 @@ void Layer::initializeBasedOnType(layergroupid::TypeID id, ghoul::Dictionary ini std::string name = initDict.value(KeyName); LDEBUG("Initializing tile provider for layer: '" + name + "'"); } - _tileProvider = tileprovider::createFromDictionary(id, std::move(initDict)); + _tileProvider = TileProvider::createFromDictionary(id, std::move(initDict)); break; } case layergroupid::TypeID::SolidColor: { diff --git a/modules/globebrowsing/src/layer.h b/modules/globebrowsing/src/layer.h index d42ea1fdd9..5b90055c87 100644 --- a/modules/globebrowsing/src/layer.h +++ b/modules/globebrowsing/src/layer.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -40,8 +40,7 @@ namespace openspace::globebrowsing { struct LayerGroup; struct TileIndex; - -namespace tileprovider { struct TileProvider; } +struct TileProvider; class Layer : public properties::PropertyOwner { public: @@ -59,7 +58,7 @@ public: TileDepthTransform depthTransform() const; void setEnabled(bool enabled); bool enabled() const; - tileprovider::TileProvider* tileProvider() const; + TileProvider* tileProvider() const; glm::vec3 solidColor() const; const LayerRenderSettings& renderSettings() const; const LayerAdjustment& layerAdjustment() const; @@ -89,7 +88,7 @@ private: properties::StringProperty _guiDescription; layergroupid::TypeID _type; - std::unique_ptr _tileProvider; + std::unique_ptr _tileProvider; properties::Vec3Property _solidColor; LayerRenderSettings _renderSettings; LayerAdjustment _layerAdjustment; diff --git a/modules/globebrowsing/src/layeradjustment.cpp b/modules/globebrowsing/src/layeradjustment.cpp index 2205bcd9d9..a28cb273f8 100644 --- a/modules/globebrowsing/src/layeradjustment.cpp +++ b/modules/globebrowsing/src/layeradjustment.cpp @@ -74,7 +74,7 @@ documentation::Documentation LayerAdjustment::Documentation() { } LayerAdjustment::LayerAdjustment() - : properties::PropertyOwner({ "adjustment" }) + : properties::PropertyOwner({ "Adjustment" }) , _chromaKeyColor(ChromaKeyColorInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(1.f)) , _chromaKeyTolerance(ChromaKeyToleranceInfo, 0.f, 0.f, 1.f) , _typeOption(TypeInfo, properties::OptionProperty::DisplayType::Dropdown) diff --git a/modules/globebrowsing/src/layeradjustment.h b/modules/globebrowsing/src/layeradjustment.h index c864ab1b93..cbd2616e1d 100644 --- a/modules/globebrowsing/src/layeradjustment.h +++ b/modules/globebrowsing/src/layeradjustment.h @@ -36,8 +36,6 @@ namespace openspace::documentation { struct Documentation; } namespace openspace::globebrowsing { -namespace tileprovider { struct TileProvider; } - class LayerAdjustment : public properties::PropertyOwner { public: LayerAdjustment(); diff --git a/modules/globebrowsing/src/layergroup.cpp b/modules/globebrowsing/src/layergroup.cpp index 65245e6255..3adf42d366 100644 --- a/modules/globebrowsing/src/layergroup.cpp +++ b/modules/globebrowsing/src/layergroup.cpp @@ -212,16 +212,9 @@ void LayerGroup::deleteLayer(const std::string& layerName) { LERROR("Could not find layer " + layerName); } -void LayerGroup::moveLayers(int oldPosition, int newPosition) { +void LayerGroup::moveLayer(int oldPosition, int newPosition) { oldPosition = std::max(0, oldPosition); - newPosition = std::min(newPosition, static_cast(_layers.size())); - - // We need to adjust the new position as we first delete the old position, if this - // position is before the new position we have reduced the size of the vector by 1 and - // need to adapt where we want to put the value in - if (oldPosition < newPosition) { - newPosition -= 1; - } + newPosition = std::min(newPosition, static_cast(_layers.size() - 1)); // There are two synchronous vectors that we have to update here. The _layers vector // is used to determine the order while rendering, the _subowners is the order in diff --git a/modules/globebrowsing/src/layergroup.h b/modules/globebrowsing/src/layergroup.h index 3b2d2b7148..565826c76f 100644 --- a/modules/globebrowsing/src/layergroup.h +++ b/modules/globebrowsing/src/layergroup.h @@ -33,8 +33,7 @@ namespace openspace::globebrowsing { class Layer; - -namespace tileprovider { struct TileProvider; } +struct TileProvider; /** * Convenience class for dealing with multiple Layers. @@ -52,7 +51,7 @@ struct LayerGroup : public properties::PropertyOwner { Layer* addLayer(const ghoul::Dictionary& layerDict); void deleteLayer(const std::string& layerName); - void moveLayers(int oldPosition, int newPosition); + void moveLayer(int oldPosition, int newPosition); /// @returns const vector of all layers std::vector layers() const; diff --git a/modules/globebrowsing/src/layermanager.cpp b/modules/globebrowsing/src/layermanager.cpp index 8674ec1af2..24e0faad86 100644 --- a/modules/globebrowsing/src/layermanager.cpp +++ b/modules/globebrowsing/src/layermanager.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include @@ -155,7 +155,7 @@ void LayerManager::reset(bool includeDisabled) { for (std::unique_ptr& layerGroup : _layerGroups) { for (Layer* layer : layerGroup->layers()) { if ((layer->enabled() || includeDisabled) && layer->tileProvider()) { - tileprovider::reset(*layer->tileProvider()); + layer->tileProvider()->reset(); } } } diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 24907b6e96..3458fae95a 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -322,7 +322,7 @@ ChunkTileVector tilesAndSettingsUnsorted(const LayerGroup& layerGroup, for (Layer* layer : layerGroup.activeLayers()) { if (layer->tileProvider()) { tilesAndSettings.emplace_back( - tileprovider::chunkTile(*layer->tileProvider(), tileIndex), + layer->tileProvider()->chunkTile(tileIndex), &layer->renderSettings() ); } @@ -403,7 +403,7 @@ bool colorAvailableForChunk(const Chunk& chunk, const LayerManager& lm) { for (Layer* lyr : colorLayers.activeLayers()) { if (lyr->tileProvider()) { - ChunkTile t = tileprovider::chunkTile(*lyr->tileProvider(), chunk.tileIndex); + ChunkTile t = lyr->tileProvider()->chunkTile(chunk.tileIndex); if (t.tile.status == Tile::Status::Unavailable) { return false; } @@ -1886,16 +1886,15 @@ float RenderableGlobe::getHeight(const glm::dvec3& position) const { _layerManager.layerGroup(layergroupid::GroupID::HeightLayers).activeLayers(); for (Layer* layer : heightMapLayers) { - tileprovider::TileProvider* tileProvider = layer->tileProvider(); + TileProvider* tileProvider = layer->tileProvider(); if (!tileProvider) { continue; } // Transform the uv coordinates to the current tile texture - const ChunkTile chunkTile = tileprovider::chunkTile(*tileProvider, tileIndex); + const ChunkTile chunkTile = tileProvider->chunkTile(tileIndex); const Tile& tile = chunkTile.tile; const TileUvTransform& uvTransform = chunkTile.uvTransform; - const TileDepthTransform& depthTransform = - tileprovider::depthTransform(*tileProvider); + const TileDepthTransform& depthTransform = tileProvider->depthTransform(); if (tile.status != Tile::Status::OK) { return 0; } @@ -1953,10 +1952,10 @@ float RenderableGlobe::getHeight(const glm::dvec3& position) const { std::isnan(sample11); const bool anySampleIsNoData = - sample00 == tileprovider::noDataValueAsFloat(*tileProvider) || - sample01 == tileprovider::noDataValueAsFloat(*tileProvider) || - sample10 == tileprovider::noDataValueAsFloat(*tileProvider) || - sample11 == tileprovider::noDataValueAsFloat(*tileProvider); + sample00 == tileProvider->noDataValueAsFloat() || + sample01 == tileProvider->noDataValueAsFloat() || + sample10 == tileProvider->noDataValueAsFloat() || + sample11 == tileProvider->noDataValueAsFloat(); if (anySampleIsNaN || anySampleIsNoData) { continue; diff --git a/modules/globebrowsing/src/tileprovider.cpp b/modules/globebrowsing/src/tileprovider.cpp deleted file mode 100644 index 72b0b57b15..0000000000 --- a/modules/globebrowsing/src/tileprovider.cpp +++ /dev/null @@ -1,2009 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2022 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cpl_minixml.h" - -namespace ghoul { - template <> - constexpr openspace::globebrowsing::tileprovider::TemporalTileProvider::TimeFormatType - from_string(std::string_view string) - { - using namespace openspace::globebrowsing::tileprovider; - if (string == "YYYY-MM-DD") { - return TemporalTileProvider::TimeFormatType::YYYY_MM_DD; - } - else if (string == "YYYY-MM-DDThh:mm:ssZ") { - return TemporalTileProvider::TimeFormatType::YYYY_MM_DDThhColonmmColonssZ; - } - else if (string == "YYYY-MM-DDThh_mm_ssZ") { - return TemporalTileProvider::TimeFormatType::YYYY_MM_DDThh_mm_ssZ; - } - else if (string == "YYYYMMDD_hhmmss") { - return TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmmss; - } - else if (string == "YYYYMMDD_hhmm") { - return TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmm; - } - else { - throw ghoul::RuntimeError("Unknown timeformat '" + std::string(string) + "'"); - } - } -} // namespace ghoul - - -namespace openspace::globebrowsing::tileprovider { - -namespace { - -std::unique_ptr DefaultTileTexture; -Tile DefaultTile = Tile { nullptr, std::nullopt, Tile::Status::Unavailable }; - -constexpr const char* KeyFilePath = "FilePath"; - -namespace defaultprovider { - constexpr const char* KeyPerformPreProcessing = "PerformPreProcessing"; - constexpr const char* KeyTilePixelSize = "TilePixelSize"; - constexpr const char* KeyPadTiles = "PadTiles"; - - constexpr openspace::properties::Property::PropertyInfo FilePathInfo = { - "FilePath", - "File Path", - "The path of the GDAL file or the image file that is to be used in this tile " - "provider." - }; - - constexpr openspace::properties::Property::PropertyInfo TilePixelSizeInfo = { - "TilePixelSize", - "Tile Pixel Size", - "This value is the preferred size (in pixels) for each tile. Choosing the right " - "value is a tradeoff between more efficiency (larger images) and better quality " - "(smaller images). The tile pixel size has to be smaller than the size of the " - "complete image if a single image is used." - }; -} // namespace defaultprovider - -namespace singleimageprovider { - constexpr openspace::properties::Property::PropertyInfo FilePathInfo = { - "FilePath", - "File Path", - "The file path that is used for this image provider. The file must point to an " - "image that is then loaded and used for all tiles." - }; -} // namespace singleimageprovider - -namespace imagesequenceprovider { - constexpr openspace::properties::Property::PropertyInfo IndexInfo = { - "Index", - "Index", - "The index into the list of images that is used to pick the currently displayed " - "image" - }; - - constexpr openspace::properties::Property::PropertyInfo CurrentImageInfo = { - "CurrentImage", - "Current Image", - "The read-only value of the currently selected image" - }; - - constexpr openspace::properties::Property::PropertyInfo FolderPathInfo = { - "FolderPath", - "Folder Path", - "The path that is used to look for images for this image provider. The path must " - "point to an existing folder that contains images" - }; -} // namepsace imagesequenceprovider - -namespace sizereferenceprovider { - constexpr const char* KeyRadii = "Radii"; -} // namespace sizereferenceprovider - -namespace byindexprovider { - constexpr const char* KeyDefaultProvider = "DefaultProvider"; - constexpr const char* KeyProviders = "IndexTileProviders"; - constexpr const char* KeyTileIndex = "TileIndex"; - constexpr const char* KeyTileProvider = "TileProvider"; -} // namespace byindexprovider - -namespace bylevelprovider { - constexpr const char* KeyProviders = "LevelTileProviders"; - constexpr const char* KeyMaxLevel = "MaxLevel"; - constexpr const char* KeyTileProvider = "TileProvider"; - constexpr const char* KeyLayerGroupID = "LayerGroupID"; -} // namespace bylevelprovider - -namespace temporal { - constexpr const char* KeyBasePath = "BasePath"; - - constexpr const char* UrlTimePlaceholder = "${OpenSpaceTimeId}"; - constexpr const char* TimeStart = "OpenSpaceTimeStart"; - constexpr const char* TimeEnd = "OpenSpaceTimeEnd"; - constexpr const char* TimeResolution = "OpenSpaceTimeResolution"; - constexpr const char* TimeFormat = "OpenSpaceTimeIdFormat"; - constexpr const char* TimeInterpolation = "OpenSpaceTimeInterpolation"; - constexpr const char* TransferFunction = "OpenSpaceTransferFunction"; - constexpr openspace::properties::Property::PropertyInfo FilePathInfo = { - "FilePath", - "File Path", - "This is the path to the XML configuration file that describes the temporal tile " - "information." - }; - - constexpr openspace::properties::Property::PropertyInfo UseFixedTimeInfo = { - "UseFixedTime", - "Use Fixed Time", - "If this value is enabled, the time-varying timevarying dataset will always use " - "the time that is specified in the 'FixedTime' property, rather than using the " - "actual time from OpenSpace" - }; - - constexpr openspace::properties::Property::PropertyInfo FixedTimeInfo = { - "FixedTime", - "Fixed Time", - "If the 'UseFixedTime' is enabled, this time will be used instead of the actual " - "time taken from OpenSpace for the displayed tiles." - }; -} // namespace temporal - - -// -// DefaultTileProvider -// - -void initAsyncTileDataReader(DefaultTileProvider& t, TileTextureInitData initData) { - ZoneScoped - - t.asyncTextureDataProvider = std::make_unique( - t.name, - std::make_unique( - t.filePath, - initData, - RawTileDataReader::PerformPreprocessing(t.performPreProcessing) - ) - ); -} - -bool initTexturesFromLoadedData(DefaultTileProvider& t) { - ZoneScoped - - if (t.asyncTextureDataProvider) { - std::optional tile = t.asyncTextureDataProvider->popFinishedRawTile(); - if (tile) { - const cache::ProviderTileKey key = { tile->tileIndex, t.uniqueIdentifier }; - ghoul_assert(!t.tileCache->exist(key), "Tile must not be existing in cache"); - t.tileCache->createTileAndPut(key, std::move(*tile)); - return true; - } - } - return false; -} - - -// -// TextTileProvider -// - -void initialize(TextTileProvider& t) { - ZoneScoped - - t.font = global::fontManager->font("Mono", static_cast(t.fontSize)); - t.fontRenderer = ghoul::fontrendering::FontRenderer::createDefault(); - t.fontRenderer->setFramebufferSize(glm::vec2(t.initData.dimensions)); - glGenFramebuffers(1, &t.fbo); -} - -void deinitialize(TextTileProvider& t) { - glDeleteFramebuffers(1, &t.fbo); -} - -Tile tile(TextTileProvider& t, const TileIndex& tileIndex) { - ZoneScoped - TracyGpuZone("tile") - - cache::ProviderTileKey key = { tileIndex, t.uniqueIdentifier }; - Tile tile = t.tileCache->get(key); - if (!tile.texture) { - ghoul::opengl::Texture* texture = t.tileCache->texture(t.initData); - - // Keep track of defaultFBO and viewport to be able to reset state when done - GLint defaultFBO; - defaultFBO = global::renderEngine->openglStateCache().defaultFramebuffer(); - - // Render to texture - glBindFramebuffer(GL_FRAMEBUFFER, t.fbo); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - *texture, - 0 - ); - - GLsizei w = static_cast(texture->width()); - GLsizei h = static_cast(texture->height()); - glViewport(0, 0, w, h); - glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_COLOR_BUFFER_BIT); - - t.fontRenderer->render(*t.font, t.textPosition, t.text, t.textColor); - - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - global::renderEngine->openglStateCache().resetViewportState(); - - tile = Tile{ texture, std::nullopt, Tile::Status::OK }; - t.tileCache->put(key, t.initData.hashKey, tile); - } - return tile; -} - -void reset(TextTileProvider& t) { - ZoneScoped - - t.tileCache->clear(); -} - - -// -// TileProviderByLevel -// - -TileProvider* levelProvider(TileProviderByLevel& t, int level) { - ZoneScoped - - if (!t.levelTileProviders.empty()) { - int clampedLevel = glm::clamp( - level, - 0, - static_cast(t.providerIndices.size() - 1) - ); - int idx = t.providerIndices[clampedLevel]; - return t.levelTileProviders[idx].get(); - } - else { - return nullptr; - } -} - - -// -// TemporalTileProvider -// - -// Buffer needs at least 22 characters space -std::string_view timeStringify(TemporalTileProvider::TimeFormatType type, const Time& t) { - ZoneScoped - - char* buffer = reinterpret_cast( - global::memoryManager->TemporaryMemory.allocate(22) - ); - - std::memset(buffer, 0, 22); - const double time = t.j2000Seconds(); - - switch (type) { - case TemporalTileProvider::TimeFormatType::YYYY_MM_DD: - { - constexpr const char Format[] = "YYYY-MM-DD"; - constexpr const int Size = sizeof(Format); - SpiceManager::ref().dateFromEphemerisTime(time, buffer, Size, Format); - return std::string_view(buffer, Size - 1); - } - case TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmmss: { - constexpr const char Format[] = "YYYYMMDD_HRMNSC"; - constexpr const int Size = sizeof(Format); - SpiceManager::ref().dateFromEphemerisTime(time, buffer, Size, Format); - return std::string_view(buffer, Size - 1); - } - case TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmm: { - constexpr const char Format[] = "YYYYMMDD_HRMN"; - constexpr const int Size = sizeof(Format); - SpiceManager::ref().dateFromEphemerisTime(time, buffer, Size, Format); - return std::string_view(buffer, Size - 1); - } - case TemporalTileProvider::TimeFormatType::YYYY_MM_DDThhColonmmColonssZ: - { - constexpr const char Format[] = "YYYY-MM-DDTHR:MN:SCZ"; - constexpr const int Size = sizeof(Format); - SpiceManager::ref().dateFromEphemerisTime(time, buffer, Size, Format); - return std::string_view(buffer, Size - 1); - } - case TemporalTileProvider::TimeFormatType::YYYY_MM_DDThh_mm_ssZ: { - constexpr const char Format[] = "YYYY-MM-DDTHR_MN_SCZ"; - constexpr const int Size = sizeof(Format); - SpiceManager::ref().dateFromEphemerisTime(time, buffer, Size, Format); - return std::string_view(buffer, Size - 1); - } - default: - throw ghoul::MissingCaseException(); - } -} - -std::unique_ptr initTileProvider(TemporalTileProvider& t, - std::string_view timekey) -{ - ZoneScoped - - static const std::vector IgnoredTokens = { - // From: http://www.gdal.org/frmt_wms.html - "${x}", - "${y}", - "${z}", - "${version}", - "${format}", - "${layer}" - }; - - - std::string xmlTemplate(t.gdalXmlTemplate); - const size_t pos = xmlTemplate.find(temporal::UrlTimePlaceholder); - const size_t numChars = strlen(temporal::UrlTimePlaceholder); - // @FRAGILE: This will only find the first instance. Dangerous if that instance is - // commented out ---abock - std::string xml = xmlTemplate.replace(pos, numChars, timekey); - - xml = FileSys.expandPathTokens(std::move(xml), IgnoredTokens).string(); - - t.initDict.setValue(KeyFilePath, xml); - return std::make_unique(t.initDict); -} - -TileProvider* getTileProvider(TemporalTileProvider& t, std::string_view timekey) { - ZoneScoped - - // @TODO (abock, 2020-08-20) This std::string creation can be removed once we switch - // to C++20 thanks to P0919R2 - const auto it = t.tileProviderMap.find(std::string(timekey)); - if (it != t.tileProviderMap.end()) { - return it->second.get(); - } - else { - std::unique_ptr tileProvider = initTileProvider(t, timekey); - initialize(*tileProvider); - - TileProvider* res = tileProvider.get(); - t.tileProviderMap[std::string(timekey)] = std::move(tileProvider); - return res; - } -} - -TileProvider* getTileProvider(TemporalTileProvider& t, const Time& time) { - ZoneScoped - - if (!t.interpolation) { - if (t.useFixedTime && !t.fixedTime.value().empty()) { - try { - return getTileProvider(t, t.fixedTime.value()); - } - catch (const ghoul::RuntimeError& e) { - LERRORC("TemporalTileProvider", e.message); - return nullptr; - } - } - else { - Time tCopy(time); - if (t.timeQuantizer.quantize(tCopy, true)) { - std::string_view timeStr = timeStringify(t.timeFormat, tCopy); - try { - return getTileProvider(t, timeStr); - } - catch (const ghoul::RuntimeError& e) { - LERRORC("TemporalTileProvider", e.message); - return nullptr; - } - } - } - } - - Time tCopy(time); - if (!t.timeQuantizer.quantize(tCopy, true)) { - return nullptr; - } - - Time simulationTime(time); - Time nextTile; - Time nextNextTile; - Time prevTile; - Time secondToLast; - Time secondToFirst; - - std::string_view tCopyStr = timeStringify(t.timeFormat, tCopy); - try { - t.interpolateTileProvider->t1 = getTileProvider(t, tCopyStr); - } - catch (const ghoul::RuntimeError& e) { - LERRORC("TemporalTileProvider", e.message); - return nullptr; - } - // if the images are for each hour - if (t.myResolution == "1h") { - // the second tile to interpolate between - nextTile.setTime(tCopy.j2000Seconds() + 60 * 60); - // the tile after the second tile - nextNextTile.setTime(tCopy.j2000Seconds() + 120 * 60); - // the tile before the first tile - prevTile.setTime(tCopy.j2000Seconds() - 60 * 60 + 1); - // to make sure that an image outside the dataset is not searched for both ends of - // the dataset are calculated - secondToLast.setTime(t.endTimeJ2000 - 60 * 60); - secondToFirst.setTime(t.startTimeJ2000 + 60 * 60); - } - // if the images are for each month - if (t.myResolution == "1M") { - // the second tile to interpolate between - nextTile.setTime(tCopy.j2000Seconds() + 32 * 60 * 60 * 24); - // the tile after the second tile - nextNextTile.setTime(tCopy.j2000Seconds() + 64 * 60 * 60 * 24); - // the tile before the first tile - prevTile.setTime(tCopy.j2000Seconds() - 2 * 60 * 60 * 24); - // to make sure that an image outside the dataset is not searched for both ends of - // the dataset are calculated - secondToLast.setTime(t.endTimeJ2000 - 2 * 60 * 60 * 24); - secondToFirst.setTime(t.startTimeJ2000 + 32 * 60 * 60 * 24); - - // since months vary in length the time strings are set to the first of each month - auto setToFirstOfMonth = [](Time& time) { - std::string timeString = std::string(time.ISO8601()); - timeString[8] = '0'; - timeString[9] = '1'; - time.setTime(timeString); - }; - - setToFirstOfMonth(nextTile); - setToFirstOfMonth(nextNextTile); - setToFirstOfMonth(prevTile); - setToFirstOfMonth(secondToLast); - setToFirstOfMonth(secondToFirst); - } - - std::string_view nextTileStr = timeStringify(t.timeFormat, nextTile); - std::string_view nextNextTileStr = timeStringify(t.timeFormat, nextNextTile); - std::string_view prevTileStr = timeStringify(t.timeFormat, prevTile); - try { - // the necessary tile providers are loaded if they exist within the - // dataset's timespan - if (secondToLast.j2000Seconds() > simulationTime.j2000Seconds() && - secondToFirst.j2000Seconds() < simulationTime.j2000Seconds()) - { - t.interpolateTileProvider->t2 = getTileProvider(t, nextTileStr); - t.interpolateTileProvider->future = getTileProvider(t, nextNextTileStr); - t.interpolateTileProvider->before = getTileProvider(t, prevTileStr); - } - else if (secondToLast.j2000Seconds() < simulationTime.j2000Seconds() && - t.endTimeJ2000 > simulationTime.j2000Seconds()) - { - t.interpolateTileProvider->t2 = getTileProvider(t, nextTileStr); - t.interpolateTileProvider->future = getTileProvider(t, tCopyStr); - t.interpolateTileProvider->before = getTileProvider(t, prevTileStr); - } - else if (secondToFirst.j2000Seconds() > simulationTime.j2000Seconds() && - t.startTimeJ2000 < simulationTime.j2000Seconds()) - { - t.interpolateTileProvider->t2 = getTileProvider(t, nextTileStr); - t.interpolateTileProvider->future = getTileProvider(t, nextNextTileStr); - t.interpolateTileProvider->before = getTileProvider(t, tCopyStr); - } - else { - t.interpolateTileProvider->t2 = getTileProvider(t, tCopyStr); - t.interpolateTileProvider->future = getTileProvider(t, tCopyStr); - t.interpolateTileProvider->before = getTileProvider(t, tCopyStr); - } - t.interpolateTileProvider->factor = - (simulationTime.j2000Seconds() - tCopy.j2000Seconds()) / - (nextTile.j2000Seconds() - tCopy.j2000Seconds()); - - if (t.interpolateTileProvider->factor > 1) { - t.interpolateTileProvider->factor = 1; - } - return t.interpolateTileProvider.get(); - } - catch (const ghoul::RuntimeError& e) { - LERRORC("TemporalTileProvider", e.message); - return nullptr; - } -} - -void ensureUpdated(TemporalTileProvider& t) { - ZoneScoped - - if (!t.currentTileProvider) { - update(t); - } -} - -std::string xmlValue(TemporalTileProvider& t, CPLXMLNode* node, const std::string& key, - const std::string& defaultVal, bool isOptional = false) -{ - CPLXMLNode* n = CPLSearchXMLNode(node, key.c_str()); - if (!n && !isOptional) { - throw ghoul::RuntimeError( - fmt::format("Unable to parse file {}. {} missing", t.filePath.value(), key) - ); - } - - const bool hasValue = n && n->psChild && n->psChild->pszValue; - return hasValue ? n->psChild->pszValue : defaultVal; -} - -std::string consumeTemporalMetaData(TemporalTileProvider& t, const std::string& xml) { - ZoneScoped - - CPLXMLNode* node = CPLParseXMLString(xml.c_str()); - - std::string timeStart = xmlValue(t, node, temporal::TimeStart, "2000 Jan 1"); - std::string timeResolution = xmlValue(t, node, temporal::TimeResolution, "2d"); - std::string timeEnd = xmlValue(t, node, temporal::TimeEnd, "Today"); - std::string timeIdFormat = xmlValue( - t, - node, - temporal::TimeFormat, - "YYYY-MM-DDThh:mm:ssZ" - ); - std::string timeInterpolation = xmlValue( - t, - node, - temporal::TimeInterpolation, - "none", - true - ); - t.colormap = xmlValue(t, node, temporal::TransferFunction, "none", true); - - Time start = Time(timeStart); - Time end = Time::now(); - Time endOfInterval = Time(timeEnd); - t.startTimeJ2000 = start.j2000Seconds(); - t.endTimeJ2000 = endOfInterval.j2000Seconds(); - if (timeEnd == "Yesterday") { - end.advanceTime(-60.0 * 60.0 * 24.0); // Go back one day - } - else if (timeEnd != "Today") { - end.setTime(std::move(timeEnd)); - } - - try { - t.timeQuantizer.setStartEndRange( - std::string(start.ISO8601()), - std::string(end.ISO8601()) - ); - t.timeQuantizer.setResolution(timeResolution); - t.myResolution = timeResolution; - } - catch (const ghoul::RuntimeError& e) { - throw ghoul::RuntimeError(fmt::format( - "Could not create time quantizer for Temporal GDAL dataset '{}'. {}", - t.filePath.value(), e.message - )); - } - t.timeFormat = ghoul::from_string(timeIdFormat); - t.interpolation = (timeInterpolation == "linear"); - - CPLXMLNode* gdalNode = CPLSearchXMLNode(node, "GDAL_WMS"); - if (gdalNode) { - std::string gdalDescription = CPLSerializeXMLTree(gdalNode); - return gdalDescription; - } - else { - gdalNode = CPLSearchXMLNode(node, "FilePath"); - if (gdalNode) { - std::string gdalDescription = std::string(gdalNode->psChild->pszValue); - return gdalDescription; - } - else { - return ""; - } - } -} - -void readFilePath(TemporalTileProvider& t) { - ZoneScoped - - std::ifstream in(t.filePath.value()); - std::string xml; - if (in.is_open()) { - // read file - xml = std::string( - std::istreambuf_iterator(in), - (std::istreambuf_iterator()) - ); - } - else { - // Assume that it is already an xml - xml = t.filePath; - } - - // File path was not a path to a file but a GDAL config or empty - std::filesystem::path f(t.filePath.value()); - if (std::filesystem::is_regular_file(f)) { - t.initDict.setValue(temporal::KeyBasePath, f.parent_path().string()); - } - - t.gdalXmlTemplate = consumeTemporalMetaData(t, xml); -} - -} // namespace - -unsigned int TileProvider::NumTileProviders = 0; - - -// -// General functions -// -void initializeDefaultTile() { - ZoneScoped - - ghoul_assert(!DefaultTile.texture, "Default tile should not have been created"); - using namespace ghoul::opengl; - - // Create pixel data - TileTextureInitData initData( - 8, - 8, - GL_UNSIGNED_BYTE, - Texture::Format::RGBA, - TileTextureInitData::PadTiles::No, - TileTextureInitData::ShouldAllocateDataOnCPU::Yes - ); - char* pixels = new char[initData.totalNumBytes]; - memset(pixels, 0, initData.totalNumBytes * sizeof(char)); - - // Create ghoul texture - DefaultTileTexture = std::make_unique(initData.dimensions, GL_TEXTURE_2D); - DefaultTileTexture->setDataOwnership(Texture::TakeOwnership::Yes); - DefaultTileTexture->setPixelData(pixels); - DefaultTileTexture->uploadTexture(); - DefaultTileTexture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); - - // Create tile - DefaultTile = Tile{ DefaultTileTexture.get(), std::nullopt, Tile::Status::OK }; -} - -void deinitializeDefaultTile() { - DefaultTileTexture = nullptr; -} - -std::unique_ptr createFromDictionary(layergroupid::TypeID layerTypeID, - const ghoul::Dictionary& dictionary) -{ - ZoneScoped - - const char* type = layergroupid::LAYER_TYPE_NAMES[static_cast(layerTypeID)]; - auto factory = FactoryManager::ref().factory(); - TileProvider* result = factory->create(type, dictionary); - return std::unique_ptr(result); -} - -TileProvider::TileProvider() : properties::PropertyOwner({ "tileProvider" }) {} - -DefaultTileProvider::DefaultTileProvider(const ghoul::Dictionary& dictionary) - : filePath(defaultprovider::FilePathInfo, "") - , tilePixelSize(defaultprovider::TilePixelSizeInfo, 32, 32, 2048) -{ - ZoneScoped - - type = Type::DefaultTileProvider; - - tileCache = global::moduleEngine->module()->tileCache(); - name = "Name unspecified"; - if (dictionary.hasValue("Name")) { - name = dictionary.value("Name"); - } - std::string _loggerCat = "DefaultTileProvider (" + name + ")"; - - // 1. Get required Keys - filePath = dictionary.value(KeyFilePath); - layerGroupID = - static_cast(dictionary.value("LayerGroupID")); - - // 2. Initialize default values for any optional Keys - // getValue does not work for integers - int pixelSize = 0; - if (dictionary.hasValue(defaultprovider::KeyTilePixelSize)) { - pixelSize = static_cast( - dictionary.value(defaultprovider::KeyTilePixelSize) - ); - LDEBUG(fmt::format("Default pixel size overridden: {}", pixelSize)); - } - - if (dictionary.hasValue(defaultprovider::KeyPadTiles)) { - padTiles = dictionary.value(defaultprovider::KeyPadTiles); - } - - TileTextureInitData initData( - tileTextureInitData(layerGroupID, padTiles, pixelSize) - ); - tilePixelSize = initData.dimensions.x; - - - // Only preprocess height layers by default - switch (layerGroupID) { - case layergroupid::GroupID::HeightLayers: performPreProcessing = true; break; - default: performPreProcessing = false; break; - } - - if (dictionary.hasValue(defaultprovider::KeyPerformPreProcessing)) { - performPreProcessing = dictionary.value( - defaultprovider::KeyPerformPreProcessing - ); - LDEBUG(fmt::format( - "Default PerformPreProcessing overridden: {}", performPreProcessing - )); - } - - initAsyncTileDataReader(*this, initData); - - addProperty(filePath); - addProperty(tilePixelSize); -} - - - - - -SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) - : filePath(singleimageprovider::FilePathInfo) -{ - ZoneScoped - - type = Type::SingleImageTileProvider; - - filePath = dictionary.value(KeyFilePath); - addProperty(filePath); - - reset(*this); -} - - - - - -ImageSequenceTileProvider::ImageSequenceTileProvider(const ghoul::Dictionary& dictionary) - : index(imagesequenceprovider::IndexInfo, 0) - , currentImage(imagesequenceprovider::CurrentImageInfo) - , folderPath(imagesequenceprovider::FolderPathInfo) - , initDict(dictionary) -{ - ZoneScoped - - type = Type::ImageSequenceTileProvider; - - if (dictionary.hasValue(imagesequenceprovider::IndexInfo.identifier)) { - index = dictionary.value(imagesequenceprovider::IndexInfo.identifier); - } - index.setMinValue(0); - index.onChange([this]() { isImageDirty = true; }); - addProperty(index); - - folderPath.setReadOnly(true); - addProperty(folderPath); - - folderPath = dictionary.value( - imagesequenceprovider::FolderPathInfo.identifier - ); - addProperty(folderPath); - - reset(*this); -} - - - - - -TextTileProvider::TextTileProvider(TileTextureInitData initData_, size_t fontSize_) - : initData(std::move(initData_)) - , fontSize(fontSize_) -{ - ZoneScoped - - tileCache = global::moduleEngine->module()->tileCache(); -} - - - - - -SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& dictionary) - : TextTileProvider(tileTextureInitData(layergroupid::GroupID::ColorLayers, false)) -{ - ZoneScoped - - type = Type::SizeReferenceTileProvider; - - font = global::fontManager->font("Mono", static_cast(fontSize)); - - if (dictionary.hasValue(sizereferenceprovider::KeyRadii)) { - ellipsoid = dictionary.value(sizereferenceprovider::KeyRadii); - } - else if (dictionary.hasValue(sizereferenceprovider::KeyRadii)) { - const double r = dictionary.value(sizereferenceprovider::KeyRadii); - ellipsoid = glm::dvec3(r, r, r); - } -} - - - - - -TileIndexTileProvider::TileIndexTileProvider(const ghoul::Dictionary&) - : TextTileProvider(tileTextureInitData(layergroupid::GroupID::ColorLayers, false)) -{ - ZoneScoped - - type = Type::TileIndexTileProvider; -} - - - - - -TileProviderByIndex::TileProviderByIndex(const ghoul::Dictionary& dictionary) { - ZoneScoped - - type = Type::ByIndexTileProvider; - - const ghoul::Dictionary& defaultProviderDict = dictionary.value( - byindexprovider::KeyDefaultProvider - ); - - layergroupid::TypeID typeID; - if (defaultProviderDict.hasValue("Type")) { - const std::string& t = defaultProviderDict.value("Type"); - typeID = ghoul::from_string(t); - - if (typeID == layergroupid::TypeID::Unknown) { - throw ghoul::RuntimeError("Unknown layer type: " + t); - } - } - else { - typeID = layergroupid::TypeID::DefaultTileLayer; - } - - defaultTileProvider = createFromDictionary(typeID, defaultProviderDict); - - const ghoul::Dictionary& indexProvidersDict = dictionary.value( - byindexprovider::KeyProviders - ); - for (size_t i = 1; i <= indexProvidersDict.size(); i++) { - ghoul::Dictionary indexProviderDict = indexProvidersDict.value( - std::to_string(i) - ); - ghoul::Dictionary tileIndexDict = indexProviderDict.value( - byindexprovider::KeyTileIndex - ); - ghoul::Dictionary providerDict = indexProviderDict.value( - byindexprovider::KeyTileProvider - ); - - constexpr const char* KeyLevel = "Level"; - constexpr const char* KeyX = "X"; - constexpr const char* KeyY = "Y"; - - int level = static_cast(tileIndexDict.value(KeyLevel)); - ghoul_assert(level < std::numeric_limits::max(), "Level too large"); - int x = static_cast(tileIndexDict.value(KeyX)); - int y = static_cast(tileIndexDict.value(KeyY)); - - const TileIndex tileIndex(x, y, static_cast(level)); - - layergroupid::TypeID providerTypeID = layergroupid::TypeID::DefaultTileLayer; - if (defaultProviderDict.hasValue("Type")) { - const std::string& t = defaultProviderDict.value("Type"); - providerTypeID = ghoul::from_string(t); - - if (providerTypeID == layergroupid::TypeID::Unknown) { - throw ghoul::RuntimeError("Unknown layer type: " + t); - } - } - - std::unique_ptr stp = createFromDictionary( - providerTypeID, - providerDict - ); - TileIndex::TileHashKey key = tileIndex.hashKey(); - tileProviderMap.insert(std::make_pair(key, std::move(stp))); - } -} - - - - - -TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) { - ZoneScoped - - type = Type::ByLevelTileProvider; - - layergroupid::GroupID layerGroupID = static_cast( - dictionary.value(bylevelprovider::KeyLayerGroupID) - ); - - if (dictionary.hasValue(bylevelprovider::KeyProviders)) { - ghoul::Dictionary providers = dictionary.value( - bylevelprovider::KeyProviders - ); - - for (size_t i = 1; i <= providers.size(); i++) { - ghoul::Dictionary levelProviderDict = providers.value( - std::to_string(i) - ); - double floatMaxLevel = levelProviderDict.value( - bylevelprovider::KeyMaxLevel - ); - int maxLevel = static_cast(std::round(floatMaxLevel)); - - ghoul::Dictionary providerDict = levelProviderDict.value( - bylevelprovider::KeyTileProvider - ); - providerDict.setValue( - bylevelprovider::KeyLayerGroupID, - static_cast(layerGroupID) - ); - - layergroupid::TypeID typeID; - if (providerDict.hasValue("Type")) - { - const std::string& typeString = providerDict.value("Type"); - typeID = ghoul::from_string(typeString); - - if (typeID == layergroupid::TypeID::Unknown) { - throw ghoul::RuntimeError("Unknown layer type: " + typeString); - } - } - else { - typeID = layergroupid::TypeID::DefaultTileLayer; - } - - std::unique_ptr tp = createFromDictionary(typeID, providerDict); - - std::string provId = providerDict.value("Identifier"); - tp->setIdentifier(provId); - std::string providerName = providerDict.value("Name"); - tp->setGuiName(providerName); - addPropertySubOwner(tp.get()); - - levelTileProviders.push_back(std::move(tp)); - - // Ensure we can represent the max level - if (static_cast(providerIndices.size()) < maxLevel) { - providerIndices.resize(maxLevel + 1, -1); - } - - // map this level to the tile provider index - providerIndices[maxLevel] = static_cast(levelTileProviders.size()) - 1; - } - } - - // Fill in the gaps (value -1 ) in provider indices, from back to end - for (int i = static_cast(providerIndices.size()) - 2; i >= 0; --i) { - if (providerIndices[i] == -1) { - providerIndices[i] = providerIndices[i + 1]; - } - } -} - - - - - -TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary) - : initDict(dictionary) - , filePath(temporal::FilePathInfo) - , useFixedTime(temporal::UseFixedTimeInfo, false) - , fixedTime(temporal::FixedTimeInfo) -{ - ZoneScoped - - type = Type::TemporalTileProvider; - - filePath = dictionary.value(KeyFilePath); - addProperty(filePath); - - if (dictionary.hasValue(temporal::UseFixedTimeInfo.identifier)) { - useFixedTime = dictionary.value(temporal::UseFixedTimeInfo.identifier); - } - addProperty(useFixedTime); - - if (dictionary.hasValue(temporal::FixedTimeInfo.identifier)) { - fixedTime = dictionary.value(temporal::FixedTimeInfo.identifier); - } - addProperty(fixedTime); - - readFilePath(*this); - - if (interpolation) { - interpolateTileProvider = std::make_unique(dictionary); - interpolateTileProvider->colormap = colormap; - initialize(*interpolateTileProvider); - ghoul::Dictionary dict; - dict.setValue("FilePath", colormap); - interpolateTileProvider->singleImageProvider = - std::make_unique(dict); - } -} - - - - - - -bool initialize(TileProvider& tp) { - ZoneScoped - - ghoul_assert(!tp.isInitialized, "TileProvider can only be initialized once."); - - if (TileProvider::NumTileProviders > - static_cast(std::numeric_limits::max()) - 1) - { - LERRORC( - "TileProvider", - "Number of tile providers exceeds 65535. Something will break soon" - ); - TileProvider::NumTileProviders = 0; - } - tp.uniqueIdentifier = static_cast(TileProvider::NumTileProviders++); - if (TileProvider::NumTileProviders == std::numeric_limits::max()) { - --TileProvider::NumTileProviders; - return false; - } - - tp.isInitialized = true; - - switch (tp.type) { - case Type::DefaultTileProvider: - break; - case Type::SingleImageTileProvider: - break; - case Type::ImageSequenceTileProvider: - break; - case Type::SizeReferenceTileProvider: { - SizeReferenceTileProvider& t = static_cast(tp); - initialize(t); - break; - } - case Type::TileIndexTileProvider: { - TileIndexTileProvider& t = static_cast(tp); - initialize(t); - break; - } - case Type::ByIndexTileProvider: - break; - case Type::ByLevelTileProvider: { - TileProviderByLevel& t = static_cast(tp); - bool success = true; - for (const std::unique_ptr& prov : t.levelTileProviders) { - success &= initialize(*prov); - } - return success; - } - case Type::InterpolateTileProvider: - break; - case Type::TemporalTileProvider: - break; - default: - throw ghoul::MissingCaseException(); - } - - return true; -} - - - - - - -bool deinitialize(TileProvider& tp) { - ZoneScoped - - switch (tp.type) { - case Type::DefaultTileProvider: - break; - case Type::SingleImageTileProvider: - break; - case Type::ImageSequenceTileProvider: - break; - case Type::SizeReferenceTileProvider: { - SizeReferenceTileProvider& t = static_cast(tp); - deinitialize(t); - break; - } - case Type::TileIndexTileProvider: { - TileIndexTileProvider& t = static_cast(tp); - deinitialize(t); - break; - } - case Type::ByIndexTileProvider: - break; - case Type::ByLevelTileProvider: { - TileProviderByLevel& t = static_cast(tp); - bool success = true; - for (const std::unique_ptr& prov : t.levelTileProviders) { - success &= deinitialize(*prov); - } - return success; - } - case Type::InterpolateTileProvider: - break; - case Type::TemporalTileProvider: - break; - default: - throw ghoul::MissingCaseException(); - } - return true; -} - - -// -// InterpolateTileProvider -// -InterpolateTileProvider::InterpolateTileProvider(const ghoul::Dictionary&) { - ZoneScoped - - type = Type::InterpolateTileProvider; - glGenFramebuffers(1, &fbo); - glGenVertexArrays(1, &vaoQuad); - glGenBuffers(1, &vboQuad); - glBindVertexArray(vaoQuad); - glBindBuffer(GL_ARRAY_BUFFER, vboQuad); - tileCache = global::moduleEngine->module()->tileCache(); - // Quad for fullscreen with vertex (xy) and texture coordinates (uv) - const GLfloat vertexData[] = { - // x y u v - -1.f, -1.f, 0.f, 0.f, - 1.f, 1.f, 1.f, 1.f, - -1.f, 1.f, 0.f, 1.f, - -1.f, -1.f, 0.f, 0.f, - 1.f, -1.f, 1.f, 0.f, - 1.f, 1.f, 1.f, 1.f - }; - glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); - // vertex coordinates at location 0 - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); - glEnableVertexAttribArray(0); - // texture coords at location 1 - glVertexAttribPointer( - 1, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(GLfloat), - reinterpret_cast(sizeof(GLfloat) * 2) - ); - glEnableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - shaderProgram = global::renderEngine->buildRenderProgram( - "InterpolatingProgram", - absPath("${MODULE_GLOBEBROWSING}/shaders/interpolate_vs.glsl"), - absPath("${MODULE_GLOBEBROWSING}/shaders/interpolate_fs.glsl") - ); -} - -InterpolateTileProvider::~InterpolateTileProvider() { - glDeleteFramebuffers(1, &fbo); - glDeleteBuffers(1, &vboQuad); - glDeleteVertexArrays(1, &vaoQuad); -} - -Tile InterpolateTileProvider::calculateTile(const TileIndex& tileIndex) { - ZoneScoped - TracyGpuZone("tile"); - - // prev and next are the two tiles to interpolate between - Tile prev = tile(*t1, tileIndex); - Tile next = tile(*t2, tileIndex); - // the tile before and the tile after the interpolation interval are loaded so the - // interpolation goes smoother - Tile prevprev = tile(*before, tileIndex); - Tile nextnext = tile(*future, tileIndex); - cache::ProviderTileKey key = { tileIndex, uniqueIdentifier }; - - if (!prev.texture || !next.texture) { - return Tile{ nullptr, std::nullopt, Tile::Status::Unavailable }; - } - - // There is a previous and next texture to interpolate between so do the interpolation - - // The texture that will give the color for the interpolated texture - ghoul::opengl::Texture* colormapTexture = singleImageProvider->tile.texture; - long long hkey = cache::ProviderTileHasher()(key); - // The data for initializing the texture - TileTextureInitData initData( - prev.texture->dimensions().x, - prev.texture->dimensions().y, - prev.texture->dataType(), - prev.texture->format(), - TileTextureInitData::PadTiles::No, - TileTextureInitData::ShouldAllocateDataOnCPU::No - ); - - // Check if a tile exists for the given key in the tileCache - // Initializing the tile that will contian the interpolated texture - Tile ourTile; - // The texture that will contain the interpolated image - ghoul::opengl::Texture* writeTexture; - if (tileCache->exist(key)) { - // Get the tile from the tilecache - ourTile = tileCache->get(key); - // Use the texture from the tileCache - writeTexture = ourTile.texture; - } - else { - // Create a texture with the initialization data - writeTexture = tileCache->texture(initData); - // Create a tile with the texture - ourTile = Tile{ writeTexture, std::nullopt, Tile::Status::OK }; - // Add it to the tilecache - tileCache->put(key, initData.hashKey, ourTile); - } - - // Saves current state - GLint currentFBO; - GLint viewport[4]; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFBO); - global::renderEngine->openglStateCache().viewport(viewport); - // Bind render texture to FBO - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - *writeTexture, - 0 - ); - glDisable(GL_BLEND); - GLenum textureBuffers[1] = { GL_COLOR_ATTACHMENT0 }; - glDrawBuffers(1, textureBuffers); - // Check that our framebuffer is ok - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - LERRORC("TileProvider", "Incomplete framebuffer"); - } - // Setup our own viewport settings - GLsizei w = static_cast(writeTexture->width()); - GLsizei h = static_cast(writeTexture->height()); - glViewport(0, 0, w, h); - glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_COLOR_BUFFER_BIT); - GLint id; - glGetIntegerv(GL_CURRENT_PROGRAM, &id); - // Activate shader and bind uniforms - shaderProgram->activate(); - shaderProgram->setUniform("blendFactor", factor); - - ghoul::opengl::TextureUnit colormapUnit; - colormapUnit.activate(); - colormapTexture->bind(); - shaderProgram->setUniform("colormapTexture", colormapUnit); - - ghoul::opengl::TextureUnit prevUnit; - prevUnit.activate(); - prev.texture->bind(); - shaderProgram->setUniform("prevTexture", prevUnit); - - ghoul::opengl::TextureUnit nextUnit; - nextUnit.activate(); - next.texture->bind(); - shaderProgram->setUniform("nextTexture", nextUnit); - - // Render to the texture - glBindVertexArray(vaoQuad); - glDrawArrays(GL_TRIANGLES, 0, 6); // 2 triangles - // Deactivate shader program (when rendering is completed) - shaderProgram->deactivate(); - glUseProgram(id); - // Restores system state - glBindFramebuffer(GL_FRAMEBUFFER, currentFBO); - glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); - // Restores OpenGL Rendering State - global::renderEngine->openglStateCache().resetColorState(); - global::renderEngine->openglStateCache().resetBlendState(); - global::renderEngine->openglStateCache().resetDepthState(); - global::renderEngine->openglStateCache().resetPolygonAndClippingState(); - global::renderEngine->openglStateCache().resetViewportState(); - - return ourTile; -} - - -Tile tile(TileProvider& tp, const TileIndex& tileIndex) { - ZoneScoped - - switch (tp.type) { - case Type::DefaultTileProvider: { - ZoneScopedN("Type::DefaultTileProvider") - DefaultTileProvider& t = static_cast(tp); - if (t.asyncTextureDataProvider) { - if (tileIndex.level > maxLevel(t)) { - return Tile { nullptr, std::nullopt, Tile::Status::OutOfRange }; - } - const cache::ProviderTileKey key = { tileIndex, t.uniqueIdentifier }; - Tile tile = t.tileCache->get(key); - if (!tile.texture) { - //TracyMessage("Enqueuing tile", 32); - t.asyncTextureDataProvider->enqueueTileIO(tileIndex); - } - - return tile; - } - else { - return Tile { nullptr, std::nullopt, Tile::Status::Unavailable }; - } - } - case Type::SingleImageTileProvider: { - ZoneScopedN("Type::SingleImageTileProvider") - SingleImageProvider& t = static_cast(tp); - return t.tile; - } - case Type::ImageSequenceTileProvider: { - ZoneScopedN("Type::ImageSequenceTileProvider") - ImageSequenceTileProvider& t = static_cast(tp); - if (t.currentTileProvider) { - return tile(*t.currentTileProvider, tileIndex); - } - else { - return Tile(); - } - } - case Type::SizeReferenceTileProvider: { - ZoneScopedN("Type::SizeReferenceTileProvider") - SizeReferenceTileProvider& t = static_cast(tp); - - const GeodeticPatch patch(tileIndex); - const bool aboveEquator = patch.isNorthern(); - const double lat = aboveEquator ? patch.minLat() : patch.maxLat(); - const double lon1 = patch.minLon(); - const double lon2 = patch.maxLon(); - int l = static_cast(t.ellipsoid.longitudalDistance(lat, lon1, lon2)); - - const bool useKm = l > 9999; - if (useKm) { - l /= 1000; - } - l = static_cast(std::round(l)); - if (useKm) { - l *= 1000; - } - double tileLongitudalLength = l; - - const char* unit; - if (tileLongitudalLength > 9999) { - tileLongitudalLength *= 0.001; - unit = "km"; - } - else { - unit = "m"; - } - - t.text = fmt::format(" {:.0f} {:s}", tileLongitudalLength, unit); - t.textPosition = { - 0.f, - aboveEquator ? - t.fontSize / 2.f : - t.initData.dimensions.y - 3.f * t.fontSize / 2.f - }; - t.textColor = glm::vec4(1.f); - - return tile(t, tileIndex); - } - case Type::TileIndexTileProvider: { - ZoneScopedN("Type::TileIndexTileProvider") - TileIndexTileProvider& t = static_cast(tp); - t.text = fmt::format( - "level: {}\nx: {}\ny: {}", tileIndex.level, tileIndex.x, tileIndex.y - ); - t.textPosition = glm::vec2( - t.initData.dimensions.x / 4 - - (t.initData.dimensions.x / 32) * log10(1 << tileIndex.level), - t.initData.dimensions.y / 2 + t.fontSize - ); - t.textColor = glm::vec4(1.f); - - return tile(t, tileIndex); - } - case Type::ByIndexTileProvider: { - ZoneScopedN("Type::ByIndexTileProvider") - TileProviderByIndex& t = static_cast(tp); - const auto it = t.tileProviderMap.find(tileIndex.hashKey()); - const bool hasProvider = it != t.tileProviderMap.end(); - return hasProvider ? tile(*it->second, tileIndex) : Tile(); - } - case Type::ByLevelTileProvider: { - ZoneScopedN("Type::ByLevelTileProvider") - TileProviderByLevel& t = static_cast(tp); - TileProvider* provider = levelProvider(t, tileIndex.level); - if (provider) { - return tile(*provider, tileIndex); - } - else { - return Tile(); - } - } - case Type::InterpolateTileProvider: { - - ZoneScopedN("Type::InterpolateTileProvider") - InterpolateTileProvider& t = static_cast(tp); - return t.calculateTile(tileIndex); - break; - } - case Type::TemporalTileProvider: { - ZoneScopedN("Type::TemporalTileProvider") - TemporalTileProvider& t = static_cast(tp); - ensureUpdated(t); - return tile(*t.currentTileProvider, tileIndex); - } - default: - throw ghoul::MissingCaseException(); - } -} - - - - -Tile::Status tileStatus(TileProvider& tp, const TileIndex& index) { - ZoneScoped - - switch (tp.type) { - case Type::DefaultTileProvider: { - DefaultTileProvider& t = static_cast(tp); - if (t.asyncTextureDataProvider) { - const RawTileDataReader& rawTileDataReader = - t.asyncTextureDataProvider->rawTileDataReader(); - - if (index.level > rawTileDataReader.maxChunkLevel()) { - return Tile::Status::OutOfRange; - } - - const cache::ProviderTileKey key = { index, t.uniqueIdentifier }; - return t.tileCache->get(key).status; - } - else { - return Tile::Status::Unavailable; - } - } - case Type::SingleImageTileProvider: { - SingleImageProvider& t = static_cast(tp); - return t.tile.status; - } - case Type::ImageSequenceTileProvider: { - ImageSequenceTileProvider& t = static_cast(tp); - if (t.currentTileProvider) { - return tileStatus(*t.currentTileProvider, index); - } - else { - return Tile::Status::Unavailable; - } - } - case Type::SizeReferenceTileProvider: - return Tile::Status::OK; - case Type::TileIndexTileProvider: - return Tile::Status::OK; - case Type::ByIndexTileProvider: { - TileProviderByIndex& t = static_cast(tp); - const auto it = t.tileProviderMap.find(index.hashKey()); - const bool hasProvider = it != t.tileProviderMap.end(); - return hasProvider ? - tileStatus(*it->second, index) : - Tile::Status::Unavailable; - } - case Type::ByLevelTileProvider: { - TileProviderByLevel& t = static_cast(tp); - TileProvider* provider = levelProvider(t, index.level); - return provider ? tileStatus(*provider, index) : Tile::Status::Unavailable; - } - case Type::InterpolateTileProvider: { - InterpolateTileProvider& t = static_cast(tp); - Tile::Status t1Stat = tileStatus(*t.t1, index); - Tile::Status t2Stat = tileStatus(*t.t2, index); - if (t1Stat <= t2Stat) { - return t1Stat; - } - else { - return t2Stat; - } - } - case Type::TemporalTileProvider: { - TemporalTileProvider& t = static_cast(tp); - ensureUpdated(t); - return tileStatus(*t.currentTileProvider, index); - } - default: - throw ghoul::MissingCaseException(); - } -} - - - - - - -TileDepthTransform depthTransform(TileProvider& tp) { - ZoneScoped - - switch (tp.type) { - case Type::DefaultTileProvider: { - DefaultTileProvider& t = static_cast(tp); - if (t.asyncTextureDataProvider) { - return t.asyncTextureDataProvider->rawTileDataReader().depthTransform(); - } - else { - return { 1.f, 0.f }; - } - } - case Type::SingleImageTileProvider: - return { 0.f, 1.f }; - case Type::ImageSequenceTileProvider: { - ImageSequenceTileProvider& t = static_cast(tp); - if (t.currentTileProvider) { - return depthTransform(*t.currentTileProvider); - } - else { - return { 1.f, 0.f }; - } - } - case Type::SizeReferenceTileProvider: - return { 0.f, 1.f }; - case Type::TileIndexTileProvider: - return { 0.f, 1.f }; - case Type::ByIndexTileProvider: { - TileProviderByIndex& t = static_cast(tp); - return depthTransform(*t.defaultTileProvider); - } - case Type::ByLevelTileProvider: - return { 0.f, 1.f }; - case Type::InterpolateTileProvider: { - InterpolateTileProvider& t = static_cast(tp); - return depthTransform(*t.t1); - } - case Type::TemporalTileProvider: { - TemporalTileProvider& t = static_cast(tp); - ensureUpdated(t); - return depthTransform(*t.currentTileProvider); - } - default: - throw ghoul::MissingCaseException(); - } -} - - - - - - -void update(TileProvider& tp) { - ZoneScoped - - switch (tp.type) { - case Type::DefaultTileProvider: { - DefaultTileProvider& t = static_cast(tp); - if (!t.asyncTextureDataProvider) { - break; - } - - t.asyncTextureDataProvider->update(); - initTexturesFromLoadedData(t); - - if (t.asyncTextureDataProvider->shouldBeDeleted()) { - t.asyncTextureDataProvider = nullptr; - initAsyncTileDataReader( - t, - tileTextureInitData(t.layerGroupID, t.padTiles, t.tilePixelSize) - ); - } - break; - } - case Type::SingleImageTileProvider: - break; - case Type::ImageSequenceTileProvider: { - ImageSequenceTileProvider& t = static_cast(tp); - - if (t.isImageDirty && !t.imagePaths.empty() && - t.index >= 0 && t.index < t.imagePaths.size()) - { - if (t.currentTileProvider) { - deinitialize(*t.currentTileProvider); - } - - std::string p = t.imagePaths[t.index].string(); - t.currentImage = p; - t.initDict.setValue(KeyFilePath, p); - t.currentTileProvider = std::make_unique(t.initDict); - initialize(*t.currentTileProvider); - t.isImageDirty = false; - } - - if (t.currentTileProvider) { - update(*t.currentTileProvider); - } - break; - } - case Type::SizeReferenceTileProvider: - break; - case Type::TileIndexTileProvider: - break; - case Type::ByIndexTileProvider: { - TileProviderByIndex& t = static_cast(tp); - using K = TileIndex::TileHashKey; - using V = std::unique_ptr; - for (std::pair& it : t.tileProviderMap) { - update(*it.second); - } - update(*t.defaultTileProvider); - break; - } - case Type::ByLevelTileProvider: { - TileProviderByLevel& t = static_cast(tp); - for (const std::unique_ptr& provider : t.levelTileProviders) { - update(*provider); - } - break; - } - case Type::InterpolateTileProvider: { - InterpolateTileProvider& t = static_cast(tp); - update(*t.t1); - update(*t.t2); - update(*t.before); - update(*t.future); - break; - } - case Type::TemporalTileProvider: { - TemporalTileProvider& t = static_cast(tp); - TileProvider* newCurr = getTileProvider(t, global::timeManager->time()); - if (newCurr) { - t.currentTileProvider = newCurr; - } - if (t.currentTileProvider) { - update(*t.currentTileProvider); - } - break; - } - default: - throw ghoul::MissingCaseException(); - } -} - - - - - - -void reset(TileProvider& tp) { - ZoneScoped - - switch (tp.type) { - case Type::DefaultTileProvider: { - DefaultTileProvider& t = static_cast(tp); - t.tileCache->clear(); - if (t.asyncTextureDataProvider) { - t.asyncTextureDataProvider->prepareToBeDeleted(); - } - else { - initAsyncTileDataReader( - t, - tileTextureInitData(t.layerGroupID, t.padTiles, t.tilePixelSize) - ); - } - break; - } - case Type::SingleImageTileProvider: { - SingleImageProvider& t = static_cast(tp); - - if (t.filePath.value().empty()) { - return; - } - t.tileTexture = ghoul::io::TextureReader::ref().loadTexture(t.filePath, 2); - if (!t.tileTexture) { - throw ghoul::RuntimeError( - fmt::format("Unable to load texture '{}'", t.filePath.value()) - ); - } - Tile::Status tileStatus = Tile::Status::OK; - - t.tileTexture->uploadTexture(); - t.tileTexture->setFilter( - ghoul::opengl::Texture::FilterMode::AnisotropicMipMap - ); - - t.tile = Tile{ t.tileTexture.get(), std::nullopt, tileStatus }; - break; - } - case Type::ImageSequenceTileProvider: { - namespace fs = std::filesystem; - ImageSequenceTileProvider& t = static_cast(tp); - std::string path = t.folderPath; - t.imagePaths.clear(); - for (const fs::directory_entry& p : fs::directory_iterator(path)) { - if (p.is_regular_file()) { - t.imagePaths.push_back(p.path()); - } - } - - t.index = 0; - t.index.setMaxValue(static_cast(t.imagePaths.size() - 1)); - - if (t.currentTileProvider) { - reset(*t.currentTileProvider); - } - break; - } - case Type::SizeReferenceTileProvider: { - SizeReferenceTileProvider& t = static_cast(tp); - reset(t); - break; - } - case Type::TileIndexTileProvider: { - TileIndexTileProvider& t = static_cast(tp); - reset(t); - break; - } - case Type::ByIndexTileProvider: { - TileProviderByIndex& t = static_cast(tp); - using K = TileIndex::TileHashKey; - using V = std::unique_ptr; - for (std::pair& it : t.tileProviderMap) { - reset(*it.second); - } - reset(*t.defaultTileProvider); - break; - } - case Type::ByLevelTileProvider: { - TileProviderByLevel& t = static_cast(tp); - for (const std::unique_ptr& provider : t.levelTileProviders) { - reset(*provider); - } - break; - } - case Type::InterpolateTileProvider: { - InterpolateTileProvider& t = static_cast(tp); - reset(*t.t1); - reset(*t.t2); - reset(*t.before); - reset(*t.future); - break; - } - case Type::TemporalTileProvider: { - TemporalTileProvider& t = static_cast(tp); - using K = TemporalTileProvider::TimeKey; - using V = std::unique_ptr; - for (std::pair& it : t.tileProviderMap) { - reset(*it.second); - } - break; - } - default: - throw ghoul::MissingCaseException(); - } -} - - - - - - -int maxLevel(TileProvider& tp) { - switch (tp.type) { - case Type::DefaultTileProvider: { - DefaultTileProvider& t = static_cast(tp); - // 22 is the current theoretical maximum based on the number of hashes that - // are possible to uniquely identify a tile. See ProviderTileHasher in - // memoryawaretilecache.h - return t.asyncTextureDataProvider ? - t.asyncTextureDataProvider->rawTileDataReader().maxChunkLevel() : - 22; - } - case Type::SingleImageTileProvider: - return 1337; // unlimited - case Type::ImageSequenceTileProvider: { - ImageSequenceTileProvider& t = static_cast(tp); - if (t.currentTileProvider) { - return maxLevel(*t.currentTileProvider); - } - else { - return 0; - } - } - case Type::SizeReferenceTileProvider: - return 1337; // unlimited - case Type::TileIndexTileProvider: - return 1337; // unlimited - case Type::ByIndexTileProvider: { - TileProviderByIndex& t = static_cast(tp); - return maxLevel(*t.defaultTileProvider); - } - case Type::ByLevelTileProvider: { - TileProviderByLevel& t = static_cast(tp); - return static_cast(t.providerIndices.size() - 1); - } - case Type::InterpolateTileProvider: { - InterpolateTileProvider& t = static_cast(tp); - return glm::min(maxLevel(*t.t1), maxLevel(*t.t2)); - } - case Type::TemporalTileProvider: { - TemporalTileProvider& t = static_cast(tp); - ensureUpdated(t); - return maxLevel(*t.currentTileProvider); - } - default: - throw ghoul::MissingCaseException(); - } -} - - - - - - -float noDataValueAsFloat(TileProvider& tp) { - ZoneScoped - - ghoul_assert(tp.isInitialized, "TileProvider was not initialized."); - switch (tp.type) { - case Type::DefaultTileProvider: { - DefaultTileProvider& t = static_cast(tp); - return t.asyncTextureDataProvider ? - t.asyncTextureDataProvider->noDataValueAsFloat() : - std::numeric_limits::min(); - } - case Type::SingleImageTileProvider: - return std::numeric_limits::min(); - case Type::ImageSequenceTileProvider: { - ImageSequenceTileProvider& t = static_cast(tp); - if (t.currentTileProvider) { - return noDataValueAsFloat(*t.currentTileProvider); - } - else { - return std::numeric_limits::min(); - } - } - case Type::SizeReferenceTileProvider: - return std::numeric_limits::min(); - case Type::TileIndexTileProvider: - return std::numeric_limits::min(); - case Type::ByIndexTileProvider: - return std::numeric_limits::min(); - case Type::ByLevelTileProvider: - return std::numeric_limits::min(); - case Type::InterpolateTileProvider: - return std::numeric_limits::min(); - case Type::TemporalTileProvider: - return std::numeric_limits::min(); - default: - throw ghoul::MissingCaseException(); - } -} - - - - - -ChunkTile chunkTile(TileProvider& tp, TileIndex tileIndex, int parents, int maxParents) { - ZoneScoped - - ghoul_assert(tp.isInitialized, "TileProvider was not initialized."); - - auto ascendToParent = [](TileIndex& ti, TileUvTransform& uv) { - uv.uvOffset *= 0.5; - uv.uvScale *= 0.5; - - uv.uvOffset += ti.positionRelativeParent(); - - ti.x /= 2; - ti.y /= 2; - ti.level--; - }; - - TileUvTransform uvTransform = { glm::vec2(0.f, 0.f), glm::vec2(1.f, 1.f) }; - - // Step 1. Traverse 0 or more parents up the chunkTree as requested by the caller - for (int i = 0; i < parents && tileIndex.level > 1; i++) { - ascendToParent(tileIndex, uvTransform); - } - maxParents -= parents; - - // Step 2. Traverse 0 or more parents up the chunkTree to make sure we're inside - // the range of defined data. - int maximumLevel = maxLevel(tp); - while (tileIndex.level > maximumLevel) { - ascendToParent(tileIndex, uvTransform); - maxParents--; - } - if (maxParents < 0) { - return ChunkTile { Tile(), uvTransform, TileDepthTransform() }; - } - - // Step 3. Traverse 0 or more parents up the chunkTree until we find a chunk that - // has a loaded tile ready to use. - while (tileIndex.level > 1) { - Tile t = tile(tp, tileIndex); - if (t.status != Tile::Status::OK) { - if (--maxParents < 0) { - return ChunkTile { Tile(), uvTransform, TileDepthTransform() }; - } - ascendToParent(tileIndex, uvTransform); - } - else { - return ChunkTile { std::move(t), uvTransform, TileDepthTransform() }; - } - } - - return ChunkTile { Tile(), uvTransform, TileDepthTransform() }; -} - - - - - - -ChunkTilePile chunkTilePile(TileProvider& tp, TileIndex tileIndex, int pileSize) { - ZoneScoped - - ghoul_assert(tp.isInitialized, "TileProvider was not initialized."); - ghoul_assert(pileSize >= 0, "pileSize must be positive"); - - ChunkTilePile chunkTilePile; - std::fill(chunkTilePile.begin(), chunkTilePile.end(), std::nullopt); - for (int i = 0; i < pileSize; ++i) { - chunkTilePile[i] = chunkTile(tp, tileIndex, i); - if (chunkTilePile[i]->tile.status == Tile::Status::Unavailable) { - if (i == 0) { - // First iteration - chunkTilePile[i]->tile = DefaultTile; - chunkTilePile[i]->uvTransform.uvOffset = { 0.f, 0.f }; - chunkTilePile[i]->uvTransform.uvScale = { 1.f, 1.f }; - } - else { - // We are iterating through the array one-by-one, so we are guaranteed - // that for tile 'i', tile 'i-1' already was initializated - chunkTilePile[i]->tile = chunkTilePile[i - 1]->tile; - chunkTilePile[i]->uvTransform.uvOffset = - chunkTilePile[i - 1]->uvTransform.uvOffset; - chunkTilePile[i]->uvTransform.uvScale = - chunkTilePile[i - 1]->uvTransform.uvScale; - } - } - } - return chunkTilePile; -} - -} // namespace openspace::globebrowsing::tileprovider diff --git a/modules/globebrowsing/src/tileprovider.h b/modules/globebrowsing/src/tileprovider.h deleted file mode 100644 index 9ff944c202..0000000000 --- a/modules/globebrowsing/src/tileprovider.h +++ /dev/null @@ -1,292 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2022 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_PROVIDER___H__ -#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_PROVIDER___H__ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct CPLXMLNode; - -namespace ghoul::fontrendering { - class Font; - class FontRenderer; -} // namespace ghoul::fontrendering - -namespace openspace { class PixelBuffer; } - -namespace openspace::globebrowsing { - class AsyncTileDataProvider; - struct RawTile; - struct TileIndex; - namespace cache { class MemoryAwareTileCache; } -} // namespace openspace::globebrowsing - -namespace openspace::globebrowsing::tileprovider { - -enum class Type { - DefaultTileProvider = 0, - SingleImageTileProvider, - ImageSequenceTileProvider, - SizeReferenceTileProvider, - TemporalTileProvider, - TileIndexTileProvider, - ByIndexTileProvider, - ByLevelTileProvider, - InterpolateTileProvider -}; - - -struct TileProvider : public properties::PropertyOwner { - static unsigned int NumTileProviders; - - Type type; - - TileProvider(); - virtual ~TileProvider() = default; - - std::string name; - - uint16_t uniqueIdentifier = 0; - bool isInitialized = false; -}; - -struct DefaultTileProvider : public TileProvider { - DefaultTileProvider(const ghoul::Dictionary& dictionary); - - std::unique_ptr asyncTextureDataProvider; - - cache::MemoryAwareTileCache* tileCache = nullptr; - - properties::StringProperty filePath; - properties::IntProperty tilePixelSize; - layergroupid::GroupID layerGroupID = layergroupid::GroupID::Unknown; - bool performPreProcessing = false; - bool padTiles = true; -}; - -struct SingleImageProvider : public TileProvider { - SingleImageProvider(const ghoul::Dictionary& dictionary); - - std::unique_ptr tileTexture; - Tile tile; - - properties::StringProperty filePath; -}; - -struct InterpolateTileProvider : public TileProvider { - InterpolateTileProvider(const ghoul::Dictionary&); - virtual ~InterpolateTileProvider(); - - Tile calculateTile(const TileIndex&); - TileProvider* before = nullptr; - TileProvider* t1 = nullptr; - TileProvider* t2 = nullptr; - TileProvider* future = nullptr; - float factor = 1.f; - GLuint vaoQuad = 0; - GLuint vboQuad = 0; - GLuint fbo = 0; - std::string colormap; - std::unique_ptr shaderProgram; - std::unique_ptr singleImageProvider; - cache::MemoryAwareTileCache* tileCache = nullptr; -}; - -struct TextTileProvider : public TileProvider { - TextTileProvider(TileTextureInitData initData, size_t fontSize = 48); - - const TileTextureInitData initData; - - std::unique_ptr fontRenderer; - std::shared_ptr font; - size_t fontSize = 0; - - std::string text; - glm::vec2 textPosition = glm::vec2(0.f); - glm::vec4 textColor = glm::vec4(0.f); - - GLuint fbo = 0; - - cache::MemoryAwareTileCache* tileCache; -}; - -struct SizeReferenceTileProvider : public TextTileProvider { - SizeReferenceTileProvider(const ghoul::Dictionary& dictionary); - - Ellipsoid ellipsoid; -}; - -struct TileIndexTileProvider : public TextTileProvider { - TileIndexTileProvider(const ghoul::Dictionary& dictionary); -}; - -struct TileProviderByIndex : public TileProvider { - TileProviderByIndex(const ghoul::Dictionary& dictionary); - - std::unordered_map< - TileIndex::TileHashKey, std::unique_ptr - > tileProviderMap; - std::unique_ptr defaultTileProvider; -}; - -struct TileProviderByLevel : public TileProvider { - TileProviderByLevel(const ghoul::Dictionary& dictionary); - - std::vector providerIndices; - std::vector> levelTileProviders; -}; - - -struct ImageSequenceTileProvider : public TileProvider { - ImageSequenceTileProvider(const ghoul::Dictionary& dictionary); - - std::unique_ptr currentTileProvider = nullptr; - - properties::IntProperty index; - properties::StringProperty currentImage; - properties::StringProperty folderPath; - - ghoul::Dictionary initDict; - bool isImageDirty = true; - std::vector imagePaths; -}; - -/** - * Provide Tiles from web map services that have temporal resolution. - * - * TemporalTileProviders are instantiated using a ghoul::Dictionary, - * and must define a filepath to a Openspace Temporal dataset description file. - * This is an xml-file that defines the same meta data as the GDAL wms description - * (http://www.gdal.org/frmt_wms.html), but augmented with some - * extra tags describing the temporal properties of the dataset. See - * TemporalTileProvider::TemporalXMLTags - * - */ -struct TemporalTileProvider : public TileProvider { - enum class TimeFormatType { - YYYY_MM_DD = 0, - YYYYMMDD_hhmmss, - YYYYMMDD_hhmm, - YYYY_MM_DDThhColonmmColonssZ, - YYYY_MM_DDThh_mm_ssZ - }; - - using TimeKey = std::string; - - TemporalTileProvider(const ghoul::Dictionary& dictionary); - - ghoul::Dictionary initDict; - properties::StringProperty filePath; - properties::BoolProperty useFixedTime; - properties::StringProperty fixedTime; - std::string gdalXmlTemplate; - - std::unordered_map> tileProviderMap; - - bool interpolation = false; - - TileProvider* currentTileProvider = nullptr; - double startTimeJ2000; - double endTimeJ2000; - TimeFormatType timeFormat; - TimeQuantizer timeQuantizer; - std::string colormap; - - std::string myResolution; - std::unique_ptr interpolateTileProvider; -}; - - - -void initializeDefaultTile(); -void deinitializeDefaultTile(); - -std::unique_ptr createFromDictionary(layergroupid::TypeID layerTypeID, - const ghoul::Dictionary& dictionary); - -bool initialize(TileProvider& tp); -bool deinitialize(TileProvider& tp); - -Tile tile(TileProvider& tp, const TileIndex& tileIndex); - -ChunkTile chunkTile(TileProvider& tp, TileIndex tileIndex, int parents = 0, - int maxParents = 1337); - -ChunkTilePile chunkTilePile(TileProvider& tp, TileIndex tileIndex, int pileSize); - -/** - * Returns the status of a Tile. The Tile::Status - * corresponds the Tile that would be returned - * if the function tile would be invoked with the same - * TileIndex argument at this point in time. - */ -Tile::Status tileStatus(TileProvider& tp, const TileIndex& index); - -/** - * Get the associated depth transform for this TileProvider. - * This is necessary for TileProviders serving height map - * data, in order to correcly map pixel values to meters. - */ -TileDepthTransform depthTransform(TileProvider& tp); - -/** - * This method should be called once per frame. Here, TileProviders - * are given the opportunity to update their internal state. - */ -void update(TileProvider& tp); - -/** - * Provides a uniform way of all TileProviders to reload or - * restore all of its internal state. This is mainly useful - * for debugging purposes. - */ -void reset(TileProvider& tp); - -/** - * \returns The maximum level as defined by TileIndex - * that this TileProvider is able provide. - */ -int maxLevel(TileProvider& tp); - -/** - * \returns the no data value for the dataset. Default is the minimum float avalue. - */ -float noDataValueAsFloat(TileProvider& tp); - -} // namespace openspace::globebrowsing::tileprovider - -#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_PROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/defaulttileprovider.cpp b/modules/globebrowsing/src/tileprovider/defaulttileprovider.cpp new file mode 100644 index 0000000000..7047094a45 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/defaulttileprovider.cpp @@ -0,0 +1,204 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo FilePathInfo = { + "FilePath", + "File Path", + "The path of the GDAL file or the image file that is to be used in this tile " + "provider." + }; + + constexpr openspace::properties::Property::PropertyInfo TilePixelSizeInfo = { + "TilePixelSize", + "Tile Pixel Size", + "This value is the preferred size (in pixels) for each tile. Choosing the right " + "value is a tradeoff between more efficiency (larger images) and better quality " + "(smaller images). The tile pixel size has to be smaller than the size of the " + "complete image if a single image is used." + }; + + struct [[codegen::Dictionary(DefaultTileProvider)]] Parameters { + // User-facing name of this tile provider + std::optional name; + + // The path to the file that is loaded by GDAL to produce tiles. Since GDAL + // supports it, this can also be the textual representation of the contents of a + // loading file + std::string filePath; + + // The layer into which this tile provider is loaded + int layerGroupID; + + // [[codegen::verbatim(TilePixelSizeInfo.description)]] + std::optional tilePixelSize; + + // Determines whether the tiles should have a padding zone around it, making the + // interpolation between tiles more pleasant + std::optional padTiles; + + // Determines if the tiles should be preprocessed before uploading to the GPU + std::optional performPreProcessing; + + }; +#include "defaulttileprovider_codegen.cpp" +} // namespace + +namespace openspace::globebrowsing { + +DefaultTileProvider::DefaultTileProvider(const ghoul::Dictionary& dictionary) + : _filePath(FilePathInfo, "") + , _tilePixelSize(TilePixelSizeInfo, 32, 32, 2048) +{ + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + name = p.name.value_or("Name unspecified"); + std::string _loggerCat = "DefaultTileProvider (" + name + ")"; + + // 1. Get required Keys + _filePath = p.filePath; + + _layerGroupID = layergroupid::GroupID(p.layerGroupID); + + // 2. Initialize default values for any optional Keys + // getValue does not work for integers + int pixelSize = p.tilePixelSize.value_or(0); + _padTiles = p.padTiles.value_or(_padTiles); + + // Only preprocess height layers by default + switch (_layerGroupID) { + case layergroupid::GroupID::HeightLayers: _performPreProcessing = true; break; + default: _performPreProcessing = false; break; + } + + _performPreProcessing = p.performPreProcessing.value_or(_performPreProcessing); + + TileTextureInitData initData( + tileTextureInitData(_layerGroupID, _padTiles, pixelSize) + ); + _tilePixelSize = initData.dimensions.x; + initAsyncTileDataReader(initData); + + addProperty(_filePath); + addProperty(_tilePixelSize); +} + +void DefaultTileProvider::initAsyncTileDataReader(TileTextureInitData initData) { + ZoneScoped + + _asyncTextureDataProvider = std::make_unique( + name, + std::make_unique( + _filePath, + initData, + RawTileDataReader::PerformPreprocessing(_performPreProcessing) + ) + ); +} + +Tile DefaultTileProvider::tile(const TileIndex& tileIndex) { + ZoneScoped + + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + if (tileIndex.level > maxLevel()) { + return Tile{ nullptr, std::nullopt, Tile::Status::OutOfRange }; + } + const cache::ProviderTileKey key = { tileIndex, uniqueIdentifier }; + cache::MemoryAwareTileCache* tileCache = + global::moduleEngine->module()->tileCache(); + Tile tile = tileCache->get(key); + if (!tile.texture) { + _asyncTextureDataProvider->enqueueTileIO(tileIndex); + } + + return tile; +} + +Tile::Status DefaultTileProvider::tileStatus(const TileIndex& index) { + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + const RawTileDataReader& reader = _asyncTextureDataProvider->rawTileDataReader(); + + if (index.level > reader.maxChunkLevel()) { + return Tile::Status::OutOfRange; + } + + const cache::ProviderTileKey key = { index, uniqueIdentifier }; + cache::MemoryAwareTileCache* tileCache = + global::moduleEngine->module()->tileCache(); + return tileCache->get(key).status; +} + +TileDepthTransform DefaultTileProvider::depthTransform() { + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + return _asyncTextureDataProvider->rawTileDataReader().depthTransform(); +} + +void DefaultTileProvider::update() { + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + _asyncTextureDataProvider->update(); + + std::optional tile = _asyncTextureDataProvider->popFinishedRawTile(); + if (tile) { + const cache::ProviderTileKey key = { tile->tileIndex, uniqueIdentifier }; + cache::MemoryAwareTileCache* tileCache = + global::moduleEngine->module()->tileCache(); + ghoul_assert(!tileCache->exist(key), "Tile must not be existing in cache"); + tileCache->createTileAndPut(key, std::move(*tile)); + } + + if (_asyncTextureDataProvider->shouldBeDeleted()) { + initAsyncTileDataReader( + tileTextureInitData(_layerGroupID, _padTiles, _tilePixelSize) + ); + } +} + +void DefaultTileProvider::reset() { + global::moduleEngine->module()->tileCache()->clear(); + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + _asyncTextureDataProvider->prepareToBeDeleted(); +} + +int DefaultTileProvider::maxLevel() { + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + return _asyncTextureDataProvider->rawTileDataReader().maxChunkLevel(); +} + +float DefaultTileProvider::noDataValueAsFloat() { + ghoul_assert(_asyncTextureDataProvider, "No data provider"); + return _asyncTextureDataProvider->noDataValueAsFloat(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/defaulttileprovider.h b/modules/globebrowsing/src/tileprovider/defaulttileprovider.h new file mode 100644 index 0000000000..51250ca02f --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/defaulttileprovider.h @@ -0,0 +1,61 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__DEFAULTTILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__DEFAULTTILEPROVIDER___H__ + +#include + +#include +#include + +namespace openspace::globebrowsing { + +class DefaultTileProvider : public TileProvider { +public: + DefaultTileProvider(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + void initAsyncTileDataReader(TileTextureInitData initData); + + properties::StringProperty _filePath; + properties::IntProperty _tilePixelSize; + + std::unique_ptr _asyncTextureDataProvider; + layergroupid::GroupID _layerGroupID = layergroupid::GroupID::Unknown; + bool _performPreProcessing = false; + bool _padTiles = true; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__DEFAULTTILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/imagesequencetileprovider.cpp b/modules/globebrowsing/src/tileprovider/imagesequencetileprovider.cpp new file mode 100644 index 0000000000..2bf0485566 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/imagesequencetileprovider.cpp @@ -0,0 +1,158 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo IndexInfo = { + "Index", + "Index", + "The index into the list of images that is used to pick the currently displayed " + "image" + }; + + constexpr openspace::properties::Property::PropertyInfo CurrentImageInfo = { + "CurrentImage", + "Current Image", + "The read-only value of the currently selected image" + }; + + constexpr openspace::properties::Property::PropertyInfo FolderPathInfo = { + "FolderPath", + "Folder Path", + "The path that is used to look for images for this image provider. The path must " + "point to an existing folder that contains images" + }; + + struct [[codegen::Dictionary(ImageSequenceTileProvider)]] Parameters { + // [[codegen::verbatim(IndexInfo.description)]] + std::optional index; + + // [[codegen::verbatim(FolderPathInfo.description)]] + std::filesystem::path folderPath [[codegen::directory()]]; + }; +#include "imagesequencetileprovider_codegen.cpp" +} // namespace + +namespace openspace::globebrowsing { + +ImageSequenceTileProvider::ImageSequenceTileProvider(const ghoul::Dictionary& dictionary) + : _index(IndexInfo, 0) + , _currentImage(CurrentImageInfo) + , _folderPath(FolderPathInfo) + , _initDict(dictionary) +{ + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + _index = p.index.value_or(_index); + _index.setMinValue(0); + _index.onChange([this]() { _isImageDirty = true; }); + addProperty(_index); + + _currentImage.setReadOnly(true); + addProperty(_currentImage); + + _folderPath = p.folderPath.string(); + addProperty(_folderPath); + + reset(); +} + +Tile ImageSequenceTileProvider::tile(const TileIndex& tileIndex) { + ZoneScoped + + return _currentTileProvider ? _currentTileProvider->tile(tileIndex) : Tile(); +} + +Tile::Status ImageSequenceTileProvider::tileStatus(const TileIndex& index) { + return _currentTileProvider ? + _currentTileProvider->tileStatus(index) : + Tile::Status::Unavailable; +} + +TileDepthTransform ImageSequenceTileProvider::depthTransform() { + if (_currentTileProvider) { + return _currentTileProvider->depthTransform(); + } + else { + return { 1.f, 0.f }; + } +} + +void ImageSequenceTileProvider::update() { + if (_isImageDirty && !_imagePaths.empty() && + _index >= 0 && _index < _imagePaths.size()) + { + if (_currentTileProvider) { + _currentTileProvider->deinitialize(); + } + + std::string p = _imagePaths[_index].string(); + _currentImage = p; + _initDict.setValue("FilePath", p); + _currentTileProvider = std::make_unique(_initDict); + _currentTileProvider->initialize(); + _isImageDirty = false; + } + + if (_currentTileProvider) { + _currentTileProvider->update(); + } +} + +void ImageSequenceTileProvider::reset() { + namespace fs = std::filesystem; + std::string path = _folderPath; + _imagePaths.clear(); + for (const fs::directory_entry& p : fs::directory_iterator(path)) { + if (p.is_regular_file()) { + _imagePaths.push_back(p.path()); + } + } + + _index = 0; + _index.setMaxValue(static_cast(_imagePaths.size() - 1)); + + if (_currentTileProvider) { + _currentTileProvider->reset(); + } +} + +int ImageSequenceTileProvider::maxLevel() { + return _currentTileProvider ? _currentTileProvider->maxLevel() : 0; +} + +float ImageSequenceTileProvider::noDataValueAsFloat() { + return _currentTileProvider ? + _currentTileProvider->noDataValueAsFloat() : + std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/imagesequencetileprovider.h b/modules/globebrowsing/src/tileprovider/imagesequencetileprovider.h new file mode 100644 index 0000000000..0ee4730db9 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/imagesequencetileprovider.h @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__IMAGESEQUENCETILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__IMAGESEQUENCETILEPROVIDER___H__ + +#include + +#include + +namespace openspace::globebrowsing { + +class ImageSequenceTileProvider : public TileProvider { +public: + ImageSequenceTileProvider(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + std::unique_ptr _currentTileProvider = nullptr; + + properties::IntProperty _index; + properties::StringProperty _currentImage; + properties::StringProperty _folderPath; + + ghoul::Dictionary _initDict; + bool _isImageDirty = true; + std::vector _imagePaths; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__IMAGESEQUENCETILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/singleimagetileprovider.cpp b/modules/globebrowsing/src/tileprovider/singleimagetileprovider.cpp new file mode 100644 index 0000000000..a4a113b2b9 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/singleimagetileprovider.cpp @@ -0,0 +1,100 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo FilePathInfo = { + "FilePath", + "File Path", + "The file path that is used for this image provider. The file must point to an " + "image that is then loaded and used for all tiles." + }; + + struct [[codegen::Dictionary(SingleImageProvider)]] Parameters { + // [[codegen::verbatim(FilePathInfo.description)]] + std::string filePath; + }; +#include "singleimagetileprovider_codegen.cpp" +} // namespace + +namespace openspace::globebrowsing { + +SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) + : _filePath(FilePathInfo) +{ + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + _filePath = p.filePath; + addProperty(_filePath); + + reset(); +} + +Tile SingleImageProvider::tile(const TileIndex&) { + ZoneScoped + return _tile; +} + +Tile::Status SingleImageProvider::tileStatus(const TileIndex&) { + return _tile.status; +} + +TileDepthTransform SingleImageProvider::depthTransform() { + return { 0.f, 1.f }; +} + +void SingleImageProvider::update() {} + +void SingleImageProvider::reset() { + if (_filePath.value().empty()) { + return; + } + + _tileTexture = ghoul::io::TextureReader::ref().loadTexture(_filePath, 2); + if (!_tileTexture) { + throw ghoul::RuntimeError( + fmt::format("Unable to load texture '{}'", _filePath.value()) + ); + } + + _tileTexture->uploadTexture(); + _tileTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _tile = Tile{ _tileTexture.get(), std::nullopt, Tile::Status::OK }; +} + +int SingleImageProvider::maxLevel() { + return 1337; // unlimited +} + +float SingleImageProvider::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/singleimagetileprovider.h b/modules/globebrowsing/src/tileprovider/singleimagetileprovider.h new file mode 100644 index 0000000000..c3fee2725b --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/singleimagetileprovider.h @@ -0,0 +1,53 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__SINGLEIMAGETILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__SINGLEIMAGETILEPROVIDER___H__ + +#include + +namespace openspace::globebrowsing { + +class SingleImageProvider : public TileProvider { +public: + SingleImageProvider(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + properties::StringProperty _filePath; + + std::unique_ptr _tileTexture; + Tile _tile; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__SINGLEIMAGETILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/sizereferencetileprovider.cpp b/modules/globebrowsing/src/tileprovider/sizereferencetileprovider.cpp new file mode 100644 index 0000000000..9343d5a99e --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/sizereferencetileprovider.cpp @@ -0,0 +1,121 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace { + struct [[codegen::Dictionary(SizeReferenceTileProvider)]] Parameters { + std::optional> radii; + }; +#include "sizereferencetileprovider_codegen.cpp" +} // namespace + +namespace openspace::globebrowsing { + +SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& dictionary) + : TextTileProvider(tileTextureInitData(layergroupid::GroupID::ColorLayers, false)) +{ + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + font = global::fontManager->font("Mono", static_cast(fontSize)); + + if (p.radii.has_value()) { + if (std::holds_alternative(*p.radii)) { + _ellipsoid = std::get(*p.radii); + } + else { + const double r = std::get(*p.radii); + _ellipsoid = glm::dvec3(r, r, r); + } + } +} + +Tile SizeReferenceTileProvider::tile(const TileIndex& tileIndex) { + ZoneScoped + + const GeodeticPatch patch(tileIndex); + const bool aboveEquator = patch.isNorthern(); + const double lat = aboveEquator ? patch.minLat() : patch.maxLat(); + const double lon1 = patch.minLon(); + const double lon2 = patch.maxLon(); + int l = static_cast(_ellipsoid.longitudalDistance(lat, lon1, lon2)); + + const bool useKm = l > 9999; + if (useKm) { + l /= 1000; + } + l = static_cast(std::round(l)); + if (useKm) { + l *= 1000; + } + double tileLongitudalLength = l; + + const char* unit; + if (tileLongitudalLength > 9999) { + tileLongitudalLength *= 0.001; + unit = "km"; + } + else { + unit = "m"; + } + + std::string text = fmt::format(" {:.0f} {:s}", tileLongitudalLength, unit); + glm::vec2 textPosition = { + 0.f, + aboveEquator ? + fontSize / 2.f : + initData.dimensions.y - 3.f * fontSize / 2.f + }; + + return TextTileProvider::renderTile(tileIndex, text, textPosition, glm::vec4(1.f)); +} + +Tile::Status SizeReferenceTileProvider::tileStatus(const TileIndex& index) { + return Tile::Status::OK; +} + +TileDepthTransform SizeReferenceTileProvider::depthTransform() { + return { 0.f, 1.f }; +} + +void SizeReferenceTileProvider::update() {} + +int SizeReferenceTileProvider::maxLevel() { + return 1337; // unlimited +} + +float SizeReferenceTileProvider::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/sizereferencetileprovider.h b/modules/globebrowsing/src/tileprovider/sizereferencetileprovider.h new file mode 100644 index 0000000000..499f8c2a53 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/sizereferencetileprovider.h @@ -0,0 +1,49 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__SIZEREFERENCETILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__SIZEREFERENCETILEPROVIDER___H__ + +#include + +namespace openspace::globebrowsing { + +class SizeReferenceTileProvider : public TextTileProvider { +public: + SizeReferenceTileProvider(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + Ellipsoid _ellipsoid; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__SIZEREFERENCETILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/temporaltileprovider.cpp b/modules/globebrowsing/src/tileprovider/temporaltileprovider.cpp new file mode 100644 index 0000000000..bc71de7b14 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/temporaltileprovider.cpp @@ -0,0 +1,822 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* KeyBasePath = "BasePath"; + + constexpr const char* TimePlaceholder = "${OpenSpaceTimeId}"; + + constexpr openspace::properties::Property::PropertyInfo FilePathInfo = { + "FilePath", + "File Path", + "This is the path to the XML configuration file that describes the temporal tile " + "information." + }; + + constexpr openspace::properties::Property::PropertyInfo UseFixedTimeInfo = { + "UseFixedTime", + "Use Fixed Time", + "If this value is enabled, the time-varying timevarying dataset will always use " + "the time that is specified in the 'FixedTime' property, rather than using the " + "actual time from OpenSpace" + }; + + constexpr openspace::properties::Property::PropertyInfo FixedTimeInfo = { + "FixedTime", + "Fixed Time", + "If the 'UseFixedTime' is enabled, this time will be used instead of the actual " + "time taken from OpenSpace for the displayed tiles." + }; + + struct [[codegen::Dictionary(TemporalTileProvider)]] Parameters { + // [[codegen::verbatim(UseFixedTimeInfo.description)]] + std::optional useFixedTime; + + // [[codegen::verbatim(FixedTimeInfo.description)]] + std::optional fixedTime; + + enum class Mode { + Prototyped, + Folder + }; + // The mode that his temporal tile provider operates in. In the `Prototyped` mode, + // a given start and end time, temporal resolution, and perscriptive time format + // is used to generate the information used by GDAL to access the data. In the + // `folder` method, a folder and a time format is provided and each file in the + // folder is scanned using the time format instead + Mode mode; + + struct Prototyped { + struct Time { + // The (inclusive) starting time of the temporal image range + std::string start; + // The (inclusive) ending time of the temporal image range + std::string end; + }; + // The starting and ending times for the range of values + Time time; + + // The temporal resolution between each image + std::string temporalResolution; + + // The specification of the date format that is used in the tile provider. The + // time format must be specified in a manner appropriate for the SPICE + // function `timout_c`. + // https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html + std::string timeFormat; + + // The text that will be used as the prototype to generate the data to load + // the image layer. Any occurance of `${OpenSpaceTimeId}` in this prototype + // is replaced with the current date according to the remaining information + // such as the resolution and the format and the resulting text is used to + // load the corresponding images + std::string prototype; + + }; + std::optional prototyped; + + struct Folder { + // The folder that is parsed for files. Every file in the provided directory + // is checked against the provided format and added if it adheres to said + // format + std::filesystem::path folder [[codegen::directory()]]; + + // The format of files that is pared in the provided folder. The format string + // has to be compatible to the C++ function get_time. + // https://en.cppreference.com/w/cpp/io/manip/get_time + std::string format; + }; + std::optional folder; + + // Determines whether this tile provider should interpolate between two adjacent + // layers + std::optional interpolation; + + // If provided, the tile provider will use this color map to convert a greyscale + // image to color + std::optional colormap; + }; +#include "temporaltileprovider_codegen.cpp" + + std::string_view timeStringify(const std::string& format, const openspace::Time& t) { + ZoneScoped + + constexpr const int BufferSize = 64; + ghoul_assert(format.size() < BufferSize, "Format string too long"); + + using namespace openspace; + + char FormatBuf[BufferSize]; + std::memset(FormatBuf, '\0', BufferSize); + std::memcpy(FormatBuf, format.c_str(), format.size()); + + char* OutBuf = reinterpret_cast( + global::memoryManager->TemporaryMemory.allocate(BufferSize) + ); + std::memset(OutBuf, '\0', BufferSize); + + const double time = t.j2000Seconds(); + SpiceManager::ref().dateFromEphemerisTime(time, OutBuf, BufferSize, FormatBuf); + return std::string_view(OutBuf, format.size()); + } +} // namespace + +namespace openspace::globebrowsing { + +TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary) + : _initDict(dictionary) + , _useFixedTime(UseFixedTimeInfo, false) + , _fixedTime(FixedTimeInfo) +{ + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + // Make sure that the user provided the data that they requested. The mode parameter + // is a required one and these two if statements tie the table requirement to the mode + if (p.mode == Parameters::Mode::Folder && !p.folder.has_value()) { + throw ghoul::RuntimeError( + "When selecting the `Folder` mode, a `Folder` table must be specified" + ); + } + if (p.mode == Parameters::Mode::Prototyped && !p.prototyped.has_value()) { + throw ghoul::RuntimeError( + "When selecting the `Prototyped` mode, a `Prototyped` table must be specified" + ); + } + + _useFixedTime = p.useFixedTime.value_or(_useFixedTime); + _useFixedTime.onChange([this]() { _fixedTimeDirty = true; }); + addProperty(_useFixedTime); + + _fixedTime = p.fixedTime.value_or(_fixedTime); + _fixedTime.onChange([this]() { _fixedTimeDirty = true; }); + addProperty(_fixedTime); + + _colormap = p.colormap.value_or(_colormap); + + if (p.prototyped.has_value()) { + _mode = Mode::Prototype; + + Time start = Time(p.prototyped->time.start); + Time end = Time::now(); + _prototyped.startTimeJ2000 = start.j2000Seconds(); + _prototyped.endTimeJ2000 = Time(p.prototyped->time.end).j2000Seconds(); + if (p.prototyped->time.end == "Yesterday") { + end.advanceTime(-60.0 * 60.0 * 24.0); // Go back one day + } + else if (p.prototyped->time.end != "Today") { + end.setTime(p.prototyped->time.end); + } + + try { + _prototyped.timeQuantizer.setStartEndRange( + std::string(start.ISO8601()), + std::string(end.ISO8601()) + ); + _prototyped.timeQuantizer.setResolution(p.prototyped->temporalResolution); + _prototyped.temporalResolution = p.prototyped->temporalResolution; + } + catch (const ghoul::RuntimeError& e) { + throw ghoul::RuntimeError(fmt::format( + "Could not create time quantizer for Temporal GDAL dataset. {}", e.message + )); + } + + if (p.prototyped->timeFormat.size() >= 64) { + throw ghoul::RuntimeError(fmt::format( + "Time format string '{}' too large. Maximum length of 64 is allowed", + p.prototyped->timeFormat + )); + } + _prototyped.timeFormat = p.prototyped->timeFormat; + _prototyped.prototype = p.prototyped->prototype; + } + + if (p.folder.has_value()) { + _mode = Mode::Folder; + + _folder.folder = p.folder->folder; + _folder.format = p.folder->format; + + namespace fs = std::filesystem; + for (const fs::directory_entry& path : fs::directory_iterator(_folder.folder)) { + if (!path.is_regular_file()) { + continue; + } + + std::string file = path.path().filename().string(); + std::istringstream ss(file); + + std::tm tm = {}; + ss >> std::get_time(&tm, p.folder->format.c_str()); + if (!ss.fail()) { + std::string date = fmt::format( + "{}-{}-{} {}:{}:{}", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec + ); + + double et = SpiceManager::ref().ephemerisTimeFromDate(date); + _folder.files.push_back({ et, path.path().string() }); + } + } + + using K = double; + using V = std::string; + std::sort( + _folder.files.begin(), + _folder.files.end(), + [](const std::pair& lhs, const std::pair& rhs) { + return lhs.first < rhs.first; + } + ); + } + + _isInterpolating = p.interpolation.value_or(_isInterpolating); + if (_isInterpolating) { + _interpolateTileProvider = std::make_unique(dictionary); + _interpolateTileProvider->initialize(); + _interpolateTileProvider->colormap = + ghoul::io::TextureReader::ref().loadTexture(_colormap, 1); + _interpolateTileProvider->colormap->uploadTexture(); + _interpolateTileProvider->colormap->setFilter( + ghoul::opengl::Texture::FilterMode::AnisotropicMipMap + ); + } +} + +Tile TemporalTileProvider::tile(const TileIndex& tileIndex) { + ZoneScoped + if (!_currentTileProvider) { + update(); + } + + return _currentTileProvider->tile(tileIndex); +} + +Tile::Status TemporalTileProvider::tileStatus(const TileIndex& index) { + if (!_currentTileProvider) { + update(); + } + + return _currentTileProvider->tileStatus(index); +} + +TileDepthTransform TemporalTileProvider::depthTransform() { + if (!_currentTileProvider) { + update(); + } + + return _currentTileProvider->depthTransform(); +} + +void TemporalTileProvider::update() { + TileProvider* newCurr = nullptr; + try { + if (_useFixedTime && !_fixedTime.value().empty()) { + if (_fixedTimeDirty) { + std::string fixedTime = _fixedTime.value(); + double et = SpiceManager::ref().ephemerisTimeFromDate(fixedTime); + newCurr = retrieveTileProvider(Time(et)); + _fixedTimeDirty = false; + } + } + else { + newCurr = tileProvider(global::timeManager->time()); + } + } + catch (const ghoul::RuntimeError& e) { + LERRORC("TemporalTileProvider", e.message); + } + + if (newCurr) { + _currentTileProvider = newCurr; + } + if (_currentTileProvider) { + _currentTileProvider->update(); + } +} + +void TemporalTileProvider::reset() { + for (std::pair& it : _tileProviderMap) { + it.second.reset(); + } +} + +int TemporalTileProvider::maxLevel() { + if (!_currentTileProvider) { + update(); + } + return _currentTileProvider->maxLevel(); +} + +float TemporalTileProvider::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +DefaultTileProvider TemporalTileProvider::createTileProvider( + std::string_view timekey) const +{ + ZoneScoped + + std::string value; + switch (_mode) { + case Mode::Prototype: { + static const std::vector IgnoredTokens = { + // From: http://www.gdal.org/frmt_wms.html + "${x}", "${y}", "${z}", "${version}" "${format}", "${layer}" + }; + + value = _prototyped.prototype; + while (true) { + const size_t pos = value.find(TimePlaceholder); + + if (pos == std::string::npos) { + break; + } + + const size_t numChars = std::string_view(TimePlaceholder).size(); + value = value.replace(pos, numChars, timekey); + } + + value = FileSys.expandPathTokens(std::move(value), IgnoredTokens).string(); + break; + } + case Mode::Folder: { + value = std::string(timekey); + break; + } + } + + ghoul::Dictionary dict = _initDict; + dict.setValue("FilePath", value); + return DefaultTileProvider(dict); +} + +DefaultTileProvider* TemporalTileProvider::retrieveTileProvider(const Time& t) { + ZoneScoped + + const double time = t.j2000Seconds(); + if (const auto it = _tileProviderMap.find(time); it != _tileProviderMap.end()) { + return &it->second; + } + + std::string_view timeStr = [this, time]() { + switch (_mode) { + case Mode::Prototype: + return timeStringify(_prototyped.timeFormat, Time(time)); + case Mode::Folder: { + // Yes this will have to be done twice since we do the check previously + // but it is only happening when the images change, so I think that should + // be fine + auto it = std::lower_bound( + _folder.files.cbegin(), + _folder.files.cend(), + time, + [](const std::pair& p, double time) { + return p.first < time; + } + ); + return std::string_view(it->second); + } + default: throw ghoul::MissingCaseException(); + }; + }(); + + DefaultTileProvider tileProvider = createTileProvider(timeStr); + tileProvider.initialize(); + + auto it = _tileProviderMap.insert({ time, std::move(tileProvider) }); + return &it.first->second; +} + +template <> +TileProvider* +TemporalTileProvider::tileProvider( + const Time& time) +{ + // Find the most current image that matches the current time. We can't pass the `time` + // variable into the retrieveTileProvider function as it would generate a new + // non-existing TileProvider for every new frame + using It = std::vector>::const_iterator; + It it = std::lower_bound( + _folder.files.begin(), + _folder.files.end(), + time.j2000Seconds(), + [](const std::pair& p, double t) { + return p.first < t; + } + ); + + if (it != _folder.files.begin()) { + it -= 1; + } + + double t = it->first; + return retrieveTileProvider(Time(t)); +} + +template <> +TileProvider* +TemporalTileProvider::tileProvider( + const Time& time) +{ + using It = std::vector>::const_iterator; + It next = std::lower_bound( + _folder.files.begin(), + _folder.files.end(), + time.j2000Seconds(), + [](const std::pair& p, double t) { + return p.first < t; + } + ); + + It curr = next != _folder.files.begin() ? next - 1 : next; + It nextNext = next != _folder.files.end() ? next + 1 : curr; + It prev = curr != _folder.files.begin() ? curr - 1 : curr; + + _interpolateTileProvider->t1 = retrieveTileProvider(Time(curr->first)); + _interpolateTileProvider->t2 = retrieveTileProvider(Time(next->first)); + _interpolateTileProvider->future = retrieveTileProvider(Time(nextNext->first)); + _interpolateTileProvider->before = retrieveTileProvider(Time(prev->first)); + + _interpolateTileProvider->factor = static_cast( + (time.j2000Seconds() - curr->first) / + (next->first - curr->first) + ); + + if (_interpolateTileProvider->factor > 1.f) { + _interpolateTileProvider->factor = 1.f; + } + + return _interpolateTileProvider.get(); +} + +template <> +TileProvider* +TemporalTileProvider::tileProvider( + const Time& time) +{ + Time tCopy(time); + if (_prototyped.timeQuantizer.quantize(tCopy, true)) { + return retrieveTileProvider(tCopy); + } + else { + return nullptr; + } +} + +template <> +TileProvider* +TemporalTileProvider::tileProvider( + const Time& time) +{ + Time tCopy(time); + if (!_prototyped.timeQuantizer.quantize(tCopy, true)) { + return nullptr; + } + + Time nextTile = tCopy; + Time nextNextTile = tCopy; + Time prevTile = tCopy; + Time secondToLast = Time(_prototyped.endTimeJ2000); + Time secondToFirst = Time(_prototyped.startTimeJ2000); + + _interpolateTileProvider->t1 = retrieveTileProvider(tCopy); + + // if the images are for each hour + if (_prototyped.temporalResolution == "1h") { + constexpr const int Hour = 60 * 60; + // the second tile to interpolate between + nextTile.advanceTime(Hour); + // the tile after the second tile + nextNextTile.advanceTime(2 * Hour); + // the tile before the first tile + prevTile.advanceTime(-Hour + 1); + // to make sure that an image outside the dataset is not searched for both + // ends of the dataset are calculated + secondToLast.advanceTime(-Hour); + secondToFirst.advanceTime(Hour); + } + // if the images are for each month + if (_prototyped.temporalResolution == "1M") { + constexpr const int Day = 24 * 60 * 60; + + // the second tile to interpolate between + nextTile.advanceTime(32 * Day); + // the tile after the second tile + nextNextTile.advanceTime(64 * Day); + // the tile before the first tile + prevTile.advanceTime(-2 * Day); + // to make sure that an image outside the dataset is not searched for both + // ends of the dataset are calculated + secondToLast.advanceTime(-2 * Day); + secondToFirst.advanceTime(32 * Day); + + // since months vary in length the time is set to the first of each month + auto setToFirstOfMonth = [](Time& t) { + std::string timeString = std::string(t.ISO8601()); + timeString[8] = '0'; + timeString[9] = '1'; + t.setTime(timeString); + }; + + setToFirstOfMonth(nextTile); + setToFirstOfMonth(nextNextTile); + setToFirstOfMonth(prevTile); + setToFirstOfMonth(secondToLast); + setToFirstOfMonth(secondToFirst); + } + + // the necessary tile providers are loaded if they exist within the timespan + if (secondToLast.j2000Seconds() > time.j2000Seconds() && + secondToFirst.j2000Seconds() < time.j2000Seconds()) + { + _interpolateTileProvider->t2 = retrieveTileProvider(nextTile); + _interpolateTileProvider->future = retrieveTileProvider(nextNextTile); + _interpolateTileProvider->before = retrieveTileProvider(prevTile); + } + else if (secondToLast.j2000Seconds() < time.j2000Seconds() && + _prototyped.endTimeJ2000 > time.j2000Seconds()) + { + _interpolateTileProvider->t2 = retrieveTileProvider(nextTile); + _interpolateTileProvider->future = retrieveTileProvider(tCopy); + _interpolateTileProvider->before = retrieveTileProvider(prevTile); + } + else if (secondToFirst.j2000Seconds() > time.j2000Seconds() && + _prototyped.startTimeJ2000 < time.j2000Seconds()) + { + _interpolateTileProvider->t2 = retrieveTileProvider(nextTile); + _interpolateTileProvider->future = retrieveTileProvider(nextNextTile); + _interpolateTileProvider->before = retrieveTileProvider(tCopy); + } + else { + _interpolateTileProvider->t2 = retrieveTileProvider(tCopy); + _interpolateTileProvider->future = retrieveTileProvider(tCopy); + _interpolateTileProvider->before = retrieveTileProvider(tCopy); + } + _interpolateTileProvider->factor = static_cast( + (time.j2000Seconds() - tCopy.j2000Seconds()) / + (nextTile.j2000Seconds() - tCopy.j2000Seconds()) + ); + + if (_interpolateTileProvider->factor > 1.f) { + _interpolateTileProvider->factor = 1.f; + } + return _interpolateTileProvider.get(); +} + +TileProvider* TemporalTileProvider::tileProvider(const Time& time) { + if (_isInterpolating) { + switch (_mode) { + case Mode::Folder: + return tileProvider(time); + case Mode::Prototype: + return tileProvider(time); + default: throw ghoul::MissingCaseException(); + } + } + else { + switch (_mode) { + case Mode::Folder: + return tileProvider(time); + case Mode::Prototype: + return tileProvider(time); + default: throw ghoul::MissingCaseException(); + } + } +} + +TemporalTileProvider::InterpolateTileProvider::InterpolateTileProvider( + const ghoul::Dictionary&) +{ + ZoneScoped + + glGenFramebuffers(1, &fbo); + glGenVertexArrays(1, &vaoQuad); + glGenBuffers(1, &vboQuad); + glBindVertexArray(vaoQuad); + glBindBuffer(GL_ARRAY_BUFFER, vboQuad); + // Quad for fullscreen with vertex (xy) and texture coordinates (uv) + const GLfloat vertexData[] = { + // x y u v + -1.f, -1.f, 0.f, 0.f, + 1.f, 1.f, 1.f, 1.f, + -1.f, 1.f, 0.f, 1.f, + -1.f, -1.f, 0.f, 0.f, + 1.f, -1.f, 1.f, 0.f, + 1.f, 1.f, 1.f, 1.f + }; + glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); + // vertex coordinates at location 0 + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(0); + // texture coords at location 1 + glVertexAttribPointer( + 1, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(GLfloat), + reinterpret_cast(2 * sizeof(GLfloat)) + ); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + shaderProgram = global::renderEngine->buildRenderProgram( + "InterpolatingProgram", + absPath("${MODULE_GLOBEBROWSING}/shaders/interpolate_vs.glsl"), + absPath("${MODULE_GLOBEBROWSING}/shaders/interpolate_fs.glsl") + ); +} + +TemporalTileProvider::InterpolateTileProvider::~InterpolateTileProvider() { + glDeleteFramebuffers(1, &fbo); + glDeleteBuffers(1, &vboQuad); + glDeleteVertexArrays(1, &vaoQuad); +} + +Tile TemporalTileProvider::InterpolateTileProvider::tile(const TileIndex& tileIndex) { + ZoneScoped + TracyGpuZone("tile"); + + // prev and next are the two tiles to interpolate between + Tile prev = t1->tile(tileIndex); + Tile next = t2->tile(tileIndex); + // the tile before and the tile after the interpolation interval are loaded so the + // interpolation goes smoother + Tile prevprev = before->tile(tileIndex); + Tile nextnext = future->tile(tileIndex); + cache::ProviderTileKey key = { tileIndex, uniqueIdentifier }; + + if (!prev.texture || !next.texture) { + return Tile{ nullptr, std::nullopt, Tile::Status::Unavailable }; + } + + // The data for initializing the texture + TileTextureInitData initData( + prev.texture->dimensions().x, + prev.texture->dimensions().y, + prev.texture->dataType(), + prev.texture->format(), + TileTextureInitData::PadTiles::No, + TileTextureInitData::ShouldAllocateDataOnCPU::No + ); + + // Check if a tile exists for the given key in the tileCache + // Initializing the tile that will contian the interpolated texture + Tile ourTile; + // The texture that will contain the interpolated image + ghoul::opengl::Texture* writeTexture; + cache::MemoryAwareTileCache* tileCache = + global::moduleEngine->module()->tileCache(); + if (tileCache->exist(key)) { + ourTile = tileCache->get(key); + writeTexture = ourTile.texture; + } + else { + // Create a texture with the initialization data + writeTexture = tileCache->texture(initData); + ourTile = Tile{ writeTexture, std::nullopt, Tile::Status::OK }; + tileCache->put(key, initData.hashKey, ourTile); + } + + // Saves current state + GLint currentFBO; + GLint viewport[4]; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, ¤tFBO); + global::renderEngine->openglStateCache().viewport(viewport); + // Bind render texture to FBO + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, *writeTexture, 0); + glDisable(GL_BLEND); + GLenum textureBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, textureBuffers); + + // Setup our own viewport settings + GLsizei w = static_cast(writeTexture->width()); + GLsizei h = static_cast(writeTexture->height()); + glViewport(0, 0, w, h); + glClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + GLint id; + glGetIntegerv(GL_CURRENT_PROGRAM, &id); + // Activate shader and bind uniforms + shaderProgram->activate(); + shaderProgram->setUniform("blendFactor", factor); + + // The texture that will give the color for the interpolated texture + ghoul::opengl::TextureUnit colormapUnit; + colormapUnit.activate(); + colormap->bind(); + shaderProgram->setUniform("colormapTexture", colormapUnit); + + ghoul::opengl::TextureUnit prevUnit; + prevUnit.activate(); + prev.texture->bind(); + shaderProgram->setUniform("prevTexture", prevUnit); + + ghoul::opengl::TextureUnit nextUnit; + nextUnit.activate(); + next.texture->bind(); + shaderProgram->setUniform("nextTexture", nextUnit); + + // Render to the texture + glBindVertexArray(vaoQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); // 2 triangles + // Deactivate shader program (when rendering is completed) + shaderProgram->deactivate(); + glUseProgram(id); + // Restores system state + glBindFramebuffer(GL_FRAMEBUFFER, currentFBO); + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + // Restores OpenGL Rendering State + global::renderEngine->openglStateCache().resetColorState(); + global::renderEngine->openglStateCache().resetBlendState(); + global::renderEngine->openglStateCache().resetDepthState(); + global::renderEngine->openglStateCache().resetPolygonAndClippingState(); + global::renderEngine->openglStateCache().resetViewportState(); + + return ourTile; +} + +Tile::Status TemporalTileProvider::InterpolateTileProvider::tileStatus( + const TileIndex& index) +{ + return std::min(t1->tileStatus(index), t2->tileStatus(index)); +} + +TileDepthTransform TemporalTileProvider::InterpolateTileProvider::depthTransform() { + return t1->depthTransform(); +} + +void TemporalTileProvider::InterpolateTileProvider::update() { + t1->update(); + t2->update(); + before->update(); + future->update(); +} + +void TemporalTileProvider::InterpolateTileProvider::reset() { + t1->reset(); + t2->reset(); + before->reset(); + future->reset(); +} + +int TemporalTileProvider::InterpolateTileProvider::maxLevel() { + return glm::min(t1->maxLevel(), t2->maxLevel()); +} + +float TemporalTileProvider::InterpolateTileProvider::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/temporaltileprovider.h b/modules/globebrowsing/src/tileprovider/temporaltileprovider.h new file mode 100644 index 0000000000..95dfc1f128 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/temporaltileprovider.h @@ -0,0 +1,130 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TEMPORALTILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TEMPORALTILEPROVIDER___H__ + +#include + +#include +#include + +namespace openspace::globebrowsing { + +/** + * Provide Tiles from web map services that have temporal resolution. + * + * TemporalTileProviders are instantiated using a ghoul::Dictionary, and must define a + * filepath to a Openspace Temporal dataset description file. This is an xml-file that + * defines the same meta data as the GDAL wms description + * (http://www.gdal.org/frmt_wms.html), but augmented with some extra tags describing the + * temporal properties of the dataset. See + * TemporalTileProvider::TemporalXMLTags + */ +class TemporalTileProvider : public TileProvider { +public: + TemporalTileProvider(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + enum class Mode { + Prototype, + Folder + }; + + struct InterpolateTileProvider : public TileProvider { + InterpolateTileProvider(const ghoul::Dictionary&); + virtual ~InterpolateTileProvider(); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + + TileProvider* before = nullptr; + TileProvider* t1 = nullptr; + TileProvider* t2 = nullptr; + TileProvider* future = nullptr; + float factor = 1.f; + GLuint vaoQuad = 0; + GLuint vboQuad = 0; + GLuint fbo = 0; + std::unique_ptr shaderProgram; + std::unique_ptr colormap; + }; + + DefaultTileProvider createTileProvider(std::string_view timekey) const; + DefaultTileProvider* retrieveTileProvider(const Time& t); + + template + TileProvider* tileProvider(const Time& time); + + TileProvider* tileProvider(const Time& time); + + Mode _mode; + + struct { + double startTimeJ2000 = 0.0; + double endTimeJ2000 = 0.0; + + std::string temporalResolution; + std::string timeFormat; + TimeQuantizer timeQuantizer; + std::string prototype; + } _prototyped; + + struct { + std::filesystem::path folder; + std::string format; + + std::vector> files; + } _folder; + + ghoul::Dictionary _initDict; + properties::BoolProperty _useFixedTime; + properties::StringProperty _fixedTime; + bool _fixedTimeDirty = true; + + TileProvider* _currentTileProvider = nullptr; + std::unordered_map _tileProviderMap; + + bool _isInterpolating = false; + + std::string _colormap; + std::unique_ptr _interpolateTileProvider; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TEMPORALTILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/texttileprovider.cpp b/modules/globebrowsing/src/tileprovider/texttileprovider.cpp new file mode 100644 index 0000000000..aab99b3b8a --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/texttileprovider.cpp @@ -0,0 +1,109 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openspace::globebrowsing { + +TextTileProvider::TextTileProvider(TileTextureInitData initData_, size_t fontSize_) + : initData(std::move(initData_)) + , fontSize(fontSize_) +{ + ZoneScoped + + tileCache = global::moduleEngine->module()->tileCache(); +} + +TextTileProvider::~TextTileProvider() {} + +void TextTileProvider::internalInitialize() { + ZoneScoped + + font = global::fontManager->font("Mono", static_cast(fontSize)); + fontRenderer = ghoul::fontrendering::FontRenderer::createDefault(); + fontRenderer->setFramebufferSize(glm::vec2(initData.dimensions)); + glGenFramebuffers(1, &fbo); +} + +void TextTileProvider::internalDeinitialize() { + glDeleteFramebuffers(1, &fbo); +} + +Tile TextTileProvider::renderTile(const TileIndex& tileIndex, const std::string& text, + const glm::vec2& position, const glm::vec4& color) +{ + ZoneScoped + TracyGpuZone("tile") + + cache::ProviderTileKey key = { tileIndex, uniqueIdentifier }; + Tile tile = tileCache->get(key); + if (!tile.texture) { + ghoul::opengl::Texture* texture = tileCache->texture(initData); + + // Keep track of defaultFBO and viewport to be able to reset state when done + GLint defaultFBO = global::renderEngine->openglStateCache().defaultFramebuffer(); + + // Render to texture + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + *texture, + 0 + ); + + GLsizei w = static_cast(texture->width()); + GLsizei h = static_cast(texture->height()); + glViewport(0, 0, w, h); + glClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + + fontRenderer->render(*font, position, text, color); + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + global::renderEngine->openglStateCache().resetViewportState(); + + tile = Tile{ texture, std::nullopt, Tile::Status::OK }; + tileCache->put(key, initData.hashKey, tile); + } + return tile; +} + +void TextTileProvider::reset() { + ZoneScoped + + tileCache->clear(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/texttileprovider.h b/modules/globebrowsing/src/tileprovider/texttileprovider.h new file mode 100644 index 0000000000..5f3b0816a1 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/texttileprovider.h @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TEXTTILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TEXTTILEPROVIDER___H__ + +#include + +namespace openspace::globebrowsing { + +class TextTileProvider : public TileProvider { +public: + TextTileProvider(TileTextureInitData initData, size_t fontSize = 48); + virtual ~TextTileProvider(); + + void reset() override; + +protected: + Tile renderTile(const TileIndex& tileIndex, const std::string& text, + const glm::vec2& position, const glm::vec4& color); + + const TileTextureInitData initData; + + std::unique_ptr fontRenderer; + std::shared_ptr font; + size_t fontSize = 0; + + GLuint fbo = 0; + + cache::MemoryAwareTileCache* tileCache; + +private: + void internalInitialize() override final; + void internalDeinitialize() override final; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TEXTTILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/tileindextileprovider.cpp b/modules/globebrowsing/src/tileprovider/tileindextileprovider.cpp new file mode 100644 index 0000000000..30489b9500 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileindextileprovider.cpp @@ -0,0 +1,65 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +namespace openspace::globebrowsing { + +TileIndexTileProvider::TileIndexTileProvider(const ghoul::Dictionary&) + : TextTileProvider(tileTextureInitData(layergroupid::GroupID::ColorLayers, false)) +{} + +Tile TileIndexTileProvider::tile(const TileIndex& tileIndex) { + ZoneScoped + std::string text = fmt::format( + "level: {}\nx: {}\ny: {}", tileIndex.level, tileIndex.x, tileIndex.y + ); + glm::vec2 textPosition = glm::vec2( + initData.dimensions.x / 4 - + (initData.dimensions.x / 32) * log10(1 << tileIndex.level), + initData.dimensions.y / 2 + fontSize + ); + + return TextTileProvider::renderTile(tileIndex, text, textPosition, glm::vec4(1.f)); +} + +Tile::Status TileIndexTileProvider::tileStatus(const TileIndex&) { + return Tile::Status::OK; +} + +TileDepthTransform TileIndexTileProvider::depthTransform() { + return { 0.f, 1.f }; +} + +void TileIndexTileProvider::update() {} + +int TileIndexTileProvider::maxLevel() { + return 1337; // unlimited +} + +float TileIndexTileProvider::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/tests/test_temporaltileprovider.cpp b/modules/globebrowsing/src/tileprovider/tileindextileprovider.h similarity index 71% rename from tests/test_temporaltileprovider.cpp rename to modules/globebrowsing/src/tileprovider/tileindextileprovider.h index bbe6ca740b..0913b59fe3 100644 --- a/tests/test_temporaltileprovider.cpp +++ b/modules/globebrowsing/src/tileprovider/tileindextileprovider.h @@ -22,14 +22,25 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include "catch2/catch.hpp" +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEINDEXTILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEINDEXTILEPROVIDER___H__ -#include -#include -#include +#include -namespace { - constexpr const char* fileName = "data/scene/debugglobe/map_service_configs/" - "VIIRS_SNPP_CorrectedReflectance_TrueColor_temporal.xml"; -} // namespace +namespace openspace::globebrowsing { +class TileIndexTileProvider : public TextTileProvider { +public: + TileIndexTileProvider(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEINDEXTILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/tileprovider.cpp b/modules/globebrowsing/src/tileprovider/tileprovider.cpp new file mode 100644 index 0000000000..cc7ea99e24 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileprovider.cpp @@ -0,0 +1,238 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpl_minixml.h" + +namespace openspace::globebrowsing { + +namespace { + +std::unique_ptr DefaultTileTexture; +Tile DefaultTile = Tile { nullptr, std::nullopt, Tile::Status::Unavailable }; + +constexpr const char* KeyFilePath = "FilePath"; + +} // namespace + +unsigned int TileProvider::NumTileProviders = 0; + +std::unique_ptr TileProvider::createFromDictionary( + layergroupid::TypeID layerTypeID, + const ghoul::Dictionary& dictionary) +{ + ZoneScoped + + const char* type = layergroupid::LAYER_TYPE_NAMES[static_cast(layerTypeID)]; + auto factory = FactoryManager::ref().factory(); + TileProvider* result = factory->create(type, dictionary); + return std::unique_ptr(result); +} + +void TileProvider::initializeDefaultTile() { + ZoneScoped + + ghoul_assert(!DefaultTile.texture, "Default tile should not have been created"); + using namespace ghoul::opengl; + + // Create pixel data + TileTextureInitData initData( + 8, + 8, + GL_UNSIGNED_BYTE, + Texture::Format::RGBA, + TileTextureInitData::PadTiles::No, + TileTextureInitData::ShouldAllocateDataOnCPU::Yes + ); + char* pixels = new char[initData.totalNumBytes]; + memset(pixels, 0, initData.totalNumBytes * sizeof(char)); + + // Create ghoul texture + DefaultTileTexture = std::make_unique(initData.dimensions, GL_TEXTURE_2D); + DefaultTileTexture->setDataOwnership(Texture::TakeOwnership::Yes); + DefaultTileTexture->setPixelData(pixels); + DefaultTileTexture->uploadTexture(); + DefaultTileTexture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + + // Create tile + DefaultTile = Tile{ DefaultTileTexture.get(), std::nullopt, Tile::Status::OK }; +} + +void TileProvider::deinitializeDefaultTile() { + DefaultTileTexture = nullptr; +} + +TileProvider::TileProvider() : properties::PropertyOwner({ "TileProvider" }) {} + +void TileProvider::initialize() { + ZoneScoped + + ghoul_assert(!isInitialized, "TileProvider can only be initialized once."); + + if (TileProvider::NumTileProviders > + static_cast(std::numeric_limits::max()) - 1) + { + LERRORC( + "TileProvider", + "Number of tile providers exceeds 65535. Something will break soon" + ); + TileProvider::NumTileProviders = 0; + } + uniqueIdentifier = static_cast(TileProvider::NumTileProviders++); + if (TileProvider::NumTileProviders == std::numeric_limits::max()) { + --TileProvider::NumTileProviders; + return; + } + + internalInitialize(); + isInitialized = true; +} + +void TileProvider::deinitialize() { + ZoneScoped + + internalDeinitialize(); +} + +void TileProvider::internalInitialize() {} +void TileProvider::internalDeinitialize() {} + +ChunkTile TileProvider::chunkTile(TileIndex tileIndex, int parents, int maxParents) { + ZoneScoped + + ghoul_assert(isInitialized, "TileProvider was not initialized."); + + auto ascendToParent = [](TileIndex& ti, TileUvTransform& uv) { + uv.uvOffset *= 0.5; + uv.uvScale *= 0.5; + + uv.uvOffset += ti.positionRelativeParent(); + + ti.x /= 2; + ti.y /= 2; + ti.level--; + }; + + TileUvTransform uvTransform = { glm::vec2(0.f, 0.f), glm::vec2(1.f, 1.f) }; + + // Step 1. Traverse 0 or more parents up the chunkTree as requested by the caller + for (int i = 0; i < parents && tileIndex.level > 1; i++) { + ascendToParent(tileIndex, uvTransform); + } + maxParents -= parents; + + // Step 2. Traverse 0 or more parents up the chunkTree to make sure we're inside + // the range of defined data. + int maximumLevel = maxLevel(); + while (tileIndex.level > maximumLevel) { + ascendToParent(tileIndex, uvTransform); + maxParents--; + } + if (maxParents < 0) { + return ChunkTile { Tile(), uvTransform, TileDepthTransform() }; + } + + // Step 3. Traverse 0 or more parents up the chunkTree until we find a chunk that + // has a loaded tile ready to use. + while (tileIndex.level > 1) { + Tile t = tile(tileIndex); + if (t.status != Tile::Status::OK) { + if (--maxParents < 0) { + return ChunkTile { Tile(), uvTransform, TileDepthTransform() }; + } + ascendToParent(tileIndex, uvTransform); + } + else { + return ChunkTile { std::move(t), uvTransform, TileDepthTransform() }; + } + } + + return ChunkTile { Tile(), uvTransform, TileDepthTransform() }; +} + +ChunkTilePile TileProvider::chunkTilePile(TileIndex tileIndex, int pileSize) { + ZoneScoped + + ghoul_assert(isInitialized, "TileProvider was not initialized."); + ghoul_assert(pileSize >= 0, "pileSize must be positive"); + + ChunkTilePile chunkTilePile; + std::fill(chunkTilePile.begin(), chunkTilePile.end(), std::nullopt); + for (int i = 0; i < pileSize; ++i) { + chunkTilePile[i] = chunkTile(tileIndex, i); + if (chunkTilePile[i]->tile.status == Tile::Status::Unavailable) { + if (i == 0) { + // First iteration + chunkTilePile[i]->tile = DefaultTile; + chunkTilePile[i]->uvTransform.uvOffset = { 0.f, 0.f }; + chunkTilePile[i]->uvTransform.uvScale = { 1.f, 1.f }; + } + else { + // We are iterating through the array one-by-one, so we are guaranteed + // that for tile 'i', tile 'i-1' already was initializated + chunkTilePile[i]->tile = chunkTilePile[i - 1]->tile; + chunkTilePile[i]->uvTransform.uvOffset = + chunkTilePile[i - 1]->uvTransform.uvOffset; + chunkTilePile[i]->uvTransform.uvScale = + chunkTilePile[i - 1]->uvTransform.uvScale; + } + } + } + return chunkTilePile; +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/tileprovider.h b/modules/globebrowsing/src/tileprovider/tileprovider.h new file mode 100644 index 0000000000..ce9b63aa4e --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileprovider.h @@ -0,0 +1,145 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDER___H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct CPLXMLNode; + +namespace ghoul::fontrendering { + class Font; + class FontRenderer; +} // namespace ghoul::fontrendering + +namespace openspace { class PixelBuffer; } + +namespace openspace::globebrowsing { + class AsyncTileDataProvider; + struct RawTile; + struct TileIndex; + namespace cache { class MemoryAwareTileCache; } +} // namespace openspace::globebrowsing + +namespace openspace::globebrowsing { + +enum class Type { + DefaultTileProvider = 0, + SingleImageTileProvider, + ImageSequenceTileProvider, + SizeReferenceTileProvider, + TemporalTileProvider, + TileIndexTileProvider, + ByIndexTileProvider, + ByLevelTileProvider, + InterpolateTileProvider +}; + + +struct TileProvider : public properties::PropertyOwner { + static unsigned int NumTileProviders; + + static std::unique_ptr createFromDictionary( + layergroupid::TypeID layerTypeID, const ghoul::Dictionary& dictionary); + + static void initializeDefaultTile(); + static void deinitializeDefaultTile(); + + TileProvider(); + virtual ~TileProvider() = default; + + void initialize(); + void deinitialize(); + + virtual Tile tile(const TileIndex& tileIndex) = 0; + + /** + * Returns the status of a Tile. The Tile::Status + * corresponds the Tile that would be returned if the function + * tile would be invoked with the same TileIndex argument at + * this point in time. + */ + virtual Tile::Status tileStatus(const TileIndex& index) = 0; + + /** + * Get the associated depth transform for this TileProvider. This is necessary for + * TileProviders serving height map data, in order to correcly map pixel values to + * meters. + */ + virtual TileDepthTransform depthTransform() = 0; + + /** + * This method should be called once per frame. Here, TileProviders are given the + * opportunity to update their internal state. + */ + virtual void update() = 0; + + /** + * Provides a uniform way of all TileProviders to reload or restore all of its + * internal state. This is mainly useful for debugging purposes. + */ + virtual void reset() = 0; + + /** + * \return The maximum level as defined by TileIndex that this + * TileProvider is able provide. + */ + virtual int maxLevel() = 0; + + /** + * \return the no data value for the dataset. Default is the minimum float value. + */ + virtual float noDataValueAsFloat() = 0; + + + ChunkTile chunkTile(TileIndex tileIndex, int parents = 0, int maxParents = 1337); + ChunkTilePile chunkTilePile(TileIndex tileIndex, int pileSize); + + + std::string name; + + uint16_t uniqueIdentifier = 0; + bool isInitialized = false; + +private: + virtual void internalInitialize(); + virtual void internalDeinitialize(); +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDER___H__ diff --git a/modules/globebrowsing/src/tileprovider/tileproviderbyindex.cpp b/modules/globebrowsing/src/tileprovider/tileproviderbyindex.cpp new file mode 100644 index 0000000000..af85ae70d8 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileproviderbyindex.cpp @@ -0,0 +1,140 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +namespace { + constexpr const char* KeyDefaultProvider = "DefaultProvider"; + constexpr const char* KeyProviders = "IndexTileProviders"; + constexpr const char* KeyTileIndex = "TileIndex"; + constexpr const char* KeyTileProvider = "TileProvider"; + + struct [[codegen::Dictionary(TileProviderByIndex)]] Parameters { + ghoul::Dictionary defaultProvider; + + struct IndexProvider { + struct Index { + int x [[codegen::greaterequal(0)]]; + int y [[codegen::greaterequal(0)]]; + int level [[codegen::inrange(0, 255)]]; + }; + Index tileIndex; + ghoul::Dictionary tileProvider; + }; + std::vector indexTileProviders; + }; +#include "tileproviderbyindex_codegen.cpp" +} // namespace + +namespace openspace::globebrowsing { + +TileProviderByIndex::TileProviderByIndex(const ghoul::Dictionary& dictionary) { + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + layergroupid::TypeID typeID = layergroupid::TypeID::DefaultTileLayer; + if (p.defaultProvider.hasValue("Type")) { + std::string type = p.defaultProvider.value("Type"); + typeID = ghoul::from_string(type); + + if (typeID == layergroupid::TypeID::Unknown) { + throw ghoul::RuntimeError("Unknown layer type: " + type); + } + } + + _defaultTileProvider = createFromDictionary(typeID, p.defaultProvider); + + for (const Parameters::IndexProvider& ip : p.indexTileProviders) { + const TileIndex tileIndex( + ip.tileIndex.x, + ip.tileIndex.y, + static_cast(ip.tileIndex.level) + ); + + layergroupid::TypeID providerTypeID = layergroupid::TypeID::DefaultTileLayer; + if (ip.tileProvider.hasValue("Type")) { + std::string type = ip.tileProvider.value("Type"); + providerTypeID = ghoul::from_string(type); + + if (providerTypeID == layergroupid::TypeID::Unknown) { + throw ghoul::RuntimeError("Unknown layer type: " + type); + } + } + + std::unique_ptr stp = createFromDictionary( + providerTypeID, + ip.tileProvider + ); + TileIndex::TileHashKey key = tileIndex.hashKey(); + _providers.insert(std::make_pair(key, std::move(stp))); + } +} + +Tile TileProviderByIndex::tile(const TileIndex& tileIndex) { + ZoneScoped + const auto it = _providers.find(tileIndex.hashKey()); + const bool hasProvider = it != _providers.end(); + return hasProvider ? it->second->tile(tileIndex) : Tile(); +} + +Tile::Status TileProviderByIndex::tileStatus(const TileIndex& index) { + const auto it = _providers.find(index.hashKey()); + const bool hasProvider = it != _providers.end(); + return hasProvider ? it->second->tileStatus(index) : Tile::Status::Unavailable; +} + +TileDepthTransform TileProviderByIndex::depthTransform() { + return _defaultTileProvider->depthTransform(); +} + +void TileProviderByIndex::update() { + using K = TileIndex::TileHashKey; + using V = std::unique_ptr; + for (std::pair& it : _providers) { + it.second->update(); + } + _defaultTileProvider->update(); +} + +void TileProviderByIndex::reset() { + using K = TileIndex::TileHashKey; + using V = std::unique_ptr; + for (std::pair& it : _providers) { + it.second->reset(); + } + _defaultTileProvider->reset(); +} + +int TileProviderByIndex::maxLevel() { + return _defaultTileProvider->maxLevel(); +} + +float TileProviderByIndex::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/tileproviderbyindex.h b/modules/globebrowsing/src/tileprovider/tileproviderbyindex.h new file mode 100644 index 0000000000..1d523748e2 --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileproviderbyindex.h @@ -0,0 +1,51 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDERBYINDEX___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDERBYINDEX___H__ + +#include + +namespace openspace::globebrowsing { + +class TileProviderByIndex : public TileProvider { +public: + TileProviderByIndex(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + std::unordered_map> _providers; + std::unique_ptr _defaultTileProvider; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDERBYINDEX___H__ diff --git a/modules/globebrowsing/src/tileprovider/tileproviderbylevel.cpp b/modules/globebrowsing/src/tileprovider/tileproviderbylevel.cpp new file mode 100644 index 0000000000..c27576012d --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileproviderbylevel.cpp @@ -0,0 +1,161 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +namespace { + struct [[codegen::Dictionary(TileProviderByLevel)]] Parameters { + int layerGroupID; + + struct Providers { + int maxLevel [[codegen::greaterequal(0)]]; + ghoul::Dictionary tileProvider; + }; + std::vector levelTileProviders; + }; +#include "tileproviderbylevel_codegen.cpp" +} // namespace + +namespace openspace::globebrowsing { + +TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) { + ZoneScoped + + const Parameters p = codegen::bake(dictionary); + + layergroupid::GroupID layerGroup = static_cast(p.layerGroupID); + + for (Parameters::Providers p : p.levelTileProviders) { + p.tileProvider.setValue("LayerGroupID", static_cast(layerGroup)); + + layergroupid::TypeID typeID = layergroupid::TypeID::DefaultTileLayer; + if (p.tileProvider.hasValue("Type")) { + std::string type = p.tileProvider.value("Type"); + typeID = ghoul::from_string(type); + + if (typeID == layergroupid::TypeID::Unknown) { + throw ghoul::RuntimeError("Unknown layer type: " + type); + } + } + + std::unique_ptr tp = createFromDictionary(typeID, p.tileProvider); + + std::string provId = p.tileProvider.value("Identifier"); + tp->setIdentifier(provId); + std::string providerName = p.tileProvider.value("Name"); + tp->setGuiName(providerName); + addPropertySubOwner(tp.get()); + + _levelTileProviders.push_back(std::move(tp)); + + // Ensure we can represent the max level + if (static_cast(_providerIndices.size()) < p.maxLevel) { + _providerIndices.resize(p.maxLevel + 1, -1); + } + + // map this level to the tile provider index + _providerIndices[p.maxLevel] = static_cast(_levelTileProviders.size()) - 1; + } + + // Fill in the gaps (value -1 ) in provider indices, from back to end + for (int i = static_cast(_providerIndices.size()) - 2; i >= 0; --i) { + if (_providerIndices[i] == -1) { + _providerIndices[i] = _providerIndices[i + 1]; + } + } +} + +void TileProviderByLevel::internalInitialize() { + for (const std::unique_ptr& prov : _levelTileProviders) { + prov->initialize(); + } +} + +void TileProviderByLevel::internalDeinitialize() { + for (const std::unique_ptr& prov : _levelTileProviders) { + prov->deinitialize(); + } +} + +Tile TileProviderByLevel::tile(const TileIndex& tileIndex) { + ZoneScoped + + TileProvider* provider = levelProvider(tileIndex.level); + if (provider) { + return provider->tile(tileIndex); + } + else { + return Tile(); + } +} + +Tile::Status TileProviderByLevel::tileStatus(const TileIndex& index) { + TileProvider* provider = levelProvider(index.level); + return provider ? provider->tileStatus(index) : Tile::Status::Unavailable; +} + +TileProvider* TileProviderByLevel::levelProvider(int level) const { + ZoneScoped + + if (!_levelTileProviders.empty()) { + int clampedLevel = glm::clamp( + level, + 0, + static_cast(_providerIndices.size() - 1) + ); + int idx = _providerIndices[clampedLevel]; + return _levelTileProviders[idx].get(); + } + else { + return nullptr; + } +} + +TileDepthTransform TileProviderByLevel::depthTransform() { + return { 0.f, 1.f }; +} + +void TileProviderByLevel::update() { + for (const std::unique_ptr& provider : _levelTileProviders) { + provider->update(); + } +} + +void TileProviderByLevel::reset() { + for (const std::unique_ptr& provider : _levelTileProviders) { + provider->reset(); + } +} + +int TileProviderByLevel::maxLevel() { + return static_cast(_providerIndices.size() - 1); +} + +float TileProviderByLevel::noDataValueAsFloat() { + return std::numeric_limits::min(); +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/tileprovider/tileproviderbylevel.h b/modules/globebrowsing/src/tileprovider/tileproviderbylevel.h new file mode 100644 index 0000000000..1ec3c4995e --- /dev/null +++ b/modules/globebrowsing/src/tileprovider/tileproviderbylevel.h @@ -0,0 +1,55 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDERBYLEVEL___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDERBYLEVEL___H__ + +#include + +namespace openspace::globebrowsing { + +class TileProviderByLevel : public TileProvider { +public: + TileProviderByLevel(const ghoul::Dictionary& dictionary); + + Tile tile(const TileIndex& tileIndex) override final; + Tile::Status tileStatus(const TileIndex& index) override final; + TileDepthTransform depthTransform() override final; + void update() override final; + void reset() override final; + int maxLevel() override final; + float noDataValueAsFloat() override final; + +private: + std::vector _providerIndices; + std::vector> _levelTileProviders; + + void internalInitialize() override final; + void internalDeinitialize() override final; + TileProvider* levelProvider(int level) const; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILEPROVIDER__TILEPROVIDERBYLEVEL___H__ diff --git a/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp b/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp index 38862178e5..16a55863b2 100644 --- a/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp +++ b/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp @@ -73,7 +73,7 @@ namespace { namespace openspace { documentation::Documentation RenderableCrawlingLine::Documentation() { - return codegen::doc("newhorizons_renderable_crawlingline"); + return codegen::doc("spacecraftinstruments_renderablecrawlingline"); } RenderableCrawlingLine::RenderableCrawlingLine(const ghoul::Dictionary& dictionary) diff --git a/modules/spacecraftinstruments/rendering/renderablefov.cpp b/modules/spacecraftinstruments/rendering/renderablefov.cpp index e7eb9a9134..993cccb0c3 100644 --- a/modules/spacecraftinstruments/rendering/renderablefov.cpp +++ b/modules/spacecraftinstruments/rendering/renderablefov.cpp @@ -196,7 +196,7 @@ namespace { namespace openspace { documentation::Documentation RenderableFov::Documentation() { - return codegen::doc("newhorizons_renderable_fieldofview"); + return codegen::doc("spacecraftinstruments_renderablefieldofview"); } RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary) diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index 3bd9ea34ba..085aa9b429 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -90,7 +90,7 @@ namespace { namespace openspace { documentation::Documentation RenderableModelProjection::Documentation() { - return codegen::doc("newhorizons_renderable_modelprojection"); + return codegen::doc("spacecraftinstruments_renderablemodelprojection"); } RenderableModelProjection::RenderableModelProjection(const ghoul::Dictionary& dictionary) diff --git a/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp index 9fac9699f6..2a5aab8aeb 100644 --- a/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp @@ -45,6 +45,7 @@ namespace { constexpr const char* _loggerCat = "RenderablePlaneProjection"; constexpr const char* GalacticFrame = "GALACTIC"; + // @TODO (emmbr 2022-01-20) Add documentation struct [[codegen::Dictionary(RenderablePlaneProjection)]] Parameters { std::optional spacecraft; std::optional instrument; @@ -58,6 +59,10 @@ namespace { namespace openspace { +documentation::Documentation RenderablePlaneProjection::Documentation() { + return codegen::doc("spacecraftinstruments_renderableorbitdisc"); +} + RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& dict) : Renderable(dict) { diff --git a/modules/spacecraftinstruments/rendering/renderableplaneprojection.h b/modules/spacecraftinstruments/rendering/renderableplaneprojection.h index cf78ef932c..5a225883f7 100644 --- a/modules/spacecraftinstruments/rendering/renderableplaneprojection.h +++ b/modules/spacecraftinstruments/rendering/renderableplaneprojection.h @@ -56,6 +56,8 @@ public: void render(const RenderData& data, RendererTasks& rendererTask) override; void update(const UpdateData& data) override; + static documentation::Documentation Documentation(); + private: void loadTexture(); void updatePlane(const Image& img, double currentTime); diff --git a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp index 05900d0cd0..804c96c71c 100644 --- a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp @@ -162,7 +162,7 @@ namespace { namespace openspace { documentation::Documentation RenderablePlanetProjection::Documentation() { - return codegen::doc("newhorizons_renderable_planetprojection"); + return codegen::doc("spacecraftinstruments_renderableplanetprojection"); } RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& dict) diff --git a/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp b/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp index 7df88e279f..30f515a775 100644 --- a/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp +++ b/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp @@ -143,7 +143,7 @@ namespace { namespace openspace { documentation::Documentation RenderableShadowCylinder::Documentation() { - return codegen::doc("newhorizons_renderable_shadowcylinder"); + return codegen::doc("spacecraftinstruments_renderableshadowcylinder"); } RenderableShadowCylinder::RenderableShadowCylinder(const ghoul::Dictionary& dictionary) diff --git a/modules/spacecraftinstruments/spacecraftinstrumentsmodule.cpp b/modules/spacecraftinstruments/spacecraftinstrumentsmodule.cpp index b60f0c0181..874b33dfbf 100644 --- a/modules/spacecraftinstruments/spacecraftinstrumentsmodule.cpp +++ b/modules/spacecraftinstruments/spacecraftinstrumentsmodule.cpp @@ -61,12 +61,12 @@ void SpacecraftInstrumentsModule::internalInitialize(const ghoul::Dictionary&) { auto fRenderable = FactoryManager::ref().factory(); ghoul_assert(fRenderable, "No renderable factory existed"); - fRenderable->registerClass("RenderableShadowCylinder"); fRenderable->registerClass("RenderableCrawlingLine"); fRenderable->registerClass("RenderableFov"); + fRenderable->registerClass("RenderableModelProjection"); fRenderable->registerClass("RenderablePlaneProjection"); fRenderable->registerClass("RenderablePlanetProjection"); - fRenderable->registerClass("RenderableModelProjection"); + fRenderable->registerClass("RenderableShadowCylinder"); auto fDecoder = FactoryManager::ref().factory(); fDecoder->registerClass("Instrument"); @@ -85,9 +85,12 @@ std::vector SpacecraftInstrumentsModule::documentations() const { return { + RenderableCrawlingLine::Documentation(), RenderableFov::Documentation(), RenderableModelProjection::Documentation(), + RenderablePlaneProjection::Documentation(), RenderablePlanetProjection::Documentation(), + RenderableShadowCylinder::Documentation(), ProjectionComponent::Documentation() }; } diff --git a/modules/spacecraftinstruments/util/projectioncomponent.cpp b/modules/spacecraftinstruments/util/projectioncomponent.cpp index 3955db5c3c..1298646143 100644 --- a/modules/spacecraftinstruments/util/projectioncomponent.cpp +++ b/modules/spacecraftinstruments/util/projectioncomponent.cpp @@ -162,7 +162,7 @@ namespace { namespace openspace { documentation::Documentation ProjectionComponent::Documentation() { - return codegen::doc("newhorizons_projectioncomponent"); + return codegen::doc("spacecraftinstruments_projectioncomponent"); } ProjectionComponent::ProjectionComponent() diff --git a/modules/toyvolume/rendering/renderabletoyvolume.cpp b/modules/toyvolume/rendering/renderabletoyvolume.cpp index bccf0aee95..c5e5b1934c 100644 --- a/modules/toyvolume/rendering/renderabletoyvolume.cpp +++ b/modules/toyvolume/rendering/renderabletoyvolume.cpp @@ -32,7 +32,7 @@ #include namespace { - constexpr const char* _loggerCat = "Renderable ToyVolume"; + constexpr const char* _loggerCat = "RenderableToyVolume"; constexpr openspace::properties::Property::PropertyInfo SizeInfo = { "Size", "Size", diff --git a/src/navigation/orbitalnavigator.cpp b/src/navigation/orbitalnavigator.cpp index 80ab5f441d..d190938d28 100644 --- a/src/navigation/orbitalnavigator.cpp +++ b/src/navigation/orbitalnavigator.cpp @@ -134,24 +134,33 @@ namespace { }; constexpr openspace::properties::Property::PropertyInfo FollowAnchorNodeInfo = { + "FollowAnchorNodeRotation", + "Follow Anchor Node Rotation", + "If true, the camera will rotate with the current achor node if within a " + "certain distance from it. When this happens, the object will appear fixed in " + "relation to the camera. The distance at which the change happens is controlled " + "through another property." + }; + + constexpr openspace::properties::Property::PropertyInfo + FollowAnchorNodeDistanceInfo = { "FollowAnchorNodeRotationDistance", - "Follow anchor node rotation distance", + "Follow Anchor Node Rotation Distance", "A factor used to determine the distance at which the camera starts rotating " - "with the anchor node. When this happends, a the object will appear fixed in " - "relation to the camera. The actual distance will be computed by multiplying " + "with the anchor node. The actual distance will be computed by multiplying " "this factor with the approximate radius of the node." }; constexpr openspace::properties::Property::PropertyInfo MinimumDistanceInfo = { "MinimumAllowedDistance", - "Minimum allowed distance", + "Minimum Allowed Distance", "Limits how close the camera can get to an object. The distance is given in " "meters above the surface." }; constexpr openspace::properties::Property::PropertyInfo VelocityZoomControlInfo = { "VelocityZoomControl", - "Velocity zoom control", + "Velocity Zoom Control", "Controls the velocity of the camera motion when zooming in to the focus node " "on a linear flight. The higher the value the faster the camera will move " "towards the focus." @@ -180,7 +189,7 @@ namespace { constexpr openspace::properties::Property::PropertyInfo StereoInterpolationTimeInfo = { "StereoInterpolationTime", - "Stereo interpolation time", + "Stereo Interpolation Time", "The time to interpolate to a new stereoscopic depth " "when the anchor node is changed, in seconds." }; @@ -188,7 +197,7 @@ namespace { constexpr openspace::properties::Property::PropertyInfo RetargetInterpolationTimeInfo = { "RetargetAnchorInterpolationTime", - "Retarget interpolation time", + "Retarget Interpolation Time", "The time to interpolate the camera rotation " "when the anchor or aim node is changed, in seconds." }; @@ -196,13 +205,13 @@ namespace { constexpr openspace::properties::Property::PropertyInfo FollowRotationInterpTimeInfo = { "FollowRotationInterpolationTime", - "Follow rotation interpolation time", + "Follow Rotation Interpolation Time", "The interpolation time when toggling following focus node rotation." }; constexpr openspace::properties::Property::PropertyInfo InvertMouseButtons = { "InvertMouseButtons", - "Invert left and right mouse buttons", + "Invert Left and Right Mouse Buttons", "If this value is 'false', the left mouse button causes the camera to rotate " "around the object and the right mouse button causes the zooming motion. If this " "value is 'true', these two functionalities are reversed." @@ -229,7 +238,7 @@ namespace { constexpr openspace::properties::Property::PropertyInfo StereoscopicDepthOfFocusSurfaceInfo = { "StereoscopicDepthOfFocusSurface", - "Stereoscopic depth of the surface in focus", + "Stereoscopic Depth of the Surface in Focus", "Set the stereoscopically perceived distance (in meters) to the closest " "point out of the surface of the anchor and the center of the aim node. " "Only used if UseAdaptiveStereoscopicDepthInfo is set to true." @@ -331,7 +340,8 @@ OrbitalNavigator::OrbitalNavigator() , _aim(AimInfo) , _retargetAnchor(RetargetAnchorInfo) , _retargetAim(RetargetAimInfo) - , _followAnchorNodeRotationDistance(FollowAnchorNodeInfo, 5.f, 0.f, 20.f) + , _followAnchorNodeRotation(FollowAnchorNodeInfo, true) + , _followAnchorNodeRotationDistance(FollowAnchorNodeDistanceInfo, 5.f, 0.f, 20.f) , _minimumAllowedDistance(MinimumDistanceInfo, 10.0f, 0.0f, 10000.f) , _mouseSensitivity(MouseSensitivityInfo, 15.f, 1.f, 50.f) , _joystickSensitivity(JoystickSensitivityInfo, 10.f, 1.0f, 50.f) @@ -491,6 +501,7 @@ OrbitalNavigator::OrbitalNavigator() addProperty(_aim); addProperty(_retargetAnchor); addProperty(_retargetAim); + addProperty(_followAnchorNodeRotation); addProperty(_followAnchorNodeRotationDistance); addProperty(_minimumAllowedDistance); @@ -954,7 +965,7 @@ void OrbitalNavigator::setRetargetInterpolationTime(float durationInSeconds) { bool OrbitalNavigator::shouldFollowAnchorRotation(const glm::dvec3& cameraPosition) const { - if (!_anchorNode) { + if (!_anchorNode || !_followAnchorNodeRotation) { return false; } diff --git a/src/navigation/path.cpp b/src/navigation/path.cpp index 6f1da4e7c1..b5fcb02891 100644 --- a/src/navigation/path.cpp +++ b/src/navigation/path.cpp @@ -466,7 +466,6 @@ Path createPathFromDictionary(const ghoul::Dictionary& dictionary, Path::Type ty break; } default: { - LERROR(fmt::format("Uknown camera path target type: {}", p.targetType)); throw ghoul::MissingCaseException(); } } diff --git a/src/navigation/pathnavigator.cpp b/src/navigation/pathnavigator.cpp index 9bd101265d..df29a3e169 100644 --- a/src/navigation/pathnavigator.cpp +++ b/src/navigation/pathnavigator.cpp @@ -200,7 +200,7 @@ void PathNavigator::updateCamera(double deltaTime) { if (_currentPath->hasReachedEnd()) { LINFO("Reached end of path"); - _isPlaying = false; + handlePathEnd(); if (_applyIdleBehaviorOnFinish) { constexpr const char* ApplyIdleBehaviorScript = @@ -263,10 +263,16 @@ void PathNavigator::startPath() { return; } - //OBS! Until we can handle simulation time: early out if not paused + // Always pause the simulation time when flying, to aovid problem with objects + // moving. However, keep track of whether the time was running before the path + // was started, so we can reset it on finish if (!global::timeManager->isPaused()) { - LERROR("Simulation time must be paused to run a camera path"); - return; + global::timeManager->setPause(true); + _startSimulationTimeOnFinish = true; + LINFO( + "Pausing time simulation during path traversal. " + "Will unpause once the camera path is finished" + ); } LINFO("Starting path"); @@ -280,9 +286,7 @@ void PathNavigator::abortPath() { LWARNING("No camera path is playing"); return; } - _isPlaying = false; - clearPath(); // TODO: instead of clearing this could be handled better - + handlePathEnd(); LINFO("Aborted camera path"); } @@ -329,6 +333,16 @@ const std::vector& PathNavigator::relevantNodes() { return _relevantNodes; } +void PathNavigator::handlePathEnd() { + _isPlaying = false; + + if (_startSimulationTimeOnFinish) { + global::timeManager->setPause(false); + } + _startSimulationTimeOnFinish = false; + clearPath(); +} + void PathNavigator::findRelevantNodes() { const std::vector& allNodes = global::renderEngine->scene()->allSceneGraphNodes(); @@ -413,8 +427,8 @@ scripting::LuaLibrary PathNavigator::luaLibrary() { "Stops a path, if one is being played" }, { - "goTo", - &luascriptfunctions::goTo, + "flyTo", + &luascriptfunctions::flyTo, "string [, bool, double]", "Move the camera to the node with the specified identifier. The optional " "double specifies the duration of the motion. If the optional bool is " @@ -422,8 +436,8 @@ scripting::LuaLibrary PathNavigator::luaLibrary() { "node. Either of the optional parameters can be left out." }, { - "goToHeight", - &luascriptfunctions::goToHeight, + "flyToHeight", + &luascriptfunctions::flyToHeight, "string, double [, bool, double]", "Move the camera to the node with the specified identifier. The second " "argument is the desired target height above the target node's bounding " @@ -433,8 +447,8 @@ scripting::LuaLibrary PathNavigator::luaLibrary() { "parameters can be left out." }, { - "goToNavigationState", - &luascriptfunctions::goToNavigationState, + "flyToNavigationState", + &luascriptfunctions::flyToNavigationState, "table, [double]", "Create a path to the navigation state described by the input table. " "The optional double specifies the target duration of the motion. Note " @@ -445,7 +459,7 @@ scripting::LuaLibrary PathNavigator::luaLibrary() { "createPath", &luascriptfunctions::createPath, "table", - "Create the path as described by the lua table input argument" + "Create a camera path as described by the lua table input argument" }, } }; diff --git a/src/navigation/pathnavigator_lua.inl b/src/navigation/pathnavigator_lua.inl index f921fb0182..de306c1d4c 100644 --- a/src/navigation/pathnavigator_lua.inl +++ b/src/navigation/pathnavigator_lua.inl @@ -68,8 +68,8 @@ int stopPath(lua_State* L) { return 0; } -int goTo(lua_State* L) { - ghoul::lua::checkArgumentsAndThrow(L, { 1, 3 }, "lua::goTo"); +int flyTo(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, { 1, 3 }, "lua::flyTo"); auto [nodeIdentifier, useUpFromTargetOrDuration, duration] = ghoul::lua::values< std::string, std::optional>, std::optional >(L); @@ -120,8 +120,8 @@ int goTo(lua_State* L) { return 0; } -int goToHeight(lua_State* L) { - ghoul::lua::checkArgumentsAndThrow(L, { 2, 4 }, "lua::goToHeight"); +int flyToHeight(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, { 2, 4 }, "lua::flyToHeight"); auto [nodeIdentifier, height, useUpFromTargetOrDuration, duration] = ghoul::lua::values< std::string, double, std::optional>, @@ -169,8 +169,8 @@ int goToHeight(lua_State* L) { return 0; } -int goToNavigationState(lua_State* L) { - ghoul::lua::checkArgumentsAndThrow(L, { 1, 2 }, "lua::goToNavigationState"); +int flyToNavigationState(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, { 1, 2 }, "lua::flyToNavigationState"); auto [navigationState, duration] = ghoul::lua::values>(L); @@ -182,7 +182,7 @@ int goToNavigationState(lua_State* L) { ); } catch (documentation::SpecificationError& e) { - LERRORC("goToNavigationState", ghoul::to_string(e.result)); + LERRORC("flyToNavigationState", ghoul::to_string(e.result)); return ghoul::lua::luaError( L, fmt::format("Unable to create a path: {}", e.what()) ); diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index b1314aece8..7b7079ee8f 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -130,6 +130,14 @@ namespace { "shown on the screen" }; + constexpr openspace::properties::Property::PropertyInfo ScreenshotWindowIdsInfo = { + "ScreenshotWindowId", + "Screenshow Window Ids", + "The list of window identifiers whose screenshot will be taken the next time " + "anyone triggers a screenshot. If this list is empty (the default), all windows " + "will have their screenshot taken. Id's that do not exist are silently ignored." + }; + constexpr openspace::properties::Property::PropertyInfo ApplyWarpingInfo = { "ApplyWarpingScreenshot", "Apply Warping to Screenshots", @@ -277,6 +285,7 @@ RenderEngine::RenderEngine() , _verticalLogOffset(VerticalLogOffsetInfo, 0.f, 0.f, 1.f) , _showVersionInfo(ShowVersionInfo, true) , _showCameraInfo(ShowCameraInfo, true) + , _screenshotWindowIds(ScreenshotWindowIdsInfo) , _applyWarping(ApplyWarpingInfo, false) , _screenshotUseDate(ScreenshotUseDateInfo, false) , _showFrameInformation(ShowFrameNumberInfo, false) @@ -342,6 +351,7 @@ RenderEngine::RenderEngine() addProperty(_value); addProperty(_globalBlackOutFactor); + addProperty(_screenshotWindowIds); addProperty(_applyWarping); _screenshotUseDate.onChange([this]() { @@ -1046,7 +1056,10 @@ void RenderEngine::takeScreenshot() { std::filesystem::create_directories(absPath("${SCREENSHOTS}")); } - _latestScreenshotNumber = global::windowDelegate->takeScreenshot(_applyWarping); + _latestScreenshotNumber = global::windowDelegate->takeScreenshot( + _applyWarping, + _screenshotWindowIds + ); } unsigned int RenderEngine::latestScreenshotNumber() const { diff --git a/support/coding/codegen b/support/coding/codegen index 4bb5b66dae..425a0a224e 160000 --- a/support/coding/codegen +++ b/support/coding/codegen @@ -1 +1 @@ -Subproject commit 4bb5b66daeb20577e7276aa2465cda4ebf14d8a5 +Subproject commit 425a0a224e28802f0f2d88c0b2034ee5a473cc3b diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 38f2dfb785..076411a2c5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,7 +40,6 @@ add_executable( test_rawvolumeio.cpp test_scriptscheduler.cpp test_spicemanager.cpp - test_temporaltileprovider.cpp test_timequantizer.cpp test_timeline.cpp