/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2024 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include #include #include #include #include namespace openspace::globebrowsing { namespace { constexpr std::string_view _loggerCat = "Layer"; constexpr std::string_view KeyIdentifier = "Identifier"; constexpr std::string_view KeyName = "Name"; constexpr std::string_view KeyDesc = "Description"; constexpr std::string_view KeyLayerGroupID = "LayerGroupID"; constexpr std::string_view KeyAdjustment = "Adjustment"; 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.", openspace::properties::Property::Visibility::Developer }; 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.", openspace::properties::Property::Visibility::AdvancedUser }; 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.", openspace::properties::Property::Visibility::NoviceUser }; 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.", openspace::properties::Property::Visibility::User }; 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.", openspace::properties::Property::Visibility::User }; 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.", openspace::properties::Property::Visibility::User }; 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 }; struct [[codegen::Dictionary(Layer), codegen::noexhaustive()]] Parameters { // The unique identifier for this layer. std::string identifier [[codegen::identifier()]]; // A human-readable name for the user interface. If this is omitted, the // identifier is used instead std::optional name; // A human-readable description of the layer to be used in informational texts // presented to the user std::optional description; // [[codegen::verbatim(ColorInfo.description)]] std::optional color [[codegen::color()]]; // Specifies the type of layer that is to be added. If this value is not // specified, the layer is a DefaultTileProvider std::optional type [[codegen::inlist("DefaultTileProvider", "SingleImageProvider", "ImageSequenceTileProvider", "SizeReferenceTileProvider", "TemporalTileProvider", "TileIndexTileProvider", "TileProviderByIndex", "TileProviderByLevel", "SolidColor", "SpoutImageProvider", "VideoTileProvider")]]; // Determine whether the layer is enabled or not. If this value is not specified, // the layer is disabled std::optional enabled; // The opacity value of the layer std::optional opacity [[codegen::inrange(0.0, 1.0)]]; struct Settings { // The gamma value that is applied to each pixel of the layer std::optional gamma; // The multiplicative factor that is applied to each pixel of the layer std::optional multiplier; // An additive offset that is applied to each pixel of the layer std::optional offset; }; // Specifies the render settings that should be applied to this layer std::optional settings; struct LayerAdjustment { enum class Type { None, ChromaKey, TransferFunction }; // Specifies the type of the adjustment that is applied std::optional type; // Specifies the chroma key used when selecting 'ChromaKey' for the 'Type' std::optional chromaKeyColor; // Specifies the tolerance to match the color to the chroma key when the // 'ChromaKey' type is selected for the 'Type' std::optional chromaKeyTolerance; }; // Parameters that set individual adjustment parameters for this layer std::optional adjustment; enum class BlendMode { Normal, Multiply, Add, Subtract, Color }; // Sets the blend mode of this layer to determine how it interacts with other // layers on top of this std::optional blendMode; }; #include "layer_codegen.cpp" } // namespace documentation::Documentation Layer::Documentation() { return codegen::doc("globebrowsing_layer"); } Layer::Layer(layers::Group::ID id, const ghoul::Dictionary& layerDict, LayerGroup& parent) : properties::PropertyOwner({ layerDict.value(KeyIdentifier), layerDict.hasKey(KeyName) ? layerDict.value(KeyName) : "", layerDict.hasKey(KeyDesc) ? layerDict.value(KeyDesc) : "" }) , _parent(parent) , _typeOption(TypeInfo, properties::OptionProperty::DisplayType::Dropdown) , _blendModeOption(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown) , _enabled(EnabledInfo, false) , _reset(ResetInfo) , _remove(RemoveInfo) , _guiDescription(GuiDescriptionInfo) , _solidColor(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) , _layerGroupId(id) { const Parameters p = codegen::bake(layerDict); const layers::Layer::ID typeID = p.type.has_value() ? ghoul::from_string(*p.type) : layers::Layer::ID::DefaultTileProvider; initializeBasedOnType(typeID, layerDict); _enabled = p.enabled.value_or(_enabled); if (p.description.has_value()) { _guiDescription = description(); addProperty(_guiDescription); } _opacity = p.opacity.value_or(_opacity); addProperty(Fadeable::_opacity); addProperty(Fadeable::_fade); if (p.settings.has_value()) { _renderSettings.gamma = p.settings->gamma.value_or(_renderSettings.gamma); _renderSettings.multiplier = p.settings->multiplier.value_or(_renderSettings.multiplier); _renderSettings.offset = p.settings->offset.value_or(_renderSettings.offset); } if (layerDict.hasValue(KeyAdjustment)) { _layerAdjustment.setValuesFromDictionary( layerDict.value(KeyAdjustment) ); } // Add options to option properties for (const layers::Layer& li : layers::Layers) { _typeOption.addOption(static_cast(li.id), std::string(li.identifier)); } _typeOption.setValue(static_cast(typeID)); _typeId = static_cast(_typeOption.value()); for (const layers::Blend& bi : layers::Blends) { _blendModeOption.addOption(static_cast(bi.id), std::string(bi.identifier)); } // Initialize blend mode if (p.blendMode.has_value()) { switch (*p.blendMode) { case Parameters::BlendMode::Normal: _blendModeOption = static_cast(layers::Blend::ID::Normal); break; case Parameters::BlendMode::Multiply: _blendModeOption = static_cast(layers::Blend::ID::Multiply); break; case Parameters::BlendMode::Add: _blendModeOption = static_cast(layers::Blend::ID::Add); break; case Parameters::BlendMode::Subtract: _blendModeOption = static_cast(layers::Blend::ID::Subtract); break; case Parameters::BlendMode::Color: _blendModeOption = static_cast(layers::Blend::ID::Color); break; } } else { _blendModeOption = static_cast(layers::Blend::ID::Normal); } // On change callbacks definitions _enabled.onChange([this]() { if (_onChangeCallback) { _onChangeCallback(this); } }); _reset.onChange([this]() { if (_tileProvider) { _tileProvider->reset(); } }); _remove.onChange([this]() { if (_tileProvider) { _tileProvider->reset(); _parent.deleteLayer(identifier()); } }); _renderSettings.onChange([this]() { // Only if we are a height layer will anyone care about these settings changing as // that will change the overall bounding box of the layer and thus require culling if (_parent.isHeightLayer() && _onChangeCallback) { _onChangeCallback(this); } }); _typeOption.onChange([this]() { switch (type()) { // Intentional fall through. Same for all tile layers case layers::Layer::ID::DefaultTileProvider: case layers::Layer::ID::SingleImageProvider: case layers::Layer::ID::SpoutImageProvider: case layers::Layer::ID::ImageSequenceTileProvider: case layers::Layer::ID::SizeReferenceTileProvider: case layers::Layer::ID::TemporalTileProvider: case layers::Layer::ID::TileIndexTileProvider: case layers::Layer::ID::TileProviderByIndex: case layers::Layer::ID::TileProviderByLevel: case layers::Layer::ID::VideoTileProvider: if (_tileProvider) { removePropertySubOwner(*_tileProvider); } break; case layers::Layer::ID::SolidColor: removeProperty(_solidColor); break; } _typeId = static_cast(_typeOption.value()); initializeBasedOnType(type(), {}); addVisibleProperties(); if (_onChangeCallback) { _onChangeCallback(this); } }); _blendModeOption.onChange([this]() { if (_onChangeCallback) { _onChangeCallback(this); } }); _layerAdjustment.onChange([this]() { 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(); } _isInitialized = true; } void Layer::deinitialize() { if (_tileProvider) { _tileProvider->deinitialize(); } } ChunkTilePile Layer::chunkTilePile(const TileIndex& tileIndex, int pileSize) const { ZoneScoped; if (_tileProvider) { return _tileProvider->chunkTilePile(tileIndex, pileSize); } else { ChunkTilePile chunkTilePile; std::fill(chunkTilePile.begin(), chunkTilePile.end(), std::nullopt); for (int i = 0; i < pileSize; i++) { ChunkTile tile; tile.uvTransform = TileUvTransform{ { 0, 0 }, { 1, 1 } }; chunkTilePile[i] = tile; } return chunkTilePile; } } Tile::Status Layer::tileStatus(const TileIndex& index) const { return _tileProvider ? _tileProvider->tileStatus(index) : Tile::Status::Unavailable; } layers::Layer::ID Layer::type() const { return _typeId; } layers::Blend::ID Layer::blendMode() const { return static_cast(_blendModeOption.value()); } TileDepthTransform Layer::depthTransform() const { return _tileProvider ? _tileProvider->depthTransform() : TileDepthTransform{ 1.f, 0.f }; } void Layer::setEnabled(bool enabled) { _enabled = enabled; } bool Layer::enabled() const { return _enabled; } bool Layer::isInitialized() const { return _isInitialized; } 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 callback) { _onChangeCallback = std::move(callback); } void Layer::update() { ZoneScoped; if (_tileProvider) { _tileProvider->update(); } } glm::vec2 Layer::tileUvToTextureSamplePosition(const TileUvTransform& uvTransform, const glm::vec2& tileUV) { const glm::vec2 uv = uvTransform.uvOffset + uvTransform.uvScale * tileUV; return uv; } void Layer::initializeBasedOnType(layers::Layer::ID id, ghoul::Dictionary initDict) { switch (id) { // Intentional fall through. Same for all tile layers case layers::Layer::ID::DefaultTileProvider: case layers::Layer::ID::SingleImageProvider: case layers::Layer::ID::SpoutImageProvider: case layers::Layer::ID::ImageSequenceTileProvider: case layers::Layer::ID::SizeReferenceTileProvider: case layers::Layer::ID::TemporalTileProvider: case layers::Layer::ID::TileIndexTileProvider: case layers::Layer::ID::TileProviderByIndex: case layers::Layer::ID::TileProviderByLevel: case layers::Layer::ID::VideoTileProvider: // We add the id to the dictionary since it needs to be known by // the tile provider initDict.setValue( std::string(KeyLayerGroupID), static_cast(_layerGroupId) ); if (initDict.hasKey(KeyName) && initDict.hasValue(KeyName)) { const std::string name = initDict.value(KeyName); LDEBUG("Initializing tile provider for layer: '" + name + "'"); } _tileProvider = TileProvider::createFromDictionary(id, initDict); break; case layers::Layer::ID::SolidColor: if (initDict.hasValue(ColorInfo.identifier)) { _solidColor = initDict.value(ColorInfo.identifier); } break; } } void Layer::addVisibleProperties() { switch (type()) { // Intentional fall through. Same for all tile layers case layers::Layer::ID::DefaultTileProvider: case layers::Layer::ID::SingleImageProvider: case layers::Layer::ID::SpoutImageProvider: case layers::Layer::ID::ImageSequenceTileProvider: case layers::Layer::ID::SizeReferenceTileProvider: case layers::Layer::ID::TemporalTileProvider: case layers::Layer::ID::TileIndexTileProvider: case layers::Layer::ID::TileProviderByIndex: case layers::Layer::ID::TileProviderByLevel: case layers::Layer::ID::VideoTileProvider: if (_tileProvider) { addPropertySubOwner(*_tileProvider); } break; case layers::Layer::ID::SolidColor: addProperty(_solidColor); break; } } } // namespace openspace::globebrowsing