mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-21 20:39:08 -06:00
Feature/globebrowsing (#281)
* Solve bug related to corrupted texture tiles for certain sizes. * Regard layer settings when sampling height map. * Make Tile in to a class instead of a struct. * Memory aware lru cache. Needs cleanup. * Clean up and comment. * Clean up and comment. * Clean up * Clean up and comment. * Fix compilation error on Windows. * Specify data type explicitly in GDAL xml config files for Utah height maps. Closes #242 * Update the key type for the memory aware lru cache and use a unordered map instead of a map. * Solve pixel row size bug. * Solve initialization bug. * Add cache size as property of the globe browsing module. * Use memory aware tile cache for text tile provider. * Log GDAL errors as GHOUL messages * Add the ability to toggle tile level limiting by available data * Add ability to toggle GDAL logging * Add lock guard to memory aware tile cache * create base class rawtiledatareader that can be extended with different implementations than GDAL. * Let GdalWrapper take care of global GDAL settings. * Move iodescription to separate file * Move some functionality from gdalrawtiledatareader to rawtiledatareader * Move functionality from gdalrawtiledatareader to rawtiledatareader. * GDAL is no longer a necessary dependency for the globebrowsing module. However to read tiles, the SimpleRawTileDataReader needs to be implemented. Otherwise GDAL is needed. * Add ifdef check for GLOBEBROWSING_USE_GDAL * Implement SimpleRawTileDataReader. Currently can only read pow 2 textures. * Change ints to unsigned long longs * Limit number of texture creations per tile provider per frame * Solve linker error on windows * Fix Windows build errors * Fix crash in reading local patches * Update lodglobe descriptions * Abstract away overviews in gdal raw tile data reader * Update Mars and Moon configs. * Update screenshot script * Update ghoul version * Remove use of interaction depth below ellipsoid * Normalize direction vector * Use scale for distance swotch * Go back to use of interaction depth below ellipsoid * Fix comments on pull request. * TileProviderByLevel error does not propagate up. * Comment on mars and moon mod file * Add model space cut off level as a property * Update ChunkTile struct * Minor clean up * Go back tu constructor for ChunkTile
This commit is contained in:
@@ -56,13 +56,14 @@ return {
|
||||
SegmentsPerPatch = 64,
|
||||
Layers = {
|
||||
ColorLayers = {
|
||||
|
||||
{
|
||||
Name = "ESRI VIIRS Combo",
|
||||
Type = "ByLevel",
|
||||
LevelTileProviders = {
|
||||
{
|
||||
MaxLevel = 7,
|
||||
TileProvider = { FilePath = "map_service_configs/GIBS/VIIRS_SNPP_CorrectedReflectance_TrueColor.xml", },
|
||||
MaxLevel = 3,
|
||||
TileProvider = { Type = "Temporal", FilePath = "map_service_configs/GIBS/Temporal_VIIRS_SNPP_CorrectedReflectance_TrueColor.xml", },
|
||||
},
|
||||
{
|
||||
MaxLevel = 22,
|
||||
@@ -71,6 +72,10 @@ return {
|
||||
},
|
||||
Enabled = true,
|
||||
},
|
||||
{
|
||||
Name = "ESRI Imagery World 2D",
|
||||
FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms",
|
||||
},
|
||||
{
|
||||
Name = "ESRI Imagery World",
|
||||
FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms"
|
||||
@@ -168,7 +173,7 @@ return {
|
||||
Name = "Terrain tileset",
|
||||
FilePath = "map_service_configs/ESRI/TERRAIN.wms",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 64,
|
||||
TilePixelSize = 64,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ return {
|
||||
Name = "Callisto Texture",
|
||||
FilePath = "textures/callisto.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 112,
|
||||
TilePixelSize = 112,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -32,7 +32,7 @@ return {
|
||||
Name = "Europa Texture",
|
||||
FilePath = "textures/europa.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 256,
|
||||
TilePixelSize = 256,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -32,7 +32,7 @@ return {
|
||||
Name = "Ganymede Texture",
|
||||
FilePath = "textures/ganymede.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 112,
|
||||
TilePixelSize = 112,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<TileLevel>6</TileLevel>
|
||||
<YOrigin>top</YOrigin>
|
||||
</DataWindow>
|
||||
<DataType>Int16</DataType>
|
||||
<Projection>GEOGCS["GCS_Mars_2000_Sphere",DATUM["D_Mars_2000_Sphere",SPHEROID["Mars_2000_Sphere_IAU_IAG",3396190.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]]</Projection>
|
||||
<BlockSizeX>360</BlockSizeX>
|
||||
<BlockSizeY>360</BlockSizeY>
|
||||
|
||||
@@ -32,20 +32,36 @@ return {
|
||||
Type = "RenderableGlobe",
|
||||
Radii = marsEllipsoid,
|
||||
CameraMinHeight = 10,
|
||||
SegmentsPerPatch = 90,
|
||||
-- Allows camera to go down 10000 meters below the reference ellipsoid
|
||||
InteractionDepthBelowEllipsoid = 10000, -- Useful when having negative height map values
|
||||
SegmentsPerPatch = 64,
|
||||
Layers = {
|
||||
ColorLayers = {
|
||||
{
|
||||
Name = "Viking combo",
|
||||
Type = "ByLevel",
|
||||
LevelTileProviders = {
|
||||
{
|
||||
MaxLevel = 3,
|
||||
TileProvider = { FilePath = "textures/mars.jpg", },
|
||||
},
|
||||
{
|
||||
MaxLevel = 22,
|
||||
TileProvider = { FilePath = "map_service_configs/MARS_Viking_MDIM21.xml" },
|
||||
},
|
||||
},
|
||||
Enabled = true,
|
||||
},
|
||||
-- {
|
||||
-- Type = "SingleImage",
|
||||
-- Name = "Debug Tiles",
|
||||
-- FilePath = "../../debugglobe/textures/test_tile.png",
|
||||
-- },
|
||||
{
|
||||
Name = "MARS_Viking",
|
||||
FilePath = "map_service_configs/MARS_Viking_MDIM21.xml",
|
||||
Enabled = true,
|
||||
},
|
||||
--{
|
||||
-- Name = "MARS_Viking",
|
||||
-- FilePath = "map_service_configs/MARS_Viking_MDIM21.xml",
|
||||
-- Enabled = true,
|
||||
--},
|
||||
{
|
||||
Name = "MOLA Pseudo Color",
|
||||
FilePath = "map_service_configs/Utah/MolaPseudoColor.xml",
|
||||
@@ -66,16 +82,22 @@ return {
|
||||
{
|
||||
Name = "CTX Mosaic [Europe]",
|
||||
FilePath = "map_service_configs/CTX_Mosaic.xml",
|
||||
Enabled = true,
|
||||
}, {
|
||||
--Enabled = true,
|
||||
},
|
||||
{
|
||||
Name = "CTX Mosaic [Utah]",
|
||||
FilePath = "map_service_configs/Utah/CTX_Mosaic.xml",
|
||||
},
|
||||
{
|
||||
Name = "West Candor Chasma",
|
||||
FilePath = "map_datasets/CTX/West_Candor_Chasma_longlat_global.vrt",
|
||||
--Enabled = true,
|
||||
},
|
||||
{
|
||||
Name = "Layered Rock Outcrops in Southwest Candor Chasma",
|
||||
FilePath = "map_datasets/HiRISE/Layered_Rock_Outcrops_in_Southwest_Candor_Chasma_Texture.vrt",
|
||||
},
|
||||
{
|
||||
--[[{
|
||||
Name = "Themis IR Day",
|
||||
FilePath = "map_service_configs/Utah/ThemisIRDay.xml",
|
||||
},
|
||||
@@ -83,12 +105,7 @@ return {
|
||||
Name = "Themis IR Night",
|
||||
FilePath = "map_service_configs/Utah/ThemisIRNight.xml",
|
||||
},
|
||||
--[[
|
||||
{
|
||||
Name = "West Candor Chasma",
|
||||
FilePath = "map_datasets/CTX/West_Candor_Chasma_longlat_global.vrt",
|
||||
--Enabled = true,
|
||||
},
|
||||
|
||||
{
|
||||
Name = "MER_Meridianni_Endeavor_Basemap_25cm",
|
||||
FilePath = "map_datasets/Basemap/MER_Meridianni_Endeavor_Basemap_25cm.vrt",
|
||||
@@ -115,41 +132,49 @@ return {
|
||||
},
|
||||
HeightLayers = {
|
||||
{
|
||||
Name = "Mola Elevation",
|
||||
FilePath = "map_service_configs/Utah/Mola_Elevation.xml",
|
||||
Name = "Mola Elevation [Europe]",
|
||||
FilePath = "map_service_configs/Mola_Elevation.xml",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 90,
|
||||
TilePixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
--[[
|
||||
{
|
||||
Name = "Mola Elevation [Utah]",
|
||||
FilePath = "map_service_configs/Utah/Mola_Elevation.xml",
|
||||
Enabled = false,
|
||||
TilePixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
{
|
||||
Name = "Mola Elevation CTX",
|
||||
FilePath = "map_service_configs/Utah/MolaCTX_Elevation.xml",
|
||||
-- Enabled = true,
|
||||
MinimumPixelSize = 90,
|
||||
TilePixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
},]]
|
||||
{
|
||||
Name = "Layered Rock Outcrops in Southwest Candor Chasma",
|
||||
FilePath = "map_datasets/HiRISE/Layered_Rock_Outcrops_in_Southwest_Candor_Chasma_Heightmap.vrt",
|
||||
MinimumPixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
--[[
|
||||
{
|
||||
Name = "Mola Elevation",
|
||||
FilePath = "map_service_configs/Mars_MGS_MOLA_DEM.xml",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 64,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
--]]
|
||||
--[[ {
|
||||
Name = "West Candor Chasma",
|
||||
FilePath = "map_datasets/CTX/West_Candor_Chasma_DEM_longlat_global.vrt",
|
||||
--Enabled = true,
|
||||
MinimumPixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
{
|
||||
Name = "Layered Rock Outcrops in Southwest Candor Chasma",
|
||||
FilePath = "map_datasets/HiRISE/Layered_Rock_Outcrops_in_Southwest_Candor_Chasma_Heightmap.vrt",
|
||||
TilePixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},
|
||||
--[[
|
||||
{
|
||||
Name = "West Candor Chasma",
|
||||
FilePath = "map_datasets/CTX/West_Candor_Chasma_DEM_longlat_global.vrt",
|
||||
--Enabled = true,
|
||||
TilePixelSize = 90,
|
||||
DoPreProcessing = true,
|
||||
},]]
|
||||
--[[
|
||||
{
|
||||
Name = "Part of Area Traversed by the Mars Exploration Rover",
|
||||
FilePath = "map_datasets/HiRISE/Part_of_Area_Traversed_by_the_Mars_Exploration_Rover_Heightmap.vrt",
|
||||
|
||||
@@ -42,7 +42,7 @@ return {
|
||||
Name = "Simple Texture",
|
||||
FilePath = "textures/mercury.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 256,
|
||||
TilePixelSize = 256,
|
||||
},
|
||||
{
|
||||
Name = "Messenger_Mosaic",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<TileLevel>7</TileLevel>
|
||||
<YOrigin>top</YOrigin>
|
||||
</DataWindow>
|
||||
<DataType>Int16</DataType>
|
||||
<Projection>GEOGCS["GCS_Moon_2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]]</Projection>
|
||||
<BlockSizeX>360</BlockSizeX>
|
||||
<BlockSizeY>360</BlockSizeY>
|
||||
|
||||
@@ -20,8 +20,9 @@ return {
|
||||
Type = "RenderableGlobe",
|
||||
Radii = {1738140, 1738140, 1735970}, -- Moons's radius
|
||||
CameraMinHeight = 300,
|
||||
InteractionDepthBelowEllipsoid = 5000, -- Useful when having negative height map values
|
||||
SegmentsPerPatch = 64,
|
||||
-- Allows camera to go down 10000 meters below the reference ellipsoid
|
||||
InteractionDepthBelowEllipsoid = 10000, -- Useful when having negative height map values
|
||||
Layers = {
|
||||
ColorLayers = {
|
||||
|
||||
@@ -63,10 +64,13 @@ return {
|
||||
FilePath = "map_service_configs/OnMoonHeight.xml",
|
||||
Enabled = true,
|
||||
DoPreProcessing = true,
|
||||
TileSize = 64,
|
||||
},
|
||||
{
|
||||
Name = "LolaDem",
|
||||
FilePath = "map_service_configs/Utah/LolaDem.wms"
|
||||
FilePath = "map_service_configs/Utah/LolaDem.wms",
|
||||
DoPreProcessing = true,
|
||||
TileSize = 64,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ return {
|
||||
Name = "Texture",
|
||||
FilePath = "textures/neptune.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 256,
|
||||
TilePixelSize = 256,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -40,7 +40,7 @@ return {
|
||||
Name = "Saturn Texture",
|
||||
FilePath = "textures/saturn.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 256,
|
||||
TilePixelSize = 256,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -40,7 +40,7 @@ return {
|
||||
Name = "Texture",
|
||||
FilePath = "textures/uranus.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 256,
|
||||
TilePixelSize = 256,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -45,7 +45,7 @@ return {
|
||||
Name = "Venus Texture",
|
||||
FilePath = "textures/venus.jpg",
|
||||
Enabled = true,
|
||||
MinimumPixelSize = 256,
|
||||
TilePixelSize = 256,
|
||||
},
|
||||
},
|
||||
GrayScaleLayers = { },
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/lrucache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/lrucache.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarecacheable.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarelrucache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarelrucache.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawaretilecache.h
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunk.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunknode.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/chunklevelevaluator.h
|
||||
@@ -57,8 +64,6 @@ set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/distanceswitch.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/lrucache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/lrucache.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.inl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/other/threadpool.h
|
||||
@@ -79,15 +84,13 @@ set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layermanager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layerrendersettings.h
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctilereader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctiledataprovider.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/chunktile.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/pixelregion.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtile.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/textureformat.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tile.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatalayout.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledataset.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatatype.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledepthtransform.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilediskcache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileindex.h
|
||||
@@ -106,19 +109,26 @@ set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileprovider.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbyindex.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbylevel.h
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/rawtiledatareader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalrawtiledatareader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/simplerawtiledatareader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalwrapper.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/iodescription.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/tiledatatype.h
|
||||
)
|
||||
|
||||
set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawaretilecache.cpp
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunk.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunknode.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/availabletiledataevaluator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/distanceevaluator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/projectedareaevaluator.cpp
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/frustumculler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/horizonculler.cpp
|
||||
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/geometry/aabb.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/geometry/ellipsoid.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/geometry/geodetic2.cpp
|
||||
@@ -153,13 +163,10 @@ set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layermanager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layerrendersettings.cpp
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctilereader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctiledataprovider.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/pixelregion.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatalayout.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledataset.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatatype.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilediskcache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileindex.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilemetadata.cpp
|
||||
@@ -175,6 +182,13 @@ set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileprovider.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbyindex.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbylevel.cpp
|
||||
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/rawtiledatareader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalrawtiledatareader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/simplerawtiledatareader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalwrapper.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/iodescription.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/tiledatatype.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
@@ -201,31 +215,34 @@ create_new_module(
|
||||
${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_include_directories(
|
||||
openspace-module-globebrowsing
|
||||
SYSTEM PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/include
|
||||
)
|
||||
option(OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL "Use GDAL" ON)
|
||||
|
||||
target_link_libraries(
|
||||
openspace-module-globebrowsing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal_i.lib
|
||||
)
|
||||
if (OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL)
|
||||
if (WIN32)
|
||||
target_include_directories(
|
||||
openspace-module-globebrowsing
|
||||
SYSTEM PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/include
|
||||
)
|
||||
|
||||
set(EXTERNAL_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal201.dll" PARENT_SCOPE)
|
||||
target_link_libraries(
|
||||
openspace-module-globebrowsing
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal_i.lib
|
||||
)
|
||||
set(EXTERNAL_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal201.dll" PARENT_SCOPE)
|
||||
else (WIN32)
|
||||
find_package(GDAL REQUIRED)
|
||||
|
||||
else (WIN32)
|
||||
find_package(GDAL REQUIRED)
|
||||
target_include_directories(
|
||||
openspace-module-globebrowsing
|
||||
SYSTEM PUBLIC
|
||||
${GDAL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
openspace-module-globebrowsing
|
||||
SYSTEM PUBLIC
|
||||
${GDAL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
openspace-module-globebrowsing
|
||||
${GDAL_LIBRARY}
|
||||
)
|
||||
endif ()
|
||||
target_link_libraries(
|
||||
openspace-module-globebrowsing
|
||||
${GDAL_LIBRARY}
|
||||
)
|
||||
endif () # WIN32
|
||||
target_compile_definitions(openspace-module-globebrowsing PRIVATE GLOBEBROWSING_USE_GDAL)
|
||||
endif () # OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL
|
||||
|
||||
@@ -30,11 +30,18 @@
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
// Templated class implementing a Least-Recently-Used Cache
|
||||
/**
|
||||
* Templated class implementing a Least-Recently-Used Cache.
|
||||
* <code>KeyType</code> needs to be an enumerable type.
|
||||
*/
|
||||
template<typename KeyType, typename ValueType>
|
||||
class LRUCache {
|
||||
public:
|
||||
/**
|
||||
* \param size is the maximum size of the cache given in number of cached items.
|
||||
*/
|
||||
LRUCache(size_t size);
|
||||
|
||||
void put(const KeyType& key, const ValueType& value);
|
||||
@@ -51,9 +58,10 @@ private:
|
||||
size_t _cacheSize;
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#include <modules/globebrowsing/other/lrucache.inl>
|
||||
#include <modules/globebrowsing/cache/lrucache.inl>
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___LRU_CACHE___H__
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
template<typename KeyType, typename ValueType>
|
||||
LRUCache<KeyType, ValueType>::LRUCache(size_t size)
|
||||
@@ -78,5 +79,6 @@ void LRUCache<KeyType, ValueType>::clean() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
58
modules/globebrowsing/cache/memoryawarecacheable.h
vendored
Normal file
58
modules/globebrowsing/cache/memoryawarecacheable.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___MEMORY_AWARE_CACHEABLE___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
/**
|
||||
* Base class to be extended by classes that need to be cached and make use of the
|
||||
* memoryImpact interface. A class extending <code>MemoryAwareCacheable</code> needs to
|
||||
* know its memory impact at initialization and hence provide the memory impact in its
|
||||
* constructors. The memory impact can not change during the lifetime of an object that is
|
||||
* a <code>MemoryAwareCacheable</code>.
|
||||
*/
|
||||
class MemoryAwareCacheable {
|
||||
public:
|
||||
/**
|
||||
* \param memoryImpact is the memory impact of the object. Can for example be given
|
||||
* in kilobytes.
|
||||
*/
|
||||
MemoryAwareCacheable(size_t memoryImpact) : _memoryImpact(memoryImpact) {};
|
||||
~MemoryAwareCacheable() {};
|
||||
|
||||
size_t memoryImpact() { return _memoryImpact; };
|
||||
|
||||
protected:
|
||||
size_t _memoryImpact;
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
|
||||
83
modules/globebrowsing/cache/memoryawarelrucache.h
vendored
Normal file
83
modules/globebrowsing/cache/memoryawarelrucache.h
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___MEMORY_AWARE_LRU_CACHE___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
/**
|
||||
* Least recently used cache that knows about its memory impact. This class is templated
|
||||
* and the second template argument <code>ValueType</code> needs to have a function
|
||||
* <code>void memoryImpact()</code> that returns the size of the object given in whatever
|
||||
* unit is used for size in the creation of the <code>MemoryAwareLRUCache</code>.
|
||||
* It can for example be given in kilobytes.
|
||||
* <code>KeyType</code> needs to be a size comparable type.
|
||||
*/
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
class MemoryAwareLRUCache {
|
||||
public:
|
||||
/**
|
||||
* \param maximumSize is the maximum size of the <code>MemoryAwareLRUCache</code>
|
||||
* Once the maximum size is reached, the cache will start removing objects that were
|
||||
* least recently used. The maximum size can for example be given in kilobytes. It
|
||||
* must be the same size unit as used by the cached object class
|
||||
* <code>ValueType</code>.
|
||||
*/
|
||||
MemoryAwareLRUCache(size_t maximumSize);
|
||||
|
||||
void put(const KeyType& key, const ValueType& value);
|
||||
void clear();
|
||||
bool exist(const KeyType& key) const;
|
||||
ValueType get(const KeyType& key);
|
||||
size_t size() const;
|
||||
size_t maximumSize() const;
|
||||
|
||||
void setMaximumSize(size_t maximumSize);
|
||||
|
||||
private:
|
||||
void clean();
|
||||
|
||||
using Item = std::pair<KeyType, ValueType>;
|
||||
using Items = std::list<Item>;
|
||||
Items _itemList;
|
||||
std::unordered_map<KeyType, decltype(_itemList.begin()), HasherType> _itemMap;
|
||||
|
||||
size_t _cacheSize;
|
||||
size_t _maximumCacheSize;
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#include <modules/globebrowsing/cache/memoryawarelrucache.inl>
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
|
||||
100
modules/globebrowsing/cache/memoryawarelrucache.inl
vendored
Normal file
100
modules/globebrowsing/cache/memoryawarelrucache.inl
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <ghoul/misc/assert.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
MemoryAwareLRUCache<KeyType, ValueType, HasherType>::MemoryAwareLRUCache(size_t maximumSize)
|
||||
: _maximumCacheSize(maximumSize)
|
||||
, _cacheSize(0)
|
||||
{}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::clear() {
|
||||
_itemList.clear();
|
||||
_itemMap.clear();
|
||||
_cacheSize = 0;
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::put(const KeyType& key, const ValueType& value) {
|
||||
auto it = _itemMap.find(key);
|
||||
if (it != _itemMap.end()) {
|
||||
_cacheSize -= it->second->second.memoryImpact();
|
||||
_itemList.erase(it->second);
|
||||
_itemMap.erase(it);
|
||||
}
|
||||
_itemList.emplace_front(key, value);
|
||||
_itemMap.emplace(key, _itemList.begin());
|
||||
_cacheSize += _itemList.begin()->second.memoryImpact();
|
||||
clean();
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
bool MemoryAwareLRUCache<KeyType, ValueType, HasherType>::exist(const KeyType& key) const {
|
||||
return _itemMap.count(key) > 0;
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
ValueType MemoryAwareLRUCache<KeyType, ValueType, HasherType>::get(const KeyType& key) {
|
||||
//ghoul_assert(exist(key), "Key " << key << " must exist");
|
||||
auto it = _itemMap.at(key);
|
||||
// Move list iterator pointing to value
|
||||
_itemList.splice(_itemList.begin(), _itemList, it);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
size_t MemoryAwareLRUCache<KeyType, ValueType, HasherType>::size() const {
|
||||
return _cacheSize;
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
size_t MemoryAwareLRUCache<KeyType, ValueType, HasherType>::maximumSize() const {
|
||||
return _maximumCacheSize;
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::setMaximumSize(size_t maximumSize) {
|
||||
_maximumCacheSize = maximumSize;
|
||||
}
|
||||
|
||||
template<typename KeyType, typename ValueType, typename HasherType>
|
||||
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::clean() {
|
||||
while (_cacheSize > _maximumCacheSize) {
|
||||
auto last_it = _itemList.end();
|
||||
last_it--;
|
||||
_itemMap.erase(last_it->first);
|
||||
_cacheSize -= last_it->second.memoryImpact();
|
||||
_itemList.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
84
modules/globebrowsing/cache/memoryawaretilecache.cpp
vendored
Normal file
84
modules/globebrowsing/cache/memoryawaretilecache.cpp
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/cache/memoryawaretilecache.h>
|
||||
|
||||
#include <ghoul/ghoul.h>
|
||||
#include <ghoul/logging/consolelog.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
MemoryAwareTileCache* MemoryAwareTileCache::_singleton = nullptr;
|
||||
std::mutex MemoryAwareTileCache::_mutexLock;
|
||||
|
||||
void MemoryAwareTileCache::create(size_t cacheSize) {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
_singleton = new MemoryAwareTileCache(cacheSize);
|
||||
}
|
||||
|
||||
void MemoryAwareTileCache::destroy() {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
delete _singleton;
|
||||
}
|
||||
|
||||
MemoryAwareTileCache& MemoryAwareTileCache::ref() {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
ghoul_assert(_singleton, "MemoryAwareTileCache not created");
|
||||
return *_singleton;
|
||||
}
|
||||
|
||||
void MemoryAwareTileCache::clear() {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
_tileCache.clear();
|
||||
}
|
||||
|
||||
bool MemoryAwareTileCache::exist(ProviderTileKey key) const {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
return _tileCache.exist(key);
|
||||
}
|
||||
|
||||
Tile MemoryAwareTileCache::get(ProviderTileKey key) {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
return _tileCache.get(key);
|
||||
}
|
||||
|
||||
void MemoryAwareTileCache::put(ProviderTileKey key, Tile tile) {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
_tileCache.put(key, tile);
|
||||
}
|
||||
|
||||
void MemoryAwareTileCache::setMaximumSize(size_t maximumSize) {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
_tileCache.setMaximumSize(maximumSize);
|
||||
}
|
||||
|
||||
MemoryAwareTileCache::MemoryAwareTileCache(size_t cacheSize)
|
||||
: _tileCache(cacheSize) {}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
113
modules/globebrowsing/cache/memoryawaretilecache.h
vendored
Normal file
113
modules/globebrowsing/cache/memoryawaretilecache.h
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___MEMORY_AWARE_TILE_CACHE___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tileindex.h>
|
||||
#include <modules/globebrowsing/cache/memoryawarelrucache.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace cache {
|
||||
|
||||
struct ProviderTileKey {
|
||||
TileIndex tileIndex;
|
||||
unsigned int providerID;
|
||||
|
||||
bool operator==(const ProviderTileKey& r) const {
|
||||
return (providerID == r.providerID) &&
|
||||
(tileIndex == r.tileIndex);
|
||||
}
|
||||
};
|
||||
|
||||
struct ProviderTileHasher {
|
||||
/**
|
||||
Creates a hash which can be used as key in hash maps.
|
||||
First set the bits to be unique for all tiles.
|
||||
+-------+------------+-------+------------+
|
||||
| USAGE | BIT RANGE | #BITS | MAX VALUE |
|
||||
+-------+------------+-------+------------+
|
||||
| level | 0 - 5 | 5 | 31 |
|
||||
| x | 5 - 35 | 30 | 1073741824 |
|
||||
| y | 35 - 64 | 29 | 536870912 |
|
||||
+-------+------------+-------+------------+
|
||||
|
||||
Bits are then shifted depending on the tile provider used.
|
||||
*/
|
||||
unsigned long long operator()(const ProviderTileKey& t) const {
|
||||
unsigned long long key = 0;
|
||||
key |= static_cast<unsigned long long>(t.tileIndex.level);
|
||||
key |= static_cast<unsigned long long>(t.tileIndex.x << 5);
|
||||
key |= static_cast<unsigned long long>(t.tileIndex.y << 35);
|
||||
// Now the key is unique for all tiles, however not for all tile providers.
|
||||
// Add to the key depending on the tile provider to avoid some hash collisions.
|
||||
// (All hash collisions can not be avoided due to the limit in 64 bit for the
|
||||
// hash key)
|
||||
// Idea: make some offset in the place of the bits for the x value. Lesser chance
|
||||
// of having different x-value than having different tile provider ids.
|
||||
key += static_cast<unsigned long long>(t.providerID << 25);
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Singleton class used to cache tiles for all <code>CachingTileProvider</code>s.
|
||||
*/
|
||||
class MemoryAwareTileCache {
|
||||
public:
|
||||
static void create(size_t cacheSize);
|
||||
static void destroy();
|
||||
|
||||
void clear();
|
||||
bool exist(ProviderTileKey key) const;
|
||||
Tile get(ProviderTileKey key);
|
||||
void put(ProviderTileKey key, Tile tile);
|
||||
|
||||
void setMaximumSize(size_t maximumSize);
|
||||
|
||||
static MemoryAwareTileCache& ref();
|
||||
|
||||
private:
|
||||
/**
|
||||
* \param cacheSize is the cache size given in bytes.
|
||||
*/
|
||||
MemoryAwareTileCache(size_t cacheSize);
|
||||
~MemoryAwareTileCache() = default;
|
||||
|
||||
static MemoryAwareTileCache* _singleton;
|
||||
MemoryAwareLRUCache<ProviderTileKey, Tile, ProviderTileHasher> _tileCache;
|
||||
static std::mutex _mutexLock;
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <modules/globebrowsing/rendering/layer/layermanager.h>
|
||||
#include <modules/globebrowsing/tile/tileselector.h>
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
#include <modules/globebrowsing/rendering/layer/layergroup.h>
|
||||
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
@@ -83,6 +85,8 @@ Chunk::Status Chunk::update(const RenderData& data) {
|
||||
}
|
||||
|
||||
Chunk::BoundingHeights Chunk::getBoundingHeights() const {
|
||||
using ChunkTileSettingsPair = std::pair<ChunkTile, const LayerRenderSettings*>;
|
||||
|
||||
BoundingHeights boundingHeights {
|
||||
0.f, 0.f,
|
||||
false
|
||||
@@ -90,50 +94,58 @@ Chunk::BoundingHeights Chunk::getBoundingHeights() const {
|
||||
|
||||
// In the future, this should be abstracted away and more easily queryable.
|
||||
// One must also handle how to sample pick one out of multiplte heightmaps
|
||||
auto layerManager = owner().chunkedLodGlobe()->layerManager();
|
||||
|
||||
std::shared_ptr<LayerManager> layerManager =
|
||||
owner().chunkedLodGlobe()->layerManager();
|
||||
|
||||
// The raster of a height map is the first one. We assume that the height map is
|
||||
// a single raster image. If it is not we will just use the first raster
|
||||
// (that is channel 0).
|
||||
const size_t HeightChannel = 0;
|
||||
const LayerGroup& heightmaps = layerManager->layerGroup(LayerManager::HeightLayers);
|
||||
std::vector<ChunkTile> chunkTiles = tileselector::getTilesSortedByHighestResolution(
|
||||
heightmaps, _tileIndex
|
||||
);
|
||||
std::vector<ChunkTileSettingsPair> chunkTileSettingPairs =
|
||||
tileselector::getTilesAndSettingsSortedByHighestResolution(
|
||||
heightmaps, _tileIndex);
|
||||
|
||||
bool lastHadMissingData = true;
|
||||
for (const auto& chunkTile : chunkTiles) {
|
||||
bool goodTile = (chunkTile.tile.status == Tile::Status::OK);
|
||||
bool hasTileMetaData = (chunkTile.tile.metaData != nullptr);
|
||||
for (const ChunkTileSettingsPair& chunkTileSettingsPair : chunkTileSettingPairs) {
|
||||
ChunkTile chunkTile = chunkTileSettingsPair.first;
|
||||
const LayerRenderSettings* settings = chunkTileSettingsPair.second;
|
||||
bool goodTile = (chunkTile.tile.status() == Tile::Status::OK);
|
||||
bool hasTileMetaData = (chunkTile.tile.metaData() != nullptr);
|
||||
|
||||
if (goodTile && hasTileMetaData) {
|
||||
auto tileMetaData = chunkTile.tile.metaData;
|
||||
std::shared_ptr<TileMetaData> tileMetaData = chunkTile.tile.metaData();
|
||||
|
||||
float minValue =
|
||||
settings->performLayerSettings(tileMetaData->minValues[HeightChannel]);
|
||||
float maxValue =
|
||||
settings->performLayerSettings(tileMetaData->maxValues[HeightChannel]);
|
||||
|
||||
if (!boundingHeights.available) {
|
||||
if (tileMetaData->hasMissingData[HeightChannel]) {
|
||||
boundingHeights.min = std::min(
|
||||
DEFAULT_HEIGHT,
|
||||
tileMetaData->minValues[HeightChannel]
|
||||
minValue
|
||||
);
|
||||
boundingHeights.max = std::max(
|
||||
DEFAULT_HEIGHT,
|
||||
tileMetaData->maxValues[HeightChannel]
|
||||
maxValue
|
||||
);
|
||||
}
|
||||
else {
|
||||
boundingHeights.min = tileMetaData->minValues[HeightChannel];
|
||||
boundingHeights.max = tileMetaData->maxValues[HeightChannel];
|
||||
boundingHeights.min = minValue;
|
||||
boundingHeights.max = maxValue;
|
||||
}
|
||||
boundingHeights.available = true;
|
||||
}
|
||||
else {
|
||||
boundingHeights.min = std::min(
|
||||
boundingHeights.min,
|
||||
tileMetaData->minValues[HeightChannel]
|
||||
minValue
|
||||
);
|
||||
boundingHeights.max = std::max(
|
||||
boundingHeights.max,
|
||||
tileMetaData->maxValues[HeightChannel]
|
||||
maxValue
|
||||
);
|
||||
}
|
||||
lastHadMissingData = tileMetaData->hasMissingData[HeightChannel];
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
|
||||
#include <modules/globebrowsing/globebrowsingmodule.h>
|
||||
|
||||
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
|
||||
#include <modules/globebrowsing/globes/renderableglobe.h>
|
||||
#include <modules/globebrowsing/other/distanceswitch.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/cachingtileprovider.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/singleimageprovider.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/sizereferencetileprovider.h>
|
||||
@@ -36,19 +38,67 @@
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileproviderbylevel.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileproviderbyindex.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/rendering/renderable.h>
|
||||
#include <openspace/util/factorymanager.h>
|
||||
|
||||
#include <ghoul/misc/templatefactory.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
|
||||
#include <ghoul/systemcapabilities/generalcapabilitiescomponent.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
GlobeBrowsingModule::GlobeBrowsingModule() : OpenSpaceModule("GlobeBrowsing") {}
|
||||
GlobeBrowsingModule::GlobeBrowsingModule()
|
||||
: OpenSpaceModule("GlobeBrowsing")
|
||||
, _openSpaceMaximumTileCacheSize(
|
||||
"maximumTileCacheSize", "Maximum tile cache size",
|
||||
512, // Default: 512 MB
|
||||
0, // Minimum: No caching
|
||||
1024, // Maximum: 1024 MB
|
||||
1) // Step: One MB
|
||||
, _clearTileCache("clearTileCache", "Clear tile cache") {}
|
||||
|
||||
void GlobeBrowsingModule::internalInitialize() {
|
||||
using namespace globebrowsing;
|
||||
|
||||
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Initialize, [&] {
|
||||
// Set maximum cache size to 25% of total RAM
|
||||
_openSpaceMaximumTileCacheSize.setMaxValue(CpuCap.installedMainMemory() * 0.25);
|
||||
|
||||
// Convert from MB to KB
|
||||
cache::MemoryAwareTileCache::create(_openSpaceMaximumTileCacheSize * 1024);
|
||||
_openSpaceMaximumTileCacheSize.onChange(
|
||||
[&]{
|
||||
// Convert from MB to KB
|
||||
cache::MemoryAwareTileCache::ref().setMaximumSize(
|
||||
_openSpaceMaximumTileCacheSize * 1024);
|
||||
});
|
||||
_clearTileCache.onChange(
|
||||
[&]{
|
||||
cache::MemoryAwareTileCache::ref().clear();
|
||||
});
|
||||
|
||||
addProperty(_openSpaceMaximumTileCacheSize);
|
||||
addProperty(_clearTileCache);
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
// Convert from MB to Bytes
|
||||
GdalWrapper::create(
|
||||
16ULL * 1024ULL * 1024ULL, // 16 MB
|
||||
CpuCap.installedMainMemory() * 0.25 * 1024 * 1024); // 25% of total RAM
|
||||
addPropertySubOwner(GdalWrapper::ref());
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
});
|
||||
|
||||
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Deinitialize, [&]{
|
||||
cache::MemoryAwareTileCache::ref().clear();
|
||||
cache::MemoryAwareTileCache::ref().destroy();
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
GdalWrapper::ref().destroy();
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
});
|
||||
|
||||
auto fRenderable = FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "Renderable factory was not created");
|
||||
fRenderable->registerClass<globebrowsing::RenderableGlobe>("RenderableGlobe");
|
||||
@@ -58,7 +108,10 @@ void GlobeBrowsingModule::internalInitialize() {
|
||||
|
||||
fTileProvider->registerClass<tileprovider::CachingTileProvider>("LRUCaching");
|
||||
fTileProvider->registerClass<tileprovider::SingleImageProvider>("SingleImage");
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
fTileProvider->registerClass<tileprovider::TemporalTileProvider>("Temporal");
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
fTileProvider->registerClass<tileprovider::TileIndexTileProvider>("TileIndex");
|
||||
fTileProvider->registerClass<tileprovider::SizeReferenceTileProvider>("SizeReference");
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___GLOBEBROWSING_MODULE___H__
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
#include <openspace/properties/scalarproperty.h>
|
||||
#include <openspace/properties/triggerproperty.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
@@ -35,6 +39,9 @@ public:
|
||||
|
||||
protected:
|
||||
void internalInitialize() override;
|
||||
private:
|
||||
properties::IntProperty _openSpaceMaximumTileCacheSize;
|
||||
properties::TriggerProperty _clearTileCache;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -143,7 +143,8 @@ int ChunkedLodGlobe::getDesiredLevel(
|
||||
int desiredLevelByAvailableData = _chunkEvaluatorByAvailableTiles->getDesiredLevel(
|
||||
chunk, renderData
|
||||
);
|
||||
if (desiredLevelByAvailableData != chunklevelevaluator::Evaluator::UnknownDesiredLevel) {
|
||||
if (desiredLevelByAvailableData != chunklevelevaluator::Evaluator::UnknownDesiredLevel &&
|
||||
_owner.debugProperties().limitLevelByAvailableData) {
|
||||
desiredLevel = glm::min(desiredLevel, desiredLevelByAvailableData);
|
||||
}
|
||||
|
||||
@@ -180,14 +181,14 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
|
||||
const auto& tile = chunkTile.tile;
|
||||
const auto& uvTransform = chunkTile.uvTransform;
|
||||
const auto& depthTransform = tileProvider->depthTransform();
|
||||
if (tile.status != Tile::Status::OK) {
|
||||
if (tile.status() != Tile::Status::OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
glm::vec2 transformedUv = Tile::TileUvToTextureSamplePosition(
|
||||
uvTransform,
|
||||
patchUV,
|
||||
glm::uvec2(tile.texture->dimensions())
|
||||
glm::uvec2(tile.texture()->dimensions())
|
||||
);
|
||||
|
||||
// Sample and do linear interpolation
|
||||
@@ -195,7 +196,7 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
|
||||
// Suggestion: a function in ghoul::opengl::Texture that takes uv coordinates
|
||||
// in range [0,1] and uses the set interpolation method and clamping.
|
||||
|
||||
glm::uvec3 dimensions = tile.texture->dimensions();
|
||||
glm::uvec3 dimensions = tile.texture()->dimensions();
|
||||
|
||||
glm::vec2 samplePos = transformedUv * glm::vec2(dimensions);
|
||||
glm::uvec2 samplePos00 = samplePos;
|
||||
@@ -219,10 +220,10 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
|
||||
glm::uvec2(dimensions) - glm::uvec2(1)
|
||||
);
|
||||
|
||||
float sample00 = tile.texture->texelAsFloat(samplePos00).x;
|
||||
float sample10 = tile.texture->texelAsFloat(samplePos10).x;
|
||||
float sample01 = tile.texture->texelAsFloat(samplePos01).x;
|
||||
float sample11 = tile.texture->texelAsFloat(samplePos11).x;
|
||||
float sample00 = tile.texture()->texelAsFloat(samplePos00).x;
|
||||
float sample10 = tile.texture()->texelAsFloat(samplePos10).x;
|
||||
float sample01 = tile.texture()->texelAsFloat(samplePos01).x;
|
||||
float sample11 = tile.texture()->texelAsFloat(samplePos11).x;
|
||||
|
||||
// In case the texture has NaN or no data values don't use this height map.
|
||||
bool anySampleIsNaN =
|
||||
@@ -248,6 +249,10 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
|
||||
|
||||
// Perform depth transform to get the value in meters
|
||||
height = depthTransform.depthOffset + depthTransform.depthScale * sample;
|
||||
// Make sure that the height value follows the layer settings.
|
||||
// For example if the multiplier is set to a value bigger than one,
|
||||
// the sampled height should be modified as well.
|
||||
height = layer->renderSettings().performLayerSettings(height);
|
||||
}
|
||||
// Return the result
|
||||
return height;
|
||||
|
||||
@@ -61,11 +61,12 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
|
||||
BoolProperty("showHeightIntensities", "show height intensities", false),
|
||||
BoolProperty("performFrustumCulling", "perform frustum culling", true),
|
||||
BoolProperty("performHorizonCulling", "perform horizon culling", true),
|
||||
BoolProperty("levelByProjectedAreaElseDistance", "level by projected area (else distance)",false),
|
||||
BoolProperty("levelByProjectedAreaElseDistance", "level by projected area (else distance)", true),
|
||||
BoolProperty("resetTileProviders", "reset tile providers", false),
|
||||
BoolProperty("toggleEnabledEveryFrame", "toggle enabled every frame", false),
|
||||
BoolProperty("collectStats", "collect stats", false),
|
||||
BoolProperty("onlyModelSpaceRendering", "Only Model Space Rendering", false)
|
||||
BoolProperty("limitLevelByAvailableData", "Limit level by available data", true),
|
||||
IntProperty("modelSpaceRenderingCutoffLevel", "Model Space Rendering Cutoff Level", 10, 1, 22)
|
||||
})
|
||||
, _texturePropertyOwner("Textures")
|
||||
{
|
||||
@@ -84,16 +85,21 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
|
||||
dictionary.getValue(keySegmentsPerPatch, patchSegmentsd);
|
||||
int patchSegments = patchSegmentsd;
|
||||
|
||||
dictionary.getValue(keyInteractionDepthBelowEllipsoid,
|
||||
_interactionDepthBelowEllipsoid);
|
||||
if (!dictionary.getValue(keyInteractionDepthBelowEllipsoid,
|
||||
_interactionDepthBelowEllipsoid)) {
|
||||
_interactionDepthBelowEllipsoid = 0;
|
||||
}
|
||||
|
||||
float cameraMinHeight;
|
||||
dictionary.getValue(keyCameraMinHeight, cameraMinHeight);
|
||||
_generalProperties.cameraMinHeight.set(cameraMinHeight);
|
||||
|
||||
// Init layer manager
|
||||
ghoul::Dictionary layersDictionary;
|
||||
if (!dictionary.getValue(keyLayers, layersDictionary))
|
||||
throw ghoul::RuntimeError(std::string(keyLayers) + " must be specified specified!");
|
||||
if (!dictionary.getValue(keyLayers, layersDictionary)) {
|
||||
throw ghoul::RuntimeError(
|
||||
std::string(keyLayers) + " must be specified specified!");
|
||||
}
|
||||
|
||||
_layerManager = std::make_shared<LayerManager>(layersDictionary);
|
||||
|
||||
@@ -127,8 +133,9 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
|
||||
_debugPropertyOwner.addProperty(_debugProperties.resetTileProviders);
|
||||
_debugPropertyOwner.addProperty(_debugProperties.toggleEnabledEveryFrame);
|
||||
_debugPropertyOwner.addProperty(_debugProperties.collectStats);
|
||||
_debugPropertyOwner.addProperty(_debugProperties.onlyModelSpaceRendering);
|
||||
|
||||
_debugPropertyOwner.addProperty(_debugProperties.limitLevelByAvailableData);
|
||||
_debugPropertyOwner.addProperty(_debugProperties.modelSpaceRenderingCutoffLevel);
|
||||
|
||||
addPropertySubOwner(_debugPropertyOwner);
|
||||
addPropertySubOwner(_layerManager.get());
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <modules/globebrowsing/other/distanceswitch.h>
|
||||
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
@@ -64,7 +65,8 @@ public:
|
||||
properties::BoolProperty resetTileProviders;
|
||||
properties::BoolProperty toggleEnabledEveryFrame;
|
||||
properties::BoolProperty collectStats;
|
||||
properties::BoolProperty onlyModelSpaceRendering;
|
||||
properties::BoolProperty limitLevelByAvailableData;
|
||||
properties::IntProperty modelSpaceRenderingCutoffLevel;
|
||||
};
|
||||
|
||||
struct GeneralProperties {
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace globebrowsing {
|
||||
DistanceSwitch::~DistanceSwitch() {}
|
||||
|
||||
bool DistanceSwitch::initialize() {
|
||||
_objectScale = 1.0;
|
||||
for (int i = 0; i < _renderables.size(); ++i) {
|
||||
_renderables[i]->initialize();
|
||||
}
|
||||
@@ -54,13 +55,13 @@ void DistanceSwitch::render(const RenderData& data) {
|
||||
pss pssDistanceToCamera = (data.camera.position() - data.position).length();
|
||||
double distanceToCamera = pssDistanceToCamera.lengthd();
|
||||
|
||||
if (distanceToCamera > _maxDistances.back()) {
|
||||
if (distanceToCamera > _maxDistances.back() * _objectScale) {
|
||||
return;
|
||||
}
|
||||
|
||||
// linear search through nodes to find which Renderable to render
|
||||
for (int i = 0; i < _renderables.size(); ++i) {
|
||||
if (distanceToCamera < _maxDistances[i]) {
|
||||
if (distanceToCamera < _maxDistances[i] * _objectScale) {
|
||||
_renderables[i]->render(data);
|
||||
return;
|
||||
}
|
||||
@@ -68,6 +69,7 @@ void DistanceSwitch::render(const RenderData& data) {
|
||||
}
|
||||
|
||||
void DistanceSwitch::update(const UpdateData& data) {
|
||||
_objectScale = data.modelTransform.scale;
|
||||
for (int i = 0; i < _renderables.size(); ++i) {
|
||||
_renderables[i]->update(data);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
private:
|
||||
std::vector<std::shared_ptr<Renderable>> _renderables;
|
||||
std::vector<double> _maxDistances;
|
||||
double _objectScale;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <modules/globebrowsing/rendering/layershadermanager.h>
|
||||
#include <modules/globebrowsing/rendering/gpu/gpulayermanager.h>
|
||||
#include <modules/globebrowsing/rendering/layer/layergroup.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
|
||||
namespace {
|
||||
const char* keyFrame = "Frame";
|
||||
@@ -64,7 +65,8 @@ ChunkRenderer::ChunkRenderer(std::shared_ptr<Grid> grid,
|
||||
|
||||
void ChunkRenderer::renderChunk(const Chunk& chunk, const RenderData& data) {
|
||||
// A little arbitrary with 10 but it works
|
||||
if (chunk.owner().debugProperties().onlyModelSpaceRendering || chunk.tileIndex().level < 10) {
|
||||
if (chunk.tileIndex().level <
|
||||
chunk.owner().debugProperties().modelSpaceRenderingCutoffLevel) {
|
||||
renderChunkGlobally(chunk, data);
|
||||
}
|
||||
else {
|
||||
@@ -98,18 +100,25 @@ ghoul::opengl::ProgramObject* ChunkRenderer::getActivatedProgramWithTileData(
|
||||
const auto& debugProps = chunk.owner().debugProperties();
|
||||
auto& pairs = layeredTexturePreprocessingData.keyValuePairs;
|
||||
|
||||
pairs.push_back(std::make_pair("useAtmosphere",
|
||||
std::to_string(generalProps.atmosphereEnabled)));
|
||||
pairs.push_back(std::make_pair("performShading",
|
||||
std::to_string(generalProps.performShading)));
|
||||
pairs.push_back(std::make_pair("showChunkEdges",
|
||||
std::to_string(debugProps.showChunkEdges)));
|
||||
pairs.push_back(std::make_pair("showHeightResolution",
|
||||
std::to_string(debugProps.showHeightResolution)));
|
||||
pairs.push_back(std::make_pair("showHeightIntensities",
|
||||
std::to_string(debugProps.showHeightIntensities)));
|
||||
pairs.push_back(std::make_pair("defaultHeight",
|
||||
std::to_string(Chunk::DEFAULT_HEIGHT)));
|
||||
pairs.emplace_back("useAtmosphere", std::to_string(generalProps.atmosphereEnabled));
|
||||
pairs.emplace_back("performShading", std::to_string(generalProps.performShading));
|
||||
pairs.emplace_back("showChunkEdges", std::to_string(debugProps.showChunkEdges));
|
||||
pairs.emplace_back("showHeightResolution",
|
||||
std::to_string(debugProps.showHeightResolution));
|
||||
pairs.emplace_back("showHeightIntensities",
|
||||
std::to_string(debugProps.showHeightIntensities));
|
||||
pairs.emplace_back("defaultHeight", std::to_string(Chunk::DEFAULT_HEIGHT));
|
||||
|
||||
pairs.emplace_back("tilePaddingStart",
|
||||
"ivec2(" +
|
||||
std::to_string(RawTileDataReader::padding.start.x) + "," +
|
||||
std::to_string(RawTileDataReader::padding.start.y) + ")"
|
||||
);
|
||||
pairs.emplace_back("tilePaddingSizeDiff",
|
||||
"ivec2(" +
|
||||
std::to_string(RawTileDataReader::padding.numPixels.x) + "," +
|
||||
std::to_string(RawTileDataReader::padding.numPixels.y) + ")"
|
||||
);
|
||||
|
||||
// Now the shader program can be accessed
|
||||
ghoul::opengl::ProgramObject* programObject =
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace globebrowsing {
|
||||
void GPUChunkTile::setValue(ghoul::opengl::ProgramObject* programObject,
|
||||
const ChunkTile& chunkTile)
|
||||
{
|
||||
gpuTexture.setValue(programObject, chunkTile.tile.texture);
|
||||
gpuTexture.setValue(programObject, chunkTile.tile.texture());
|
||||
gpuTileUvTransform.setValue(programObject, chunkTile.uvTransform);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ Layer::Layer(const ghoul::Dictionary& layerDict)
|
||||
addProperty(_enabled);
|
||||
|
||||
addPropertySubOwner(_renderSettings);
|
||||
addPropertySubOwner(*_tileProvider);
|
||||
}
|
||||
|
||||
ChunkTilePile Layer::getChunkTilePile(const TileIndex& tileIndex, int pileSize) const {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace globebrowsing {
|
||||
|
||||
LayerGroup::LayerGroup(std::string name)
|
||||
: properties::PropertyOwner(std::move(name))
|
||||
, _levelBlendingEnabled("blendTileLevels", "blend tile levels", true)
|
||||
, _levelBlendingEnabled("blendTileLevels", "blend tile levels", false)
|
||||
{
|
||||
addProperty(_levelBlendingEnabled);
|
||||
}
|
||||
|
||||
@@ -38,5 +38,25 @@ LayerRenderSettings::LayerRenderSettings()
|
||||
addProperty(multiplier);
|
||||
}
|
||||
|
||||
float LayerRenderSettings::performLayerSettings(float currentValue) const {
|
||||
float newValue = currentValue;
|
||||
|
||||
newValue = glm::sign(newValue) * glm::pow(glm::abs(newValue), gamma);
|
||||
newValue = newValue * multiplier;
|
||||
newValue = newValue * opacity;
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
glm::vec4 LayerRenderSettings::performLayerSettings(glm::vec4 currentValue) const {
|
||||
glm::vec4 newValue = glm::vec4(
|
||||
performLayerSettings(currentValue.r),
|
||||
performLayerSettings(currentValue.g),
|
||||
performLayerSettings(currentValue.b),
|
||||
performLayerSettings(currentValue.a));
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
@@ -37,6 +37,13 @@ struct LayerRenderSettings : public properties::PropertyOwner {
|
||||
properties::FloatProperty opacity;
|
||||
properties::FloatProperty gamma;
|
||||
properties::FloatProperty multiplier;
|
||||
|
||||
/// This function matches the function with the same name in the
|
||||
/// shader code
|
||||
float performLayerSettings(float currentValue) const;
|
||||
/// This function matches the function with the same name in the
|
||||
/// shader code
|
||||
glm::vec4 performLayerSettings(glm::vec4 currentValue) const;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
// Must match the values in tiledataset.cpp
|
||||
// (could be set as shader preprocessing data so that multiple definitions
|
||||
// are not needed)
|
||||
#define TILE_PIXEL_START_OFFSET ivec2(-2)
|
||||
#define TILE_PIXEL_SIZE_DIFFERENCE ivec2(4)
|
||||
#define TILE_PIXEL_START_OFFSET #{tilePaddingStart}
|
||||
#define TILE_PIXEL_SIZE_DIFFERENCE #{tilePaddingSizeDiff}
|
||||
|
||||
vec4 patchBorderOverlay(vec2 uv, vec3 borderColor, float borderSize) {
|
||||
vec2 uvOffset = uv - vec2(0.5);
|
||||
@@ -87,7 +87,7 @@ vec2 compensateSourceTextureSampling(vec2 startOffset, vec2 sizeDiff, const Chun
|
||||
|
||||
vec2 TileUVToTextureSamplePosition(const ChunkTile chunkTile, vec2 tileUV){
|
||||
vec2 uv = chunkTile.uvTransform.uvOffset + chunkTile.uvTransform.uvScale * tileUV;
|
||||
uv = compensateSourceTextureSampling(vec2(-2), vec2(4), chunkTile, uv);
|
||||
uv = compensateSourceTextureSampling(TILE_PIXEL_START_OFFSET, TILE_PIXEL_SIZE_DIFFERENCE, chunkTile, uv);
|
||||
return uv;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,28 +22,29 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/globebrowsing/tile/asynctilereader.h>
|
||||
#include <modules/globebrowsing/tile/asynctiledataprovider.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/loadjob/tileloadjob.h>
|
||||
#include <modules/globebrowsing/tile/tiledataset.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
#include <modules/globebrowsing/tile/tilediskcache.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
AsyncTileDataProvider::AsyncTileDataProvider(std::shared_ptr<TileDataset> tileDataset,
|
||||
std::shared_ptr<ThreadPool> pool)
|
||||
: _tileDataset(tileDataset)
|
||||
AsyncTileDataProvider::AsyncTileDataProvider(
|
||||
const std::shared_ptr<RawTileDataReader> rawTileDataReader,
|
||||
std::shared_ptr<ThreadPool> pool)
|
||||
: _rawTileDataReader(rawTileDataReader)
|
||||
, _concurrentJobManager(pool)
|
||||
{}
|
||||
|
||||
std::shared_ptr<TileDataset> AsyncTileDataProvider::getTextureDataProvider() const {
|
||||
return _tileDataset;
|
||||
std::shared_ptr<RawTileDataReader> AsyncTileDataProvider::getRawTileDataReader() const {
|
||||
return _rawTileDataReader;
|
||||
}
|
||||
|
||||
bool AsyncTileDataProvider::enqueueTileIO(const TileIndex& tileIndex) {
|
||||
if (satisfiesEnqueueCriteria(tileIndex)) {
|
||||
auto job = std::make_shared<TileLoadJob>(_tileDataset, tileIndex);
|
||||
auto job = std::make_shared<TileLoadJob>(_rawTileDataReader, tileIndex);
|
||||
//auto job = std::make_shared<DiskCachedTileLoadJob>(_tileDataset, tileIndex, tileDiskCache, "ReadAndWrite");
|
||||
_concurrentJobManager.enqueueJob(job);
|
||||
_enqueuedTileRequests[tileIndex.hashKey()] = tileIndex;
|
||||
@@ -61,6 +62,13 @@ std::vector<std::shared_ptr<RawTile>> AsyncTileDataProvider::getRawTiles() {
|
||||
return readyResults;
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTile> AsyncTileDataProvider::popFinishedRawTile() {
|
||||
if (_concurrentJobManager.numFinishedJobs() > 0)
|
||||
return _concurrentJobManager.popFinishedJob()->product();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AsyncTileDataProvider::satisfiesEnqueueCriteria(const TileIndex& tileIndex) const {
|
||||
// only allow tile to be enqueued if it's not already enqueued
|
||||
//return _futureTileIOResults.find(tileIndex.hashKey()) == _futureTileIOResults.end();
|
||||
@@ -77,7 +85,7 @@ void AsyncTileDataProvider::reset() {
|
||||
while (_concurrentJobManager.numFinishedJobs() > 0) {
|
||||
_concurrentJobManager.popFinishedJob();
|
||||
}
|
||||
getTextureDataProvider()->reset();
|
||||
_rawTileDataReader->reset();
|
||||
}
|
||||
|
||||
void AsyncTileDataProvider::clearRequestQueue() {
|
||||
@@ -87,8 +95,8 @@ void AsyncTileDataProvider::clearRequestQueue() {
|
||||
_enqueuedTileRequests.clear();
|
||||
}
|
||||
|
||||
float AsyncTileDataProvider::noDataValueAsFloat() {
|
||||
return _tileDataset->noDataValueAsFloat();
|
||||
float AsyncTileDataProvider::noDataValueAsFloat() const {
|
||||
return _rawTileDataReader->noDataValueAsFloat();
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
@@ -26,7 +26,6 @@
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___ASYNC_TILE_READER___H__
|
||||
|
||||
#include <modules/globebrowsing/other/concurrentjobmanager.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tileindex.h>
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -34,28 +33,29 @@
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class RawTile;
|
||||
class TileDataset;
|
||||
struct RawTile;
|
||||
class RawTileDataReader;
|
||||
|
||||
class AsyncTileDataProvider {
|
||||
public:
|
||||
AsyncTileDataProvider(std::shared_ptr<TileDataset> textureDataProvider,
|
||||
AsyncTileDataProvider(std::shared_ptr<RawTileDataReader> textureDataProvider,
|
||||
std::shared_ptr<ThreadPool> pool);
|
||||
|
||||
bool enqueueTileIO(const TileIndex& tileIndex);
|
||||
std::vector<std::shared_ptr<RawTile>> getRawTiles();
|
||||
|
||||
std::shared_ptr<RawTile> popFinishedRawTile();
|
||||
|
||||
void reset();
|
||||
void clearRequestQueue();
|
||||
|
||||
std::shared_ptr<TileDataset> getTextureDataProvider() const;
|
||||
float noDataValueAsFloat();
|
||||
std::shared_ptr<RawTileDataReader> getRawTileDataReader() const;
|
||||
float noDataValueAsFloat() const;
|
||||
|
||||
protected:
|
||||
virtual bool satisfiesEnqueueCriteria(const TileIndex&) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<TileDataset> _tileDataset;
|
||||
std::shared_ptr<RawTileDataReader> _rawTileDataReader;
|
||||
ConcurrentJobManager<RawTile> _concurrentJobManager;
|
||||
std::unordered_map<TileIndex::TileHashKey, TileIndex> _enqueuedTileRequests;
|
||||
};
|
||||
@@ -34,6 +34,12 @@ namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
struct ChunkTile {
|
||||
ChunkTile() : tile(Tile::TileUnavailable) {};
|
||||
ChunkTile(Tile tile, TileUvTransform uvTransform, TileDepthTransform depthTransform) :
|
||||
tile(tile),
|
||||
uvTransform(uvTransform),
|
||||
depthTransform(depthTransform) {};
|
||||
|
||||
Tile tile;
|
||||
TileUvTransform uvTransform;
|
||||
TileDepthTransform depthTransform;
|
||||
|
||||
@@ -25,17 +25,16 @@
|
||||
#include <modules/globebrowsing/tile/loadjob/diskcachedtileloadjob.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/tile/tiledataset.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
#include <modules/globebrowsing/tile/tilediskcache.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
DiskCachedTileLoadJob::DiskCachedTileLoadJob(
|
||||
std::shared_ptr<TileDataset> textureDataProvider,
|
||||
const TileIndex& tileIndex,
|
||||
std::shared_ptr<TileDiskCache> tdc,
|
||||
CacheMode m)
|
||||
std::shared_ptr<RawTileDataReader> textureDataProvider,
|
||||
const TileIndex& tileIndex, std::shared_ptr<TileDiskCache> tdc,
|
||||
CacheMode m)
|
||||
: TileLoadJob(textureDataProvider, tileIndex)
|
||||
, _tileDiskCache(tdc)
|
||||
, _mode(m)
|
||||
@@ -46,26 +45,26 @@ void DiskCachedTileLoadJob::execute() {
|
||||
|
||||
switch (_mode) {
|
||||
case CacheMode::Disabled:
|
||||
_rawTile = _tileDataset->readTileData(_chunkIndex);
|
||||
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
|
||||
break;
|
||||
|
||||
case CacheMode::ReadOnly:
|
||||
_rawTile = _tileDiskCache->get(_chunkIndex);
|
||||
if (_rawTile == nullptr) {
|
||||
_rawTile = _tileDataset->readTileData(_chunkIndex);
|
||||
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
|
||||
}
|
||||
break;
|
||||
|
||||
case CacheMode::ReadAndWrite:
|
||||
_rawTile = _tileDiskCache->get(_chunkIndex);
|
||||
if (_rawTile == nullptr) {
|
||||
_rawTile = _tileDataset->readTileData(_chunkIndex);
|
||||
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
|
||||
_tileDiskCache->put(_chunkIndex, _rawTile);
|
||||
}
|
||||
break;
|
||||
|
||||
case CacheMode::WriteOnly:
|
||||
_rawTile = _tileDataset->readTileData(_chunkIndex);
|
||||
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
|
||||
_tileDiskCache->put(_chunkIndex, _rawTile);
|
||||
break;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ struct DiskCachedTileLoadJob : public TileLoadJob {
|
||||
CacheHitsOnly,
|
||||
};
|
||||
|
||||
DiskCachedTileLoadJob(std::shared_ptr<TileDataset> textureDataProvider,
|
||||
DiskCachedTileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
|
||||
const TileIndex& tileIndex, std::shared_ptr<TileDiskCache> tdc,
|
||||
CacheMode cacheMode = CacheMode::ReadOnly);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class RawTile;
|
||||
struct RawTile;
|
||||
|
||||
struct LoadJob : public Job<RawTile> {
|
||||
virtual void execute() = 0;
|
||||
|
||||
@@ -24,13 +24,18 @@
|
||||
|
||||
#include <modules/globebrowsing/tile/loadjob/tileloadjob.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tiledataset.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
TileLoadJob::TileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
|
||||
const TileIndex& tileIndex)
|
||||
: _rawTileDataReader(rawTileDataReader)
|
||||
, _chunkIndex(tileIndex) {}
|
||||
|
||||
void TileLoadJob::execute() {
|
||||
_rawTile = _tileDataset->readTileData(_chunkIndex);
|
||||
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTile> TileLoadJob::product() const {
|
||||
|
||||
@@ -33,15 +33,12 @@
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class TileDataset;
|
||||
class RawTile;
|
||||
class RawTileDataReader;
|
||||
struct RawTile;
|
||||
|
||||
struct TileLoadJob : LoadJob {
|
||||
TileLoadJob(std::shared_ptr<TileDataset> textureDataProvider,
|
||||
const TileIndex& tileIndex)
|
||||
: _tileDataset(textureDataProvider)
|
||||
, _chunkIndex(tileIndex)
|
||||
{}
|
||||
TileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
|
||||
const TileIndex& tileIndex);
|
||||
|
||||
virtual ~TileLoadJob() = default;
|
||||
|
||||
@@ -51,7 +48,7 @@ struct TileLoadJob : LoadJob {
|
||||
|
||||
protected:
|
||||
TileIndex _chunkIndex;
|
||||
std::shared_ptr<TileDataset> _tileDataset;
|
||||
std::shared_ptr<RawTileDataReader> _rawTileDataReader;
|
||||
std::shared_ptr<RawTile> _rawTile;
|
||||
};
|
||||
|
||||
|
||||
@@ -119,10 +119,12 @@ void PixelRegion::scale(double s) {
|
||||
|
||||
void PixelRegion::downscalePow2(int exponent, PixelCoordinate wrt) {
|
||||
start += wrt;
|
||||
start.x >>= exponent;
|
||||
start.y >>= exponent;
|
||||
numPixels.x >>= exponent;
|
||||
numPixels.y >>= exponent;
|
||||
start.x = ceil(start.x / static_cast<float>(pow(2, exponent)));// >>= exponent;
|
||||
start.y = ceil(start.y / static_cast<float>(pow(2, exponent)));// >>= exponent;
|
||||
numPixels.x =
|
||||
ceil(numPixels.x / static_cast<float>(pow(2, exponent)));// >>= exponent;
|
||||
numPixels.y =
|
||||
ceil(numPixels.y / static_cast<float>(pow(2, exponent)));// >>= exponent;
|
||||
start -= wrt;
|
||||
}
|
||||
|
||||
@@ -170,15 +172,20 @@ void PixelRegion::forceNumPixelToDifferByNearestMultipleOf(unsigned int multiple
|
||||
numPixels.y += sizeDiff % multiple;
|
||||
}
|
||||
else {
|
||||
numPixels.x += static_cast<int>(std::abs(static_cast<double>(sizeDiff))) % multiple;
|
||||
numPixels.x += static_cast<int>(
|
||||
std::abs(static_cast<double>(sizeDiff))) % multiple;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PixelRegion::roundUpNumPixelToNearestMultipleOf(unsigned int multiple) {
|
||||
ghoul_assert(multiple > 0, "multiple must be 1 or larger");
|
||||
numPixels.x += numPixels.x % multiple;
|
||||
numPixels.y += numPixels.y % multiple;
|
||||
numPixels.x += (numPixels.x % multiple == 0) ? 0 :
|
||||
(multiple - (numPixels.x % multiple));
|
||||
numPixels.y += (numPixels.y % multiple == 0) ? 0 :
|
||||
(multiple - (numPixels.y % multiple));
|
||||
ghoul_assert((numPixels.x % multiple) == 0, "Round to nearest multiple failed");
|
||||
ghoul_assert((numPixels.y % multiple) == 0, "Round to nearest multiple failed");
|
||||
}
|
||||
|
||||
void PixelRegion::roundDownToQuadratic() {
|
||||
|
||||
@@ -73,8 +73,8 @@ struct PixelRegion {
|
||||
|
||||
void scale(const glm::dvec2& s);
|
||||
void scale(double s);
|
||||
void downscalePow2(int exponent, PixelCoordinate wrt = {0,0});
|
||||
void upscalePow2(int exponent, PixelCoordinate wrt = { 0,0 });
|
||||
void downscalePow2(int exponent, PixelCoordinate wrt = { 0, 0 });
|
||||
void upscalePow2(int exponent, PixelCoordinate wrt = { 0, 0 });
|
||||
|
||||
void move(Side side, int amount);
|
||||
void pad(const PixelRegion& padding);
|
||||
|
||||
@@ -26,10 +26,8 @@
|
||||
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "Tile";
|
||||
const char* _loggerCat = "RawTile";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
@@ -40,7 +38,7 @@ RawTile::RawTile()
|
||||
, dimensions(0, 0, 0)
|
||||
, tileMetaData(nullptr)
|
||||
, tileIndex(0, 0, 0)
|
||||
, error(CE_None)
|
||||
, error(ReadError::None)
|
||||
, nBytesImageData(0)
|
||||
{}
|
||||
|
||||
@@ -58,7 +56,7 @@ RawTile RawTile::createDefaultRes() {
|
||||
void RawTile::serializeMetaData(std::ostream& os) {
|
||||
os << dimensions.x << " " << dimensions.y << " " << dimensions.z << std::endl;
|
||||
os << tileIndex.x << " " << tileIndex.y << " " << tileIndex.level << std::endl;
|
||||
os << error << std::endl;
|
||||
os << static_cast<int>(error) << std::endl;
|
||||
|
||||
// preprocess data
|
||||
os << (tileMetaData != nullptr) << std::endl;
|
||||
@@ -73,7 +71,7 @@ RawTile RawTile::deserializeMetaData(std::istream& is) {
|
||||
RawTile res;
|
||||
is >> res.dimensions.x >> res.dimensions.y >> res.dimensions.z;
|
||||
is >> res.tileIndex.x >> res.tileIndex.y >> res.tileIndex.level;
|
||||
int err; is >> err; res.error = (CPLErr) err;
|
||||
int err; is >> err; res.error = static_cast<ReadError>(err);
|
||||
|
||||
res.tileMetaData = nullptr;
|
||||
bool hastileMetaData;
|
||||
|
||||
@@ -26,28 +26,38 @@
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___RAWTILE___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/tileindex.h>
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <cpl_error.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
struct TileMetaData;
|
||||
|
||||
struct RawTile {
|
||||
|
||||
enum class ReadError {
|
||||
None = 0,
|
||||
Debug = 1,
|
||||
Warning = 2,
|
||||
Failure = 3,
|
||||
Fatal = 4
|
||||
};
|
||||
|
||||
RawTile();
|
||||
|
||||
char* imageData;
|
||||
glm::uvec3 dimensions;
|
||||
std::shared_ptr<TileMetaData> tileMetaData;
|
||||
TileIndex tileIndex;
|
||||
CPLErr error;
|
||||
ReadError error;
|
||||
size_t nBytesImageData;
|
||||
GLuint glType;
|
||||
TextureFormat textureFormat;
|
||||
|
||||
void serializeMetaData(std::ostream& s);
|
||||
static RawTile deserializeMetaData(std::istream& s);
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h>
|
||||
|
||||
#include <modules/globebrowsing/geometry/geodetic2.h>
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
#include <modules/globebrowsing/geometry/angle.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h> // abspath
|
||||
#include <ghoul/filesystem/file.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
|
||||
#include <ogr_featurestyle.h>
|
||||
#include <ogr_spatialref.h>
|
||||
#include <cpl_virtualmem.h>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "GdalRawTileDataReader";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
|
||||
return os << pr.start.x << ", " << pr.start.y << " with size " << pr.numPixels.x <<
|
||||
", " << pr.numPixels.y;
|
||||
}
|
||||
|
||||
GdalRawTileDataReader::GdalRawTileDataReader(
|
||||
const std::string& filePath, const Configuration& config)
|
||||
: RawTileDataReader(config)
|
||||
, _dataset(nullptr)
|
||||
{
|
||||
_initData = { "", filePath, config.tilePixelSize, config.dataType };
|
||||
_initData.initDirectory = CPLGetCurrentDir();
|
||||
ensureInitialized();
|
||||
}
|
||||
|
||||
GdalRawTileDataReader::~GdalRawTileDataReader() {
|
||||
if (_dataset != nullptr) {
|
||||
GDALClose((GDALDatasetH)_dataset);
|
||||
_dataset = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GdalRawTileDataReader::reset() {
|
||||
_cached._maxLevel = -1;
|
||||
if (_dataset != nullptr) {
|
||||
GDALClose((GDALDatasetH)_dataset);
|
||||
_dataset = nullptr;
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::maxChunkLevel() {
|
||||
ensureInitialized();
|
||||
if (_cached._maxLevel < 0) {
|
||||
int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount();
|
||||
_cached._maxLevel = -_cached._tileLevelDifference;
|
||||
if (numOverviews > 0) {
|
||||
_cached._maxLevel += numOverviews - 1;
|
||||
}
|
||||
}
|
||||
return _cached._maxLevel;
|
||||
}
|
||||
|
||||
float GdalRawTileDataReader::noDataValueAsFloat() const {
|
||||
float noDataValue;
|
||||
if (_dataset && _dataset->GetRasterBand(1)) {
|
||||
noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue();;
|
||||
}
|
||||
else {
|
||||
noDataValue = std::numeric_limits<float>::min();
|
||||
}
|
||||
return noDataValue;
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::rasterXSize() const {
|
||||
return _dataset->GetRasterXSize();
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::rasterYSize() const {
|
||||
return _dataset->GetRasterYSize();
|
||||
}
|
||||
|
||||
float GdalRawTileDataReader::depthOffset() const {
|
||||
return _dataset->GetRasterBand(1)->GetOffset();
|
||||
}
|
||||
|
||||
float GdalRawTileDataReader::depthScale() const {
|
||||
return _dataset->GetRasterBand(1)->GetScale();
|
||||
}
|
||||
|
||||
std::array<double, 6> GdalRawTileDataReader::getGeoTransform() const {
|
||||
std::array<double, 6> padfTransform;
|
||||
CPLErr err = _dataset->GetGeoTransform(&padfTransform[0]);
|
||||
if (err == CE_Failure) {
|
||||
return RawTileDataReader::getGeoTransform();
|
||||
}
|
||||
return padfTransform;
|
||||
}
|
||||
|
||||
IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex) const {
|
||||
IODescription io;
|
||||
io.read.region = highestResPixelRegion(tileIndex);
|
||||
|
||||
// write region starts in origin
|
||||
io.write.region.start = PixelRegion::PixelCoordinate(0, 0);
|
||||
io.write.region.numPixels = PixelRegion::PixelCoordinate(_initData.tilePixelSize, _initData.tilePixelSize);
|
||||
|
||||
io.read.overview = 0;
|
||||
io.read.fullRegion = gdalPixelRegion(
|
||||
_dataset->GetRasterBand(1));
|
||||
// For correct sampling in dataset, we need to pad the texture tile
|
||||
|
||||
PixelRegion scaledPadding = padding;
|
||||
double scale = io.read.region.numPixels.x / static_cast<double>(io.write.region.numPixels.x);
|
||||
scaledPadding.numPixels *= scale;
|
||||
scaledPadding.start *= scale;
|
||||
|
||||
io.read.region.pad(scaledPadding);
|
||||
io.write.region.pad(padding);
|
||||
io.write.region.start = PixelRegion::PixelCoordinate(0, 0);
|
||||
|
||||
|
||||
|
||||
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
|
||||
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
|
||||
|
||||
// OpenGL does not like if the number of bytes per line is not 4
|
||||
if (io.write.bytesPerLine % 4 != 0) {
|
||||
io.write.region.roundUpNumPixelToNearestMultipleOf(4);
|
||||
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
|
||||
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
void GdalRawTileDataReader::initialize() {
|
||||
_dataset = openGdalDataset(_initData.datasetFilePath);
|
||||
|
||||
//Do any other initialization needed for the GdalRawTileDataReader
|
||||
_dataLayout = getTileDataLayout(_initData.dataType);
|
||||
_depthTransform = calculateTileDepthTransform();
|
||||
_cached._tileLevelDifference =
|
||||
calculateTileLevelDifference(_initData.tilePixelSize);
|
||||
}
|
||||
|
||||
char* GdalRawTileDataReader::readImageData(
|
||||
IODescription& io, RawTile::ReadError& worstError) const {
|
||||
// allocate memory for the image
|
||||
char* imageData = new char[io.write.totalNumBytes];
|
||||
|
||||
// In case there are extra channels not existing in the GDAL dataset
|
||||
// we set the bytes to 255 (for example an extra alpha channel that)
|
||||
// needs to be 1.
|
||||
if (_dataLayout.numRasters > _dataLayout.numRastersAvailable) {
|
||||
memset(imageData, 255, io.write.totalNumBytes);
|
||||
}
|
||||
|
||||
if (_dataLayout.numRastersAvailable == 3) // RGB -> BGR
|
||||
{
|
||||
for (size_t i = 0; i < _dataLayout.numRastersAvailable; i++) {
|
||||
// The final destination pointer is offsetted by one datum byte size
|
||||
// for every raster (or data channel, i.e. R in RGB)
|
||||
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
|
||||
|
||||
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
|
||||
|
||||
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
}
|
||||
else if (_dataLayout.numRastersAvailable == 4) // RGBA -> BGRA
|
||||
{
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
// The final destination pointer is offsetted by one datum byte size
|
||||
// for every raster (or data channel, i.e. R in RGB)
|
||||
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
|
||||
|
||||
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
|
||||
|
||||
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
|
||||
// The final destination pointer is offsetted by one datum byte size
|
||||
// for every raster (or data channel, i.e. R in RGB)
|
||||
char* dataDestination = imageData + (3 * _dataLayout.bytesPerDatum);
|
||||
|
||||
RawTile::ReadError err = repeatedRasterRead(4, io, dataDestination);
|
||||
|
||||
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the data (each rasterband is a separate channel)
|
||||
for (size_t i = 0; i < _dataLayout.numRastersAvailable; i++) {
|
||||
GDALRasterBand* rasterBand = _dataset->GetRasterBand(i + 1);
|
||||
|
||||
// The final destination pointer is offsetted by one datum byte size
|
||||
// for every raster (or data channel, i.e. R in RGB)
|
||||
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
|
||||
|
||||
RawTile::ReadError err = repeatedRasterRead(i + 1, io, dataDestination);
|
||||
|
||||
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
}
|
||||
|
||||
// GDAL reads pixel lines top to bottom, we want the opposite
|
||||
return imageData;
|
||||
}
|
||||
|
||||
RawTile::ReadError GdalRawTileDataReader::rasterRead(
|
||||
int rasterBand, const IODescription& io, char* dataDestination) const
|
||||
{
|
||||
ghoul_assert(io.read.region.isInside(io.read.fullRegion), "write region of bounds!");
|
||||
ghoul_assert(
|
||||
io.write.region.start.x >= 0 && io.write.region.start.y >= 0,
|
||||
"Invalid write region"
|
||||
);
|
||||
|
||||
PixelRegion::PixelCoordinate end = io.write.region.end();
|
||||
size_t largestIndex =
|
||||
(end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _dataLayout.bytesPerPixel;
|
||||
ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region");
|
||||
|
||||
char* dataDest = dataDestination;
|
||||
|
||||
// GDAL reads pixels top to bottom, but we want our pixels bottom to top.
|
||||
// Therefore, we increment the destination pointer to the last line on in the
|
||||
// buffer, and the we specify in the rasterIO call that we want negative line
|
||||
// spacing. Doing this compensates the flipped Y axis
|
||||
dataDest += (io.write.totalNumBytes - io.write.bytesPerLine);
|
||||
|
||||
// handle requested write region. Note -= since flipped y axis
|
||||
dataDest -= io.write.region.start.y * io.write.bytesPerLine;
|
||||
dataDest += io.write.region.start.x * _dataLayout.bytesPerPixel;
|
||||
|
||||
CPLErr readError = _dataset->GetRasterBand(rasterBand)->RasterIO(
|
||||
GF_Read,
|
||||
io.read.region.start.x, // Begin read x
|
||||
io.read.region.start.y, // Begin read y
|
||||
io.read.region.numPixels.x, // width to read x
|
||||
io.read.region.numPixels.y, // width to read y
|
||||
dataDest, // Where to put data
|
||||
io.write.region.numPixels.x, // width to write x in destination
|
||||
io.write.region.numPixels.y, // width to write y in destination
|
||||
_gdalType, // Type
|
||||
_dataLayout.bytesPerPixel, // Pixel spacing
|
||||
-io.write.bytesPerLine // Line spacing
|
||||
);
|
||||
|
||||
// Convert error to RawTile::ReadError
|
||||
RawTile::ReadError error;
|
||||
switch (readError) {
|
||||
case CE_None: error = RawTile::ReadError::None; break;
|
||||
case CE_Debug: error = RawTile::ReadError::Debug; break;
|
||||
case CE_Warning: error = RawTile::ReadError::Warning; break;
|
||||
case CE_Failure: error = RawTile::ReadError::Failure; break;
|
||||
case CE_Fatal: error = RawTile::ReadError::Fatal; break;
|
||||
default: error = RawTile::ReadError::Failure; break;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
GDALDataset* GdalRawTileDataReader::openGdalDataset(const std::string& filePath) {
|
||||
GDALDataset* dataset = static_cast<GDALDataset*>(GDALOpen(filePath.c_str(), GA_ReadOnly));
|
||||
if (!dataset) {
|
||||
using namespace ghoul::filesystem;
|
||||
std::string correctedPath = FileSystem::ref().pathByAppendingComponent(
|
||||
_initData.initDirectory, filePath
|
||||
);
|
||||
dataset = (GDALDataset *)GDALOpen(correctedPath.c_str(), GA_ReadOnly);
|
||||
if (!dataset) {
|
||||
throw ghoul::RuntimeError("Failed to load dataset:\n" + filePath);
|
||||
}
|
||||
}
|
||||
return dataset;
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) {
|
||||
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
|
||||
GDALRasterBand* maxOverview;
|
||||
int numOverviews = firstBand->GetOverviewCount();
|
||||
int sizeLevel0;
|
||||
if (numOverviews <= 0) { // No overviews. Use first band.
|
||||
maxOverview = firstBand;
|
||||
}
|
||||
else { // Pick the highest overview.
|
||||
maxOverview = firstBand->GetOverview(numOverviews - 1);
|
||||
}
|
||||
sizeLevel0 = maxOverview->GetXSize();
|
||||
double diff = log2(minimumPixelSize) - log2(sizeLevel0);
|
||||
return diff;
|
||||
}
|
||||
|
||||
bool GdalRawTileDataReader::gdalHasOverviews() const {
|
||||
return _dataset->GetRasterBand(1)->GetOverviewCount() > 0;
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::gdalOverview(
|
||||
const PixelRegion::PixelRange& regionSizeOverviewZero) const {
|
||||
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
|
||||
|
||||
int minNumPixels0 = glm::min(regionSizeOverviewZero.x, regionSizeOverviewZero.y);
|
||||
|
||||
int overviews = firstBand->GetOverviewCount();
|
||||
GDALRasterBand* maxOverview =
|
||||
overviews ? firstBand->GetOverview(overviews - 1) : firstBand;
|
||||
|
||||
int sizeLevel0 = maxOverview->GetXSize();
|
||||
// The dataset itself may not have overviews but even if it does not, an overview
|
||||
// for the data region can be calculated and possibly be used to sample greater
|
||||
// Regions of the original dataset.
|
||||
int ov = std::log2(minNumPixels0) - std::log2(sizeLevel0 + 1) -
|
||||
_cached._tileLevelDifference;
|
||||
ov = glm::clamp(ov, 0, overviews - 1);
|
||||
|
||||
return ov;
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::gdalOverview(const TileIndex& tileIndex) const {
|
||||
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
|
||||
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
|
||||
return glm::clamp(ov, 0, overviews - 1);
|
||||
}
|
||||
|
||||
int GdalRawTileDataReader::gdalVirtualOverview(const TileIndex& tileIndex) const {
|
||||
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
|
||||
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
|
||||
return ov;
|
||||
}
|
||||
|
||||
PixelRegion GdalRawTileDataReader::gdalPixelRegion(GDALRasterBand* rasterBand) const {
|
||||
PixelRegion gdalRegion;
|
||||
gdalRegion.start.x = 0;
|
||||
gdalRegion.start.y = 0;
|
||||
gdalRegion.numPixels.x = rasterBand->GetXSize();
|
||||
gdalRegion.numPixels.y = rasterBand->GetYSize();
|
||||
return gdalRegion;
|
||||
}
|
||||
|
||||
TileDataLayout GdalRawTileDataReader::getTileDataLayout(GLuint preferredGlType) {
|
||||
TileDataLayout layout;
|
||||
|
||||
// Assume all raster bands have the same data type
|
||||
_gdalType = preferredGlType != 0 ?
|
||||
tiledatatype::getGdalDataType(preferredGlType) :
|
||||
_dataset->GetRasterBand(1)->GetRasterDataType();
|
||||
|
||||
layout.glType = tiledatatype::getOpenGLDataType(_gdalType);
|
||||
layout.numRastersAvailable = _dataset->GetRasterCount();
|
||||
layout.numRasters = layout.numRastersAvailable;
|
||||
|
||||
// This is to avoid corrupted textures that can appear when the number of
|
||||
// bytes per row is not a multiplie of 4.
|
||||
// Info here: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout
|
||||
// This also mean that we need to make sure not to read from non existing
|
||||
// rasters from the GDAL dataset
|
||||
if (layout.numRasters == 3) {
|
||||
layout.numRasters = 4;
|
||||
}
|
||||
|
||||
layout.bytesPerDatum = tiledatatype::numberOfBytes(_gdalType);
|
||||
layout.bytesPerPixel = layout.bytesPerDatum * layout.numRasters;
|
||||
layout.textureFormat = tiledatatype::getTextureFormatOptimized(layout.numRasters, _gdalType);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
@@ -0,0 +1,123 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___GDAL_RAW_TILE_DATA_READER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_RAW_TILE_DATA_READER___H__
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <gdal.h>
|
||||
#include <string>
|
||||
|
||||
class GDALDataset;
|
||||
class GDALRasterBand;
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class GeodeticPatch;
|
||||
|
||||
class GdalRawTileDataReader : public RawTileDataReader {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Opens a GDALDataset in readonly mode and calculates meta data required for
|
||||
* reading tile using a TileIndex.
|
||||
*
|
||||
* \param filePath, a path to a specific file GDAL can read
|
||||
* \param config, Configuration used for initialization
|
||||
*/
|
||||
GdalRawTileDataReader(const std::string& filePath, const Configuration& config);
|
||||
|
||||
virtual ~GdalRawTileDataReader() override;
|
||||
|
||||
// Public virtual function overloading
|
||||
virtual void reset() override;
|
||||
virtual int maxChunkLevel() override;
|
||||
virtual float noDataValueAsFloat() const override;
|
||||
virtual int rasterXSize() const override;
|
||||
virtual int rasterYSize() const override;
|
||||
virtual float depthOffset() const override;
|
||||
virtual float depthScale() const override;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Returns the geo transform from raster space to projection coordinates as defined
|
||||
* by GDAL.
|
||||
* If the transform is not available, the function returns a transform to map
|
||||
* the pixel coordinates to cover the whole geodetic lat long space.
|
||||
*/
|
||||
virtual std::array<double, 6> getGeoTransform() const override;
|
||||
virtual IODescription getIODescription(const TileIndex& tileIndex) const override;
|
||||
|
||||
private:
|
||||
// Private virtual function overloading
|
||||
virtual void initialize() override;
|
||||
virtual char* readImageData(
|
||||
IODescription& io, RawTile::ReadError& worstError) const override;
|
||||
virtual RawTile::ReadError rasterRead(
|
||||
int rasterBand, const IODescription& io, char* dst) const override;
|
||||
|
||||
// GDAL Helper methods
|
||||
GDALDataset* openGdalDataset(const std::string& filePath);
|
||||
int calculateTileLevelDifference(int minimumPixelSize);
|
||||
bool gdalHasOverviews() const;
|
||||
int gdalOverview(const PixelRegion::PixelRange& baseRegionSize) const;
|
||||
int gdalOverview(const TileIndex& tileIndex) const;
|
||||
int gdalVirtualOverview(const TileIndex& tileIndex) const;
|
||||
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
|
||||
TileDataLayout getTileDataLayout(GLuint prefferedGLType);
|
||||
|
||||
// Member variables
|
||||
struct InitData {
|
||||
std::string initDirectory;
|
||||
std::string datasetFilePath;
|
||||
int tilePixelSize;
|
||||
GLuint dataType;
|
||||
} _initData;
|
||||
|
||||
GDALDataset* _dataset;
|
||||
GDALDataType _gdalType; // The type to reinterpret to when reading
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_RAW_TILE_DATA_READER___H__
|
||||
186
modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.cpp
Normal file
186
modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/engine/configurationmanager.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem.h> // abspath
|
||||
#include <ghoul/ghoul.h>
|
||||
#include <ghoul/logging/consolelog.h>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
|
||||
namespace {
|
||||
const char* _loggerCat = "GdalWrapper";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
void gdalErrorHandler(CPLErr eErrClass, int errNo, const char *msg) {
|
||||
if (GdalWrapper::ref().logGdalErrors()) {
|
||||
switch (eErrClass) {
|
||||
case CE_None: break;
|
||||
case CE_Debug: LDEBUG ("GDAL " << msg); break;
|
||||
case CE_Warning: LWARNING("GDAL " << msg); break;
|
||||
case CE_Failure: LERROR ("GDAL " << msg); break;
|
||||
case CE_Fatal: LFATAL ("GDAL " << msg); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GdalWrapper* GdalWrapper::_singleton = nullptr;
|
||||
std::mutex GdalWrapper::_mutexLock;
|
||||
|
||||
void GdalWrapper::create(size_t maximumCacheSize,
|
||||
size_t maximumMaximumCacheSize) {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
_singleton = new GdalWrapper(maximumCacheSize, maximumMaximumCacheSize);
|
||||
}
|
||||
|
||||
void GdalWrapper::destroy() {
|
||||
std::lock_guard<std::mutex> guard(_mutexLock);
|
||||
ghoul_assert(_singleton, "Cannot delete null");
|
||||
delete _singleton;
|
||||
}
|
||||
|
||||
GdalWrapper& GdalWrapper::ref() {
|
||||
ghoul_assert(_singleton, "GdalWrapper not created");
|
||||
return *_singleton;
|
||||
}
|
||||
|
||||
size_t GDALCacheUsed() {
|
||||
return GDALGetCacheUsed64();
|
||||
}
|
||||
|
||||
size_t GDALMaximumCacheSize() {
|
||||
return GDALGetCacheMax64();
|
||||
}
|
||||
|
||||
bool GdalWrapper::logGdalErrors() const {
|
||||
return _logGdalErrors;
|
||||
}
|
||||
|
||||
GdalWrapper::GdalWrapper(size_t maximumCacheSize,
|
||||
size_t maximumMaximumCacheSize)
|
||||
: PropertyOwner("GdalWrapper")
|
||||
, _logGdalErrors("logGdalErrors", "Log GDAL errors", true)
|
||||
, _gdalMaximumCacheSize (
|
||||
"gdalMaximumCacheSize", "GDAL maximum cache size",
|
||||
maximumCacheSize / (1024 * 1024), // Default
|
||||
0, // Minimum: No caching
|
||||
maximumMaximumCacheSize / (1024 * 1024), // Maximum
|
||||
1 // Step: One MB
|
||||
) {
|
||||
addProperty(_logGdalErrors);
|
||||
addProperty(_gdalMaximumCacheSize);
|
||||
|
||||
GDALAllRegister();
|
||||
CPLSetConfigOption(
|
||||
"GDAL_DATA",
|
||||
absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str()
|
||||
);
|
||||
setGdalProxyConfiguration();
|
||||
CPLSetErrorHandler(gdalErrorHandler);
|
||||
|
||||
_gdalMaximumCacheSize.onChange([&] {
|
||||
// MB to Bytes
|
||||
GDALSetCacheMax64(
|
||||
static_cast<size_t>(_gdalMaximumCacheSize) *
|
||||
1024ULL * 1024ULL
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void GdalWrapper::setGdalProxyConfiguration() {
|
||||
ghoul::Dictionary proxySettings;
|
||||
bool proxyEnabled = OsEng.configurationManager().getValue(
|
||||
ConfigurationManager::KeyHttpProxy, proxySettings
|
||||
);
|
||||
if (proxyEnabled) {
|
||||
std::string proxyAddress, proxyPort, proxyUser, proxyPassword,
|
||||
proxyAuth;
|
||||
|
||||
bool success = proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyAddress,
|
||||
proxyAddress
|
||||
);
|
||||
success &= proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyPort,
|
||||
proxyPort
|
||||
);
|
||||
proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyAuthentication,
|
||||
proxyAuth
|
||||
);
|
||||
|
||||
std::string proxyAuthString = "BASIC";
|
||||
if (proxyAuth == "basic" || proxyAuth == "") {
|
||||
proxyAuthString = "BASIC";
|
||||
} else if (proxyAuth == "ntlm") {
|
||||
proxyAuthString = "NTLM";
|
||||
} else if (proxyAuth == "digest") {
|
||||
proxyAuthString = "DIGEST";
|
||||
} else if (proxyAuth == "any") {
|
||||
proxyAuthString = "ANY";
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
bool userAndPassword = proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyUser,
|
||||
proxyUser
|
||||
);
|
||||
userAndPassword &= proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyPassword,
|
||||
proxyPassword
|
||||
);
|
||||
|
||||
if (success) {
|
||||
std::string proxy = proxyAddress + ":" + proxyPort;
|
||||
CPLSetConfigOption("GDAL_HTTP_PROXY", proxy.c_str());
|
||||
LDEBUG("Using proxy server " << proxy);
|
||||
if (userAndPassword) {
|
||||
std::string proxyUserPwd = proxyUser + ":" + proxyPassword;
|
||||
CPLSetConfigOption("GDAL_HTTP_PROXYUSERPWD", proxyUserPwd.c_str());
|
||||
CPLSetConfigOption("GDAL_HTTP_PROXYAUTH", proxyAuthString.c_str());
|
||||
LDEBUG("Using authentication method: " << proxyAuthString);
|
||||
}
|
||||
} else {
|
||||
LERROR("Invalid proxy settings for GDAL");
|
||||
}
|
||||
} else {
|
||||
LDEBUG("Setting up GDAL without proxy server");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
96
modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h
Normal file
96
modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___GDAL_WRAPPER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_WRAPPER___H__
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
#include <openspace/properties/scalarproperty.h>
|
||||
#include <openspace/properties/triggerproperty.h>
|
||||
|
||||
#include <gdal.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
/**
|
||||
* Function for passing GDAL error messages to the GHOUL logging system.
|
||||
*/
|
||||
static void gdalErrorHandler(CPLErr eErrClass, int errNo, const char* msg);
|
||||
|
||||
/**
|
||||
* Singleton class interfacing with global GDAL functions.
|
||||
*/
|
||||
class GdalWrapper : public properties::PropertyOwner {
|
||||
public:
|
||||
/**
|
||||
* Create the singleton. Must be called before the class can be used.
|
||||
* \param maximumCacheSize is the current maximum cache size GDAL can use
|
||||
* for caching blocks in memory given in bytes.
|
||||
* \param maximumMaximumCacheSize is the maximum cache size GDAL can use
|
||||
* for caching blocks in memory given in bytes.
|
||||
*/
|
||||
static void create(size_t maximumCacheSize, size_t maximumMaximumCacheSize);
|
||||
static void destroy();
|
||||
|
||||
static GdalWrapper& ref();
|
||||
|
||||
/**
|
||||
* Get the current size of the GDAL in memory cache.
|
||||
* \returns the number of bytes currently in the GDAL memory cache.
|
||||
*/
|
||||
static size_t GDALCacheUsed();
|
||||
|
||||
/**
|
||||
* Get the maximum GDAL in memory cache size.
|
||||
* \returns the maximum number of bytes allowed for the GDAL cache.
|
||||
*/
|
||||
static size_t GDALMaximumCacheSize();
|
||||
|
||||
bool logGdalErrors() const;
|
||||
|
||||
private:
|
||||
GdalWrapper(size_t maximumCacheSize, size_t maximumMaximumCacheSize);
|
||||
~GdalWrapper() = default;
|
||||
|
||||
void setGdalProxyConfiguration();
|
||||
|
||||
properties::IntProperty _gdalMaximumCacheSize;
|
||||
properties::BoolProperty _logGdalErrors;
|
||||
|
||||
static GdalWrapper* _singleton;
|
||||
static std::mutex _mutexLock;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_WRAPPER___H__
|
||||
@@ -0,0 +1,70 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/tile/rawtiledatareader/iodescription.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
|
||||
namespace {
|
||||
const char* _loggerCat = "IODescription";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
IODescription IODescription::cut(PixelRegion::Side side, int pos) {
|
||||
PixelRegion readPreCut = read.region;
|
||||
PixelRegion writePreCut = write.region;
|
||||
|
||||
glm::dvec2 ratio;
|
||||
ratio.x = write.region.numPixels.x / static_cast<double>(read.region.numPixels.x);
|
||||
ratio.y = write.region.numPixels.y / static_cast<double>(read.region.numPixels.y);
|
||||
|
||||
IODescription whatCameOff = *this;
|
||||
whatCameOff.read.region = read.region.globalCut(side, pos);
|
||||
|
||||
PixelRegion::PixelRange cutSize = whatCameOff.read.region.numPixels;
|
||||
PixelRegion::PixelRange localWriteCutSize = ratio * glm::dvec2(cutSize);
|
||||
|
||||
if (cutSize.x == 0 || cutSize.y == 0) {
|
||||
ghoul_assert(
|
||||
read.region.equals(readPreCut),
|
||||
"Read region should not have been modified"
|
||||
);
|
||||
ghoul_assert(
|
||||
write.region.equals(writePreCut),
|
||||
"Write region should not have been modified"
|
||||
);
|
||||
}
|
||||
|
||||
int localWriteCutPos =
|
||||
(side == PixelRegion::Side::LEFT || side == PixelRegion::Side::RIGHT)
|
||||
? localWriteCutSize.x : localWriteCutSize.y;
|
||||
whatCameOff.write.region = write.region.localCut(side, localWriteCutPos);
|
||||
|
||||
return whatCameOff;
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
63
modules/globebrowsing/tile/rawtiledatareader/iodescription.h
Normal file
63
modules/globebrowsing/tile/rawtiledatareader/iodescription.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___IO_DESCRIPTION___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
struct IODescription {
|
||||
struct ReadData {
|
||||
int overview;
|
||||
PixelRegion region;
|
||||
PixelRegion fullRegion;
|
||||
} read;
|
||||
|
||||
struct WriteData {
|
||||
PixelRegion region;
|
||||
size_t bytesPerLine;
|
||||
size_t totalNumBytes;
|
||||
} write;
|
||||
|
||||
IODescription cut(PixelRegion::Side side, int pos);
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__
|
||||
@@ -0,0 +1,399 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
|
||||
#include <modules/globebrowsing/geometry/geodetic2.h>
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
#include <modules/globebrowsing/geometry/angle.h>
|
||||
|
||||
#include <openspace/engine/configurationmanager.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <limits>
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
namespace {
|
||||
const char* _loggerCat = "RawTileDataReader";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
const glm::ivec2 RawTileDataReader::tilePixelStartOffset = glm::ivec2(-2);
|
||||
const glm::ivec2 RawTileDataReader::tilePixelSizeDifference = glm::ivec2(4);
|
||||
|
||||
const PixelRegion RawTileDataReader::padding = PixelRegion(
|
||||
tilePixelStartOffset,
|
||||
tilePixelSizeDifference
|
||||
);
|
||||
|
||||
RawTileDataReader::RawTileDataReader(const Configuration& config)
|
||||
: _config(config)
|
||||
, _hasBeenInitialized(false)
|
||||
{}
|
||||
|
||||
void RawTileDataReader::ensureInitialized() {
|
||||
if (!_hasBeenInitialized) {
|
||||
initialize();
|
||||
_hasBeenInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTile> RawTileDataReader::defaultTileData() {
|
||||
PixelRegion pixelRegion = {
|
||||
PixelRegion::PixelCoordinate(0, 0),
|
||||
PixelRegion::PixelRange(16, 16)
|
||||
};
|
||||
int bytesPerPixel = 1; // GL_R -> 1 bpp
|
||||
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
|
||||
rawTile->tileIndex = { 0, 0, 0 };
|
||||
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
|
||||
rawTile->nBytesImageData =
|
||||
rawTile->dimensions.x * rawTile->dimensions.y * bytesPerPixel;
|
||||
rawTile->imageData = new char[rawTile->nBytesImageData];
|
||||
rawTile->glType = GL_UNSIGNED_BYTE;
|
||||
rawTile->textureFormat = { ghoul::opengl::Texture::Format::Red, GL_R };
|
||||
|
||||
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
|
||||
rawTile->imageData[i] = 0;
|
||||
}
|
||||
rawTile->error = RawTile::ReadError::None;
|
||||
|
||||
return rawTile;
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTile> RawTileDataReader::readTileData(TileIndex tileIndex) {
|
||||
ensureInitialized();
|
||||
IODescription io = getIODescription(tileIndex);
|
||||
RawTile::ReadError worstError = RawTile::ReadError::None;
|
||||
|
||||
// Build the RawTile from the data we querred
|
||||
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
|
||||
rawTile->imageData = readImageData(io, worstError);
|
||||
rawTile->error = worstError;
|
||||
rawTile->tileIndex = tileIndex;
|
||||
rawTile->dimensions = glm::uvec3(io.write.region.numPixels, 1);
|
||||
rawTile->nBytesImageData = io.write.totalNumBytes;
|
||||
rawTile->glType = _dataLayout.glType;
|
||||
rawTile->textureFormat = _dataLayout.textureFormat;
|
||||
|
||||
if (_config.doPreProcessing) {
|
||||
rawTile->tileMetaData = getTileMetaData(rawTile, io.write.region);
|
||||
rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
|
||||
}
|
||||
|
||||
return rawTile;
|
||||
}
|
||||
|
||||
TileDepthTransform RawTileDataReader::getDepthTransform() const {
|
||||
return _depthTransform;
|
||||
}
|
||||
|
||||
std::array<double, 6> RawTileDataReader::getGeoTransform() const {
|
||||
std::array<double, 6> padfTransform;
|
||||
|
||||
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2.0, M_PI));
|
||||
padfTransform[1] = Angle<double>::fromRadians(
|
||||
globalCoverage.size().lon).asDegrees() / rasterXSize();
|
||||
padfTransform[5] = -Angle<double>::fromRadians(
|
||||
globalCoverage.size().lat).asDegrees() / rasterYSize();
|
||||
padfTransform[0] = Angle<double>::fromRadians(
|
||||
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
|
||||
padfTransform[3] = Angle<double>::fromRadians(
|
||||
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
|
||||
padfTransform[2] = 0;
|
||||
padfTransform[4] = 0;
|
||||
return padfTransform;
|
||||
}
|
||||
|
||||
PixelRegion::PixelCoordinate RawTileDataReader::geodeticToPixel(
|
||||
const Geodetic2& geo) const {
|
||||
std::array<double, 6> padfTransform = getGeoTransform();
|
||||
|
||||
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
|
||||
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
|
||||
|
||||
// convert from pixel and line to geodetic coordinates
|
||||
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
|
||||
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
|
||||
|
||||
// <=>
|
||||
double* a = &(padfTransform[0]);
|
||||
double* b = &(padfTransform[3]);
|
||||
|
||||
// Xp = a[0] + P*a[1] + L*a[2];
|
||||
// Yp = b[0] + P*b[1] + L*b[2];
|
||||
|
||||
// <=>
|
||||
double divisor = (a[2] * b[1] - a[1] * b[2]);
|
||||
ghoul_assert(divisor != 0.0, "Division by zero!");
|
||||
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
|
||||
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
|
||||
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
|
||||
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
|
||||
|
||||
double Xp = a[0] + P*a[1] + L*a[2];
|
||||
double Yp = b[0] + P*b[1] + L*b[2];
|
||||
|
||||
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
|
||||
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
|
||||
|
||||
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
|
||||
}
|
||||
|
||||
Geodetic2 RawTileDataReader::pixelToGeodetic(
|
||||
const PixelRegion::PixelCoordinate& p) const {
|
||||
std::array<double, 6> padfTransform = getGeoTransform();
|
||||
Geodetic2 geodetic;
|
||||
// Should be using radians and not degrees?
|
||||
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
|
||||
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
|
||||
return geodetic;
|
||||
}
|
||||
|
||||
PixelRegion RawTileDataReader::highestResPixelRegion(const GeodeticPatch& geodeticPatch) const {
|
||||
Geodetic2 nwCorner = geodeticPatch.getCorner(Quad::NORTH_WEST);
|
||||
Geodetic2 swCorner = geodeticPatch.getCorner(Quad::SOUTH_EAST);
|
||||
PixelRegion::PixelCoordinate pixelStart = geodeticToPixel(nwCorner);
|
||||
PixelRegion::PixelCoordinate pixelEnd = geodeticToPixel(swCorner);
|
||||
PixelRegion region(pixelStart, pixelEnd - pixelStart);
|
||||
return region;
|
||||
}
|
||||
|
||||
RawTile::ReadError RawTileDataReader::repeatedRasterRead(
|
||||
int rasterBand, const IODescription& fullIO, char* dataDestination,
|
||||
int depth) const
|
||||
{
|
||||
RawTile::ReadError worstError = RawTile::ReadError::None;
|
||||
|
||||
// NOTE:
|
||||
// Ascii graphics illustrates the implementation details of this method, for one
|
||||
// specific case. Even though the illustrated case is specific, readers can
|
||||
// hopefully find it useful to get the general idea.
|
||||
|
||||
// Make a copy of the full IO desription as we will have to modify it
|
||||
IODescription io = fullIO;
|
||||
|
||||
// Example:
|
||||
// We have an io description that defines a WRITE and a READ region.
|
||||
// In this case the READ region extends outside of the defined io.read.fullRegion,
|
||||
// meaning we will have to perform wrapping
|
||||
|
||||
// io.write.region io.read.region
|
||||
// | |
|
||||
// V V
|
||||
// +-------+ +-------+
|
||||
// | | | |--------+
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// +-------+ +-------+ |
|
||||
// | | <-- io.read.fullRegion
|
||||
// | |
|
||||
// +--------------+
|
||||
|
||||
if (!io.read.region.isInside(io.read.fullRegion)) {
|
||||
// Loop through each side: left, top, right, bottom
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
|
||||
// Example:
|
||||
// We are currently considering the left side of the pixel region
|
||||
PixelRegion::Side side = (PixelRegion::Side) i;
|
||||
IODescription cutoff = io.cut(side, io.read.fullRegion.edge(side));
|
||||
|
||||
// Example:
|
||||
// We cut off the left part that was outside the io.read.fullRegion, and we
|
||||
// now have an additional io description for the cut off region.
|
||||
// Note that the cut-method used above takes care of the corresponding
|
||||
// WRITE region for us.
|
||||
|
||||
// cutoff.write.region cutoff.read.region
|
||||
// | io.write.region | io.read.region
|
||||
// | | | |
|
||||
// V V V V
|
||||
// +-+-----+ +-+-----+
|
||||
// | | | | | |--------+
|
||||
// | | | | | | |
|
||||
// | | | | | | |
|
||||
// +-+-----+ +-+-----+ |
|
||||
// | | <-- io.read.fullRegion
|
||||
// | |
|
||||
// +--------------+
|
||||
|
||||
if (cutoff.read.region.area() > 0) {
|
||||
// Wrap by repeating
|
||||
PixelRegion::Side oppositeSide = (PixelRegion::Side) ((i + 2) % 4);
|
||||
|
||||
cutoff.read.region.align(
|
||||
oppositeSide, io.read.fullRegion.edge(oppositeSide));
|
||||
|
||||
// Example:
|
||||
// The cut off region is wrapped to the opposite side of the region,
|
||||
// i.e. "repeated". Note that we don't want WRITE region to change,
|
||||
// we're only wrapping the READ region.
|
||||
|
||||
// cutoff.write.region io.read.region cutoff.read.region
|
||||
// | io.write.region | |
|
||||
// | | V V
|
||||
// V V +-----+ +-+
|
||||
// +-+-----+ | |------| |
|
||||
// | | | | | | |
|
||||
// | | | | | | |
|
||||
// | | | +-----+ +-+
|
||||
// +-+-----+ | | <-- io.read.fullRegion
|
||||
// | |
|
||||
// +--------------+
|
||||
|
||||
// Example:
|
||||
// The cutoff region has been repeated along one of its sides, but
|
||||
// as we can see in this example, it still has a top part outside the
|
||||
// defined gdal region. This is handled through recursion.
|
||||
RawTile::ReadError err = repeatedRasterRead(
|
||||
rasterBand, cutoff, dataDestination, depth + 1);
|
||||
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RawTile::ReadError err = rasterRead(rasterBand, io, dataDestination);
|
||||
|
||||
// The return error from a repeated rasterRead is ONLY based on the main region,
|
||||
// which in the usual case will cover the main area of the patch anyway
|
||||
return err;
|
||||
}
|
||||
|
||||
std::shared_ptr<TileMetaData> RawTileDataReader::getTileMetaData(
|
||||
std::shared_ptr<RawTile> rawTile, const PixelRegion& region)
|
||||
{
|
||||
ensureInitialized();
|
||||
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
|
||||
|
||||
TileMetaData* preprocessData = new TileMetaData();
|
||||
preprocessData->maxValues.resize(_dataLayout.numRasters);
|
||||
preprocessData->minValues.resize(_dataLayout.numRasters);
|
||||
preprocessData->hasMissingData.resize(_dataLayout.numRasters);
|
||||
|
||||
std::vector<float> noDataValues;
|
||||
noDataValues.resize(_dataLayout.numRasters);
|
||||
|
||||
for (size_t raster = 0; raster < _dataLayout.numRasters; ++raster) {
|
||||
preprocessData->maxValues[raster] = -FLT_MAX;
|
||||
preprocessData->minValues[raster] = FLT_MAX;
|
||||
preprocessData->hasMissingData[raster] = false;
|
||||
noDataValues[raster] = noDataValueAsFloat();
|
||||
}
|
||||
|
||||
for (size_t y = 0; y < region.numPixels.y; ++y) {
|
||||
size_t yi = (region.numPixels.y - 1 - y) * bytesPerLine;
|
||||
size_t i = 0;
|
||||
for (size_t x = 0; x < region.numPixels.x; ++x) {
|
||||
for (size_t raster = 0; raster < _dataLayout.numRasters; ++raster) {
|
||||
float noDataValue = noDataValueAsFloat();
|
||||
float val = tiledatatype::interpretFloat(
|
||||
_dataLayout.glType,
|
||||
&(rawTile->imageData[yi + i])
|
||||
);
|
||||
if (val != noDataValue) {
|
||||
preprocessData->maxValues[raster] = std::max(
|
||||
val,
|
||||
preprocessData->maxValues[raster]
|
||||
);
|
||||
preprocessData->minValues[raster] = std::min(
|
||||
val,
|
||||
preprocessData->minValues[raster]
|
||||
);
|
||||
}
|
||||
else {
|
||||
preprocessData->hasMissingData[raster] = true;
|
||||
}
|
||||
i += _dataLayout.bytesPerDatum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::shared_ptr<TileMetaData>(preprocessData);
|
||||
}
|
||||
|
||||
float RawTileDataReader::depthOffset() const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float RawTileDataReader::depthScale() const {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
TileDepthTransform RawTileDataReader::calculateTileDepthTransform() {
|
||||
bool isFloat =
|
||||
(_dataLayout.glType == GL_FLOAT || _dataLayout.glType == GL_DOUBLE);
|
||||
double maximumValue =
|
||||
isFloat ? 1.0 : tiledatatype::getMaximumValue(_dataLayout.glType);
|
||||
|
||||
TileDepthTransform transform;
|
||||
transform.depthOffset = depthOffset();
|
||||
transform.depthScale = depthScale() * maximumValue;
|
||||
return transform;
|
||||
}
|
||||
|
||||
RawTile::ReadError RawTileDataReader::postProcessErrorCheck(
|
||||
std::shared_ptr<const RawTile> rawTile, const IODescription& io)
|
||||
{
|
||||
ensureInitialized();
|
||||
|
||||
double missingDataValue = noDataValueAsFloat();
|
||||
|
||||
bool hasMissingData = false;
|
||||
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
hasMissingData |= rawTile->tileMetaData->maxValues[c] == missingDataValue;
|
||||
}
|
||||
|
||||
bool onHighLevel = rawTile->tileIndex.level > 6;
|
||||
if (hasMissingData && onHighLevel) {
|
||||
return RawTile::ReadError::Fatal;
|
||||
}
|
||||
return RawTile::ReadError::None;
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
180
modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h
Normal file
180
modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h
Normal file
@@ -0,0 +1,180 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___RAW_TILE_DATA_READER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___RAW_TILE_DATA_READER___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/iodescription.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class GeodeticPatch;
|
||||
|
||||
class RawTileDataReader {
|
||||
public:
|
||||
struct Configuration {
|
||||
bool doPreProcessing;
|
||||
int tilePixelSize;
|
||||
GLuint dataType = 0; // default = no datatype reinterpretation
|
||||
};
|
||||
|
||||
RawTileDataReader(const Configuration& config);
|
||||
virtual ~RawTileDataReader() = default;
|
||||
|
||||
/**
|
||||
* Reads data from the current dataset and initializes a <code>RawTile</code>
|
||||
* which gets returned.
|
||||
*/
|
||||
std::shared_ptr<RawTile> readTileData(TileIndex tileIndex);
|
||||
TileDepthTransform getDepthTransform() const;
|
||||
|
||||
/**
|
||||
* \returns the maximum chunk level available in the dataset. Should be a value
|
||||
* between 2 and 31.
|
||||
*/
|
||||
virtual int maxChunkLevel() = 0;
|
||||
|
||||
/**
|
||||
* Reset the dataset to its initial state. This is the place to clear any cache used.
|
||||
*/
|
||||
virtual void reset() = 0;
|
||||
virtual float noDataValueAsFloat() const = 0;
|
||||
virtual int rasterXSize() const = 0;
|
||||
virtual int rasterYSize() const = 0;
|
||||
virtual float depthOffset() const;
|
||||
virtual float depthScale() const;
|
||||
|
||||
/**
|
||||
* Returns a single channeled empty <code>RawTile</code> of size 16 * 16 pixels.
|
||||
*/
|
||||
std::shared_ptr<RawTile> defaultTileData();
|
||||
|
||||
const static glm::ivec2 tilePixelStartOffset;
|
||||
const static glm::ivec2 tilePixelSizeDifference;
|
||||
|
||||
/// Padding around all tiles to read to make sure edge blending works.
|
||||
const static PixelRegion padding; // same as the two above
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* This function should set the variables <code>_cached</code>,
|
||||
* <code>_dataLayout</code> and <code>_depthTransform</code>.
|
||||
*/
|
||||
virtual void initialize() = 0;
|
||||
|
||||
/**
|
||||
* Call this in the constructor of the class extending <code>RawTileDataReader</code>
|
||||
*/
|
||||
void ensureInitialized();
|
||||
|
||||
/**
|
||||
* The function returns a transform to map
|
||||
* the pixel coordinates to cover the whole geodetic lat long space.
|
||||
*/
|
||||
virtual std::array<double, 6> getGeoTransform() const;
|
||||
|
||||
/**
|
||||
* Read image data as described by the given <code>IODescription</code>.
|
||||
* \param <code>io</code> describes how to read the data.
|
||||
* \param <code>worstError</code> should be set to the error code returned when
|
||||
* reading the data.
|
||||
*/
|
||||
virtual char* readImageData(
|
||||
IODescription& io, RawTile::ReadError& worstError) const = 0;
|
||||
|
||||
virtual RawTile::ReadError rasterRead(
|
||||
int rasterBand, const IODescription& io, char* dst) const = 0;
|
||||
|
||||
virtual IODescription getIODescription(const TileIndex& tileIndex) const = 0;
|
||||
|
||||
/**
|
||||
* Get the pixel corresponding to a specific position on the globe defined by the
|
||||
* <code>Geodetic2</code> coordinate <code>geo</code>. If the dataset has overviews
|
||||
* the function returns the pixel at the lowest overview (highest resolution).
|
||||
* \param <code>geo</code> is the position on the globe to convert to pixel space.
|
||||
* \returns a pixel coordinate in the dataset.
|
||||
*/
|
||||
PixelRegion::PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
|
||||
|
||||
/**
|
||||
* Get the geodetic coordinate corresponding to the given pixel in the dataset. If
|
||||
* The dataset has overviews it is the lowest overview that is used. That is the
|
||||
* one with highest resolution.
|
||||
*/
|
||||
Geodetic2 pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const;
|
||||
|
||||
/**
|
||||
* Get a pixel region corresponding to the given <code>GeodeticPatch</code>. If the
|
||||
* dataset has overviews the function returns the pixel region at the lowest overview
|
||||
* (highest resolution).
|
||||
* \param <code>geodeticPatch</code> is a patch covering an area in geodetic
|
||||
* coordinates
|
||||
* \returns a <code>PixelRegion</code> covering the given geodetic patch at highest
|
||||
* resolution.
|
||||
*/
|
||||
PixelRegion highestResPixelRegion(const GeodeticPatch& geodeticPatch) const;
|
||||
|
||||
/**
|
||||
* A recursive function that is able to perform wrapping in case the read region of
|
||||
* the given <code>IODescription</code> is outside of the given write region.
|
||||
*/
|
||||
RawTile::ReadError repeatedRasterRead(
|
||||
int rasterBand, const IODescription& io, char* dst, int depth = 0) const;
|
||||
|
||||
std::shared_ptr<TileMetaData> getTileMetaData(
|
||||
std::shared_ptr<RawTile> result, const PixelRegion& region);
|
||||
TileDepthTransform calculateTileDepthTransform();
|
||||
RawTile::ReadError postProcessErrorCheck(
|
||||
std::shared_ptr<const RawTile> ioResult, const IODescription& io);
|
||||
|
||||
struct Cached {
|
||||
int _maxLevel = -1;
|
||||
double _tileLevelDifference;
|
||||
} _cached;
|
||||
const Configuration _config;
|
||||
TileDataLayout _dataLayout;
|
||||
TileDepthTransform _depthTransform;
|
||||
|
||||
private:
|
||||
bool _hasBeenInitialized;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___RAW_TILE_DATA_READER___H__
|
||||
@@ -0,0 +1,184 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h>
|
||||
|
||||
#include <modules/globebrowsing/geometry/geodetic2.h>
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
#include <modules/globebrowsing/geometry/angle.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h> // abspath
|
||||
#include <ghoul/filesystem/file.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
|
||||
#include <ghoul/io/texture/texturereader.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "SimpleRawTileDataReader";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
SimpleRawTileDataReader::SimpleRawTileDataReader(
|
||||
const std::string& filePath, const Configuration& config)
|
||||
: RawTileDataReader(config)
|
||||
{
|
||||
_datasetFilePath = filePath;
|
||||
ensureInitialized();
|
||||
}
|
||||
|
||||
void SimpleRawTileDataReader::reset() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
int SimpleRawTileDataReader::maxChunkLevel() {
|
||||
return _cached._maxLevel;
|
||||
}
|
||||
|
||||
float SimpleRawTileDataReader::noDataValueAsFloat() const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int SimpleRawTileDataReader::rasterXSize() const {
|
||||
return _dataTexture->dimensions().x;
|
||||
}
|
||||
|
||||
int SimpleRawTileDataReader::rasterYSize() const {
|
||||
return _dataTexture->dimensions().y;
|
||||
}
|
||||
|
||||
float SimpleRawTileDataReader::depthOffset() const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float SimpleRawTileDataReader::depthScale() const {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
IODescription SimpleRawTileDataReader::getIODescription(const TileIndex& tileIndex) const {
|
||||
IODescription io;
|
||||
io.read.overview = 0;
|
||||
io.read.region = highestResPixelRegion(tileIndex);
|
||||
io.read.fullRegion = PixelRegion({0, 0}, {rasterXSize(), rasterYSize()});
|
||||
io.write.region = PixelRegion({0, 0}, io.read.region.numPixels);
|
||||
io.write.bytesPerLine = _dataTexture->bytesPerPixel() * io.write.region.numPixels.x;
|
||||
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
void SimpleRawTileDataReader::initialize() {
|
||||
_dataTexture = ghoul::io::TextureReader::ref().loadTexture(_datasetFilePath);
|
||||
if (_dataTexture == nullptr) {
|
||||
throw ghoul::RuntimeError(
|
||||
"Unable to read dataset: " + _datasetFilePath +
|
||||
".\nCompiling OpenSpace with GDAL will allow for better support for different"
|
||||
"formats."
|
||||
);
|
||||
}
|
||||
float exponentX = log2(_dataTexture->dimensions().x);
|
||||
float exponentY = log2(_dataTexture->dimensions().y);
|
||||
if ( (exponentX - static_cast<int>(exponentX)) > 0.0001 ||
|
||||
(exponentY - static_cast<int>(exponentY)) > 0.0001 ) {
|
||||
throw ghoul::RuntimeError(
|
||||
"Unable to read dataset: " + _datasetFilePath +
|
||||
".\nCurrently only supporting power of 2 textures."
|
||||
);
|
||||
}
|
||||
|
||||
_cached._maxLevel = 2;
|
||||
_cached._tileLevelDifference = 0;
|
||||
|
||||
_dataLayout.glType = _dataTexture->dataType();
|
||||
_dataLayout.bytesPerDatum = tiledatatype::numberOfBytes(_dataLayout.glType);
|
||||
_dataLayout.numRasters = tiledatatype::numberOfRasters(_dataTexture->format());
|
||||
_dataLayout.numRastersAvailable = _dataLayout.numRasters;
|
||||
_dataLayout.bytesPerPixel = _dataLayout.bytesPerDatum * _dataLayout.numRasters;
|
||||
_dataLayout.textureFormat = {_dataTexture->format(), _dataTexture->internalFormat()};
|
||||
|
||||
_depthTransform = {depthScale(), depthOffset()};
|
||||
}
|
||||
|
||||
char* SimpleRawTileDataReader::readImageData(
|
||||
IODescription& io, RawTile::ReadError& worstError) const {
|
||||
// allocate memory for the image
|
||||
char* imageData = new char[io.write.totalNumBytes];
|
||||
|
||||
// In case there are extra channels not existing in the dataset
|
||||
// we set the bytes to 255 (for example an extra alpha channel that)
|
||||
// needs to be 1.
|
||||
if (_dataLayout.numRasters > _dataLayout.numRastersAvailable) {
|
||||
memset(imageData, 255, io.write.totalNumBytes);
|
||||
}
|
||||
|
||||
// Modify to match OpenGL texture layout:
|
||||
IODescription modifiedIO = io;
|
||||
modifiedIO.read.region.start.y = modifiedIO.read.fullRegion.numPixels.y - modifiedIO.read.region.numPixels.y - modifiedIO.read.region.start.y;
|
||||
|
||||
RawTile::ReadError err = repeatedRasterRead(0, modifiedIO, imageData);
|
||||
|
||||
// None = 0, Debug = 1, Warning = 2, Failure = 3, Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
|
||||
return imageData;
|
||||
}
|
||||
|
||||
RawTile::ReadError SimpleRawTileDataReader::rasterRead(
|
||||
int rasterBand, const IODescription& io, char* dataDestination) const
|
||||
{
|
||||
ghoul_assert(io.read.fullRegion.numPixels.x == _dataTexture->dimensions().x,
|
||||
"IODescription does not match data texture.");
|
||||
ghoul_assert(io.read.fullRegion.numPixels.y == _dataTexture->dimensions().y,
|
||||
"IODescription does not match data texture.");
|
||||
ghoul_assert(io.read.region.numPixels.x == io.write.region.numPixels.x,
|
||||
"IODescription does not match data texture.");
|
||||
ghoul_assert(io.read.region.numPixels.y == io.write.region.numPixels.y,
|
||||
"IODescription does not match data texture.");
|
||||
|
||||
char* pixelWriteRow = dataDestination;
|
||||
try {
|
||||
// For each row
|
||||
for (int y = 0; y < io.read.region.numPixels.y; y++) {
|
||||
int bytesPerLineDataTexture =
|
||||
_dataTexture->bytesPerPixel() * _dataTexture->dimensions().x;
|
||||
const char* textureRow = (static_cast<const char*>(_dataTexture->pixelData())
|
||||
+ io.read.region.start.x * _dataTexture->bytesPerPixel())
|
||||
+ io.read.region.start.y * bytesPerLineDataTexture
|
||||
+ y * bytesPerLineDataTexture;
|
||||
memcpy(pixelWriteRow, textureRow, io.write.bytesPerLine);
|
||||
pixelWriteRow += io.write.bytesPerLine;
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
return RawTile::ReadError::Failure;
|
||||
}
|
||||
return RawTile::ReadError::None;
|
||||
}
|
||||
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
@@ -22,71 +22,61 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <ogr_featurestyle.h>
|
||||
#include <ogr_spatialref.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h> // abspath
|
||||
#include <ghoul/misc/assert.h>
|
||||
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___SIMPLE_RAW_TILE_DATA_READER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___SIMPLE_RAW_TILE_DATA_READER___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
|
||||
|
||||
#include <modules/globebrowsing/geometry/angle.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/engine/configurationmanager.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ghoul/filesystem/file.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <ghoul/misc/threadpool.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledatatype.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
#include <modules/globebrowsing/geometry/geodetic2.h>
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "TileDataset";
|
||||
}
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
TileDataLayout::TileDataLayout() {}
|
||||
class GeodeticPatch;
|
||||
|
||||
TileDataLayout::TileDataLayout(GDALDataset* dataSet, GLuint preferredGlType) {
|
||||
// Assume all raster bands have the same data type
|
||||
gdalType =preferredGlType != 0 ?
|
||||
tiledatatype::getGdalDataType(preferredGlType) :
|
||||
dataSet->GetRasterBand(1)->GetRasterDataType();
|
||||
class SimpleRawTileDataReader : public RawTileDataReader {
|
||||
public:
|
||||
|
||||
SimpleRawTileDataReader(const std::string& filePath, const Configuration& config);
|
||||
|
||||
// Public virtual function overloading
|
||||
virtual void reset() override;
|
||||
virtual int maxChunkLevel() override;
|
||||
virtual float noDataValueAsFloat() const override;
|
||||
virtual int rasterXSize() const override;
|
||||
virtual int rasterYSize() const override;
|
||||
virtual float depthOffset() const override;
|
||||
virtual float depthScale() const override;
|
||||
|
||||
protected:
|
||||
|
||||
virtual IODescription getIODescription(const TileIndex& tileIndex) const override;
|
||||
|
||||
private:
|
||||
// Private virtual function overloading
|
||||
virtual void initialize() override;
|
||||
virtual char* readImageData(
|
||||
IODescription& io, RawTile::ReadError& worstError) const override;
|
||||
virtual RawTile::ReadError rasterRead(
|
||||
int rasterBand, const IODescription& io, char* dst) const override;
|
||||
|
||||
// Member variables
|
||||
std::string _datasetFilePath;
|
||||
std::unique_ptr<ghoul::opengl::Texture> _dataTexture;
|
||||
};
|
||||
|
||||
glType = tiledatatype::getOpenGLDataType(gdalType);
|
||||
numRasters = dataSet->GetRasterCount();
|
||||
bytesPerDatum = tiledatatype::numberOfBytes(gdalType);
|
||||
bytesPerPixel = bytesPerDatum * numRasters;
|
||||
textureFormat = tiledatatype::getTextureFormat(numRasters, gdalType);
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___SIMPLE_RAW_TILE_DATA_READER___H__
|
||||
@@ -22,10 +22,12 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/globebrowsing/tile/tiledatatype.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
#include <ogr_featurestyle.h>
|
||||
#include <ogr_spatialref.h>
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h> // abspath
|
||||
@@ -39,13 +41,15 @@
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "RawTile";
|
||||
const char* _loggerCat = "TileDataType";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace tiledatatype {
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
float interpretFloat(GDALDataType gdalType, const char* src) {
|
||||
switch (gdalType) {
|
||||
case GDT_Byte:
|
||||
@@ -233,6 +237,132 @@ TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType) {
|
||||
return format;
|
||||
}
|
||||
|
||||
TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType) {
|
||||
TextureFormat format;
|
||||
|
||||
switch (rasterCount) {
|
||||
case 1: // Red
|
||||
format.ghoulFormat = ghoul::opengl::Texture::Format::Red;
|
||||
switch (gdalType) {
|
||||
case GDT_Byte:
|
||||
format.glFormat = GL_R8;
|
||||
break;
|
||||
case GDT_UInt16:
|
||||
format.glFormat = GL_R16UI;
|
||||
break;
|
||||
case GDT_Int16:
|
||||
format.glFormat = GL_R16_SNORM;
|
||||
break;
|
||||
case GDT_UInt32:
|
||||
format.glFormat = GL_R32UI;
|
||||
break;
|
||||
case GDT_Int32:
|
||||
format.glFormat = GL_R32I;
|
||||
break;
|
||||
case GDT_Float32:
|
||||
format.glFormat = GL_R32F;
|
||||
break;
|
||||
// No representation of 64 bit float?
|
||||
//case GDT_Float64:
|
||||
// format.glFormat = GL_RED;
|
||||
// break;
|
||||
default:
|
||||
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
format.ghoulFormat = ghoul::opengl::Texture::Format::RG;
|
||||
switch (gdalType) {
|
||||
case GDT_Byte:
|
||||
format.glFormat = GL_RG8;
|
||||
break;
|
||||
case GDT_UInt16:
|
||||
format.glFormat = GL_RG16UI;
|
||||
break;
|
||||
case GDT_Int16:
|
||||
format.glFormat = GL_RG16_SNORM;
|
||||
break;
|
||||
case GDT_UInt32:
|
||||
format.glFormat = GL_RG32UI;
|
||||
break;
|
||||
case GDT_Int32:
|
||||
format.glFormat = GL_RG32I;
|
||||
break;
|
||||
case GDT_Float32:
|
||||
format.glFormat = GL_RG32F;
|
||||
break;
|
||||
case GDT_Float64:
|
||||
format.glFormat = GL_RED;
|
||||
break;
|
||||
default:
|
||||
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
format.ghoulFormat = ghoul::opengl::Texture::Format::BGR;
|
||||
switch (gdalType) {
|
||||
case GDT_Byte:
|
||||
format.glFormat = GL_RGB8;
|
||||
break;
|
||||
case GDT_UInt16:
|
||||
format.glFormat = GL_RGB16UI;
|
||||
break;
|
||||
case GDT_Int16:
|
||||
format.glFormat = GL_RGB16_SNORM;
|
||||
break;
|
||||
case GDT_UInt32:
|
||||
format.glFormat = GL_RGB32UI;
|
||||
break;
|
||||
case GDT_Int32:
|
||||
format.glFormat = GL_RGB32I;
|
||||
break;
|
||||
case GDT_Float32:
|
||||
format.glFormat = GL_RGB32F;
|
||||
break;
|
||||
// No representation of 64 bit float?
|
||||
//case GDT_Float64:
|
||||
// format.glFormat = GL_RED;
|
||||
// break;
|
||||
default:
|
||||
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
format.ghoulFormat = ghoul::opengl::Texture::Format::BGRA;
|
||||
switch (gdalType) {
|
||||
case GDT_Byte:
|
||||
format.glFormat = GL_RGBA8;
|
||||
break;
|
||||
case GDT_UInt16:
|
||||
format.glFormat = GL_RGBA16UI;
|
||||
break;
|
||||
case GDT_Int16:
|
||||
format.glFormat = GL_RGB16_SNORM;
|
||||
break;
|
||||
case GDT_UInt32:
|
||||
format.glFormat = GL_RGBA32UI;
|
||||
break;
|
||||
case GDT_Int32:
|
||||
format.glFormat = GL_RGBA32I;
|
||||
break;
|
||||
case GDT_Float32:
|
||||
format.glFormat = GL_RGBA32F;
|
||||
break;
|
||||
// No representation of 64 bit float?
|
||||
//case GDT_Float64:
|
||||
// format.glFormat = GL_RED;
|
||||
// break;
|
||||
default:
|
||||
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LERROR("Unknown number of channels for OpenGL texture: " << rasterCount);
|
||||
break;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
GLuint getOpenGLDataType(GDALDataType gdalType) {
|
||||
switch (gdalType) {
|
||||
case GDT_Byte:
|
||||
@@ -277,6 +407,70 @@ GDALDataType getGdalDataType(GLuint glType) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
size_t numberOfRasters(ghoul::opengl::Texture::Format format) {
|
||||
switch (format) {
|
||||
case ghoul::opengl::Texture::Format::Red: return 1;
|
||||
case ghoul::opengl::Texture::Format::RG: return 2;
|
||||
case ghoul::opengl::Texture::Format::RGB: return 3;
|
||||
case ghoul::opengl::Texture::Format::RGBA: return 4;
|
||||
default: ghoul_assert(false, "Unknown format");
|
||||
}
|
||||
}
|
||||
|
||||
size_t numberOfBytes(GLuint glType) {
|
||||
switch (glType) {
|
||||
case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
|
||||
case GL_UNSIGNED_SHORT: return sizeof(GLushort);
|
||||
case GL_SHORT: return sizeof(GLshort);
|
||||
case GL_UNSIGNED_INT: return sizeof(GLuint);
|
||||
case GL_INT: return sizeof(GLint);
|
||||
case GL_FLOAT: return sizeof(GLfloat);
|
||||
case GL_DOUBLE: return sizeof(GLdouble);
|
||||
default:
|
||||
ghoul_assert(false, "Unknown data type");
|
||||
}
|
||||
}
|
||||
|
||||
size_t getMaximumValue(GLuint glType) {
|
||||
switch (glType) {
|
||||
case GL_UNSIGNED_BYTE:
|
||||
return 1 << 8;
|
||||
case GL_UNSIGNED_SHORT:
|
||||
return 1 << 16;
|
||||
case GL_SHORT:
|
||||
return 1 << 15;
|
||||
case GL_UNSIGNED_INT:
|
||||
return size_t(1) << 32;
|
||||
case GL_INT:
|
||||
return 1 << 31;
|
||||
default:
|
||||
ghoul_assert(false, "Unknown data type");
|
||||
}
|
||||
}
|
||||
|
||||
float interpretFloat(GLuint glType, const char* src) {
|
||||
switch (glType) {
|
||||
case GL_UNSIGNED_BYTE:
|
||||
return static_cast<float>(*reinterpret_cast<const GLubyte*>(src));
|
||||
case GL_UNSIGNED_SHORT:
|
||||
return static_cast<float>(*reinterpret_cast<const GLushort*>(src));
|
||||
case GL_SHORT:
|
||||
return static_cast<float>(*reinterpret_cast<const GLshort*>(src));
|
||||
case GL_UNSIGNED_INT:
|
||||
return static_cast<float>(*reinterpret_cast<const GLuint*>(src));
|
||||
case GL_INT:
|
||||
return static_cast<float>(*reinterpret_cast<const GLint*>(src));
|
||||
case GL_FLOAT:
|
||||
return static_cast<float>(*reinterpret_cast<const GLfloat*>(src));
|
||||
case GL_DOUBLE:
|
||||
return static_cast<float>(*reinterpret_cast<const GLdouble*>(src));
|
||||
default:
|
||||
ghoul_assert(false, "Unknown data type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tiledatatype
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
@@ -26,28 +26,32 @@
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
#include <gdal.h>
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace tiledatatype {
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
GLuint getOpenGLDataType(GDALDataType gdalType);
|
||||
|
||||
GDALDataType getGdalDataType(GLuint glType);
|
||||
|
||||
TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType);
|
||||
|
||||
TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType);
|
||||
size_t getMaximumValue(GDALDataType gdalType);
|
||||
|
||||
size_t numberOfBytes(GDALDataType gdalType);
|
||||
|
||||
float interpretFloat(GDALDataType gdalType, const char* src);
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
size_t numberOfRasters(ghoul::opengl::Texture::Format format);
|
||||
size_t numberOfBytes(GLuint glType);
|
||||
size_t getMaximumValue(GLuint glType);
|
||||
float interpretFloat(GLuint glType, const char* src);
|
||||
|
||||
} // namespace tiledatatype
|
||||
} // namespace globebrowsing
|
||||
@@ -33,7 +33,7 @@ namespace globebrowsing {
|
||||
|
||||
struct TextureFormat {
|
||||
ghoul::opengl::Texture::Format ghoulFormat;
|
||||
GLuint glFormat;
|
||||
GLint glFormat;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/globebrowsing/tile/tiledataset.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "Tile";
|
||||
@@ -33,8 +34,22 @@ namespace {
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
const Tile Tile::TileUnavailable = {nullptr, nullptr, Tile::Status::Unavailable };
|
||||
|
||||
const Tile Tile::TileUnavailable = Tile(nullptr, nullptr, Tile::Status::Unavailable);
|
||||
|
||||
Tile::Tile(std::shared_ptr<ghoul::opengl::Texture> texture,
|
||||
std::shared_ptr<TileMetaData> metaData, Status status)
|
||||
: MemoryAwareCacheable(
|
||||
(sizeof(Tile) +
|
||||
(metaData ? sizeof(TileMetaData) : 0) +
|
||||
(texture ? sizeof(ghoul::opengl::Texture) + texture->expectedPixelDataSize() * 2 : 0))
|
||||
// Multiply by two since double memory is used when creating mip maps.
|
||||
/ 1000 // Convert from bytes to kilobytes
|
||||
)
|
||||
, _texture(texture)
|
||||
, _metaData(metaData)
|
||||
, _status(status)
|
||||
{}
|
||||
|
||||
Tile Tile::createPlainTile(const glm::uvec2& size, const glm::uvec4& color) {
|
||||
using namespace ghoul::opengl;
|
||||
|
||||
@@ -58,13 +73,9 @@ Tile Tile::createPlainTile(const glm::uvec2& size, const glm::uvec4& color) {
|
||||
texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
|
||||
// Create tile
|
||||
Tile tile;
|
||||
tile.status = Tile::Status::OK;
|
||||
tile.metaData = nullptr;
|
||||
tile.texture = texture;
|
||||
|
||||
Tile tile(texture, nullptr, Tile::Status::OK);
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 Tile::compensateSourceTextureSampling(glm::vec2 startOffset, glm::vec2 sizeDiff,
|
||||
glm::uvec2 resolution, glm::vec2 tileUV)
|
||||
@@ -81,8 +92,8 @@ glm::vec2 Tile::TileUvToTextureSamplePosition(const TileUvTransform& uvTransform
|
||||
{
|
||||
glm::vec2 uv = uvTransform.uvOffset + uvTransform.uvScale * tileUV;
|
||||
uv = compensateSourceTextureSampling(
|
||||
TileDataset::tilePixelStartOffset,
|
||||
TileDataset::tilePixelSizeDifference,
|
||||
RawTileDataReader::tilePixelStartOffset,
|
||||
RawTileDataReader::tilePixelSizeDifference,
|
||||
resolution,
|
||||
uv);
|
||||
return uv;
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/tileindex.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tileuvtransform.h>
|
||||
|
||||
#include <modules/globebrowsing/cache/memoryawarecacheable.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ghoul { namespace opengl {
|
||||
@@ -44,42 +45,49 @@ struct TileUvTransform;
|
||||
/**
|
||||
* Defines a status and may have a Texture and TileMetaData
|
||||
*/
|
||||
struct Tile {
|
||||
std::shared_ptr<ghoul::opengl::Texture> texture;
|
||||
std::shared_ptr<TileMetaData> metaData;
|
||||
|
||||
/**
|
||||
class Tile : public cache::MemoryAwareCacheable {
|
||||
public:
|
||||
/**
|
||||
* 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.
|
||||
* texture and tileMetaData are both null
|
||||
*/
|
||||
Unavailable,
|
||||
enum class Status {
|
||||
/**
|
||||
* E.g when texture data is not currently in memory.
|
||||
* texture and tileMetaData are both null
|
||||
*/
|
||||
Unavailable,
|
||||
|
||||
/**
|
||||
* Can be set by <code>TileProvider</code>s if the requested
|
||||
* <code>TileIndex</code> is undefined for that particular
|
||||
* provider.
|
||||
* texture and metaData are both null
|
||||
*/
|
||||
OutOfRange,
|
||||
/**
|
||||
* Can be set by <code>TileProvider</code>s if the requested
|
||||
* <code>TileIndex</code> is undefined for that particular
|
||||
* provider.
|
||||
* texture and metaData are both null
|
||||
*/
|
||||
OutOfRange,
|
||||
|
||||
/**
|
||||
* An IO Error happend
|
||||
* texture and metaData are both null
|
||||
*/
|
||||
IOError,
|
||||
/**
|
||||
* An IO Error happend
|
||||
* texture and metaData are both null
|
||||
*/
|
||||
IOError,
|
||||
|
||||
/**
|
||||
* The Texture is uploaded to the GPU and good for usage.
|
||||
* texture is defined. metaData may be defined.
|
||||
*/
|
||||
OK
|
||||
};
|
||||
|
||||
Tile(std::shared_ptr<ghoul::opengl::Texture> texture,
|
||||
std::shared_ptr<TileMetaData> metaData,
|
||||
Status status);
|
||||
~Tile() = default;
|
||||
|
||||
std::shared_ptr<TileMetaData> metaData() const { return _metaData; };
|
||||
Status status() const { return _status; };
|
||||
std::shared_ptr<ghoul::opengl::Texture> texture() const { return _texture; };
|
||||
|
||||
/**
|
||||
* The Texture is uploaded to the GPU and good for usage.
|
||||
* texture is defined. metaData may be defined.
|
||||
*/
|
||||
OK
|
||||
} status;
|
||||
|
||||
/**
|
||||
* Instantiates a new tile with a single color.
|
||||
*
|
||||
@@ -90,18 +98,20 @@ struct Tile {
|
||||
* with the requested size and color
|
||||
*/
|
||||
static Tile createPlainTile(const glm::uvec2& size, const glm::uvec4& color);
|
||||
|
||||
static glm::vec2 compensateSourceTextureSampling(glm::vec2 startOffset,
|
||||
glm::vec2 sizeDiff, glm::uvec2 resolution, glm::vec2 tileUV);
|
||||
|
||||
static glm::vec2 TileUvToTextureSamplePosition(const TileUvTransform& uvTransform,
|
||||
glm::vec2 tileUV, glm::uvec2 resolution);
|
||||
/**
|
||||
* A tile with status unavailable that any user can return to
|
||||
* indicate that a tile was unavailable.
|
||||
*/
|
||||
static const Tile TileUnavailable;
|
||||
|
||||
/**
|
||||
* A tile with status unavailable that any user can return to
|
||||
* indicate that a tile was unavailable.
|
||||
*/
|
||||
static const Tile TileUnavailable;
|
||||
private:
|
||||
std::shared_ptr<ghoul::opengl::Texture> _texture;
|
||||
std::shared_ptr<TileMetaData> _metaData;
|
||||
Status _status;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
|
||||
@@ -30,22 +30,21 @@
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
|
||||
#include <gdal.h>
|
||||
|
||||
class GDALDataset;
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
struct TileDataLayout {
|
||||
TileDataLayout();
|
||||
TileDataLayout(GDALDataset* dataSet, GLuint preferredGlType);
|
||||
|
||||
GDALDataType gdalType;
|
||||
GLuint glType;
|
||||
|
||||
size_t bytesPerDatum;
|
||||
size_t numRasters;
|
||||
/// Number of rasters available in the GDAL dataset.
|
||||
/// Does not necessarily have to be equal to numRasters.
|
||||
/// In case an extra alpha channel needs to be added that
|
||||
/// does not exist in the GDAL dataset for example
|
||||
size_t numRastersAvailable;
|
||||
size_t bytesPerPixel;
|
||||
|
||||
TextureFormat textureFormat;
|
||||
|
||||
150
modules/globebrowsing/tile/tiledatareader/tiledatareader.cpp
Normal file
150
modules/globebrowsing/tile/tiledatareader/tiledatareader.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/tile/tiledatareader.h>
|
||||
|
||||
#include <modules/globebrowsing/geometry/angle.h>
|
||||
#include <modules/globebrowsing/geometry/geodetic2.h>
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledatatype.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <float.h>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
const char* _loggerCat = "TileDataReader";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
|
||||
return os << pr.start.x << ", " << pr.start.y <<
|
||||
" with size " << pr.numPixels.x << ", " << pr.numPixels.y;
|
||||
}
|
||||
|
||||
TileDataReader::TileDataReader(const Configuration& config)
|
||||
: _config(config)
|
||||
{}
|
||||
|
||||
std::shared_ptr<RawTile> TileDataReader::defaultTileData() {
|
||||
ensureInitialized();
|
||||
PixelRegion pixelRegion = {
|
||||
PixelRegion::PixelCoordinate(0, 0),
|
||||
PixelRegion::PixelRange(16, 16)
|
||||
};
|
||||
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
|
||||
rawTile->tileIndex = { 0, 0, 0 };
|
||||
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
|
||||
rawTile->nBytesImageData =
|
||||
rawTile->dimensions.x * rawTile->dimensions.y * _dataLayout.bytesPerPixel;
|
||||
rawTile->imageData = new char[rawTile->nBytesImageData];
|
||||
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
|
||||
rawTile->imageData[i] = 0;
|
||||
}
|
||||
rawTile->error = RawTile::ReadError::None;
|
||||
|
||||
if (_config.doPreProcessing) {
|
||||
rawTile->tileMetaData = getTileMetaData(rawTile, pixelRegion);
|
||||
}
|
||||
|
||||
return rawTile;
|
||||
}
|
||||
|
||||
std::array<double, 6> TileDataset::getGeoTransform() const {
|
||||
std::array<double, 6> padfTransform;
|
||||
|
||||
// Global coverage of the whole latlon space
|
||||
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2, M_PI));
|
||||
padfTransform[1] = Angle<double>::fromRadians(
|
||||
globalCoverage.size().lon).asDegrees() / rasterXSize();
|
||||
padfTransform[5] = -Angle<double>::fromRadians(
|
||||
globalCoverage.size().lat).asDegrees() / rasterYSize();
|
||||
padfTransform[0] = Angle<double>::fromRadians(
|
||||
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
|
||||
padfTransform[3] = Angle<double>::fromRadians(
|
||||
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
|
||||
padfTransform[2] = 0;
|
||||
padfTransform[4] = 0;
|
||||
|
||||
return padfTransform;
|
||||
}
|
||||
|
||||
PixelRegion::PixelCoordinate TileDataReader::geodeticToPixel(const Geodetic2& geo) const {
|
||||
std::array<double, 6> padfTransform = getGeoTransform();
|
||||
|
||||
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
|
||||
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
|
||||
|
||||
// convert from pixel and line to geodetic coordinates
|
||||
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
|
||||
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
|
||||
|
||||
// <=>
|
||||
double* a = &(padfTransform[0]);
|
||||
double* b = &(padfTransform[3]);
|
||||
|
||||
// Xp = a[0] + P*a[1] + L*a[2];
|
||||
// Yp = b[0] + P*b[1] + L*b[2];
|
||||
|
||||
// <=>
|
||||
double divisor = (a[2] * b[1] - a[1] * b[2]);
|
||||
ghoul_assert(divisor != 0.0, "Division by zero!");
|
||||
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
|
||||
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
|
||||
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
|
||||
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
|
||||
|
||||
double Xp = a[0] + P*a[1] + L*a[2];
|
||||
double Yp = b[0] + P*b[1] + L*b[2];
|
||||
|
||||
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
|
||||
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
|
||||
|
||||
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
|
||||
}
|
||||
|
||||
Geodetic2 TileDataReader::pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const {
|
||||
std::array<double, 6> padfTransform = getGeoTransform();
|
||||
Geodetic2 geodetic;
|
||||
// Should be using radians and not degrees?
|
||||
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
|
||||
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
|
||||
return geodetic;
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
92
modules/globebrowsing/tile/tiledatareader/tiledatareader.h
Normal file
92
modules/globebrowsing/tile/tiledatareader/tiledatareader.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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_DATAREADER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATAREADER___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <gdal.h>
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
struct RawTile;
|
||||
class GeodeticPatch;
|
||||
|
||||
/**
|
||||
* Interface for reading <code>RawTile</code>s given a <code>TileIndex</code>
|
||||
*/
|
||||
class TileDataReader {
|
||||
public:
|
||||
struct Configuration {
|
||||
bool doPreProcessing;
|
||||
int minimumTilePixelSize;
|
||||
GLuint dataType = 0; // default = no datatype reinterpretation
|
||||
};
|
||||
|
||||
virtual TileDataReader(const Configuration& config);
|
||||
|
||||
std::shared_ptr<RawTile> defaultTileData();
|
||||
|
||||
virtual std::shared_ptr<RawTile> readTileData(TileIndex tileIndex) = 0;
|
||||
virtual int maxChunkLevel() = 0;
|
||||
virtual TileDepthTransform getDepthTransform() = 0;
|
||||
virtual const TileDataLayout& getDataLayout() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual float noDataValueAsFloat() = 0;
|
||||
virtual size_t rasterXSize() = 0;
|
||||
virtual size_t rasterYSize() = 0;
|
||||
|
||||
const static glm::ivec2 tilePixelStartOffset;
|
||||
const static glm::ivec2 tilePixelSizeDifference;
|
||||
const static PixelRegion padding; // same as the two above
|
||||
|
||||
const static glm::ivec2 tilePixelStartOffset;
|
||||
const static glm::ivec2 tilePixelSizeDifference;
|
||||
const static PixelRegion padding; // same as the two above
|
||||
|
||||
static bool logReadErrors;
|
||||
|
||||
protected:
|
||||
Configuration _config;
|
||||
|
||||
virtual std::array<double, 6> padfTransform getGeoTransform();
|
||||
PixelRegion::PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
|
||||
Geodetic2 pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATAREADER___H__
|
||||
@@ -1,823 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/tile/tiledataset.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <ogr_featurestyle.h>
|
||||
#include <ogr_spatialref.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h> // abspath
|
||||
#include <ghoul/misc/assert.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
|
||||
|
||||
#include <modules/globebrowsing/geometry/angle.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/engine/configurationmanager.h>
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ghoul/filesystem/file.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <ghoul/misc/threadpool.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledatatype.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
#include <modules/globebrowsing/geometry/geodetic2.h>
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "TileDataset";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
|
||||
return os << pr.start.x << ", " << pr.start.y << " with size " << pr.numPixels.x << ", " << pr.numPixels.y;
|
||||
}
|
||||
|
||||
TileDataset::IODescription TileDataset::IODescription::cut(PixelRegion::Side side, int pos) {
|
||||
PixelRegion readPreCut = read.region;
|
||||
PixelRegion writePreCut = write.region;
|
||||
|
||||
glm::dvec2 ratio;
|
||||
ratio.x = write.region.numPixels.x / (double) read.region.numPixels.x;
|
||||
ratio.y = write.region.numPixels.y / (double) read.region.numPixels.y;
|
||||
|
||||
// double ratioRatio = ratio.x / ratio.y;
|
||||
|
||||
//ghoul_assert(glm::abs(ratioRatio - 1.0) < 0.01, "Different read/write aspect ratio!");
|
||||
|
||||
IODescription whatCameOff = *this;
|
||||
whatCameOff.read.region = read.region.globalCut(side, pos);
|
||||
|
||||
PixelRegion::PixelRange cutSize = whatCameOff.read.region.numPixels;
|
||||
PixelRegion::PixelRange localWriteCutSize = ratio * glm::dvec2(cutSize);
|
||||
|
||||
if (cutSize.x == 0 || cutSize.y == 0) {
|
||||
ghoul_assert(
|
||||
read.region.equals(readPreCut),
|
||||
"Read region should not have been modified"
|
||||
);
|
||||
ghoul_assert(
|
||||
write.region.equals(writePreCut),
|
||||
"Write region should not have been modified"
|
||||
);
|
||||
}
|
||||
|
||||
int localWriteCutPos = (side == PixelRegion::Side::LEFT || side == PixelRegion::Side::RIGHT)
|
||||
? localWriteCutSize.x : localWriteCutSize.y;
|
||||
whatCameOff.write.region = write.region.localCut(side, localWriteCutPos);
|
||||
|
||||
return whatCameOff;
|
||||
}
|
||||
|
||||
const glm::ivec2 TileDataset::tilePixelStartOffset = glm::ivec2(-2);
|
||||
const glm::ivec2 TileDataset::tilePixelSizeDifference = glm::ivec2(4);
|
||||
|
||||
const PixelRegion TileDataset::padding = PixelRegion(
|
||||
tilePixelStartOffset,
|
||||
tilePixelSizeDifference
|
||||
);
|
||||
|
||||
bool TileDataset::GdalHasBeenInitialized = false;
|
||||
|
||||
TileDataset::TileDataset(const std::string& gdalDatasetDesc, const Configuration& config)
|
||||
: _config(config)
|
||||
, hasBeenInitialized(false)
|
||||
{
|
||||
_initData = { "", gdalDatasetDesc, config.minimumTilePixelSize, config.dataType };
|
||||
ensureInitialized();
|
||||
_initData.initDirectory = CPLGetCurrentDir();
|
||||
}
|
||||
|
||||
void TileDataset::reset() {
|
||||
_cached._maxLevel = -1;
|
||||
if (_dataset != nullptr) {
|
||||
GDALClose((GDALDatasetH)_dataset);
|
||||
}
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
float TileDataset::noDataValueAsFloat() {
|
||||
float noDataValue;
|
||||
if (_dataset && _dataset->GetRasterBand(1)) {
|
||||
noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue();;
|
||||
}
|
||||
else {
|
||||
noDataValue = std::numeric_limits<float>::min();
|
||||
}
|
||||
return noDataValue;
|
||||
}
|
||||
|
||||
void TileDataset::ensureInitialized() {
|
||||
if (!hasBeenInitialized) {
|
||||
initialize();
|
||||
hasBeenInitialized = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TileDataset::initialize() {
|
||||
gdalEnsureInitialized();
|
||||
|
||||
_dataset = gdalDataset(_initData.gdalDatasetDesc);
|
||||
|
||||
//Do any other initialization needed for the TileDataset
|
||||
_dataLayout = TileDataLayout(_dataset, _initData.dataType);
|
||||
_depthTransform = calculateTileDepthTransform();
|
||||
_cached._tileLevelDifference = calculateTileLevelDifference(_initData.minimumPixelSize);
|
||||
}
|
||||
|
||||
void TileDataset::gdalEnsureInitialized() {
|
||||
if (!GdalHasBeenInitialized) {
|
||||
GDALAllRegister();
|
||||
CPLSetConfigOption(
|
||||
"GDAL_DATA",
|
||||
absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str()
|
||||
);
|
||||
setGdalProxyConfiguration();
|
||||
GdalHasBeenInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TileDataset::setGdalProxyConfiguration() {
|
||||
ghoul::Dictionary proxySettings;
|
||||
bool proxyEnabled = OsEng.configurationManager().getValue(
|
||||
ConfigurationManager::KeyHttpProxy, proxySettings
|
||||
);
|
||||
if (proxyEnabled) {
|
||||
std::string proxyAddress, proxyPort, proxyUser, proxyPassword, proxyAuth;
|
||||
|
||||
bool success = proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyAddress,
|
||||
proxyAddress
|
||||
);
|
||||
success &= proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyPort,
|
||||
proxyPort
|
||||
);
|
||||
proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyAuthentication,
|
||||
proxyAuth
|
||||
);
|
||||
|
||||
std::string proxyAuthString = "BASIC";
|
||||
if (proxyAuth == "basic" || proxyAuth == "") {
|
||||
proxyAuthString = "BASIC";
|
||||
} else if (proxyAuth == "ntlm") {
|
||||
proxyAuthString = "NTLM";
|
||||
} else if (proxyAuth == "digest") {
|
||||
proxyAuthString = "DIGEST";
|
||||
} else if (proxyAuth == "any") {
|
||||
proxyAuthString = "ANY";
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
|
||||
bool userAndPassword = proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyUser,
|
||||
proxyUser
|
||||
);
|
||||
userAndPassword &= proxySettings.getValue(
|
||||
ConfigurationManager::PartHttpProxyPassword,
|
||||
proxyPassword
|
||||
);
|
||||
|
||||
if (success) {
|
||||
std::string proxy = proxyAddress + ":" + proxyPort;
|
||||
CPLSetConfigOption("GDAL_HTTP_PROXY", proxy.c_str());
|
||||
LDEBUG("Using proxy server " << proxy);
|
||||
if (userAndPassword) {
|
||||
std::string proxyUserPwd = proxyUser + ":" + proxyPassword;
|
||||
CPLSetConfigOption("GDAL_HTTP_PROXYUSERPWD", proxyUserPwd.c_str());
|
||||
CPLSetConfigOption("GDAL_HTTP_PROXYAUTH", proxyAuthString.c_str());
|
||||
LDEBUG("Using authentication method: " << proxyAuthString);
|
||||
}
|
||||
} else {
|
||||
LERROR("Invalid proxy settings for GDAL");
|
||||
}
|
||||
} else {
|
||||
LDEBUG("Setting up GDAL without proxy server");
|
||||
}
|
||||
}
|
||||
|
||||
GDALDataset* TileDataset::gdalDataset(const std::string& gdalDatasetDesc) {
|
||||
GDALDataset* dataset = (GDALDataset *)GDALOpen(gdalDatasetDesc.c_str(), GA_ReadOnly);
|
||||
if (!dataset) {
|
||||
using namespace ghoul::filesystem;
|
||||
std::string correctedPath = FileSystem::ref().pathByAppendingComponent(
|
||||
_initData.initDirectory, gdalDatasetDesc
|
||||
);
|
||||
dataset = (GDALDataset *)GDALOpen(correctedPath.c_str(), GA_ReadOnly);
|
||||
if (!dataset) {
|
||||
throw ghoul::RuntimeError("Failed to load dataset:\n" + gdalDatasetDesc);
|
||||
}
|
||||
}
|
||||
|
||||
// Commenting away the following for now since it is not supported for older
|
||||
// versions of GDAL. Only used for debug info.
|
||||
/*
|
||||
const std::string originalDriverName = dataset->GetDriverName();
|
||||
|
||||
if (originalDriverName != "WMS") {
|
||||
LDEBUG(" " << originalDriverName);
|
||||
LDEBUG(" " << dataset->GetGCPProjection());
|
||||
LDEBUG(" " << dataset->GetProjectionRef());
|
||||
|
||||
GDALDriver* driver = dataset->GetDriver();
|
||||
char** metadata = driver->GetMetadata();
|
||||
for (int i = 0; metadata[i] != nullptr; i++) {
|
||||
LDEBUG(" " << metadata[i]);
|
||||
}
|
||||
|
||||
const char* in_memory = "";
|
||||
//GDALDataset* vrtDataset = driver->CreateCopy(in_memory, dataset, false, nullptr, nullptr, nullptr);
|
||||
}
|
||||
*/
|
||||
|
||||
return dataset;
|
||||
}
|
||||
|
||||
TileDataset::~TileDataset() {
|
||||
delete _dataset;
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTile> TileDataset::readTileData(TileIndex tileIndex) {
|
||||
ensureInitialized();
|
||||
IODescription io = getIODescription(tileIndex);
|
||||
CPLErr worstError = CPLErr::CE_None;
|
||||
|
||||
// Build the RawTile from the data we querred
|
||||
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
|
||||
rawTile->imageData = readImageData(io, worstError);
|
||||
rawTile->error = worstError;
|
||||
rawTile->tileIndex = tileIndex;
|
||||
rawTile->dimensions = glm::uvec3(io.write.region.numPixels, 1);
|
||||
rawTile->nBytesImageData = io.write.totalNumBytes;
|
||||
|
||||
if (_config.doPreProcessing) {
|
||||
rawTile->tileMetaData = getTileMetaData(rawTile, io.write.region);
|
||||
rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
|
||||
}
|
||||
|
||||
return rawTile;
|
||||
}
|
||||
|
||||
std::shared_ptr<RawTile> TileDataset::defaultTileData() {
|
||||
ensureInitialized();
|
||||
PixelRegion pixelRegion = {
|
||||
PixelRegion::PixelCoordinate(0, 0),
|
||||
PixelRegion::PixelRange(16, 16)
|
||||
};
|
||||
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
|
||||
rawTile->tileIndex = { 0, 0, 0 };
|
||||
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
|
||||
rawTile->nBytesImageData =
|
||||
rawTile->dimensions.x * rawTile->dimensions.y * _dataLayout.bytesPerPixel;
|
||||
rawTile->imageData = new char[rawTile->nBytesImageData];
|
||||
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
|
||||
rawTile->imageData[i] = 0;
|
||||
}
|
||||
rawTile->error = CPLErr::CE_None;
|
||||
|
||||
if (_config.doPreProcessing) {
|
||||
rawTile->tileMetaData = getTileMetaData(rawTile, pixelRegion);
|
||||
//rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
|
||||
}
|
||||
|
||||
return rawTile;
|
||||
}
|
||||
|
||||
int TileDataset::maxChunkLevel() {
|
||||
ensureInitialized();
|
||||
if (_cached._maxLevel < 0) {
|
||||
int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount();
|
||||
_cached._maxLevel = -_cached._tileLevelDifference;
|
||||
if (numOverviews > 0) {
|
||||
_cached._maxLevel += numOverviews - 1;
|
||||
}
|
||||
}
|
||||
return _cached._maxLevel;
|
||||
}
|
||||
|
||||
TileDepthTransform TileDataset::getDepthTransform() {
|
||||
ensureInitialized();
|
||||
return _depthTransform;
|
||||
}
|
||||
|
||||
const TileDataLayout& TileDataset::getDataLayout() {
|
||||
ensureInitialized();
|
||||
return _dataLayout;
|
||||
}
|
||||
|
||||
int TileDataset::calculateTileLevelDifference(int minimumPixelSize) {
|
||||
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
|
||||
GDALRasterBand* maxOverview;
|
||||
int numOverviews = firstBand->GetOverviewCount();
|
||||
int sizeLevel0;
|
||||
if (numOverviews <= 0) { // No overviews. Use first band.
|
||||
maxOverview = firstBand;
|
||||
}
|
||||
else { // Pick the highest overview.
|
||||
maxOverview = firstBand->GetOverview(numOverviews - 1);
|
||||
}
|
||||
sizeLevel0 = maxOverview->GetXSize();
|
||||
double diff = log2(minimumPixelSize) - log2(sizeLevel0);
|
||||
return diff;
|
||||
}
|
||||
|
||||
TileDepthTransform TileDataset::calculateTileDepthTransform() {
|
||||
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
|
||||
|
||||
bool isFloat =
|
||||
(_dataLayout.gdalType == GDT_Float32 || _dataLayout.gdalType == GDT_Float64);
|
||||
double maximumValue =
|
||||
isFloat ? 1.0 : tiledatatype::getMaximumValue(_dataLayout.gdalType);
|
||||
|
||||
TileDepthTransform transform;
|
||||
transform.depthOffset = firstBand->GetOffset();
|
||||
transform.depthScale = firstBand->GetScale() * maximumValue;
|
||||
return transform;
|
||||
}
|
||||
|
||||
bool TileDataset::gdalHasOverviews() const {
|
||||
return _dataset->GetRasterBand(1)->GetOverviewCount() > 0;
|
||||
}
|
||||
|
||||
int TileDataset::gdalOverview(const PixelRegion::PixelRange& regionSizeOverviewZero) const {
|
||||
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
|
||||
|
||||
int minNumPixels0 = glm::min(regionSizeOverviewZero.x, regionSizeOverviewZero.y);
|
||||
|
||||
int overviews = firstBand->GetOverviewCount();
|
||||
GDALRasterBand* maxOverview =
|
||||
overviews ? firstBand->GetOverview(overviews - 1) : firstBand;
|
||||
|
||||
int sizeLevel0 = maxOverview->GetXSize();
|
||||
// The dataset itself may not have overviews but even if it does not, an overview
|
||||
// for the data region can be calculated and possibly be used to sample greater
|
||||
// Regions of the original dataset.
|
||||
int ov = std::log2(minNumPixels0) - std::log2(sizeLevel0 + 1) -
|
||||
_cached._tileLevelDifference;
|
||||
ov = glm::clamp(ov, 0, overviews - 1);
|
||||
|
||||
return ov;
|
||||
}
|
||||
|
||||
int TileDataset::gdalOverview(const TileIndex& tileIndex) const {
|
||||
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
|
||||
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
|
||||
return glm::clamp(ov, 0, overviews - 1);
|
||||
}
|
||||
|
||||
int TileDataset::gdalVirtualOverview(const TileIndex& tileIndex) const {
|
||||
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
|
||||
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
|
||||
return ov;
|
||||
}
|
||||
|
||||
PixelRegion TileDataset::gdalPixelRegion(GDALRasterBand* rasterBand) const {
|
||||
PixelRegion gdalRegion;
|
||||
gdalRegion.start.x = 0;
|
||||
gdalRegion.start.y = 0;
|
||||
gdalRegion.numPixels.x = rasterBand->GetXSize();
|
||||
gdalRegion.numPixels.y = rasterBand->GetYSize();
|
||||
return gdalRegion;
|
||||
}
|
||||
|
||||
PixelRegion TileDataset::gdalPixelRegion(const GeodeticPatch& geodeticPatch) const {
|
||||
Geodetic2 nwCorner = geodeticPatch.getCorner(Quad::NORTH_WEST);
|
||||
Geodetic2 swCorner = geodeticPatch.getCorner(Quad::SOUTH_EAST);
|
||||
PixelRegion::PixelCoordinate pixelStart = geodeticToPixel(nwCorner);
|
||||
PixelRegion::PixelCoordinate pixelEnd = geodeticToPixel(swCorner);
|
||||
PixelRegion gdalRegion(pixelStart, pixelEnd - pixelStart);
|
||||
return gdalRegion;
|
||||
}
|
||||
|
||||
GDALRasterBand* TileDataset::gdalRasterBand(int overview, int raster) const {
|
||||
GDALRasterBand* rasterBand = _dataset->GetRasterBand(raster);
|
||||
// int numberOfOverviews = rasterBand->GetOverviewCount();
|
||||
rasterBand = gdalHasOverviews() ? rasterBand->GetOverview(overview) : rasterBand;
|
||||
ghoul_assert(rasterBand != nullptr, "Rasterband is null");
|
||||
return rasterBand;
|
||||
}
|
||||
|
||||
std::array<double, 6> TileDataset::getGeoTransform() const {
|
||||
std::array<double, 6> padfTransform;
|
||||
CPLErr err = _dataset->GetGeoTransform(&padfTransform[0]);
|
||||
if (err == CE_Failure) {
|
||||
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2, M_PI));
|
||||
padfTransform[1] = Angle<double>::fromRadians(
|
||||
globalCoverage.size().lon).asDegrees() / _dataset->GetRasterXSize();
|
||||
padfTransform[5] = -Angle<double>::fromRadians(
|
||||
globalCoverage.size().lat).asDegrees() / _dataset->GetRasterYSize();
|
||||
padfTransform[0] = Angle<double>::fromRadians(
|
||||
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
|
||||
padfTransform[3] = Angle<double>::fromRadians(
|
||||
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
|
||||
padfTransform[2] = 0;
|
||||
padfTransform[4] = 0;
|
||||
}
|
||||
return padfTransform;
|
||||
}
|
||||
|
||||
PixelRegion::PixelCoordinate TileDataset::geodeticToPixel(const Geodetic2& geo) const {
|
||||
std::array<double, 6> padfTransform = getGeoTransform();
|
||||
|
||||
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
|
||||
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
|
||||
|
||||
// convert from pixel and line to geodetic coordinates
|
||||
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
|
||||
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
|
||||
|
||||
// <=>
|
||||
double* a = &(padfTransform[0]);
|
||||
double* b = &(padfTransform[3]);
|
||||
|
||||
// Xp = a[0] + P*a[1] + L*a[2];
|
||||
// Yp = b[0] + P*b[1] + L*b[2];
|
||||
|
||||
// <=>
|
||||
double divisor = (a[2] * b[1] - a[1] * b[2]);
|
||||
ghoul_assert(divisor != 0.0, "Division by zero!");
|
||||
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
|
||||
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
|
||||
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
|
||||
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
|
||||
|
||||
double Xp = a[0] + P*a[1] + L*a[2];
|
||||
double Yp = b[0] + P*b[1] + L*b[2];
|
||||
|
||||
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
|
||||
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
|
||||
|
||||
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
|
||||
}
|
||||
|
||||
Geodetic2 TileDataset::pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const {
|
||||
std::array<double, 6> padfTransform = getGeoTransform();
|
||||
Geodetic2 geodetic;
|
||||
// Should be using radians and not degrees?
|
||||
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
|
||||
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
|
||||
return geodetic;
|
||||
}
|
||||
|
||||
TileDataset::IODescription TileDataset::getIODescription(const TileIndex& tileIndex) const {
|
||||
IODescription io;
|
||||
io.read.region = gdalPixelRegion(tileIndex);
|
||||
|
||||
if (gdalHasOverviews()) {
|
||||
int overview = gdalOverview(tileIndex);
|
||||
io.read.overview = overview;
|
||||
io.read.region.downscalePow2(overview + 1);
|
||||
io.write.region = io.read.region;
|
||||
io.read.region.pad(padding);
|
||||
}
|
||||
else {
|
||||
io.read.overview = 0;
|
||||
io.write.region = io.read.region;
|
||||
int virtualOverview = gdalVirtualOverview(tileIndex);
|
||||
io.write.region.downscalePow2(virtualOverview + 1);
|
||||
PixelRegion scaledPadding = padding;
|
||||
|
||||
scaledPadding.upscalePow2(std::max(virtualOverview + 1, 0));
|
||||
io.read.region.pad(scaledPadding);
|
||||
}
|
||||
|
||||
// For correct sampling in height dataset, we need to pad the texture tile
|
||||
io.write.region.pad(padding);
|
||||
PixelRegion::PixelRange preRound = io.write.region.numPixels;
|
||||
io.write.region.roundDownToQuadratic();
|
||||
io.write.region.roundUpNumPixelToNearestMultipleOf(2);
|
||||
if (preRound != io.write.region.numPixels) {
|
||||
LDEBUG(tileIndex << " | " << preRound.x << ", " << preRound.y << " --> " << io.write.region.numPixels.x << ", " << io.write.region.numPixels.y);
|
||||
}
|
||||
|
||||
io.write.region.start = PixelRegion::PixelCoordinate(0, 0); // write region starts in origin
|
||||
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
|
||||
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
char* TileDataset::readImageData(IODescription& io, CPLErr& worstError) const {
|
||||
// allocate memory for the image
|
||||
char* imageData = new char[io.write.totalNumBytes];
|
||||
|
||||
// Read the data (each rasterband is a separate channel)
|
||||
for (size_t i = 0; i < _dataLayout.numRasters; i++) {
|
||||
GDALRasterBand* rasterBand = gdalRasterBand(io.read.overview, i + 1);
|
||||
|
||||
// The final destination pointer is offsetted by one datum byte size
|
||||
// for every raster (or data channel, i.e. R in RGB)
|
||||
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
|
||||
|
||||
CPLErr err = repeatedRasterIO(rasterBand, io, dataDestination);
|
||||
|
||||
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
|
||||
// GDAL reads pixel lines top to bottom, we want the opposit
|
||||
return imageData;
|
||||
}
|
||||
|
||||
CPLErr TileDataset::repeatedRasterIO(GDALRasterBand* rasterBand, const IODescription& fullIO, char* dataDestination, int depth) const {
|
||||
std::string spaces = " ";
|
||||
std::string indentation = spaces.substr(0, 2 * depth);
|
||||
|
||||
CPLErr worstError = CPLErr::CE_None;
|
||||
|
||||
// NOTE:
|
||||
// Ascii graphics illustrates the implementation details of this method, for one
|
||||
// specific case. Even though the illustrated case is specific, readers can
|
||||
// hopefully find it useful to get the general idea.
|
||||
|
||||
// Make a copy of the full IO desription as we will have to modify it
|
||||
IODescription io = fullIO;
|
||||
PixelRegion gdalRegion = gdalPixelRegion(rasterBand);
|
||||
|
||||
|
||||
// Example:
|
||||
// We have an io description that defines a WRITE and a READ region.
|
||||
// In this case the READ region extends outside of the defined gdal region,
|
||||
// meaning we will have to do wrapping
|
||||
|
||||
|
||||
// io.write.region io.read.region
|
||||
// | |
|
||||
// V V
|
||||
// +-------+ +-------+
|
||||
// | | | |--------+
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// +-------+ +-------+ |
|
||||
// | | <-- gdalRegion
|
||||
// | |
|
||||
// +--------------+
|
||||
|
||||
|
||||
//LDEBUG(indentation << "-");
|
||||
//LDEBUG(indentation << "repeated read: " << io.read.region);
|
||||
//LDEBUG(indentation << "repeated write: " << io.write.region);
|
||||
|
||||
bool didCutOff = false;
|
||||
|
||||
if (!io.read.region.isInside(gdalRegion)) {
|
||||
// Loop through each side: left, top, right, bottom
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
|
||||
// Example:
|
||||
// We are currently considering the left side of the pixel region
|
||||
PixelRegion::Side side = (PixelRegion::Side) i;
|
||||
IODescription cutoff = io.cut(side, gdalRegion.edge(side));
|
||||
|
||||
// Example:
|
||||
// We cut off the left part that was outside the gdal region, and we now
|
||||
// have an additional io description for the cut off region.
|
||||
// Note that the cut-method used above takes care of the corresponding
|
||||
// WRITE region for us.
|
||||
|
||||
// cutoff.write.region cutoff.read.region
|
||||
// | io.write.region | io.read.region
|
||||
// | | | |
|
||||
// V V V V
|
||||
// +-+-----+ +-+-----+
|
||||
// | | | | | |--------+
|
||||
// | | | | | | |
|
||||
// | | | | | | |
|
||||
// +-+-----+ +-+-----+ |
|
||||
// | | <-- gdalRegion
|
||||
// | |
|
||||
// +--------------+
|
||||
|
||||
if (cutoff.read.region.area() > 0) {
|
||||
// didCutOff = true;
|
||||
|
||||
// Wrap by repeating
|
||||
PixelRegion::Side oppositeSide = (PixelRegion::Side) ((i + 2) % 4);
|
||||
|
||||
cutoff.read.region.align(oppositeSide, gdalRegion.edge(oppositeSide));
|
||||
|
||||
// Example:
|
||||
// The cut off region is wrapped to the opposite side of the region,
|
||||
// i.e. "repeated". Note that we don't want WRITE region to change,
|
||||
// we're only wrapping the READ region.
|
||||
|
||||
// cutoff.write.region io.read.region cutoff.read.region
|
||||
// | io.write.region | |
|
||||
// | | V V
|
||||
// V V +-----+ +-+
|
||||
// +-+-----+ | |------| |
|
||||
// | | | | | | |
|
||||
// | | | | | | |
|
||||
// | | | +-----+ +-+
|
||||
// +-+-----+ | | <-- gdalRegion
|
||||
// | |
|
||||
// +--------------+
|
||||
|
||||
// Example:
|
||||
// The cutoff region has been repeated along one of its sides, but
|
||||
// as we can see in this example, it still has a top part outside the
|
||||
// defined gdal region. This is handled through recursion.
|
||||
CPLErr err = repeatedRasterIO(rasterBand, cutoff, dataDestination, depth + 1);
|
||||
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (worstError > CPLErr::CE_None) {
|
||||
LDEBUG(indentation << "Error reading padding: " << worstError);
|
||||
}
|
||||
|
||||
CPLErr err = rasterIO(rasterBand, io, dataDestination);
|
||||
// worstError = std::max(worstError, err);
|
||||
|
||||
// The return error from a repeated rasterIO is ONLY based on the main region,
|
||||
// which in the usual case will cover the main area of the patch anyway
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
CPLErr TileDataset::rasterIO(GDALRasterBand* rasterBand, const IODescription& io,
|
||||
char* dataDestination) const
|
||||
{
|
||||
PixelRegion gdalRegion = gdalPixelRegion(rasterBand);
|
||||
|
||||
ghoul_assert(io.read.region.isInside(gdalRegion), "write region of bounds!");
|
||||
ghoul_assert(
|
||||
io.write.region.start.x >= 0 && io.write.region.start.y >= 0,
|
||||
"Invalid write region"
|
||||
);
|
||||
|
||||
PixelRegion::PixelCoordinate end = io.write.region.end();
|
||||
size_t largestIndex =
|
||||
(end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _dataLayout.bytesPerPixel;
|
||||
ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region");
|
||||
|
||||
char* dataDest = dataDestination;
|
||||
|
||||
// OBS! GDAL reads pixels top to bottom, but we want our pixels bottom to top.
|
||||
// Therefore, we increment the destination pointer to the last line on in the
|
||||
// buffer, and the we specify in the rasterIO call that we want negative line
|
||||
// spacing. Doing this compensates the flipped Y axis
|
||||
dataDest += (io.write.totalNumBytes - io.write.bytesPerLine);
|
||||
|
||||
// handle requested write region
|
||||
dataDest -= io.write.region.start.y * io.write.bytesPerLine; // note -= since flipped y axis
|
||||
dataDest += io.write.region.start.x * _dataLayout.bytesPerPixel;
|
||||
|
||||
return rasterBand->RasterIO(
|
||||
GF_Read,
|
||||
io.read.region.start.x, // Begin read x
|
||||
io.read.region.start.y, // Begin read y
|
||||
io.read.region.numPixels.x, // width to read x
|
||||
io.read.region.numPixels.y, // width to read y
|
||||
dataDest, // Where to put data
|
||||
io.write.region.numPixels.x, // width to write x in destination
|
||||
io.write.region.numPixels.y, // width to write y in destination
|
||||
_dataLayout.gdalType, // Type
|
||||
_dataLayout.bytesPerPixel, // Pixel spacing
|
||||
-io.write.bytesPerLine // Line spacing
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<TileMetaData> TileDataset::getTileMetaData(
|
||||
std::shared_ptr<RawTile> rawTile,
|
||||
const PixelRegion& region) const
|
||||
{
|
||||
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
|
||||
// size_t totalNumBytes = bytesPerLine * region.numPixels.y;
|
||||
|
||||
TileMetaData* preprocessData = new TileMetaData();
|
||||
preprocessData->maxValues.resize(_dataLayout.numRasters);
|
||||
preprocessData->minValues.resize(_dataLayout.numRasters);
|
||||
preprocessData->hasMissingData.resize(_dataLayout.numRasters);
|
||||
|
||||
std::vector<float> noDataValues;
|
||||
noDataValues.resize(_dataLayout.numRasters);
|
||||
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
preprocessData->maxValues[c] = -FLT_MAX;
|
||||
preprocessData->minValues[c] = FLT_MAX;
|
||||
preprocessData->hasMissingData[c] = false;
|
||||
noDataValues[c] = _dataset->GetRasterBand(1)->GetNoDataValue();
|
||||
}
|
||||
|
||||
for (size_t y = 0; y < region.numPixels.y; y++) {
|
||||
size_t yi = (region.numPixels.y - 1 - y) * bytesPerLine;
|
||||
size_t i = 0;
|
||||
for (size_t x = 0; x < region.numPixels.x; x++) {
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
float noDataValue = _dataset->GetRasterBand(c + 1)->GetNoDataValue();
|
||||
float val = tiledatatype::interpretFloat(
|
||||
_dataLayout.gdalType,
|
||||
&(rawTile->imageData[yi + i])
|
||||
);
|
||||
if (val != noDataValue) {
|
||||
preprocessData->maxValues[c] = std::max(
|
||||
val,
|
||||
preprocessData->maxValues[c]
|
||||
);
|
||||
preprocessData->minValues[c] = std::min(
|
||||
val,
|
||||
preprocessData->minValues[c]
|
||||
);
|
||||
}
|
||||
else {
|
||||
preprocessData->hasMissingData[c] = true;
|
||||
}
|
||||
i += _dataLayout.bytesPerDatum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
if (preprocessData->maxValues[c] > 8800.0f) {
|
||||
//LDEBUG("Bad preprocess data: " << preprocessData->maxValues[c] << " at " << region.tileIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return std::shared_ptr<TileMetaData>(preprocessData);
|
||||
}
|
||||
|
||||
CPLErr TileDataset::postProcessErrorCheck(std::shared_ptr<const RawTile> rawTile,
|
||||
const IODescription& io) const
|
||||
{
|
||||
int success;
|
||||
|
||||
double missingDataValue = gdalRasterBand(io.read.overview)->GetNoDataValue(&success);
|
||||
if (!success) {
|
||||
// missing data value for TERRAIN.wms. Should be specified in XML
|
||||
missingDataValue = 32767;
|
||||
}
|
||||
|
||||
bool hasMissingData = false;
|
||||
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
hasMissingData |= rawTile->tileMetaData->maxValues[c] == missingDataValue;
|
||||
}
|
||||
|
||||
bool onHighLevel = rawTile->tileIndex.level > 6;
|
||||
if (hasMissingData && onHighLevel) {
|
||||
return CE_Fatal;
|
||||
}
|
||||
// ugly test for heightmap overlay
|
||||
if (_dataLayout.textureFormat.ghoulFormat == ghoul::opengl::Texture::Format::RG) {
|
||||
// check the alpha
|
||||
if (rawTile->tileMetaData->maxValues[1] == 0.0
|
||||
&& rawTile->tileMetaData->minValues[1] == 0.0)
|
||||
{
|
||||
//return CE_Warning;
|
||||
}
|
||||
}
|
||||
return CE_None;
|
||||
}
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
@@ -1,188 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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_DATASET___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATASET___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/textureformat.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/tile/tiledepthtransform.h>
|
||||
#include <modules/globebrowsing/tile/tiledatalayout.h>
|
||||
#include <modules/globebrowsing/tile/tiledataset.h>
|
||||
#include <modules/globebrowsing/tile/pixelregion.h>
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <gdal.h>
|
||||
#include <string>
|
||||
|
||||
class GDALDataset;
|
||||
class GDALRasterBand;
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class RawTile;
|
||||
class GeodeticPatch;
|
||||
|
||||
class TileDataset {
|
||||
public:
|
||||
struct Configuration {
|
||||
bool doPreProcessing;
|
||||
int minimumTilePixelSize;
|
||||
GLuint dataType = 0; // default = no datatype reinterpretation
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Opens a GDALDataset in readonly mode and calculates meta data required for
|
||||
* reading tile using a TileIndex.
|
||||
*
|
||||
* \param gdalDatasetDesc - A path to a specific file or raw XML describing the dataset
|
||||
* \param minimumPixelSize - minimum number of pixels per side per tile requested
|
||||
* \param datatype - datatype for storing pixel data in requested tile
|
||||
*/
|
||||
TileDataset(const std::string& gdalDatasetDesc, const Configuration& config);
|
||||
|
||||
~TileDataset();
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Public interface //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
std::shared_ptr<RawTile> readTileData(TileIndex tileIndex);
|
||||
std::shared_ptr<RawTile> defaultTileData();
|
||||
int maxChunkLevel();
|
||||
TileDepthTransform getDepthTransform();
|
||||
const TileDataLayout& getDataLayout();
|
||||
void reset();
|
||||
float noDataValueAsFloat();
|
||||
|
||||
|
||||
const static glm::ivec2 tilePixelStartOffset;
|
||||
const static glm::ivec2 tilePixelSizeDifference;
|
||||
const static PixelRegion padding; // same as the two above
|
||||
|
||||
|
||||
private:
|
||||
struct IODescription {
|
||||
struct ReadData {
|
||||
int overview;
|
||||
PixelRegion region;
|
||||
} read;
|
||||
|
||||
struct WriteData {
|
||||
PixelRegion region;
|
||||
size_t bytesPerLine;
|
||||
size_t totalNumBytes;
|
||||
} write;
|
||||
|
||||
IODescription cut(PixelRegion::Side side, int pos);
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Initialization //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void initialize();
|
||||
void ensureInitialized();
|
||||
TileDepthTransform calculateTileDepthTransform();
|
||||
int calculateTileLevelDifference(int minimumPixelSize);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// GDAL helper methods //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void gdalEnsureInitialized();
|
||||
void setGdalProxyConfiguration();
|
||||
GDALDataset* gdalDataset(const std::string& gdalDatasetDesc);
|
||||
bool gdalHasOverviews() const;
|
||||
int gdalOverview(const PixelRegion::PixelRange& baseRegionSize) const;
|
||||
int gdalOverview(const TileIndex& tileIndex) const;
|
||||
int gdalVirtualOverview(const TileIndex& tileIndex) const;
|
||||
PixelRegion gdalPixelRegion(const GeodeticPatch& geodeticPatch) const;
|
||||
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
|
||||
GDALRasterBand* gdalRasterBand(int overview, int raster = 1) const;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// ReadTileData helper functions //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
Returns the geo transform from raster space to projection coordinates as defined
|
||||
by GDAL.
|
||||
|
||||
If the transform is not available, the function returns a transform to map
|
||||
the pixel coordinates to cover the whole geodetic lat long space.
|
||||
*/
|
||||
std::array<double, 6> getGeoTransform() const;
|
||||
|
||||
PixelRegion::PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
|
||||
Geodetic2 pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const;
|
||||
IODescription getIODescription(const TileIndex& tileIndex) const;
|
||||
char* readImageData(IODescription& io, CPLErr& worstError) const;
|
||||
CPLErr rasterIO(GDALRasterBand* rasterBand, const IODescription& io, char* dst) const;
|
||||
CPLErr repeatedRasterIO(GDALRasterBand* rasterBand, const IODescription& io, char* dst, int depth = 0) const;
|
||||
std::shared_ptr<TileMetaData> getTileMetaData(std::shared_ptr<RawTile> result, const PixelRegion& region) const;
|
||||
CPLErr postProcessErrorCheck(std::shared_ptr<const RawTile> ioResult, const IODescription& io) const;
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Member variables //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// init data
|
||||
struct InitData {
|
||||
std::string initDirectory;
|
||||
std::string gdalDatasetDesc;
|
||||
int minimumPixelSize;
|
||||
GLuint dataType;
|
||||
} _initData;
|
||||
|
||||
struct Cached {
|
||||
int _maxLevel = -1;
|
||||
double _tileLevelDifference;
|
||||
} _cached;
|
||||
|
||||
const Configuration _config;
|
||||
|
||||
|
||||
GDALDataset* _dataset;
|
||||
TileDepthTransform _depthTransform;
|
||||
TileDataLayout _dataLayout;
|
||||
|
||||
static bool GdalHasBeenInitialized;
|
||||
bool hasBeenInitialized;
|
||||
};
|
||||
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATASET___H__
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include <modules/globebrowsing/tile/tilemetadata.h>
|
||||
|
||||
#include <gdal_priv.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
|
||||
@@ -24,9 +24,12 @@
|
||||
|
||||
#include <modules/globebrowsing/tile/tileprovider/cachingtileprovider.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/asynctilereader.h>
|
||||
#include <modules/globebrowsing/tile/tiledataset.h>
|
||||
#include <modules/globebrowsing/tile/asynctiledataprovider.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h>
|
||||
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
|
||||
#include <modules/globebrowsing/tile/rawtile.h>
|
||||
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
@@ -35,9 +38,8 @@ namespace {
|
||||
const char* _loggerCat = "CachingTileProvider";
|
||||
|
||||
const char* KeyDoPreProcessing = "DoPreProcessing";
|
||||
const char* KeyMinimumPixelSize = "MinimumPixelSize";
|
||||
const char* KeyTilePixelSize = "TilePixelSize";
|
||||
const char* KeyFilePath = "FilePath";
|
||||
const char* KeyCacheSize = "CacheSize";
|
||||
const char* KeyFlushInterval = "FlushInterval";
|
||||
}
|
||||
|
||||
@@ -46,7 +48,9 @@ namespace globebrowsing {
|
||||
namespace tileprovider {
|
||||
|
||||
CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
|
||||
: _framesSinceLastRequestFlush(0)
|
||||
: TileProvider(dictionary)
|
||||
, _framesSinceLastRequestFlush(0)
|
||||
, _defaultTile(Tile::TileUnavailable)
|
||||
{
|
||||
std::string name = "Name unspecified";
|
||||
dictionary.getValue("Name", name);
|
||||
@@ -59,25 +63,21 @@ CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
|
||||
}
|
||||
|
||||
// 2. Initialize default values for any optional Keys
|
||||
TileDataset::Configuration config;
|
||||
RawTileDataReader::Configuration config;
|
||||
config.doPreProcessing = false;
|
||||
config.minimumTilePixelSize = 512;
|
||||
config.tilePixelSize = 512;
|
||||
|
||||
// getValue does not work for integers
|
||||
double minimumPixelSize;
|
||||
double cacheSize = 512;
|
||||
double minimumPixelSize;
|
||||
double framesUntilRequestFlush = 60;
|
||||
|
||||
// 3. Check for used spcified optional keys
|
||||
if (dictionary.getValue<bool>(KeyDoPreProcessing, config.doPreProcessing)) {
|
||||
LDEBUG("Default doPreProcessing overridden: " << config.doPreProcessing);
|
||||
}
|
||||
if (dictionary.getValue<double>(KeyMinimumPixelSize, minimumPixelSize)) {
|
||||
if (dictionary.getValue<double>(KeyTilePixelSize, minimumPixelSize)) {
|
||||
LDEBUG("Default minimumPixelSize overridden: " << minimumPixelSize);
|
||||
config.minimumTilePixelSize = static_cast<int>(minimumPixelSize);
|
||||
}
|
||||
if (dictionary.getValue<double>(KeyCacheSize, cacheSize)) {
|
||||
LDEBUG("Default cacheSize overridden: " << cacheSize);
|
||||
config.tilePixelSize = static_cast<int>(minimumPixelSize);
|
||||
}
|
||||
if (dictionary.getValue<double>(KeyFlushInterval, framesUntilRequestFlush)) {
|
||||
LDEBUG("Default framesUntilRequestFlush overridden: " <<
|
||||
@@ -85,7 +85,11 @@ CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
|
||||
}
|
||||
|
||||
// Initialize instance variables
|
||||
auto tileDataset = std::make_shared<TileDataset>(filePath, config);
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
auto tileDataset = std::make_shared<GdalRawTileDataReader>(filePath, config);
|
||||
#else // GLOBEBROWSING_USE_GDAL
|
||||
auto tileDataset = std::make_shared<SimpleRawTileDataReader>(filePath, config);
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
// only one thread per provider supported atm
|
||||
// (GDAL does not handle multiple threads for a single dataset very well
|
||||
@@ -94,18 +98,16 @@ CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
|
||||
|
||||
_asyncTextureDataProvider = std::make_shared<AsyncTileDataProvider>(
|
||||
tileDataset, threadPool);
|
||||
_tileCache = std::make_shared<TileCache>(static_cast<size_t>(cacheSize));
|
||||
_framesUntilRequestFlush = framesUntilRequestFlush;
|
||||
}
|
||||
|
||||
CachingTileProvider::CachingTileProvider(
|
||||
std::shared_ptr<AsyncTileDataProvider> tileReader,
|
||||
std::shared_ptr<TileCache> tileCache,
|
||||
int framesUntilFlushRequestQueue)
|
||||
: _asyncTextureDataProvider(tileReader)
|
||||
, _tileCache(tileCache)
|
||||
, _framesUntilRequestFlush(framesUntilFlushRequestQueue)
|
||||
, _framesSinceLastRequestFlush(0)
|
||||
, _defaultTile(Tile::TileUnavailable)
|
||||
{}
|
||||
|
||||
CachingTileProvider::~CachingTileProvider(){
|
||||
@@ -120,32 +122,29 @@ void CachingTileProvider::update() {
|
||||
}
|
||||
|
||||
void CachingTileProvider::reset() {
|
||||
_tileCache->clear();
|
||||
_asyncTextureDataProvider->reset();
|
||||
cache::MemoryAwareTileCache::ref().clear();
|
||||
//_asyncTextureDataProvider->reset();
|
||||
}
|
||||
|
||||
int CachingTileProvider::maxLevel() {
|
||||
return _asyncTextureDataProvider->getTextureDataProvider()->maxChunkLevel();
|
||||
return _asyncTextureDataProvider->getRawTileDataReader()->maxChunkLevel();
|
||||
}
|
||||
|
||||
Tile CachingTileProvider::getTile(const TileIndex& tileIndex) {
|
||||
Tile tile = Tile::TileUnavailable;
|
||||
|
||||
if (tileIndex.level > maxLevel()) {
|
||||
tile.status = Tile::Status::OutOfRange;
|
||||
return tile;
|
||||
return Tile(nullptr, nullptr, Tile::Status::OutOfRange);
|
||||
}
|
||||
|
||||
TileIndex::TileHashKey key = tileIndex.hashKey();
|
||||
cache::ProviderTileKey key = { tileIndex, uniqueIdentifier() };
|
||||
|
||||
if (_tileCache->exist(key)) {
|
||||
return _tileCache->get(key);
|
||||
if (cache::MemoryAwareTileCache::ref().exist(key)) {
|
||||
return cache::MemoryAwareTileCache::ref().get(key);
|
||||
}
|
||||
else {
|
||||
_asyncTextureDataProvider->enqueueTileIO(tileIndex);
|
||||
}
|
||||
|
||||
return tile;
|
||||
return Tile::TileUnavailable;
|
||||
}
|
||||
|
||||
float CachingTileProvider::noDataValueAsFloat() {
|
||||
@@ -153,20 +152,20 @@ float CachingTileProvider::noDataValueAsFloat() {
|
||||
}
|
||||
|
||||
Tile CachingTileProvider::getDefaultTile() {
|
||||
if (_defaultTile.texture == nullptr) {
|
||||
if (_defaultTile.texture() == nullptr) {
|
||||
_defaultTile = createTile(
|
||||
_asyncTextureDataProvider->getTextureDataProvider()->defaultTileData()
|
||||
_asyncTextureDataProvider->getRawTileDataReader()->defaultTileData()
|
||||
);
|
||||
}
|
||||
return _defaultTile;
|
||||
}
|
||||
|
||||
void CachingTileProvider::initTexturesFromLoadedData() {
|
||||
auto rawTiles = _asyncTextureDataProvider->getRawTiles();
|
||||
for (auto rawTile : rawTiles){
|
||||
TileIndex::TileHashKey key = rawTile->tileIndex.hashKey();
|
||||
std::shared_ptr<RawTile> rawTile = _asyncTextureDataProvider->popFinishedRawTile();
|
||||
if (rawTile) {
|
||||
cache::ProviderTileKey key = { rawTile->tileIndex, uniqueIdentifier() };
|
||||
Tile tile = createTile(rawTile);
|
||||
_tileCache->put(key, tile);
|
||||
cache::MemoryAwareTileCache::ref().put(key, tile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,56 +175,49 @@ void CachingTileProvider::clearRequestQueue() {
|
||||
}
|
||||
|
||||
Tile::Status CachingTileProvider::getTileStatus(const TileIndex& tileIndex) {
|
||||
auto tileDataset = _asyncTextureDataProvider->getTextureDataProvider();
|
||||
if (tileIndex.level > tileDataset->maxChunkLevel()) {
|
||||
auto rawTileDataReader = _asyncTextureDataProvider->getRawTileDataReader();
|
||||
if (tileIndex.level > rawTileDataReader->maxChunkLevel()) {
|
||||
return Tile::Status::OutOfRange;
|
||||
}
|
||||
|
||||
TileIndex::TileHashKey key = tileIndex.hashKey();
|
||||
cache::ProviderTileKey key = { tileIndex, uniqueIdentifier() };
|
||||
|
||||
if (_tileCache->exist(key)) {
|
||||
return _tileCache->get(key).status;
|
||||
if (cache::MemoryAwareTileCache::ref().exist(key)) {
|
||||
return cache::MemoryAwareTileCache::ref().get(key).status();
|
||||
}
|
||||
|
||||
return Tile::Status::Unavailable;
|
||||
}
|
||||
|
||||
TileDepthTransform CachingTileProvider::depthTransform() {
|
||||
return _asyncTextureDataProvider->getTextureDataProvider()->getDepthTransform();
|
||||
return _asyncTextureDataProvider->getRawTileDataReader()->getDepthTransform();
|
||||
}
|
||||
|
||||
Tile CachingTileProvider::createTile(std::shared_ptr<RawTile> rawTile) {
|
||||
if (rawTile->error != CE_None) {
|
||||
return{ nullptr, nullptr, Tile::Status::IOError };
|
||||
if (rawTile->error != RawTile::ReadError::None) {
|
||||
return Tile(nullptr, nullptr, Tile::Status::IOError);
|
||||
}
|
||||
|
||||
// TileIndex::TileHashKey key = rawTile->tileIndex.hashKey();
|
||||
TileDataLayout dataLayout =
|
||||
_asyncTextureDataProvider->getTextureDataProvider()->getDataLayout();
|
||||
//TileDataLayout dataLayout =
|
||||
// _asyncTextureDataProvider->getTextureDataProvider()->getDataLayout();
|
||||
|
||||
// The texture should take ownership of the data
|
||||
using ghoul::opengl::Texture;
|
||||
std::shared_ptr<Texture> texture = std::make_shared<Texture>(
|
||||
rawTile->imageData,
|
||||
rawTile->dimensions,
|
||||
dataLayout.textureFormat.ghoulFormat,
|
||||
dataLayout.textureFormat.glFormat,
|
||||
dataLayout.glType,
|
||||
rawTile->textureFormat.ghoulFormat,
|
||||
rawTile->textureFormat.glFormat,
|
||||
rawTile->glType,
|
||||
Texture::FilterMode::Linear,
|
||||
Texture::WrappingMode::ClampToEdge);
|
||||
|
||||
texture->uploadTexture();
|
||||
|
||||
// AnisotropicMipMap must be set after texture is uploaded. Why?!
|
||||
// AnisotropicMipMap must be set after texture is uploaded
|
||||
texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
|
||||
|
||||
Tile tile = {
|
||||
texture,
|
||||
rawTile->tileMetaData,
|
||||
Tile::Status::OK
|
||||
};
|
||||
|
||||
return tile;
|
||||
return Tile(texture, rawTile->tileMetaData, Tile::Status::OK);
|
||||
}
|
||||
|
||||
} // namespace tileprovider
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class AsyncTileDataProvider;
|
||||
class RawTile;
|
||||
struct RawTile;
|
||||
|
||||
namespace tileprovider {
|
||||
|
||||
@@ -44,8 +45,7 @@ public:
|
||||
CachingTileProvider(const ghoul::Dictionary& dictionary);
|
||||
|
||||
CachingTileProvider(
|
||||
std::shared_ptr<AsyncTileDataProvider> tileReader,
|
||||
std::shared_ptr<TileCache> tileCache,
|
||||
std::shared_ptr<AsyncTileDataProvider> tileReader,
|
||||
int framesUntilFlushRequestQueue);
|
||||
|
||||
virtual ~CachingTileProvider();
|
||||
@@ -87,7 +87,7 @@ private:
|
||||
void clearRequestQueue();
|
||||
|
||||
std::shared_ptr<AsyncTileDataProvider> _asyncTextureDataProvider;
|
||||
std::shared_ptr<TileCache> _tileCache;
|
||||
//std::shared_ptr<TileCache> _tileCache;
|
||||
|
||||
int _framesSinceLastRequestFlush;
|
||||
int _framesUntilRequestFlush;
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/globebrowsing/tile/tileprovider/projectiontileprovider.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/util/time.h>
|
||||
#include <openspace/util/powerscaledcoordinate.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
|
||||
#include <modules/space/rendering/planetgeometry.h>
|
||||
#include <modules/newhorizons/util/imagesequencer.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
#include <ghoul/opengl/textureunit.h>
|
||||
|
||||
namespace {
|
||||
const char* _loggerCat = "ProjectionTileProvider";
|
||||
|
||||
const char* keyGeometry = "Geometry";
|
||||
const char* keyProjection = "Projection";
|
||||
const char* keyMeridianShift = "Textures.MeridianShift";
|
||||
const char* keyColorTexture = "Textures.Color";
|
||||
const char* keyHeightTexture = "Textures.Height";
|
||||
|
||||
const char* keyRadius = "Geometry.Radius";
|
||||
const char* keyShading = "PerformShading";
|
||||
const char* _mainFrame = "GALACTIC";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace tileprovider {
|
||||
|
||||
/*
|
||||
documentation::Documentation ProjectionTileProvider::Documentation() {
|
||||
using namespace openspace::documentation;
|
||||
return {
|
||||
"Renderable Planet Projection",
|
||||
"newhorizons_renderable_planetprojection",
|
||||
{
|
||||
{
|
||||
"Type",
|
||||
new StringEqualVerifier("RenderablePlanetProjection"),
|
||||
"",
|
||||
Optional::No
|
||||
},
|
||||
{
|
||||
keyGeometry,
|
||||
new ReferencingVerifier("space_geometry_planet"),
|
||||
"The geometry that is used for rendering this planet.",
|
||||
Optional::No
|
||||
},
|
||||
{
|
||||
keyProjection,
|
||||
new ReferencingVerifier("newhorizons_projectioncomponent"),
|
||||
"Contains information about projecting onto this planet.",
|
||||
Optional::No
|
||||
},
|
||||
{
|
||||
keyMeridianShift,
|
||||
new BoolVerifier,
|
||||
"Determines whether the meridian of the planet should be shifted by 180 "
|
||||
"degrees. The default value is 'false'",
|
||||
Optional::Yes
|
||||
},
|
||||
{
|
||||
keyColorTexture,
|
||||
new StringVerifier,
|
||||
"The path to the base color texture that is used on the planet prior to "
|
||||
"any image projection. The path can use tokens of the form '${...}' or "
|
||||
"be specified relative to the directory of the mod file.",
|
||||
Optional::No
|
||||
},
|
||||
{
|
||||
keyHeightTexture,
|
||||
new StringVerifier,
|
||||
"The path to the height map texture that is used on the planet. The path "
|
||||
"can use tokens of the form '${...}' or be specified relative to the "
|
||||
"directory of the mod file. If no height map is specified the planet "
|
||||
"does not use a height field.",
|
||||
Optional::Yes
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
ProjectionTileProvider::ProjectionTileProvider(const ghoul::Dictionary& dictionary)
|
||||
: _fboProgramObject(nullptr)
|
||||
, _capture(false)
|
||||
, _defaultTile(Tile::TileUnavailable)
|
||||
{
|
||||
|
||||
ghoul::Dictionary geometryDictionary;
|
||||
bool success = dictionary.getValue(
|
||||
keyGeometry, geometryDictionary);
|
||||
if (success) {
|
||||
geometryDictionary.setValue(SceneGraphNode::KeyName, "TestGeometry");
|
||||
using planetgeometry::PlanetGeometry;
|
||||
_geometry = std::unique_ptr<PlanetGeometry>(
|
||||
PlanetGeometry::createFromDictionary(geometryDictionary)
|
||||
);
|
||||
}
|
||||
|
||||
_projectionComponent.initialize(dictionary.value<ghoul::Dictionary>(keyProjection));
|
||||
|
||||
|
||||
addPropertySubOwner(_geometry.get());
|
||||
addPropertySubOwner(_projectionComponent);
|
||||
}
|
||||
|
||||
ProjectionTileProvider::~ProjectionTileProvider(){
|
||||
|
||||
}
|
||||
|
||||
bool ProjectionTileProvider::initialize() {
|
||||
bool completeSuccess = true;
|
||||
completeSuccess &= TileProvider::initialize();
|
||||
|
||||
_fboProgramObject = ghoul::opengl::ProgramObject::Build("fboPassProgram",
|
||||
"${MODULE_NEWHORIZONS}/shaders/renderablePlanetProjection_vs.glsl",
|
||||
"${MODULE_NEWHORIZONS}/shaders/renderablePlanetProjection_fs.glsl"
|
||||
);
|
||||
|
||||
completeSuccess &= _projectionComponent.initializeGL();
|
||||
completeSuccess &= _geometry->initialize(nullptr);
|
||||
|
||||
if (completeSuccess) {
|
||||
//completeSuccess &= auxiliaryRendertarget();
|
||||
// SCREEN-QUAD
|
||||
const GLfloat size = 1.f;
|
||||
const GLfloat w = 1.f;
|
||||
const GLfloat vertex_data[] = {
|
||||
-size, -size, 0.f, w, 0.f, 0.f,
|
||||
size, size, 0.f, w, 1.f, 1.f,
|
||||
-size, size, 0.f, w, 0.f, 1.f,
|
||||
-size, -size, 0.f, w, 0.f, 0.f,
|
||||
size, -size, 0.f, w, 1.f, 0.f,
|
||||
size, size, 0.f, w, 1.f, 1.f,
|
||||
};
|
||||
|
||||
glGenVertexArrays(1, &_quad);
|
||||
glBindVertexArray(_quad);
|
||||
glGenBuffers(1, &_vertexPositionBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast<void*>(0));
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast<void*>(sizeof(GLfloat) * 4));
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
return completeSuccess;
|
||||
}
|
||||
|
||||
bool ProjectionTileProvider::deinitialize() {
|
||||
_projectionComponent.deinitialize();
|
||||
_geometry = nullptr;
|
||||
|
||||
glDeleteVertexArrays(1, &_quad);
|
||||
glDeleteBuffers(1, &_vertexPositionBuffer);
|
||||
|
||||
_fboProgramObject = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProjectionTileProvider::update() {
|
||||
// Update
|
||||
if (_fboProgramObject->isDirty()) {
|
||||
_fboProgramObject->rebuildFromFile();
|
||||
}
|
||||
|
||||
_projectionComponent.update();
|
||||
|
||||
_time = Time::ref().j2000Seconds();
|
||||
_capture = false;
|
||||
|
||||
if (openspace::ImageSequencer::ref().isReady()){
|
||||
openspace::ImageSequencer::ref().updateSequencer(_time);
|
||||
if (_projectionComponent.doesPerformProjection()) {
|
||||
_capture = openspace::ImageSequencer::ref().getImagePaths(
|
||||
_imageTimes,
|
||||
_projectionComponent.projecteeId(),
|
||||
_projectionComponent.instrumentId()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_stateMatrix = glm::dmat3(1.0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Projection
|
||||
|
||||
if (_projectionComponent.needsClearProjection())
|
||||
_projectionComponent.clearAllProjections();
|
||||
|
||||
_camScaling = glm::vec2(1.0);// data.camera.scaling();
|
||||
_up = glm::vec3(0,1,0);// data.camera.lookUpVectorCameraSpace();
|
||||
|
||||
if (_capture && _projectionComponent.doesPerformProjection()) {
|
||||
for (const Image& img : _imageTimes) {
|
||||
attitudeParameters(img.timeRange.start);
|
||||
imageProjectGPU(_projectionComponent.loadProjectionTexture(img.path));
|
||||
}
|
||||
_capture = false;
|
||||
}
|
||||
attitudeParameters(_time);
|
||||
_imageTimes.clear();
|
||||
|
||||
}
|
||||
|
||||
void ProjectionTileProvider::reset() {
|
||||
}
|
||||
|
||||
int ProjectionTileProvider::maxLevel() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
Tile ProjectionTileProvider::getTile(const TileIndex& tileIndex) {
|
||||
_projectionComponent.projectionTexture();
|
||||
}
|
||||
|
||||
float ProjectionTileProvider::noDataValueAsFloat() {
|
||||
|
||||
}
|
||||
|
||||
Tile ProjectionTileProvider::getDefaultTile() {
|
||||
|
||||
}
|
||||
|
||||
Tile::Status ProjectionTileProvider::getTileStatus(const TileIndex& tileIndex) {
|
||||
return Tile::Status::OK;
|
||||
}
|
||||
|
||||
TileDepthTransform ProjectionTileProvider::depthTransform() {
|
||||
|
||||
}
|
||||
|
||||
void ProjectionTileProvider::attitudeParameters(double time) {
|
||||
// precomputations for shader
|
||||
_instrumentMatrix = SpiceManager::ref().positionTransformMatrix(
|
||||
_projectionComponent.instrumentId(), _mainFrame, time
|
||||
);
|
||||
|
||||
_transform = glm::mat4(1);
|
||||
//90 deg rotation w.r.t spice req.
|
||||
glm::mat4 rot = glm::rotate(
|
||||
_transform,
|
||||
static_cast<float>(M_PI_2),
|
||||
glm::vec3(1, 0, 0)
|
||||
);
|
||||
glm::mat4 roty = glm::rotate(
|
||||
_transform,
|
||||
static_cast<float>(M_PI_2),
|
||||
glm::vec3(0, -1, 0)
|
||||
);
|
||||
|
||||
_transform = glm::mat4(_stateMatrix);
|
||||
|
||||
glm::dvec3 bs;
|
||||
try {
|
||||
SpiceManager::FieldOfViewResult res = SpiceManager::ref().fieldOfView(_projectionComponent.instrumentId());
|
||||
bs = std::move(res.boresightVector);
|
||||
}
|
||||
catch (const SpiceManager::SpiceException& e) {
|
||||
LERRORC(e.component, e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
double lightTime;
|
||||
glm::dvec3 p = SpiceManager::ref().targetPosition(
|
||||
_projectionComponent.projectorId(),
|
||||
_projectionComponent.projecteeId(),
|
||||
_mainFrame,
|
||||
_projectionComponent.aberration(),
|
||||
time,
|
||||
lightTime
|
||||
);
|
||||
psc position = PowerScaledCoordinate::CreatePowerScaledCoordinate(p.x, p.y, p.z);
|
||||
|
||||
//change to KM and add psc camera scaling.
|
||||
position[3] += (3 + _camScaling[1]);
|
||||
//position[3] += 3;
|
||||
glm::vec3 cpos = position.vec3();
|
||||
|
||||
float distance = glm::length(cpos);
|
||||
float radius = 1185000.0f;
|
||||
|
||||
_projectorMatrix = _projectionComponent.computeProjectorMatrix(
|
||||
cpos,
|
||||
bs,
|
||||
_up,
|
||||
_instrumentMatrix,
|
||||
_projectionComponent.fieldOfViewY(),
|
||||
_projectionComponent.aspectRatio(),
|
||||
distance - radius,
|
||||
distance + radius,
|
||||
_boresight
|
||||
);
|
||||
}
|
||||
|
||||
void ProjectionTileProvider::imageProjectGPU(
|
||||
std::shared_ptr<ghoul::opengl::Texture> projectionTexture)
|
||||
{
|
||||
_projectionComponent.imageProjectBegin();
|
||||
|
||||
_fboProgramObject->activate();
|
||||
|
||||
ghoul::opengl::TextureUnit unitFbo;
|
||||
unitFbo.activate();
|
||||
projectionTexture->bind();
|
||||
_fboProgramObject->setUniform("projectionTexture", unitFbo);
|
||||
|
||||
_fboProgramObject->setUniform("ProjectorMatrix", _projectorMatrix);
|
||||
_fboProgramObject->setUniform("ModelTransform" , _transform);
|
||||
_fboProgramObject->setUniform("_scaling" , _camScaling);
|
||||
_fboProgramObject->setUniform("boresight" , _boresight);
|
||||
|
||||
if (_geometry->hasProperty("radius")){
|
||||
ghoul::any r = _geometry->property("radius")->get();
|
||||
if (glm::vec4* radius = ghoul::any_cast<glm::vec4>(&r)){
|
||||
_fboProgramObject->setUniform("_radius", radius);
|
||||
}
|
||||
}else{
|
||||
LERROR("Geometry object needs to provide radius");
|
||||
}
|
||||
if (_geometry->hasProperty("segments")){
|
||||
ghoul::any s = _geometry->property("segments")->get();
|
||||
if (int* segments = ghoul::any_cast<int>(&s)){
|
||||
_fboProgramObject->setUniform("_segments", segments[0]);
|
||||
}
|
||||
}else{
|
||||
LERROR("Geometry object needs to provide segment count");
|
||||
}
|
||||
|
||||
glBindVertexArray(_quad);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
_fboProgramObject->deactivate();
|
||||
|
||||
_projectionComponent.imageProjectEnd();
|
||||
}
|
||||
|
||||
|
||||
} // namespace tileprovider
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
121
modules/globebrowsing/tile/tileprovider/projectiontileprovider.h
Normal file
121
modules/globebrowsing/tile/tileprovider/projectiontileprovider.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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___CACHING_TILE_PROVIDER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__
|
||||
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
|
||||
#include <modules/space/rendering/planetgeometry.h>
|
||||
#include <modules/newhorizons/util/projectioncomponent.h>
|
||||
#include <modules/newhorizons/util/sequenceparser.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
|
||||
class AsyncTileDataProvider;
|
||||
class RawTile;
|
||||
|
||||
namespace tileprovider {
|
||||
|
||||
class ProjectionTileProvider : public TileProvider {
|
||||
public:
|
||||
ProjectionTileProvider(const ghoul::Dictionary& dictionary);
|
||||
|
||||
virtual ~ProjectionTileProvider() override;
|
||||
|
||||
virtual Tile getTile(const TileIndex& tileIndex) override;
|
||||
|
||||
virtual Tile getDefaultTile() override;
|
||||
virtual Tile::Status getTileStatus(const TileIndex& tileIndex) override;
|
||||
virtual TileDepthTransform depthTransform() override;
|
||||
virtual void update() override;
|
||||
virtual void reset() override;
|
||||
virtual int maxLevel() override;
|
||||
virtual float noDataValueAsFloat() override;
|
||||
|
||||
bool initialize() override;
|
||||
bool deinitialize() override;
|
||||
|
||||
//static documentation::Documentation Documentation();
|
||||
|
||||
protected:
|
||||
bool loadTextures();
|
||||
void attitudeParameters(double time);
|
||||
|
||||
private:
|
||||
void imageProjectGPU(std::shared_ptr<ghoul::opengl::Texture> projectionTexture);
|
||||
|
||||
ProjectionComponent _projectionComponent;
|
||||
|
||||
//properties::StringProperty _colorTexturePath;
|
||||
//properties::StringProperty _heightMapTexturePath;
|
||||
|
||||
//properties::IntProperty _rotation;
|
||||
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _fboProgramObject;
|
||||
|
||||
//std::unique_ptr<ghoul::opengl::Texture> _baseTexture;
|
||||
//std::unique_ptr<ghoul::opengl::Texture> _heightMapTexture;
|
||||
|
||||
//properties::BoolProperty _shiftMeridianBy180;
|
||||
|
||||
//properties::FloatProperty _heightExaggeration;
|
||||
//properties::FloatProperty _debugProjectionTextureRotation;
|
||||
|
||||
std::unique_ptr<planetgeometry::PlanetGeometry> _geometry;
|
||||
|
||||
glm::vec2 _camScaling;
|
||||
glm::vec3 _up;
|
||||
glm::mat4 _transform;
|
||||
glm::mat4 _projectorMatrix;
|
||||
|
||||
glm::dmat3 _stateMatrix;
|
||||
glm::dmat3 _instrumentMatrix;
|
||||
glm::vec3 _boresight;
|
||||
|
||||
double _time;
|
||||
|
||||
std::vector<Image> _imageTimes;
|
||||
|
||||
std::string _frame;
|
||||
|
||||
bool _capture;
|
||||
|
||||
GLuint _quad;
|
||||
GLuint _vertexPositionBuffer;
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
Tile _defaultTile;
|
||||
};
|
||||
|
||||
} // namespace tileprovider
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__
|
||||
@@ -36,7 +36,9 @@ namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace tileprovider {
|
||||
|
||||
SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) {
|
||||
SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary)
|
||||
: _tile(nullptr, nullptr, Tile::Status::Unavailable)
|
||||
{
|
||||
// Required input
|
||||
if (!dictionary.getValue<std::string>(KeyFilePath, _imagePath)) {
|
||||
throw std::runtime_error(std::string("Must define key '") + KeyFilePath + "'");
|
||||
@@ -47,6 +49,7 @@ SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) {
|
||||
|
||||
SingleImageProvider::SingleImageProvider(const std::string& imagePath)
|
||||
: _imagePath(imagePath)
|
||||
, _tile(nullptr, nullptr, Tile::Status::Unavailable)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
@@ -60,7 +63,7 @@ Tile SingleImageProvider::getDefaultTile() {
|
||||
}
|
||||
|
||||
Tile::Status SingleImageProvider::getTileStatus(const TileIndex& index) {
|
||||
return _tile.status;
|
||||
return _tile.status();
|
||||
}
|
||||
|
||||
TileDepthTransform SingleImageProvider::depthTransform() {
|
||||
@@ -75,15 +78,16 @@ void SingleImageProvider::update() {
|
||||
}
|
||||
|
||||
void SingleImageProvider::reset() {
|
||||
_tile = Tile();
|
||||
_tile.texture = std::shared_ptr<ghoul::opengl::Texture>(
|
||||
auto tileTexture = std::shared_ptr<ghoul::opengl::Texture>(
|
||||
std::move(ghoul::io::TextureReader::ref().loadTexture(_imagePath))
|
||||
);
|
||||
_tile.status = _tile.texture != nullptr ? Tile::Status::OK : Tile::Status::IOError;
|
||||
_tile.metaData = nullptr;
|
||||
auto tileStatus = tileTexture != nullptr ? Tile::Status::OK : Tile::Status::IOError;
|
||||
auto tileMetaData = nullptr;
|
||||
|
||||
_tile.texture->uploadTexture();
|
||||
_tile.texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
tileTexture->uploadTexture();
|
||||
tileTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
|
||||
_tile = Tile(tileTexture, tileMetaData, tileStatus);
|
||||
}
|
||||
|
||||
int SingleImageProvider::maxLevel() {
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace {
|
||||
}
|
||||
|
||||
SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& dictionary)
|
||||
: _backgroundTile(Tile::TileUnavailable)
|
||||
{
|
||||
_fontSize = 50;
|
||||
_font = OsEng.fontManager().font("Mono", _fontSize);
|
||||
@@ -56,16 +57,19 @@ SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& di
|
||||
}
|
||||
_ellipsoid = Ellipsoid(radii);
|
||||
|
||||
_backgroundTile.status = Tile::Status::Unavailable;
|
||||
std::string backgroundImagePath;
|
||||
auto backgroundTileStatus = Tile::Status::Unavailable;
|
||||
std::shared_ptr<ghoul::opengl::Texture> backgroundTileTexture;
|
||||
|
||||
std::string backgroundImagePath;
|
||||
if (dictionary.getValue(KeyBackgroundImagePath, backgroundImagePath)) {
|
||||
using namespace ghoul::io;
|
||||
std::string imgAbsPath = absPath(backgroundImagePath);
|
||||
_backgroundTile.texture = TextureReader::ref().loadTexture(imgAbsPath);
|
||||
_backgroundTile.texture->uploadTexture();
|
||||
_backgroundTile.texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
_backgroundTile.status = Tile::Status::OK;
|
||||
backgroundTileTexture = TextureReader::ref().loadTexture(imgAbsPath);
|
||||
backgroundTileTexture->uploadTexture();
|
||||
backgroundTileTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
backgroundTileStatus = Tile::Status::OK;
|
||||
}
|
||||
_backgroundTile = Tile(backgroundTileTexture, nullptr, backgroundTileStatus);
|
||||
}
|
||||
|
||||
void SizeReferenceTileProvider::renderText(const ghoul::fontrendering::FontRenderer&
|
||||
@@ -124,12 +128,11 @@ TileIndex::TileHashKey SizeReferenceTileProvider::toHash(const TileIndex& tileIn
|
||||
}
|
||||
|
||||
Tile SizeReferenceTileProvider::backgroundTile(const TileIndex& tileIndex) const {
|
||||
if (_backgroundTile.status == Tile::Status::OK) {
|
||||
Tile tile;
|
||||
auto t = _backgroundTile.texture;
|
||||
if (_backgroundTile.status() == Tile::Status::OK) {
|
||||
auto t = _backgroundTile.texture();
|
||||
void* pixelData = new char[t->expectedPixelDataSize()];
|
||||
memcpy(pixelData, t->pixelData(), t->expectedPixelDataSize());
|
||||
tile.texture = std::make_shared<ghoul::opengl::Texture>(
|
||||
auto tileTexture = std::make_shared<ghoul::opengl::Texture>(
|
||||
pixelData,
|
||||
t->dimensions(),
|
||||
t->format(),
|
||||
@@ -138,10 +141,10 @@ Tile SizeReferenceTileProvider::backgroundTile(const TileIndex& tileIndex) const
|
||||
t->filter(),
|
||||
t->wrapping()
|
||||
);
|
||||
tile.texture->uploadTexture();
|
||||
tile.texture->setDataOwnership(ghoul::opengl::Texture::TakeOwnership::Yes);
|
||||
tile.status = Tile::Status::OK;
|
||||
return tile;
|
||||
tileTexture->uploadTexture();
|
||||
tileTexture->setDataOwnership(ghoul::opengl::Texture::TakeOwnership::Yes);
|
||||
auto tileStatus = Tile::Status::OK;
|
||||
return Tile(tileTexture, nullptr, tileStatus);
|
||||
}
|
||||
else {
|
||||
// use default background
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include <modules/globebrowsing/tile/tileprovider/temporaltileprovider.h>
|
||||
|
||||
#include <modules/globebrowsing/tile/tileprovider/cachingtileprovider.h>
|
||||
@@ -58,7 +60,6 @@ const char* TemporalTileProvider::TemporalXMLTags::TIME_FORMAT = "OpenSpaceTimeI
|
||||
TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
|
||||
: _initDict(dictionary)
|
||||
{
|
||||
|
||||
if (!dictionary.getValue<std::string>(KeyFilePath, _datasetFile)) {
|
||||
throw std::runtime_error(std::string("Must define key '") + KeyFilePath + "'");
|
||||
}
|
||||
@@ -74,9 +75,6 @@ TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
|
||||
(std::istreambuf_iterator<char>())
|
||||
);
|
||||
_gdalXmlTemplate = consumeTemporalMetaData(xml);
|
||||
std::shared_ptr<TileProvider> tileProvider = getTileProvider();
|
||||
ghoul_assert(tileProvider, "No tile provider found");
|
||||
_defaultTile = tileProvider->getDefaultTile();
|
||||
}
|
||||
|
||||
std::string TemporalTileProvider::consumeTemporalMetaData(const std::string& xml) {
|
||||
@@ -168,7 +166,7 @@ Tile TemporalTileProvider::getTile(const TileIndex& tileIndex) {
|
||||
}
|
||||
|
||||
Tile TemporalTileProvider::getDefaultTile() {
|
||||
return _defaultTile;
|
||||
return getTileProvider()->getDefaultTile();
|
||||
}
|
||||
|
||||
int TemporalTileProvider::maxLevel() {
|
||||
@@ -384,3 +382,5 @@ bool TimeQuantizer::quantize(Time& t, bool clamp) const {
|
||||
} // namespace tileprovider
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TEMPORAL_TILE_PROVIDER___H__
|
||||
#define __OPENSPACE_MODULE_GLOBEBROWSING___TEMPORAL_TILE_PROVIDER___H__
|
||||
|
||||
@@ -287,8 +288,6 @@ private:
|
||||
// Used for creation of time specific instances of CachingTileProvider
|
||||
ghoul::Dictionary _initDict;
|
||||
|
||||
Tile _defaultTile;
|
||||
|
||||
std::shared_ptr<TileProvider> _currentTileProvider;
|
||||
|
||||
TimeFormat* _timeFormat;
|
||||
@@ -300,3 +299,4 @@ private:
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TEMPORAL_TILE_PROVIDER___H__
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <modules/globebrowsing/geometry/geodeticpatch.h>
|
||||
#include <modules/globebrowsing/tile/tileindex.h>
|
||||
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
|
||||
@@ -42,8 +43,7 @@ namespace globebrowsing {
|
||||
namespace tileprovider {
|
||||
|
||||
TextTileProvider::TextTileProvider(const glm::uvec2& textureSize, size_t fontSize)
|
||||
: _tileCache(500)
|
||||
, _textureSize(textureSize)
|
||||
: _textureSize(textureSize)
|
||||
, _fontSize(fontSize)
|
||||
{
|
||||
_font = OsEng.fontManager().font("Mono", _fontSize);
|
||||
@@ -59,13 +59,14 @@ TextTileProvider::~TextTileProvider() {
|
||||
}
|
||||
|
||||
Tile TextTileProvider::getTile(const TileIndex& tileIndex) {
|
||||
TileIndex::TileHashKey key = tileIndex.hashKey();
|
||||
|
||||
if (!_tileCache.exist(key)) {
|
||||
_tileCache.put(key, createChunkIndexTile(tileIndex));
|
||||
}
|
||||
cache::ProviderTileKey key = { tileIndex, uniqueIdentifier() };
|
||||
|
||||
return _tileCache.get(key);
|
||||
if (!cache::MemoryAwareTileCache::ref().exist(key)) {
|
||||
cache::MemoryAwareTileCache::ref().put(
|
||||
key, createChunkIndexTile(tileIndex));
|
||||
}
|
||||
|
||||
return cache::MemoryAwareTileCache::ref().get(key);
|
||||
}
|
||||
|
||||
Tile TextTileProvider::getDefaultTile() {
|
||||
@@ -86,7 +87,7 @@ TileDepthTransform TextTileProvider::depthTransform() {
|
||||
void TextTileProvider::update() {}
|
||||
|
||||
void TextTileProvider::reset() {
|
||||
_tileCache.clear();
|
||||
cache::MemoryAwareTileCache::ref().clear();
|
||||
}
|
||||
|
||||
Tile TextTileProvider::createChunkIndexTile(const TileIndex& tileIndex) {
|
||||
@@ -104,14 +105,14 @@ Tile TextTileProvider::createChunkIndexTile(const TileIndex& tileIndex) {
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
*(tile.texture),
|
||||
*(tile.texture()),
|
||||
0
|
||||
);
|
||||
|
||||
glViewport(
|
||||
0, 0,
|
||||
static_cast<GLsizei>(tile.texture->width()),
|
||||
static_cast<GLsizei>(tile.texture->height())
|
||||
static_cast<GLsizei>(tile.texture()->width()),
|
||||
static_cast<GLsizei>(tile.texture()->height())
|
||||
);
|
||||
|
||||
ghoul_assert(_fontRenderer != nullptr, "_fontRenderer must not be null");
|
||||
|
||||
@@ -95,7 +95,6 @@ private:
|
||||
Tile createChunkIndexTile(const TileIndex& tileIndex);
|
||||
std::unique_ptr<ghoul::fontrendering::FontRenderer> _fontRenderer;
|
||||
|
||||
TileCache _tileCache;
|
||||
GLuint _fbo;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "TileProvider";
|
||||
|
||||
@@ -40,7 +42,9 @@ namespace {
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace tileprovider {
|
||||
|
||||
|
||||
unsigned int TileProvider::_numTileProviders = 0;
|
||||
|
||||
std::unique_ptr<TileProvider> TileProvider::createFromDictionary(const ghoul::Dictionary& dictionary) {
|
||||
std::string type = "LRUCaching";
|
||||
dictionary.getValue(KeyType, type);
|
||||
@@ -55,13 +59,26 @@ std::unique_ptr<TileProvider> TileProvider::createFromDictionary(const ghoul::Di
|
||||
return result;
|
||||
}
|
||||
|
||||
TileProvider::TileProvider(const ghoul::Dictionary& dictionary) {};
|
||||
TileProvider::TileProvider() :
|
||||
properties::PropertyOwner("tileProvider"),
|
||||
_initialized(false) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
TileProvider::TileProvider(const ghoul::Dictionary& dictionary)
|
||||
: properties::PropertyOwner("tileProvider")
|
||||
, _initialized(false)
|
||||
{
|
||||
initialize();
|
||||
};
|
||||
|
||||
float TileProvider::noDataValueAsFloat() {
|
||||
ghoul_assert(_initialized, "TileProvider was not initialized.");
|
||||
return std::numeric_limits<float>::min();
|
||||
}
|
||||
|
||||
ChunkTile TileProvider::getChunkTile(TileIndex tileIndex, int parents, int maxParents) {
|
||||
ghoul_assert(_initialized, "TileProvider was not initialized.");
|
||||
TileUvTransform uvTransform;
|
||||
uvTransform.uvOffset = glm::vec2(0, 0);
|
||||
uvTransform.uvScale = glm::vec2(1, 1);
|
||||
@@ -80,34 +97,35 @@ ChunkTile TileProvider::getChunkTile(TileIndex tileIndex, int parents, int maxPa
|
||||
maxParents--;
|
||||
}
|
||||
if(maxParents < 0){
|
||||
return { Tile::TileUnavailable, uvTransform };
|
||||
return ChunkTile{ Tile::TileUnavailable, 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 tile = getTile(tileIndex);
|
||||
if (tile.status != Tile::Status::OK) {
|
||||
if (tile.status() != Tile::Status::OK) {
|
||||
if (--maxParents < 0){
|
||||
return{ Tile::TileUnavailable, uvTransform };
|
||||
return ChunkTile{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
|
||||
}
|
||||
tileselector::ascendToParent(tileIndex, uvTransform);
|
||||
}
|
||||
else {
|
||||
return { tile, uvTransform };
|
||||
return ChunkTile{ tile, uvTransform, TileDepthTransform() };
|
||||
}
|
||||
}
|
||||
|
||||
return { Tile::TileUnavailable, uvTransform };
|
||||
return ChunkTile{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
|
||||
}
|
||||
|
||||
ChunkTilePile TileProvider::getChunkTilePile(TileIndex tileIndex, int pileSize){
|
||||
ghoul_assert(_initialized, "TileProvider was not initialized.");
|
||||
ghoul_assert(pileSize >= 0, "pileSize must be positive");
|
||||
ChunkTilePile chunkTilePile;
|
||||
chunkTilePile.resize(pileSize);
|
||||
for (size_t i = 0; i < pileSize; ++i) {
|
||||
chunkTilePile[i] = getChunkTile(tileIndex, i);
|
||||
if (chunkTilePile[i].tile.status == Tile::Status::Unavailable) {
|
||||
if (chunkTilePile[i].tile.status() == Tile::Status::Unavailable) {
|
||||
if (i>0) {
|
||||
chunkTilePile[i].tile = chunkTilePile[i-1].tile;
|
||||
chunkTilePile[i].uvTransform.uvOffset = chunkTilePile[i-1].uvTransform.uvOffset;
|
||||
@@ -123,6 +141,24 @@ ChunkTilePile TileProvider::getChunkTilePile(TileIndex tileIndex, int pileSize){
|
||||
return std::move(chunkTilePile);
|
||||
}
|
||||
|
||||
bool TileProvider::initialize() {
|
||||
ghoul_assert(!_initialized, "TileProvider can only be initialized once.");
|
||||
_uniqueIdentifier = _numTileProviders;
|
||||
_numTileProviders++;
|
||||
if (_numTileProviders == UINT_MAX) {
|
||||
_numTileProviders--;
|
||||
return false;
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int TileProvider::uniqueIdentifier() const {
|
||||
ghoul_assert(_initialized, "TileProvider was not initialized.");
|
||||
return _uniqueIdentifier;
|
||||
}
|
||||
|
||||
} // namespace tileprovider
|
||||
} // namespace globebrowsing
|
||||
} // namespace openspace
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
|
||||
#include <modules/globebrowsing/tile/chunktile.h>
|
||||
#include <modules/globebrowsing/tile/tile.h>
|
||||
#include <modules/globebrowsing/other/lrucache.h>
|
||||
#include <modules/globebrowsing/cache/lrucache.h>
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -39,7 +41,7 @@ namespace tileprovider {
|
||||
* Interface for providing <code>Tile</code>s given a
|
||||
* <code>TileIndex</code>.
|
||||
*/
|
||||
class TileProvider {
|
||||
class TileProvider : public properties::PropertyOwner {
|
||||
public:
|
||||
/**
|
||||
* Factory method for instantiating different implementations of
|
||||
@@ -50,9 +52,9 @@ public:
|
||||
static std::unique_ptr<TileProvider> createFromDictionary(const ghoul::Dictionary& dictionary);
|
||||
|
||||
/**
|
||||
* Empty default constructor
|
||||
* Default constructor.
|
||||
*/
|
||||
TileProvider() = default;
|
||||
TileProvider();
|
||||
|
||||
/**
|
||||
* Implementations of the TileProvider interface must implement
|
||||
@@ -66,7 +68,10 @@ public:
|
||||
* Virtual destructor that subclasses should override to do
|
||||
* clean up.
|
||||
*/
|
||||
virtual ~TileProvider() { }
|
||||
virtual ~TileProvider() = default;
|
||||
|
||||
virtual bool initialize();
|
||||
virtual bool deinitialize() { return true; };
|
||||
|
||||
/**
|
||||
* Method for querying tiles, given a specified <code>TileIndex</code>.
|
||||
@@ -140,9 +145,19 @@ public:
|
||||
* \returns the no data value for the dataset. Default is the minimum float avalue.
|
||||
*/
|
||||
virtual float noDataValueAsFloat();
|
||||
};
|
||||
|
||||
using TileCache = LRUCache<TileIndex::TileHashKey, Tile>;
|
||||
/**
|
||||
* \returns a unique identifier for the <code>TileProvider<\code>. All
|
||||
* <code>TileProviders<\code> have an ID starting at 0 from the first created.
|
||||
* The maximum number of unique identifiers is UINT_MAX
|
||||
*/
|
||||
unsigned int uniqueIdentifier() const;
|
||||
|
||||
private:
|
||||
static unsigned int _numTileProviders;
|
||||
unsigned int _uniqueIdentifier;
|
||||
bool _initialized;
|
||||
};
|
||||
|
||||
} // namespace tileprovider
|
||||
} // namespace globebrowsing
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileproviderbylevel.h>
|
||||
|
||||
#include <ghoul/misc/dictionary.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
namespace {
|
||||
const char* KeyProviders = "LevelTileProviders";
|
||||
@@ -37,43 +38,56 @@ namespace globebrowsing {
|
||||
namespace tileprovider {
|
||||
|
||||
TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) {
|
||||
std::string name = "Name unspecified";
|
||||
dictionary.getValue("Name", name);
|
||||
const char* _loggerCat = ("TileProviderByLevel" + name).c_str();
|
||||
|
||||
ghoul::Dictionary providers = dictionary.value<ghoul::Dictionary>(KeyProviders);
|
||||
|
||||
for (size_t i = 0; i < providers.size(); i++) {
|
||||
std::string dictKey = std::to_string(i + 1);
|
||||
ghoul::Dictionary levelProviderDict = providers.value<ghoul::Dictionary>(
|
||||
dictKey
|
||||
);
|
||||
double floatMaxLevel;
|
||||
int maxLevel = 0;
|
||||
if (!levelProviderDict.getValue<double>(KeyMaxLevel, floatMaxLevel)) {
|
||||
throw std::runtime_error(
|
||||
"Must define key '" + std::string(KeyMaxLevel) + "'"
|
||||
try {
|
||||
std::string dictKey = std::to_string(i + 1);
|
||||
ghoul::Dictionary levelProviderDict = providers.value<ghoul::Dictionary>(
|
||||
dictKey
|
||||
);
|
||||
}
|
||||
maxLevel = std::round(floatMaxLevel);
|
||||
|
||||
ghoul::Dictionary providerDict;
|
||||
if (!levelProviderDict.getValue<ghoul::Dictionary>(KeyTileProvider, providerDict))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Must define key '" + std::string(KeyTileProvider) + "'"
|
||||
double floatMaxLevel;
|
||||
int maxLevel = 0;
|
||||
if (!levelProviderDict.getValue<double>(KeyMaxLevel, floatMaxLevel)) {
|
||||
throw std::runtime_error(
|
||||
"Must define key '" + std::string(KeyMaxLevel) + "'"
|
||||
);
|
||||
}
|
||||
maxLevel = std::round(floatMaxLevel);
|
||||
|
||||
ghoul::Dictionary providerDict;
|
||||
if (!levelProviderDict.getValue<ghoul::Dictionary>(KeyTileProvider, providerDict)) {
|
||||
throw std::runtime_error(
|
||||
"Must define key '" + std::string(KeyTileProvider) + "'"
|
||||
);
|
||||
}
|
||||
|
||||
_levelTileProviders.push_back(
|
||||
std::shared_ptr<TileProvider>(TileProvider::createFromDictionary(providerDict))
|
||||
);
|
||||
}
|
||||
|
||||
_levelTileProviders.push_back(
|
||||
std::shared_ptr<TileProvider>(TileProvider::createFromDictionary(providerDict))
|
||||
);
|
||||
|
||||
// Ensure we can represent the max level
|
||||
if(_providerIndices.size() < maxLevel){
|
||||
_providerIndices.resize(maxLevel+1, -1);
|
||||
// Ensure we can represent the max level
|
||||
if(_providerIndices.size() < maxLevel){
|
||||
_providerIndices.resize(maxLevel+1, -1);
|
||||
}
|
||||
|
||||
// map this level to the tile provider index
|
||||
_providerIndices[maxLevel] = _levelTileProviders.size() - 1;
|
||||
}
|
||||
|
||||
// map this level to the tile provider index
|
||||
_providerIndices[maxLevel] = _levelTileProviders.size() - 1;
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LWARNING("Unable to create tile provider: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If all failed
|
||||
if (_levelTileProviders.size() == 0) {
|
||||
throw ghoul::RuntimeError("Failed to create tile provider: " + name);
|
||||
}
|
||||
|
||||
// Fill in the gaps (value -1) in provider indices, from back to end
|
||||
for(int i = _providerIndices.size() - 2; i >= 0; --i){
|
||||
if(_providerIndices[i] == -1){
|
||||
|
||||
@@ -26,20 +26,23 @@
|
||||
|
||||
#include <modules/globebrowsing/rendering/layer/layergroup.h>
|
||||
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
|
||||
#include <modules/globebrowsing/rendering/layer/layerrendersettings.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace globebrowsing {
|
||||
namespace tileselector {
|
||||
|
||||
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, TileIndex tileIndex) {
|
||||
ChunkTile mostHighResolution;
|
||||
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, const TileIndex& tileIndex) {
|
||||
TileUvTransform uvTransform;
|
||||
uvTransform.uvScale.x = 0;
|
||||
ChunkTile mostHighResolution{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
|
||||
mostHighResolution.tile = Tile::TileUnavailable;
|
||||
mostHighResolution.uvTransform.uvScale.x = 0;
|
||||
|
||||
|
||||
for (const auto& layer : layerGroup.activeLayers()) {
|
||||
ChunkTile chunkTile = layer->tileProvider()->getChunkTile(tileIndex);
|
||||
bool tileIsOk = chunkTile.tile.status == Tile::Status::OK;
|
||||
bool tileHasMetaData = chunkTile.tile.metaData != nullptr;
|
||||
bool tileIsOk = chunkTile.tile.status() == Tile::Status::OK;
|
||||
bool tileHasMetaData = chunkTile.tile.metaData() != nullptr;
|
||||
bool tileIsHigherResolution =
|
||||
chunkTile.uvTransform.uvScale.x > mostHighResolution.uvTransform.uvScale.x;
|
||||
if (tileIsOk && tileHasMetaData && tileIsHigherResolution) {
|
||||
@@ -69,6 +72,28 @@ std::vector<ChunkTile> getTilesSortedByHighestResolution(const LayerGroup& layer
|
||||
return tiles;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<ChunkTile, const LayerRenderSettings*> >
|
||||
getTilesAndSettingsSortedByHighestResolution(const LayerGroup& layerGroup,
|
||||
const TileIndex& tileIndex)
|
||||
{
|
||||
std::vector<std::pair<ChunkTile, const LayerRenderSettings*> > tilesAndSettings;
|
||||
for (const auto& layer : layerGroup.activeLayers()) {
|
||||
tilesAndSettings.push_back({ layer->tileProvider()->getChunkTile(tileIndex), &layer->renderSettings() });
|
||||
}
|
||||
std::sort(
|
||||
tilesAndSettings.begin(),
|
||||
tilesAndSettings.end(),
|
||||
[](const std::pair<ChunkTile, const LayerRenderSettings*> & lhs,
|
||||
const std::pair<ChunkTile, const LayerRenderSettings*> & rhs)
|
||||
{
|
||||
return lhs.first.uvTransform.uvScale.x > rhs.first.uvTransform.uvScale.x;
|
||||
}
|
||||
);
|
||||
|
||||
return tilesAndSettings;
|
||||
}
|
||||
|
||||
void ascendToParent(TileIndex& tileIndex, TileUvTransform& uv) {
|
||||
uv.uvOffset *= 0.5;
|
||||
uv.uvScale *= 0.5;
|
||||
|
||||
@@ -34,13 +34,17 @@ namespace globebrowsing {
|
||||
|
||||
struct LayerGroup;
|
||||
struct TileIndex;
|
||||
struct LayerRenderSettings;
|
||||
|
||||
namespace tileselector {
|
||||
|
||||
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, TileIndex& tileIndex);
|
||||
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, const TileIndex& tileIndex);
|
||||
|
||||
std::vector<ChunkTile> getTilesSortedByHighestResolution(const LayerGroup& layerGroup,
|
||||
const TileIndex& tileIndex);
|
||||
const TileIndex& tileIndex);
|
||||
std::vector<std::pair<ChunkTile, const LayerRenderSettings*> >
|
||||
getTilesAndSettingsSortedByHighestResolution(const LayerGroup& layerGroup,
|
||||
const TileIndex& tileIndex);
|
||||
|
||||
void ascendToParent(TileIndex& tileIndex, TileUvTransform& uv);
|
||||
|
||||
|
||||
@@ -28,10 +28,9 @@ helper.setCommonKeys = function()
|
||||
"openspace.toggleShutdown()",
|
||||
"Toggles the shutdown that will stop OpenSpace after a grace period. Press again to cancel the shutdown during this period."
|
||||
)
|
||||
|
||||
openspace.bindKeyLocal(
|
||||
"PRINT_SCREEN",
|
||||
"openspace.takeScreenshot()",
|
||||
"openspace.setPropertyValueSingle('RenderEngine.takeScreenshot', nil)",
|
||||
"Saves the contents of the screen to a file in the working directory."
|
||||
)
|
||||
openspace.bindKey(
|
||||
|
||||
@@ -551,7 +551,7 @@ void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& came
|
||||
_globe->ellipsoid().geodeticSurfaceNormal(
|
||||
_globe->ellipsoid().cartesianToGeodetic2(cameraPositionModelSpace));
|
||||
dvec3 directionFromSurfaceToCamera =
|
||||
dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace;
|
||||
normalize(dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
|
||||
dvec3 centerToEllipsoidSurface = dmat3(modelTransform) * (_globe->projectOnEllipsoid(cameraPositionModelSpace) -
|
||||
directionFromSurfaceToCameraModelSpace * ellipsoidShrinkTerm);
|
||||
dvec3 ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
@@ -639,7 +639,7 @@ void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& came
|
||||
_globe->ellipsoid().geodeticSurfaceNormal(
|
||||
_globe->ellipsoid().cartesianToGeodetic2(cameraPositionModelSpace));
|
||||
directionFromSurfaceToCamera =
|
||||
dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace;
|
||||
normalize(dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
|
||||
centerToEllipsoidSurface = dmat3(modelTransform) * (_globe->projectOnEllipsoid(cameraPositionModelSpace) -
|
||||
directionFromSurfaceToCameraModelSpace * ellipsoidShrinkTerm);
|
||||
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#ifdef GLOBEBROWSING_USE_GDAL
|
||||
|
||||
#include "gdal.h"
|
||||
#include "gdal_priv.h"
|
||||
|
||||
@@ -60,3 +62,5 @@ TEST_F(GdalWmsTest, Simple) {
|
||||
// This assertion fails
|
||||
//ASSERT_NE(poDataset, nullptr) << "Failed to load testFile";
|
||||
}
|
||||
|
||||
#endif // GLOBEBROWSING_USE_GDAL
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <modules/globebrowsing/other/lrucache.h>
|
||||
#include <modules/globebrowsing/cache/lrucache.h>
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
@@ -33,14 +33,14 @@
|
||||
class LRUCacheTest : public testing::Test {};
|
||||
|
||||
TEST_F(LRUCacheTest, Get) {
|
||||
openspace::globebrowsing::LRUCache<int, std::string> lru(4);
|
||||
openspace::globebrowsing::cache::LRUCache<int, std::string> lru(4);
|
||||
lru.put(1, "hej");
|
||||
lru.put(12, "san");
|
||||
ASSERT_STREQ(lru.get(1).c_str(), "hej") << "testing get";
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheTest, CleaningCache) {
|
||||
openspace::globebrowsing::LRUCache<int, double> lru(4);
|
||||
openspace::globebrowsing::cache::LRUCache<int, double> lru(4);
|
||||
lru.put(1, 1.2);
|
||||
lru.put(12, 2.3);
|
||||
lru.put(123, 33.4);
|
||||
@@ -72,7 +72,7 @@ namespace std {
|
||||
}
|
||||
|
||||
TEST_F(LRUCacheTest, StructKey) {
|
||||
openspace::globebrowsing::LRUCache<MyKey, std::string> lru(4);
|
||||
openspace::globebrowsing::cache::LRUCache<MyKey, std::string> lru(4);
|
||||
|
||||
// These two custom keys should be treated as equal
|
||||
MyKey key1 = { 2, 3 };
|
||||
|
||||
Reference in New Issue
Block a user