mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-25 05:48:59 -05:00
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
This commit is contained in:
@@ -293,6 +293,11 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) {
|
||||
layergroupid::TypeID::SingleImageTileLayer
|
||||
)]
|
||||
);
|
||||
fTileProvider->registerClass<tileprovider::ImageSequenceTileProvider>(
|
||||
layergroupid::LAYER_TYPE_NAMES[static_cast<int>(
|
||||
layergroupid::TypeID::ImageSequenceTileLayer
|
||||
)]
|
||||
);
|
||||
fTileProvider->registerClass<tileprovider::TemporalTileProvider>(
|
||||
layergroupid::LAYER_TYPE_NAMES[static_cast<int>(
|
||||
layergroupid::TypeID::TemporalTileLayer
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<std::string> 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<layergroupid::TypeID>(_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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<int>(imagesequenceprovider::IndexInfo.identifier)) {
|
||||
index = dictionary.value<int>(imagesequenceprovider::IndexInfo.identifier);
|
||||
}
|
||||
index.setMinValue(0);
|
||||
index.onChange([this]() { isImageDirty = true; });
|
||||
addProperty(index);
|
||||
|
||||
folderPath.setReadOnly(true);
|
||||
addProperty(folderPath);
|
||||
|
||||
folderPath = dictionary.value<std::string>(
|
||||
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<SizeReferenceTileProvider&>(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<SizeReferenceTileProvider&>(tp);
|
||||
deinitialize(t);
|
||||
@@ -1322,6 +1380,16 @@ Tile tile(TileProvider& tp, const TileIndex& tileIndex) {
|
||||
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
|
||||
return t.tile;
|
||||
}
|
||||
case Type::ImageSequenceTileProvider: {
|
||||
ZoneScopedN("Type::ImageSequenceTileProvider")
|
||||
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
|
||||
if (t.currentTileProvider) {
|
||||
return tile(*t.currentTileProvider, tileIndex);
|
||||
}
|
||||
else {
|
||||
return Tile();
|
||||
}
|
||||
}
|
||||
case Type::SizeReferenceTileProvider: {
|
||||
ZoneScopedN("Type::SizeReferenceTileProvider")
|
||||
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
|
||||
@@ -1447,6 +1515,15 @@ Tile::Status tileStatus(TileProvider& tp, const TileIndex& index) {
|
||||
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
|
||||
return t.tile.status;
|
||||
}
|
||||
case Type::ImageSequenceTileProvider: {
|
||||
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(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<ImageSequenceTileProvider&>(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<ImageSequenceTileProvider&>(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<DefaultTileProvider>(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<ImageSequenceTileProvider&>(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<int>(t.imagePaths.size() - 1));
|
||||
|
||||
if (t.currentTileProvider) {
|
||||
reset(*t.currentTileProvider);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::SizeReferenceTileProvider: {
|
||||
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
|
||||
reset(t);
|
||||
@@ -1733,6 +1861,15 @@ int maxLevel(TileProvider& tp) {
|
||||
}
|
||||
case Type::SingleImageTileProvider:
|
||||
return 1337; // unlimited
|
||||
case Type::ImageSequenceTileProvider: {
|
||||
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(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<float>::min();
|
||||
case Type::ImageSequenceTileProvider: {
|
||||
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
|
||||
if (t.currentTileProvider) {
|
||||
return noDataValueAsFloat(*t.currentTileProvider);
|
||||
}
|
||||
else {
|
||||
return std::numeric_limits<float>::min();
|
||||
}
|
||||
}
|
||||
case Type::SizeReferenceTileProvider:
|
||||
return std::numeric_limits<float>::min();
|
||||
case Type::TileIndexTileProvider:
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <unordered_map>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
|
||||
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<DefaultTileProvider> currentTileProvider = nullptr;
|
||||
|
||||
properties::IntProperty index;
|
||||
properties::StringProperty currentImage;
|
||||
properties::StringProperty folderPath;
|
||||
|
||||
ghoul::Dictionary initDict;
|
||||
bool isImageDirty = true;
|
||||
std::vector<std::filesystem::path> imagePaths;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide <code>Tile</code>s from web map services that have temporal resolution.
|
||||
*
|
||||
|
||||
@@ -386,6 +386,11 @@ void renderDoubleProperty(properties::Property* prop, const std::string& ownerNa
|
||||
float value = static_cast<float>(*p);
|
||||
float min = static_cast<float>(p->minValue());
|
||||
float max = static_cast<float>(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<float>::min() / 2.f);
|
||||
max = std::min(max, std::numeric_limits<float>::max() / 2.f);
|
||||
|
||||
bool changed = ImGui::SliderFloat(
|
||||
name.c_str(),
|
||||
|
||||
Reference in New Issue
Block a user