From 7cf5c374745348516098f6cfd78ea9fdf2b17e47 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 27 Dec 2021 18:58:05 +0100 Subject: [PATCH] Image sequence tileprovider (#1798) * Fix for rendering DoublePropertys in ImGui * Adding an ImageSequenceTileProvider that loads a folder of images and makes them available through a user-selectable index --- modules/globebrowsing/globebrowsingmodule.cpp | 5 + .../shaders/texturetilemapping.glsl | 15 +- modules/globebrowsing/src/gpulayergroup.cpp | 6 +- modules/globebrowsing/src/layer.cpp | 12 +- modules/globebrowsing/src/layergroupid.h | 20 +-- modules/globebrowsing/src/tileprovider.cpp | 146 ++++++++++++++++++ modules/globebrowsing/src/tileprovider.h | 16 ++ modules/imgui/src/renderproperties.cpp | 5 + 8 files changed, 204 insertions(+), 21 deletions(-) diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index 09b75daa7b..9003102be1 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -293,6 +293,11 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { layergroupid::TypeID::SingleImageTileLayer )] ); + fTileProvider->registerClass( + layergroupid::LAYER_TYPE_NAMES[static_cast( + layergroupid::TypeID::ImageSequenceTileLayer + )] + ); fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::TemporalTileLayer diff --git a/modules/globebrowsing/shaders/texturetilemapping.glsl b/modules/globebrowsing/shaders/texturetilemapping.glsl index 5b39d02c42..fa026392ef 100644 --- a/modules/globebrowsing/shaders/texturetilemapping.glsl +++ b/modules/globebrowsing/shaders/texturetilemapping.glsl @@ -149,18 +149,21 @@ vec4 getSample#{layerGroup}#{i}(vec2 uv, vec3 levelWeights, c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); #elif (#{#{layerGroup}#{i}LayerType} == 1) // SingleImageTileLayer c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); -#elif (#{#{layerGroup}#{i}LayerType} == 2) // SizeReferenceTileLayer +#elif (#{#{layerGroup}#{i}LayerType} == 2) // ImageSequenceTileLayer c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); -#elif (#{#{layerGroup}#{i}LayerType} == 3) // TemporalTileLayer +#elif (#{#{layerGroup}#{i}LayerType} == 3) // SizeReferenceTileLayer c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); -#elif (#{#{layerGroup}#{i}LayerType} == 4) // TileIndexTileLayer +#elif (#{#{layerGroup}#{i}LayerType} == 4) // TemporalTileLayer c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); -#elif (#{#{layerGroup}#{i}LayerType} == 5) // ByIndexTileLayer +#elif (#{#{layerGroup}#{i}LayerType} == 5) // TileIndexTileLayer c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); -#elif (#{#{layerGroup}#{i}LayerType} == 6) // ByLevelTileLayer +#elif (#{#{layerGroup}#{i}LayerType} == 6) // ByIndexTileLayer c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); -#elif (#{#{layerGroup}#{i}LayerType} == 7) // SolidColor +#elif (#{#{layerGroup}#{i}LayerType} == 7) // ByLevelTileLayer + c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding); +#elif (#{#{layerGroup}#{i}LayerType} == 8) // SolidColor c.rgb = #{layerGroup}[#{i}].color; + #endif return c; diff --git a/modules/globebrowsing/src/gpulayergroup.cpp b/modules/globebrowsing/src/gpulayergroup.cpp index 7511d5ef90..d3fba0f108 100644 --- a/modules/globebrowsing/src/gpulayergroup.cpp +++ b/modules/globebrowsing/src/gpulayergroup.cpp @@ -68,6 +68,7 @@ void GPULayerGroup::setValue(ghoul::opengl::ProgramObject& program, // Intentional fall through. Same for all tile layers case layergroupid::TypeID::DefaultTileLayer: case layergroupid::TypeID::SingleImageTileLayer: + case layergroupid::TypeID::ImageSequenceTileLayer: case layergroupid::TypeID::SizeReferenceTileLayer: case layergroupid::TypeID::TemporalTileLayer: case layergroupid::TypeID::TileIndexTileLayer: @@ -103,7 +104,7 @@ void GPULayerGroup::setValue(ghoul::opengl::ProgramObject& program, program.setUniform(galuc.color, al.solidColor()); break; default: - break; + throw ghoul::MissingCaseException(); } if (gal.isHeightLayer) { @@ -148,6 +149,7 @@ void GPULayerGroup::bind(ghoul::opengl::ProgramObject& p, // Intentional fall through. Same for all tile layers case layergroupid::TypeID::DefaultTileLayer: case layergroupid::TypeID::SingleImageTileLayer: + case layergroupid::TypeID::ImageSequenceTileLayer: case layergroupid::TypeID::SizeReferenceTileLayer: case layergroupid::TypeID::TemporalTileLayer: case layergroupid::TypeID::TileIndexTileLayer: @@ -177,7 +179,7 @@ void GPULayerGroup::bind(ghoul::opengl::ProgramObject& p, galuc.color = p.uniformLocation(name + "color"); break; default: - break; + throw ghoul::MissingCaseException(); } if (gal.isHeightLayer) { diff --git a/modules/globebrowsing/src/layer.cpp b/modules/globebrowsing/src/layer.cpp index 0d9e4fbb45..014f440b0f 100644 --- a/modules/globebrowsing/src/layer.cpp +++ b/modules/globebrowsing/src/layer.cpp @@ -113,8 +113,9 @@ namespace { // Specifies the type of layer that is to be added. If this value is not // specified, the layer is a DefaultTileLayer std::optional type [[codegen::inlist("DefaultTileLayer", - "SingleImageTileLayer", "SizeReferenceTileLayer", "TemporalTileLayer", - "TileIndexTileLayer", "ByIndexTileLayer", "ByLevelTileLayer", "SolidColor")]]; + "SingleImageTileLayer", "ImageSequenceTileLayer", "SizeReferenceTileLayer", + "TemporalTileLayer", "TileIndexTileLayer", "ByIndexTileLayer", + "ByLevelTileLayer", "SolidColor")]]; // Determine whether the layer is enabled or not. If this value is not specified, // the layer is disabled @@ -301,6 +302,7 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, // Intentional fall through. Same for all tile layers case layergroupid::TypeID::DefaultTileLayer: case layergroupid::TypeID::SingleImageTileLayer: + case layergroupid::TypeID::ImageSequenceTileLayer: case layergroupid::TypeID::SizeReferenceTileLayer: case layergroupid::TypeID::TemporalTileLayer: case layergroupid::TypeID::TileIndexTileLayer: @@ -314,7 +316,7 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, removeProperty(_solidColor); break; default: - break; + throw ghoul::MissingCaseException(); } _type = static_cast(_typeOption.value()); @@ -471,6 +473,7 @@ void Layer::initializeBasedOnType(layergroupid::TypeID id, ghoul::Dictionary ini // Intentional fall through. Same for all tile layers case layergroupid::TypeID::DefaultTileLayer: case layergroupid::TypeID::SingleImageTileLayer: + case layergroupid::TypeID::ImageSequenceTileLayer: case layergroupid::TypeID::SizeReferenceTileLayer: case layergroupid::TypeID::TemporalTileLayer: case layergroupid::TypeID::TileIndexTileLayer: @@ -502,6 +505,7 @@ void Layer::addVisibleProperties() { // Intentional fall through. Same for all tile layers case layergroupid::TypeID::DefaultTileLayer: case layergroupid::TypeID::SingleImageTileLayer: + case layergroupid::TypeID::ImageSequenceTileLayer: case layergroupid::TypeID::SizeReferenceTileLayer: case layergroupid::TypeID::TemporalTileLayer: case layergroupid::TypeID::TileIndexTileLayer: @@ -517,7 +521,7 @@ void Layer::addVisibleProperties() { break; } default: - break; + throw ghoul::MissingCaseException(); } } diff --git a/modules/globebrowsing/src/layergroupid.h b/modules/globebrowsing/src/layergroupid.h index 1679803143..e83403dd6b 100644 --- a/modules/globebrowsing/src/layergroupid.h +++ b/modules/globebrowsing/src/layergroupid.h @@ -53,19 +53,20 @@ enum GroupID { Overlays, NightLayers, WaterMasks, - Unknown, + Unknown }; -static constexpr const int NUM_LAYER_TYPES = 8; +static constexpr const int NUM_LAYER_TYPES = 9; static constexpr const char* LAYER_TYPE_NAMES[NUM_LAYER_TYPES] = { "DefaultTileLayer", "SingleImageTileLayer", + "ImageSequenceTileLayer", "SizeReferenceTileLayer", "TemporalTileLayer", "TileIndexTileLayer", "ByIndexTileLayer", "ByLevelTileLayer", - "SolidColor", + "SolidColor" }; /** @@ -75,12 +76,13 @@ enum class TypeID { Unknown = -1, DefaultTileLayer = 0, SingleImageTileLayer = 1, - SizeReferenceTileLayer = 2, - TemporalTileLayer = 3, - TileIndexTileLayer = 4, - ByIndexTileLayer = 5, - ByLevelTileLayer = 6, - SolidColor = 7, + ImageSequenceTileLayer = 2, + SizeReferenceTileLayer = 3, + TemporalTileLayer = 4, + TileIndexTileLayer = 5, + ByIndexTileLayer = 6, + ByLevelTileLayer = 7, + SolidColor = 8 }; static constexpr int NUM_ADJUSTMENT_TYPES = 3; diff --git a/modules/globebrowsing/src/tileprovider.cpp b/modules/globebrowsing/src/tileprovider.cpp index 2b50b423ad..8761ab1e09 100644 --- a/modules/globebrowsing/src/tileprovider.cpp +++ b/modules/globebrowsing/src/tileprovider.cpp @@ -118,6 +118,28 @@ namespace singleimageprovider { }; } // namespace singleimageprovider +namespace imagesequenceprovider { + constexpr openspace::properties::Property::PropertyInfo IndexInfo = { + "Index", + "Index", + "The index into the list of images that is used to pick the currently displayed " + "image" + }; + + constexpr openspace::properties::Property::PropertyInfo CurrentImageInfo = { + "CurrentImage", + "Current Image", + "The read-only value of the currently selected image" + }; + + constexpr openspace::properties::Property::PropertyInfo FolderPathInfo = { + "FolderPath", + "Folder Path", + "The path that is used to look for images for this image provider. The path must " + "point to an existing folder that contains images" + }; +} // namepsace imagesequenceprovider + namespace sizereferenceprovider { constexpr const char* KeyRadii = "Radii"; } // namespace sizereferenceprovider @@ -779,6 +801,38 @@ SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) +ImageSequenceTileProvider::ImageSequenceTileProvider(const ghoul::Dictionary& dictionary) + : index(imagesequenceprovider::IndexInfo, 0) + , currentImage(imagesequenceprovider::CurrentImageInfo) + , folderPath(imagesequenceprovider::FolderPathInfo) + , initDict(dictionary) +{ + ZoneScoped + + type = Type::ImageSequenceTileProvider; + + if (dictionary.hasValue(imagesequenceprovider::IndexInfo.identifier)) { + index = dictionary.value(imagesequenceprovider::IndexInfo.identifier); + } + index.setMinValue(0); + index.onChange([this]() { isImageDirty = true; }); + addProperty(index); + + folderPath.setReadOnly(true); + addProperty(folderPath); + + folderPath = dictionary.value( + imagesequenceprovider::FolderPathInfo.identifier + ); + addProperty(folderPath); + + reset(*this); +} + + + + + TextTileProvider::TextTileProvider(TileTextureInitData initData_, size_t fontSize_) : initData(std::move(initData_)) , fontSize(fontSize_) @@ -1047,6 +1101,8 @@ bool initialize(TileProvider& tp) { break; case Type::SingleImageTileProvider: break; + case Type::ImageSequenceTileProvider: + break; case Type::SizeReferenceTileProvider: { SizeReferenceTileProvider& t = static_cast(tp); initialize(t); @@ -1091,6 +1147,8 @@ bool deinitialize(TileProvider& tp) { break; case Type::SingleImageTileProvider: break; + case Type::ImageSequenceTileProvider: + break; case Type::SizeReferenceTileProvider: { SizeReferenceTileProvider& t = static_cast(tp); deinitialize(t); @@ -1322,6 +1380,16 @@ Tile tile(TileProvider& tp, const TileIndex& tileIndex) { SingleImageProvider& t = static_cast(tp); return t.tile; } + case Type::ImageSequenceTileProvider: { + ZoneScopedN("Type::ImageSequenceTileProvider") + ImageSequenceTileProvider& t = static_cast(tp); + if (t.currentTileProvider) { + return tile(*t.currentTileProvider, tileIndex); + } + else { + return Tile(); + } + } case Type::SizeReferenceTileProvider: { ZoneScopedN("Type::SizeReferenceTileProvider") SizeReferenceTileProvider& t = static_cast(tp); @@ -1447,6 +1515,15 @@ Tile::Status tileStatus(TileProvider& tp, const TileIndex& index) { SingleImageProvider& t = static_cast(tp); return t.tile.status; } + case Type::ImageSequenceTileProvider: { + ImageSequenceTileProvider& t = static_cast(tp); + if (t.currentTileProvider) { + return tileStatus(*t.currentTileProvider, index); + } + else { + return Tile::Status::Unavailable; + } + } case Type::SizeReferenceTileProvider: return Tile::Status::OK; case Type::TileIndexTileProvider: @@ -1510,6 +1587,15 @@ TileDepthTransform depthTransform(TileProvider& tp) { } case Type::SingleImageTileProvider: return { 0.f, 1.f }; + case Type::ImageSequenceTileProvider: { + ImageSequenceTileProvider& t = static_cast(tp); + if (t.currentTileProvider) { + return depthTransform(*t.currentTileProvider); + } + else { + return { 1.f, 0.f }; + } + } case Type::SizeReferenceTileProvider: return { 0.f, 1.f }; case Type::TileIndexTileProvider: @@ -1571,6 +1657,29 @@ int update(TileProvider& tp) { } case Type::SingleImageTileProvider: break; + case Type::ImageSequenceTileProvider: { + ImageSequenceTileProvider& t = static_cast(tp); + + if (t.isImageDirty && !t.imagePaths.empty() && + t.index >= 0 && t.index < t.imagePaths.size()) + { + if (t.currentTileProvider) { + deinitialize(*t.currentTileProvider); + } + + std::string p = t.imagePaths[t.index].string(); + t.currentImage = p; + t.initDict.setValue(KeyFilePath, p); + t.currentTileProvider = std::make_unique(t.initDict); + initialize(*t.currentTileProvider); + t.isImageDirty = false; + } + + if (t.currentTileProvider) { + update(*t.currentTileProvider); + } + break; + } case Type::SizeReferenceTileProvider: break; case Type::TileIndexTileProvider: @@ -1664,6 +1773,25 @@ void reset(TileProvider& tp) { t.tile = Tile{ t.tileTexture.get(), std::nullopt, tileStatus }; break; } + case Type::ImageSequenceTileProvider: { + namespace fs = std::filesystem; + ImageSequenceTileProvider& t = static_cast(tp); + std::string path = t.folderPath; + t.imagePaths.clear(); + for (const fs::directory_entry& p : fs::directory_iterator(path)) { + if (p.is_regular_file()) { + t.imagePaths.push_back(p.path()); + } + } + + t.index = 0; + t.index.setMaxValue(static_cast(t.imagePaths.size() - 1)); + + if (t.currentTileProvider) { + reset(*t.currentTileProvider); + } + break; + } case Type::SizeReferenceTileProvider: { SizeReferenceTileProvider& t = static_cast(tp); reset(t); @@ -1733,6 +1861,15 @@ int maxLevel(TileProvider& tp) { } case Type::SingleImageTileProvider: return 1337; // unlimited + case Type::ImageSequenceTileProvider: { + ImageSequenceTileProvider& t = static_cast(tp); + if (t.currentTileProvider) { + return maxLevel(*t.currentTileProvider); + } + else { + return 0; + } + } case Type::SizeReferenceTileProvider: return 1337; // unlimited case Type::TileIndexTileProvider: @@ -1782,6 +1919,15 @@ float noDataValueAsFloat(TileProvider& tp) { } case Type::SingleImageTileProvider: return std::numeric_limits::min(); + case Type::ImageSequenceTileProvider: { + ImageSequenceTileProvider& t = static_cast(tp); + if (t.currentTileProvider) { + return noDataValueAsFloat(*t.currentTileProvider); + } + else { + return std::numeric_limits::min(); + } + } case Type::SizeReferenceTileProvider: return std::numeric_limits::min(); case Type::TileIndexTileProvider: diff --git a/modules/globebrowsing/src/tileprovider.h b/modules/globebrowsing/src/tileprovider.h index 20c4eed05c..c141d8b041 100644 --- a/modules/globebrowsing/src/tileprovider.h +++ b/modules/globebrowsing/src/tileprovider.h @@ -38,6 +38,7 @@ #include #include #include + struct CPLXMLNode; namespace ghoul::fontrendering { @@ -59,6 +60,7 @@ namespace openspace::globebrowsing::tileprovider { enum class Type { DefaultTileProvider = 0, SingleImageTileProvider, + ImageSequenceTileProvider, SizeReferenceTileProvider, TemporalTileProvider, TileIndexTileProvider, @@ -169,6 +171,20 @@ struct TileProviderByLevel : public TileProvider { }; +struct ImageSequenceTileProvider : public TileProvider { + ImageSequenceTileProvider(const ghoul::Dictionary& dictionary); + + std::unique_ptr currentTileProvider = nullptr; + + properties::IntProperty index; + properties::StringProperty currentImage; + properties::StringProperty folderPath; + + ghoul::Dictionary initDict; + bool isImageDirty = true; + std::vector imagePaths; +}; + /** * Provide Tiles from web map services that have temporal resolution. * diff --git a/modules/imgui/src/renderproperties.cpp b/modules/imgui/src/renderproperties.cpp index 620745e581..f8a804b5dc 100644 --- a/modules/imgui/src/renderproperties.cpp +++ b/modules/imgui/src/renderproperties.cpp @@ -386,6 +386,11 @@ void renderDoubleProperty(properties::Property* prop, const std::string& ownerNa float value = static_cast(*p); float min = static_cast(p->minValue()); float max = static_cast(p->maxValue()); + + // Since we are doing a DoubleProperty, it would actually overflow here and produce + // -inf and inf as the min and max which confuses ImGui + min = std::max(min, std::numeric_limits::min() / 2.f); + max = std::min(max, std::numeric_limits::max() / 2.f); bool changed = ImGui::SliderFloat( name.c_str(),