mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-10 21:49:38 -06:00
561 lines
20 KiB
C++
561 lines
20 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2020 *
|
|
* *
|
|
* 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/src/layer.h>
|
|
|
|
#include <openspace/documentation/documentation.h>
|
|
#include <openspace/documentation/verifier.h>
|
|
#include <modules/globebrowsing/src/layergroup.h>
|
|
#include <modules/globebrowsing/src/layermanager.h>
|
|
#include <modules/globebrowsing/src/tileindex.h>
|
|
#include <modules/globebrowsing/src/tiletextureinitdata.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
|
|
namespace openspace::globebrowsing {
|
|
|
|
namespace {
|
|
constexpr const char* _loggerCat = "Layer";
|
|
|
|
constexpr const char* KeyIdentifier = "Identifier";
|
|
constexpr const char* KeyName = "Name";
|
|
constexpr const char* KeyDesc = "Description";
|
|
constexpr const char* KeyLayerGroupID = "LayerGroupID";
|
|
constexpr const char* KeySettings = "Settings";
|
|
constexpr const char* KeyAdjustment = "Adjustment";
|
|
constexpr const char* KeyPadTiles = "PadTiles";
|
|
|
|
constexpr const char* KeyOpacity = "Opacity";
|
|
constexpr const char* KeyGamma = "Gamma";
|
|
constexpr const char* KeyMultiplier = "Multiplier";
|
|
constexpr const char* KeyOffset = "Offset";
|
|
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo TypeInfo = {
|
|
"Type",
|
|
"Type",
|
|
"The type of this Layer. This value is a read-only property and thus cannot be "
|
|
"changed."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo BlendModeInfo = {
|
|
"BlendMode",
|
|
"Blend Mode",
|
|
"This value specifies the blend mode that is applied to this layer. The blend "
|
|
"mode determines how this layer is added to the underlying layers beneath."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo EnabledInfo = {
|
|
"Enabled",
|
|
"Enabled",
|
|
"If this value is enabled, the layer will be used for the final composition of "
|
|
"the planet. If this value is disabled, the layer will be ignored in the "
|
|
"composition."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ResetInfo = {
|
|
"Reset",
|
|
"Reset",
|
|
"If this value is triggered, this layer will be reset. This will delete the "
|
|
"local cache for this layer and will trigger a fresh load of all tiles."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo RemoveInfo = {
|
|
"Remove",
|
|
"Remove",
|
|
"If this value is triggered, a script will be executed that will remove this "
|
|
"layer before the next frame."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
|
|
"Color",
|
|
"Color",
|
|
"If the 'Type' of this layer is a solid color, this value determines what this "
|
|
"solid color is."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo GuiDescriptionInfo = {
|
|
"GuiDescription",
|
|
"Gui Description",
|
|
"This is the description for the scene graph node to be shown in the gui "
|
|
"example: Earth is a special place",
|
|
openspace::properties::Property::Visibility::Hidden
|
|
};
|
|
} // namespace
|
|
|
|
documentation::Documentation Layer::Documentation() {
|
|
using namespace documentation;
|
|
return {
|
|
"Layer",
|
|
"globebrowsing_layer",
|
|
{
|
|
{
|
|
KeyIdentifier,
|
|
new StringVerifier,
|
|
Optional::No,
|
|
"The unique identifier for this layer. May not contain '.' or spaces."
|
|
},
|
|
{
|
|
KeyName,
|
|
new StringVerifier,
|
|
Optional::Yes,
|
|
"A human-readable name for the user interface. If this is omitted, the "
|
|
"identifier is used instead."
|
|
},
|
|
{
|
|
KeyDesc,
|
|
new StringVerifier,
|
|
Optional::Yes,
|
|
"A human-readable description of the layer to be used in informational "
|
|
"texts presented to the user."
|
|
},
|
|
{
|
|
"Type",
|
|
new StringInListVerifier({
|
|
"DefaultTileLayer", "SingleImageTileLayer", "SizeReferenceTileLayer",
|
|
"TemporalTileLayer", "TileIndexTileLayer", "ByIndexTileLayer",
|
|
"ByLevelTileLayer", "SolidColor"
|
|
}),
|
|
Optional::Yes,
|
|
"Specifies the type of layer that is to be added. If this value is not "
|
|
"specified, the layer is a DefaultTileLayer."
|
|
},
|
|
{
|
|
EnabledInfo.identifier,
|
|
new BoolVerifier,
|
|
Optional::Yes,
|
|
"Determine whether the layer is enabled or not. If this value is not "
|
|
"specified, the layer is disabled."
|
|
},
|
|
{
|
|
KeyPadTiles,
|
|
new BoolVerifier,
|
|
Optional::Yes,
|
|
"Determines whether the downloaded tiles should have a padding added to "
|
|
"the borders."
|
|
},
|
|
{
|
|
KeySettings,
|
|
new TableVerifier({
|
|
{
|
|
KeyOpacity,
|
|
new DoubleInRangeVerifier(0.0, 1.0),
|
|
Optional::Yes,
|
|
"The opacity value of the layer."
|
|
},
|
|
{
|
|
KeyGamma,
|
|
new DoubleVerifier,
|
|
Optional::Yes,
|
|
"The gamma value that is applied to each pixel of the layer."
|
|
},
|
|
{
|
|
KeyMultiplier,
|
|
new DoubleVerifier,
|
|
Optional::Yes,
|
|
"The multiplicative factor that is applied to each pixel of the "
|
|
"layer."
|
|
},
|
|
{
|
|
KeyOffset,
|
|
new DoubleVerifier,
|
|
Optional::Yes,
|
|
"An additive offset that is applied to each pixel of the layer."
|
|
}
|
|
}),
|
|
Optional::Yes,
|
|
"Specifies the render settings that should be applied to this layer."
|
|
},
|
|
{
|
|
KeyAdjustment,
|
|
new ReferencingVerifier("globebrowsing_layeradjustment"),
|
|
Optional::Yes,
|
|
""
|
|
},
|
|
{
|
|
BlendModeInfo.identifier,
|
|
new StringInListVerifier({
|
|
"Normal", "Multiply", "Add", "Subtract", "Color"
|
|
}),
|
|
Optional::Yes,
|
|
"Sets the blend mode of this layer to determine how it interacts with "
|
|
"other layers on top of this."
|
|
},
|
|
{
|
|
"Fallback",
|
|
new ReferencingVerifier("globebrowsing_layer"),
|
|
Optional::Yes,
|
|
"If the primary layer creation fails, this layer is used as a fallback"
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict,
|
|
LayerGroup& parent)
|
|
: properties::PropertyOwner({
|
|
layerDict.value<std::string>(KeyIdentifier),
|
|
layerDict.hasKey(KeyName) ? layerDict.value<std::string>(KeyName) : "",
|
|
layerDict.hasKey(KeyDesc) ? layerDict.value<std::string>(KeyDesc) : ""
|
|
})
|
|
, _parent(parent)
|
|
, _typeOption(TypeInfo, properties::OptionProperty::DisplayType::Dropdown)
|
|
, _blendModeOption(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
|
|
, _enabled(EnabledInfo, false)
|
|
, _reset(ResetInfo)
|
|
, _remove(RemoveInfo)
|
|
, _solidColor(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
|
|
, _layerGroupId(id)
|
|
, _guiDescription(GuiDescriptionInfo)
|
|
|
|
{
|
|
documentation::testSpecificationAndThrow(Documentation(), layerDict, "Layer");
|
|
|
|
layergroupid::TypeID typeID;
|
|
if (layerDict.hasKeyAndValue<std::string>("Type")) {
|
|
const std::string& typeString = layerDict.value<std::string>("Type");
|
|
typeID = ghoul::from_string<layergroupid::TypeID>(typeString);
|
|
}
|
|
else {
|
|
typeID = layergroupid::TypeID::DefaultTileLayer;
|
|
}
|
|
if (typeID == layergroupid::TypeID::Unknown) {
|
|
throw ghoul::RuntimeError("Unknown layer type!");
|
|
}
|
|
|
|
initializeBasedOnType(typeID, layerDict);
|
|
|
|
if (layerDict.hasKeyAndValue<bool>(EnabledInfo.identifier)) {
|
|
_enabled = layerDict.value<bool>(EnabledInfo.identifier);
|
|
}
|
|
|
|
if (layerDict.hasKey(KeyDesc)) {
|
|
_guiDescription = description();
|
|
addProperty(_guiDescription);
|
|
}
|
|
|
|
bool padTiles = true;
|
|
if (layerDict.hasKeyAndValue<bool>(KeyPadTiles)) {
|
|
padTiles = layerDict.value<bool>(KeyPadTiles);
|
|
}
|
|
|
|
TileTextureInitData initData = tileTextureInitData(_layerGroupId, padTiles);
|
|
_padTilePixelStartOffset = initData.tilePixelStartOffset;
|
|
_padTilePixelSizeDifference = initData.tilePixelSizeDifference;
|
|
|
|
if (layerDict.hasKeyAndValue<ghoul::Dictionary>(KeySettings)) {
|
|
ghoul::Dictionary dict = layerDict.value<ghoul::Dictionary>(KeySettings);
|
|
if (dict.hasKeyAndValue<float>(KeyOpacity)) {
|
|
_renderSettings.opacity = dict.value<float>(KeyOpacity);
|
|
}
|
|
|
|
if (dict.hasKeyAndValue<float>(KeyGamma)) {
|
|
_renderSettings.gamma = dict.value<float>(KeyGamma);
|
|
}
|
|
|
|
if (dict.hasKeyAndValue<float>(KeyMultiplier)) {
|
|
_renderSettings.multiplier = dict.value<float>(KeyMultiplier);
|
|
}
|
|
|
|
if (dict.hasKeyAndValue<float>(KeyOffset)) {
|
|
_renderSettings.offset = dict.value<float>(KeyOffset);
|
|
}
|
|
}
|
|
if (layerDict.hasKeyAndValue<ghoul::Dictionary>(KeyAdjustment)) {
|
|
_layerAdjustment.setValuesFromDictionary(
|
|
layerDict.value<ghoul::Dictionary>(KeyAdjustment)
|
|
);
|
|
}
|
|
|
|
// Add options to option properties
|
|
for (int i = 0; i < layergroupid::NUM_LAYER_TYPES; ++i) {
|
|
_typeOption.addOption(i, layergroupid::LAYER_TYPE_NAMES[i]);
|
|
}
|
|
_typeOption.setValue(static_cast<int>(typeID));
|
|
_type = static_cast<layergroupid::TypeID>(_typeOption.value());
|
|
|
|
for (int i = 0; i < layergroupid::NUM_BLEND_MODES; ++i) {
|
|
_blendModeOption.addOption(i, layergroupid::BLEND_MODE_NAMES[i]);
|
|
}
|
|
|
|
// Initialize blend mode
|
|
if (layerDict.hasKeyAndValue<std::string>(BlendModeInfo.identifier)) {
|
|
using namespace layergroupid;
|
|
std::string blendMode = layerDict.value<std::string>(BlendModeInfo.identifier);
|
|
BlendModeID blendModeID = ghoul::from_string<BlendModeID>(blendMode);
|
|
_blendModeOption = static_cast<int>(blendModeID);
|
|
}
|
|
else {
|
|
_blendModeOption = static_cast<int>(layergroupid::BlendModeID::Normal);
|
|
}
|
|
|
|
// On change callbacks definitions
|
|
_enabled.onChange([&]() {
|
|
if (_onChangeCallback) {
|
|
_onChangeCallback(this);
|
|
}
|
|
});
|
|
|
|
_reset.onChange([&]() {
|
|
if (_tileProvider) {
|
|
tileprovider::reset(*_tileProvider);
|
|
}
|
|
});
|
|
|
|
_remove.onChange([&]() {
|
|
if (_tileProvider) {
|
|
tileprovider::reset(*_tileProvider);
|
|
_parent.deleteLayer(identifier());
|
|
}
|
|
});
|
|
|
|
_typeOption.onChange([&]() {
|
|
switch (type()) {
|
|
// Intentional fall through. Same for all tile layers
|
|
case layergroupid::TypeID::DefaultTileLayer:
|
|
case layergroupid::TypeID::SingleImageTileLayer:
|
|
case layergroupid::TypeID::SizeReferenceTileLayer:
|
|
case layergroupid::TypeID::TemporalTileLayer:
|
|
case layergroupid::TypeID::TileIndexTileLayer:
|
|
case layergroupid::TypeID::ByIndexTileLayer:
|
|
case layergroupid::TypeID::ByLevelTileLayer:
|
|
if (_tileProvider) {
|
|
removePropertySubOwner(*_tileProvider);
|
|
}
|
|
break;
|
|
case layergroupid::TypeID::SolidColor:
|
|
removeProperty(_solidColor);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_type = static_cast<layergroupid::TypeID>(_typeOption.value());
|
|
initializeBasedOnType(type(), {});
|
|
addVisibleProperties();
|
|
if (_onChangeCallback) {
|
|
_onChangeCallback(this);
|
|
}
|
|
});
|
|
|
|
_blendModeOption.onChange([&]() {
|
|
if (_onChangeCallback) {
|
|
_onChangeCallback(this);
|
|
}
|
|
});
|
|
|
|
_layerAdjustment.onChange([&]() {
|
|
if (_onChangeCallback) {
|
|
_onChangeCallback(this);
|
|
}
|
|
});
|
|
|
|
_typeOption.setReadOnly(true);
|
|
|
|
// Add the properties
|
|
addProperty(_typeOption);
|
|
addProperty(_blendModeOption);
|
|
addProperty(_enabled);
|
|
addProperty(_reset);
|
|
addProperty(_remove);
|
|
|
|
_solidColor.setViewOption(properties::Property::ViewOptions::Color);
|
|
|
|
addVisibleProperties();
|
|
|
|
addPropertySubOwner(_renderSettings);
|
|
addPropertySubOwner(_layerAdjustment);
|
|
}
|
|
|
|
void Layer::initialize() {
|
|
ZoneScoped
|
|
|
|
if (_tileProvider) {
|
|
tileprovider::initialize(*_tileProvider);
|
|
}
|
|
}
|
|
|
|
void Layer::deinitialize() {
|
|
if (_tileProvider) {
|
|
tileprovider::deinitialize(*_tileProvider);
|
|
}
|
|
}
|
|
|
|
ChunkTilePile Layer::chunkTilePile(const TileIndex& tileIndex, int pileSize) const {
|
|
ZoneScoped
|
|
|
|
if (_tileProvider) {
|
|
return tileprovider::chunkTilePile(*_tileProvider, tileIndex, pileSize);
|
|
}
|
|
else {
|
|
ChunkTilePile chunkTilePile;
|
|
std::fill(chunkTilePile.begin(), chunkTilePile.end(), std::nullopt);
|
|
for (int i = 0; i < pileSize; ++i) {
|
|
chunkTilePile[i] = ChunkTile {
|
|
Tile(), TileUvTransform { { 0, 0 }, { 1, 1 } }
|
|
};
|
|
}
|
|
return chunkTilePile;
|
|
}
|
|
}
|
|
|
|
Tile::Status Layer::tileStatus(const TileIndex& index) const {
|
|
return _tileProvider ?
|
|
tileprovider::tileStatus(*_tileProvider, index) :
|
|
Tile::Status::Unavailable;
|
|
}
|
|
|
|
layergroupid::TypeID Layer::type() const {
|
|
return _type;
|
|
}
|
|
|
|
layergroupid::BlendModeID Layer::blendMode() const {
|
|
return static_cast<layergroupid::BlendModeID>(_blendModeOption.value());
|
|
}
|
|
|
|
TileDepthTransform Layer::depthTransform() const {
|
|
return _tileProvider ?
|
|
tileprovider::depthTransform(*_tileProvider) :
|
|
TileDepthTransform{ 1.f, 0.f };
|
|
}
|
|
|
|
void Layer::setEnabled(bool enabled) {
|
|
_enabled = enabled;
|
|
}
|
|
|
|
bool Layer::enabled() const {
|
|
return _enabled;
|
|
}
|
|
|
|
tileprovider::TileProvider* Layer::tileProvider() const {
|
|
return _tileProvider.get();
|
|
}
|
|
|
|
glm::vec3 Layer::solidColor() const {
|
|
return _solidColor;
|
|
}
|
|
|
|
const LayerRenderSettings& Layer::renderSettings() const {
|
|
return _renderSettings;
|
|
}
|
|
|
|
const LayerAdjustment& Layer::layerAdjustment() const {
|
|
return _layerAdjustment;
|
|
}
|
|
|
|
void Layer::onChange(std::function<void(Layer*)> callback) {
|
|
_onChangeCallback = std::move(callback);
|
|
}
|
|
|
|
int Layer::update() {
|
|
ZoneScoped
|
|
|
|
if (_tileProvider) {
|
|
return tileprovider::update(*_tileProvider);
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
glm::ivec2 Layer::tilePixelStartOffset() const {
|
|
return _padTilePixelStartOffset;
|
|
}
|
|
|
|
glm::ivec2 Layer::tilePixelSizeDifference() const {
|
|
return _padTilePixelSizeDifference;
|
|
}
|
|
|
|
glm::vec2 Layer::tileUvToTextureSamplePosition(const TileUvTransform& uvTransform,
|
|
const glm::vec2& tileUV,
|
|
const glm::uvec2& resolution)
|
|
{
|
|
glm::vec2 uv = uvTransform.uvOffset + uvTransform.uvScale * tileUV;
|
|
|
|
const glm::vec2 sourceSize = glm::vec2(resolution) +
|
|
glm::vec2(_padTilePixelSizeDifference);
|
|
const glm::vec2 currentSize = glm::vec2(resolution);
|
|
const glm::vec2 sourceToCurrentSize = currentSize / sourceSize;
|
|
return sourceToCurrentSize * (uv - glm::vec2(_padTilePixelStartOffset) / sourceSize);
|
|
}
|
|
|
|
void Layer::initializeBasedOnType(layergroupid::TypeID id, ghoul::Dictionary initDict) {
|
|
switch (id) {
|
|
// Intentional fall through. Same for all tile layers
|
|
case layergroupid::TypeID::DefaultTileLayer:
|
|
case layergroupid::TypeID::SingleImageTileLayer:
|
|
case layergroupid::TypeID::SizeReferenceTileLayer:
|
|
case layergroupid::TypeID::TemporalTileLayer:
|
|
case layergroupid::TypeID::TileIndexTileLayer:
|
|
case layergroupid::TypeID::ByIndexTileLayer:
|
|
case layergroupid::TypeID::ByLevelTileLayer: {
|
|
// We add the id to the dictionary since it needs to be known by
|
|
// the tile provider
|
|
initDict.setValue(KeyLayerGroupID, _layerGroupId);
|
|
if (initDict.hasKeyAndValue<std::string>(KeyName)) {
|
|
std::string name = initDict.value<std::string>(KeyName);
|
|
LDEBUG("Initializing tile provider for layer: '" + name + "'");
|
|
}
|
|
_tileProvider = std::unique_ptr<tileprovider::TileProvider>(
|
|
tileprovider::createFromDictionary(id, std::move(initDict))
|
|
);
|
|
break;
|
|
}
|
|
case layergroupid::TypeID::SolidColor: {
|
|
if (initDict.hasKeyAndValue<glm::vec3>(ColorInfo.identifier)) {
|
|
_solidColor = initDict.value<glm::vec3>(ColorInfo.identifier);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
void Layer::addVisibleProperties() {
|
|
switch (type()) {
|
|
// Intentional fall through. Same for all tile layers
|
|
case layergroupid::TypeID::DefaultTileLayer:
|
|
case layergroupid::TypeID::SingleImageTileLayer:
|
|
case layergroupid::TypeID::SizeReferenceTileLayer:
|
|
case layergroupid::TypeID::TemporalTileLayer:
|
|
case layergroupid::TypeID::TileIndexTileLayer:
|
|
case layergroupid::TypeID::ByIndexTileLayer:
|
|
case layergroupid::TypeID::ByLevelTileLayer: {
|
|
if (_tileProvider) {
|
|
addPropertySubOwner(*_tileProvider);
|
|
}
|
|
break;
|
|
}
|
|
case layergroupid::TypeID::SolidColor: {
|
|
addProperty(_solidColor);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace openspace::globebrowsing
|