From 2b183b721bf2321357d7ceef9e224e2388dae1b7 Mon Sep 17 00:00:00 2001 From: Jonathas Costa Date: Thu, 16 May 2019 16:34:07 -0300 Subject: [PATCH] Feature/new globe labels (#783) Adding globe labels for planets https://github.com/OpenSpace/OpenSpace/issues/213 --- .../solarsystem/planets/earth/earth.asset | 21 +- .../planets/earth/earth_labels.asset | 7 + .../solarsystem/planets/mercury/mercury.asset | 17 + .../planets/mercury/mercury_labels.asset | 7 + ext/ghoul | 2 +- .../rendering/renderablebillboardscloud.cpp | 24 +- .../rendering/renderabledumeshes.cpp | 23 +- .../rendering/renderableplanescloud.cpp | 26 +- modules/globebrowsing/CMakeLists.txt | 4 +- .../src/globelabelscomponent.cpp | 849 ++++++++++++++++++ .../globebrowsing/src/globelabelscomponent.h | 121 +++ modules/globebrowsing/src/renderableglobe.cpp | 19 +- modules/globebrowsing/src/renderableglobe.h | 5 + 13 files changed, 1088 insertions(+), 37 deletions(-) create mode 100644 data/assets/scene/solarsystem/planets/earth/earth_labels.asset create mode 100644 data/assets/scene/solarsystem/planets/mercury/mercury_labels.asset create mode 100644 modules/globebrowsing/src/globelabelscomponent.cpp create mode 100644 modules/globebrowsing/src/globelabelscomponent.h diff --git a/data/assets/scene/solarsystem/planets/earth/earth.asset b/data/assets/scene/solarsystem/planets/earth/earth.asset index 8335fb94a9..82b9cf9a70 100644 --- a/data/assets/scene/solarsystem/planets/earth/earth.asset +++ b/data/assets/scene/solarsystem/planets/earth/earth.asset @@ -1,6 +1,8 @@ local transforms = asset.require('./transforms') local assetHelper = asset.require('util/asset_helper') local texturesPath = asset.require('./earth_textures').TexturesPath +local labelsPath = asset.require('./earth_labels').LabelsPath + asset.request('./trail') @@ -266,7 +268,24 @@ local Earth = { Radius = 1.737E6 } --Caster2 = { Name = "Independency Day Ship", Radius = 0.0, } - } + }, + Labels = { + Enable = false, + FileName = labelsPath .. "/Earth.labels", + LabelAlignmentOption = "Horizontally", -- or Circularly + LabelsFontSize = 41.0, + LabelsSize = 0.52, + LabelsMinSize = 1.0, + LabelsMaxSize = 1500.0, + ProximityEnabled = false, + LabelsFadeInEnabled = true, + LabelsFadeOutEnabled = false, + FadeInStartingDistance = 50000.0, + FadeOutStartingDistance = 80000.0, + LabelsForceDomeRendering = true, + LabelsDistanceEPS = 1500000.0, + LabelsColor = {1.0, 0.0, 0.0, 1.0} + } }, Tag = { "planet_solarSystem", "planet_terrestrial" }, GUI = { diff --git a/data/assets/scene/solarsystem/planets/earth/earth_labels.asset b/data/assets/scene/solarsystem/planets/earth/earth_labels.asset new file mode 100644 index 0000000000..61bc481f9d --- /dev/null +++ b/data/assets/scene/solarsystem/planets/earth/earth_labels.asset @@ -0,0 +1,7 @@ +local LabelsPath = asset.syncedResource({ + Name = "Earth Labels", + Type = "HttpSynchronization", + Identifier = "earth_labels", + Version = 1 +}) +asset.export("LabelsPath", LabelsPath) diff --git a/data/assets/scene/solarsystem/planets/mercury/mercury.asset b/data/assets/scene/solarsystem/planets/mercury/mercury.asset index 30b8c3fadf..35e332c368 100644 --- a/data/assets/scene/solarsystem/planets/mercury/mercury.asset +++ b/data/assets/scene/solarsystem/planets/mercury/mercury.asset @@ -1,5 +1,7 @@ local assetHelper = asset.require('util/asset_helper') local transforms = asset.require('./transforms') +local labelsPath = asset.require('./mercury_labels').LabelsPath + asset.require("spice/base") asset.request('./trail') @@ -212,6 +214,21 @@ local Mercury = { SegmentsPerPatch = 64, Layers = { ColorLayers = color_layers + }, + Labels = { + Enable = false, + FileName = labelsPath .. "/Mercury.labels", + LabelAlignmentOption = "Horizontally", -- or Circularly + LabelsFontSize = 40.0, + LabelsSize = 10.0, + LabelsMinSize = 1.0, + LabelsMaxSize = 1500.0, + ProximityEnabled = false, + FadeInStartingDistance = 40000000.0, + FadeOutStartingDistance = 80000.0, + LabelsForceDomeRendering = true, + LabelsDistanceEPS = 1500000.0, + LabelsColor = {1.0, 1.0, 0.0, 1.0} } }, Tag = { "planet_solarSystem", "planet_terrestrial" }, diff --git a/data/assets/scene/solarsystem/planets/mercury/mercury_labels.asset b/data/assets/scene/solarsystem/planets/mercury/mercury_labels.asset new file mode 100644 index 0000000000..49b6148dfa --- /dev/null +++ b/data/assets/scene/solarsystem/planets/mercury/mercury_labels.asset @@ -0,0 +1,7 @@ +local LabelsPath = asset.syncedResource({ + Name = "Mercury Labels", + Type = "HttpSynchronization", + Identifier = "mercury_labels", + Version = 1 +}) +asset.export("LabelsPath", LabelsPath) \ No newline at end of file diff --git a/ext/ghoul b/ext/ghoul index 5600165bb3..e3a388183c 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 5600165bb3a2e009161d56f6dbf760fcf79e4e87 +Subproject commit e3a388183c77c1845c50b1375a576e439db30619 diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp index 91db3fee91..42fe1577ef 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp @@ -852,6 +852,20 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, glm::vec4 textColor = _textColor; textColor.a *= fadeInVariable; textColor.a *= _opacity; + + ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; + labelInfo.orthoRight = orthoRight; + labelInfo.orthoUp = orthoUp; + labelInfo.minSize = static_cast(_textMinSize); + labelInfo.maxSize = static_cast(_textMaxSize); + labelInfo.cameraPos = data.camera.positionVec3(); + labelInfo.cameraLookUp = data.camera.lookUpVectorWorldSpace(); + labelInfo.renderType = _renderOption; + labelInfo.mvpMatrix = modelViewProjectionMatrix; + labelInfo.scale = pow(10.f, _textSize); + labelInfo.enableDepth = true; + labelInfo.enableFalseDepth = false; + for (const std::pair& pair : _labelData) { //glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0)); glm::vec3 scaledPos(pair.first); @@ -861,15 +875,7 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, scaledPos, pair.second, textColor, - pow(10.f, _textSize.value()), - static_cast(_textMinSize), - static_cast(_textMaxSize), - modelViewProjectionMatrix, - orthoRight, - orthoUp, - data.camera.positionVec3(), - data.camera.lookUpVectorWorldSpace(), - _renderOption.value() + labelInfo ); } } diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.cpp b/modules/digitaluniverse/rendering/renderabledumeshes.cpp index 33d8f50c2f..c05c205286 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.cpp +++ b/modules/digitaluniverse/rendering/renderabledumeshes.cpp @@ -552,6 +552,19 @@ void RenderableDUMeshes::renderLabels(const RenderData& data, break; } + ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; + labelInfo.orthoRight = orthoRight; + labelInfo.orthoUp = orthoUp; + labelInfo.minSize = static_cast(_textMinSize); + labelInfo.maxSize = static_cast(_textMaxSize); + labelInfo.cameraPos = data.camera.positionVec3(); + labelInfo.cameraLookUp = data.camera.lookUpVectorWorldSpace(); + labelInfo.renderType = _renderOption; + labelInfo.mvpMatrix = modelViewProjectionMatrix; + labelInfo.scale = pow(10.f, _textSize); + labelInfo.enableDepth = true; + labelInfo.enableFalseDepth = false; + for (const std::pair& pair : _labelData) { //glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0)); glm::vec3 scaledPos(pair.first); @@ -561,15 +574,7 @@ void RenderableDUMeshes::renderLabels(const RenderData& data, scaledPos, pair.second, _textColor, - pow(10.f, _textSize.value()), - static_cast(_textMinSize), - static_cast(_textMaxSize), - modelViewProjectionMatrix, - orthoRight, - orthoUp, - data.camera.positionVec3(), - data.camera.lookUpVectorWorldSpace(), - _renderOption.value() + labelInfo ); } } diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp index e191d76582..7581b01f17 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ b/modules/digitaluniverse/rendering/renderableplanescloud.cpp @@ -651,7 +651,21 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data, } glm::vec4 textColor = _textColor; - textColor.a *= fadeInVariable; + textColor.a *= fadeInVariable * _opacity; + + ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; + labelInfo.orthoRight = orthoRight; + labelInfo.orthoUp = orthoUp; + labelInfo.minSize = static_cast(_textMinSize); + labelInfo.maxSize = static_cast(_textMaxSize); + labelInfo.cameraPos = data.camera.positionVec3(); + labelInfo.cameraLookUp = data.camera.lookUpVectorWorldSpace(); + labelInfo.renderType = _renderOption; + labelInfo.mvpMatrix = modelViewProjectionMatrix; + labelInfo.scale = pow(10.f, _textSize); + labelInfo.enableDepth = true; + labelInfo.enableFalseDepth = false; + for (const std::pair& pair : _labelData) { //glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0)); glm::vec3 scaledPos(pair.first); @@ -661,15 +675,7 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data, scaledPos, pair.second, textColor, - pow(10.f, _textSize.value()), - _textMinSize, - _textMaxSize, - modelViewProjectionMatrix, - orthoRight, - orthoUp, - data.camera.positionVec3(), - data.camera.lookUpVectorWorldSpace(), - _renderOption.value() + labelInfo ); } } diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index 82b005e216..0489724bfb 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -26,13 +26,13 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/globebrowsingmodule.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/asynctiledataprovider.h ${CMAKE_CURRENT_SOURCE_DIR}/src/basictypes.h ${CMAKE_CURRENT_SOURCE_DIR}/src/dashboarditemglobelocation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/ellipsoid.h ${CMAKE_CURRENT_SOURCE_DIR}/src/gdalwrapper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/geodeticpatch.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/globelabelscomponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/globetranslation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/gpulayergroup.h ${CMAKE_CURRENT_SOURCE_DIR}/src/layer.h @@ -62,12 +62,12 @@ set(HEADER_FILES set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/globebrowsingmodule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/globebrowsingmodule_lua.inl - ${CMAKE_CURRENT_SOURCE_DIR}/src/asynctiledataprovider.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/dashboarditemglobelocation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ellipsoid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gdalwrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/geodeticpatch.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/globelabelscomponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/globetranslation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gpulayergroup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/layer.cpp diff --git a/modules/globebrowsing/src/globelabelscomponent.cpp b/modules/globebrowsing/src/globelabelscomponent.cpp new file mode 100644 index 0000000000..50efeeffc9 --- /dev/null +++ b/modules/globebrowsing/src/globelabelscomponent.cpp @@ -0,0 +1,849 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2019 * +* * +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* keyLabels = "Labels"; + constexpr const char* keyLabelsFileName = "FileName"; + + constexpr const char* _loggerCat = "GlobeLabels"; + + constexpr int8_t CurrentCacheVersion = 1; + + constexpr openspace::properties::Property::PropertyInfo LabelsInfo = { + "Labels", + "Labels Enabled", + "Enables and disables the rendering of labels on the globe surface from " + "the csv label file" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsEnableInfo = { + "Enable", + "Enable", + "Enables and disables labels' rendering from the asset file." + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsFontSizeInfo = { + "LabelsFontSize", + "Labels Font Size", + "Font size for the rendering labels. This is different fromt text size." + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsMaxSizeInfo = { + "LabelsMaxSize", + "Labels Maximum Text Size", + "Maximum label size" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsMinSizeInfo = { + "LabelsMinSize", + "Labels Minimum Text Size", + "Minimum label size" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsSizeInfo = { + "LabelsSize", + "Labels Size", + "Labels Size" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsMinHeightInfo = { + "LabelsMinHeight", + "Labels Minimum Height", + "Labels Minimum Height" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsColorInfo = { + "LabelsColor", + "Labels Color", + "Labels Color" + }; + + constexpr openspace::properties::Property::PropertyInfo + LabelsFadeInStartingDistanceInfo = + { + "FadeInStartingDistance", + "Fade In Starting Distance for Labels", + "Fade In Starting Distance for Labels" + }; + + constexpr openspace::properties::Property::PropertyInfo + LabelsFadeOutStartingDistanceInfo = + { + "FadeOutStartingDistance", + "Fade Out Starting Distance for Labels", + "Fade Out Starting Distance for Labels" + }; + + constexpr openspace::properties::Property::PropertyInfo + LabelsFadeInEnabledInfo = + { + "LabelsFadeInEnabled", + "Labels fade In enabled", + "Labels fade In enabled" + }; + + constexpr openspace::properties::Property::PropertyInfo + LabelsFadeOutEnabledInfo = + { + "LabelsFadeOutEnabled", + "Labels fade Out enabled", + "Labels fade Out enabled" + }; + + constexpr openspace::properties::Property::PropertyInfo + LabelsDisableCullingEnabledInfo = + { + "LabelsDisableCullingEnabled", + "Labels culling disabled", + "Labels culling disabled" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelsDistanceEPSInfo = { + "LabelsDistanceEPS", + "Labels culling distance from globe's center", + "Labels culling distance from globe's center" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelAlignmentOptionInfo = { + "LabelAlignmentOption", + "Label Alignment Option", + "Labels are aligned horizontally or circularly related to the planet." + }; +} // namespace + +namespace openspace { + +documentation::Documentation GlobeLabelsComponent::Documentation() { + using namespace documentation; + return { + "GlobeLabels Component", + "globebrowsing_globelabelscomponent", + { + { + LabelsInfo.identifier, + new BoolVerifier, + Optional::Yes, + LabelsInfo.description + }, + { + LabelsEnableInfo.identifier, + new BoolVerifier, + Optional::Yes, + LabelsEnableInfo.description + }, + { + LabelsFontSizeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsFontSizeInfo.description + }, + { + LabelsMaxSizeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsMaxSizeInfo.description + }, + { + LabelsMinSizeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsMinSizeInfo.description + }, + { + LabelsSizeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsSizeInfo.description + }, + { + LabelsMinHeightInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsMinHeightInfo.description + }, + { + LabelsColorInfo.identifier, + new Vector4Verifier(), + Optional::Yes, + LabelsColorInfo.description + }, + { + LabelsFadeInStartingDistanceInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsFadeInStartingDistanceInfo.description + }, + { + LabelsFadeOutStartingDistanceInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsFadeOutStartingDistanceInfo.description + }, + { + LabelsFadeInEnabledInfo.identifier, + new BoolVerifier, + Optional::Yes, + LabelsFadeInEnabledInfo.description + }, + { + LabelsFadeOutEnabledInfo.identifier, + new BoolVerifier, + Optional::Yes, + LabelsFadeOutEnabledInfo.description + }, + { + LabelsDisableCullingEnabledInfo.identifier, + new BoolVerifier, + Optional::Yes, + LabelsDisableCullingEnabledInfo.description + }, + { + LabelsDistanceEPSInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LabelsDistanceEPSInfo.description + }, + { + LabelAlignmentOptionInfo.identifier, + new StringVerifier, + Optional::Yes, + LabelAlignmentOptionInfo.description + }, + } + }; +} + +GlobeLabelsComponent::GlobeLabelsComponent() + : properties::PropertyOwner({ "Labels" }) + , _labelsEnabled(LabelsInfo, false) + , _labelsFontSize(LabelsFontSizeInfo, 30, 1, 300) + , _labelsMaxSize(LabelsMaxSizeInfo, 300, 10, 1000) + , _labelsMinSize(LabelsMinSizeInfo, 4, 1, 100) + , _labelsSize(LabelsSizeInfo, 2.5, 0, 30) + , _labelsMinHeight(LabelsMinHeightInfo, 100.0, 0.0, 10000.0) + , _labelsColor(LabelsColorInfo, glm::vec4(1.f, 1.f, 0.f, 1.f), + glm::vec4(0.f), glm::vec4(1.f)) + , _labelsFadeInDist(LabelsFadeInStartingDistanceInfo, 1E6, 1E3, 1E8) + , _labelsFadeOutDist(LabelsFadeOutStartingDistanceInfo, 1E4, 1, 1E7) + , _labelsFadeInEnabled(LabelsFadeInEnabledInfo, false) + , _labelsFadeOutEnabled(LabelsFadeOutEnabledInfo, false) + , _labelsDisableCullingEnabled(LabelsDisableCullingEnabledInfo, false) + , _labelsDistaneEPS(LabelsDistanceEPSInfo, 100000.f, 1000.f, 10000000.f) + , _labelAlignmentOption( + LabelAlignmentOptionInfo, + properties::OptionProperty::DisplayType::Dropdown + ) +{ + addProperty(_labelsEnabled); + addProperty(_labelsFontSize); + addProperty(_labelsSize); + addProperty(_labelsMinHeight); + _labelsColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(_labelsColor); + addProperty(_labelsFadeInDist); + addProperty(_labelsFadeOutDist); + addProperty(_labelsMinSize); + addProperty(_labelsFadeInEnabled); + addProperty(_labelsFadeOutEnabled); + addProperty(_labelsDisableCullingEnabled); + addProperty(_labelsDistaneEPS); + + _labelAlignmentOption.addOption(0, "Horizontally"); + _labelAlignmentOption.addOption(1, "Circularly"); + _labelAlignmentOption = Horizontally; + addProperty(_labelAlignmentOption); +} + +void GlobeLabelsComponent::initialize(const ghoul::Dictionary& dictionary, + globebrowsing::RenderableGlobe* globe) +{ + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "GlobeLabelsComponent" + ); + + _globe = globe; + + // Reads labels' file and build cache file if necessary + if (dictionary.empty()) { + return; + } + std::string labelsFile; + bool successLabels = dictionary.getValue(keyLabelsFileName, labelsFile); + if (!successLabels) { + return; + } + bool loadSuccess = loadLabelsData(absPath(labelsFile)); + if (!loadSuccess) { + return; + } + if (dictionary.hasKey(LabelsEnableInfo.identifier)) { + // In case of the label's dic is present but is disabled + _labelsEnabled = dictionary.value(LabelsEnableInfo.identifier); + } + else { + // Is the labels dic is enable in the configuration file, + // enables the label automatically. + _labelsEnabled = true; + } + + if (dictionary.hasKey(LabelsFontSizeInfo.identifier)) { + float fontSize = dictionary.value(LabelsFontSizeInfo.identifier); + _labelsFontSize = fontSize; + _labelsFontSize.onChange([this]() { initializeFonts(); }); + } + + if (dictionary.hasKey(LabelsSizeInfo.identifier)) { + _labelsSize = static_cast( + dictionary.value(LabelsSizeInfo.identifier) + ); + } + + if (dictionary.hasKey(LabelsMinHeightInfo.identifier)) { + _labelsMinHeight = dictionary.value(LabelsMinHeightInfo.identifier); + } + + if (dictionary.hasKey(LabelsColorInfo.identifier)) { + _labelsColor = dictionary.value(LabelsColorInfo.identifier); + } + + if (dictionary.hasKey(LabelsFadeInEnabledInfo.identifier)) { + _labelsFadeInEnabled = dictionary.value(LabelsFadeInEnabledInfo.identifier); + } + + if (dictionary.hasKey(LabelsFadeInStartingDistanceInfo.identifier)) { + _labelsFadeInDist = dictionary.value( + LabelsFadeInStartingDistanceInfo.identifier + ); + } + + if (dictionary.hasKey(LabelsFadeOutEnabledInfo.identifier)) { + _labelsFadeOutEnabled = dictionary.value( + LabelsFadeOutEnabledInfo.identifier + ); + } + + if (dictionary.hasKey(LabelsFadeOutStartingDistanceInfo.identifier)) { + _labelsFadeOutDist = dictionary.value( + LabelsFadeOutStartingDistanceInfo.identifier + ); + } + + if (dictionary.hasKey(LabelsMinSizeInfo.identifier)) { + _labelsMinSize = static_cast( + dictionary.value(LabelsMinSizeInfo.identifier) + ); + } + + if (dictionary.hasKey(LabelsMaxSizeInfo.identifier)) { + _labelsMaxSize = static_cast( + dictionary.value(LabelsMaxSizeInfo.identifier) + ); + } + + if (dictionary.hasKey(LabelsDisableCullingEnabledInfo.identifier)) { + bool disabled = dictionary.value( + LabelsDisableCullingEnabledInfo.identifier + ); + _labelsDisableCullingEnabled.set(disabled); + } + + if (dictionary.hasKey(LabelsDistanceEPSInfo.identifier)) { + _labelsDistaneEPS = static_cast( + dictionary.value(LabelsDistanceEPSInfo.identifier) + ); + } + + if (dictionary.hasKey(LabelAlignmentOptionInfo.identifier)) { + std::string alignment = + dictionary.value(LabelAlignmentOptionInfo.identifier); + if (alignment == "Horizontally") { + _labelAlignmentOption = Horizontally; + } + else { + _labelAlignmentOption = Circularly; + } + } + + initializeFonts(); +} + +void GlobeLabelsComponent::initializeFonts() { + _font = openspace::global::fontManager.font( + "Mono", + static_cast(_labelsFontSize), + ghoul::fontrendering::FontManager::Outline::Yes, + ghoul::fontrendering::FontManager::LoadGlyphs::No + ); +} + +bool GlobeLabelsComponent::loadLabelsData(const std::string& file) { + std::string cachedFile = FileSys.cacheManager()->cachedFilename( + ghoul::filesystem::File(file), + "GlobeLabelsComponent|" + identifier(), + ghoul::filesystem::CacheManager::Persistent::Yes + ); + + bool hasCachedFile = FileSys.fileExists(cachedFile); + if (hasCachedFile) { + LINFO(fmt::format("Cached file '{}' used for labels file: {}", cachedFile, file)); + + const bool hasCache = loadCachedFile(cachedFile); + if (hasCache) { + return true; + } + else { + FileSys.cacheManager()->removeCacheFile(file); + // Intentional fall-through to the 'else' to generate the cache + // file for the next run + } + } + else { + LINFO(fmt::format("Cache for labels file '{}' not found", file)); + } + LINFO(fmt::format("Loading labels file '{}'", file)); + + bool success = readLabelsFile(file); + if (success) { + saveCachedFile(cachedFile); + } + return success; +} + +bool GlobeLabelsComponent::readLabelsFile(const std::string& file) { + try { + std::fstream csvLabelFile(file); + if (!csvLabelFile.good()) { + LERROR(fmt::format("Failed to open labels file '{}'", file)); + return false; + } + if (!csvLabelFile.is_open()) { + return false; + } + + _labels.labelsArray.clear(); + + std::string sline; + while (!csvLabelFile.eof()) { + std::getline(csvLabelFile, sline); + if (sline.size() <= 10) { + continue; + } + + std::istringstream iss(sline); + std::string token; + std::getline(iss, token, ','); + + // First line is just the Header + if (token == "Feature_Name") { + continue; + } + + LabelEntry lEntry; + + // Non-ascii characters aren't displayed correctly by the text + // rendering (We don't have the non-ascii character in the texture + // atlas) + // Once this limitation is fixed, we can remove the next piece of code + // Removing non ASCII characters: + strncpy(lEntry.feature, token.c_str(), 256); + int tokenChar = 0; + while (tokenChar < 256) { + if ((lEntry.feature[tokenChar] < 0 || lEntry.feature[tokenChar] > 127) && + lEntry.feature[tokenChar] != '\0') + { + lEntry.feature[tokenChar] = '*'; + } + else if (lEntry.feature[tokenChar] == '\"') { + lEntry.feature[tokenChar] = '='; + } + tokenChar++; + } + + std::getline(iss, token, ','); // Target is not used + + std::getline(iss, token, ','); // Diameter + lEntry.diameter = std::stof(token); + + std::getline(iss, token, ','); // Latitude + lEntry.latitude = std::stof(token); + + std::getline(iss, token, ','); // Longitude + lEntry.longitude = std::stof(token); + + std::getline(iss, token, ','); // Coord System + std::string coordinateSystem(token); + std::size_t found = coordinateSystem.find("West"); + if (found != std::string::npos) { + lEntry.longitude = 360.0f - lEntry.longitude; + } + + // Clean white spaces + std::istringstream issFeature(lEntry.feature); + std::getline(issFeature, token, '='); + if (token == "") + std::getline(issFeature, token, '='); + strncpy(lEntry.feature, token.c_str(), 256); + + GlobeBrowsingModule* _globeBrowsingModule = + global::moduleEngine.module(); + lEntry.geoPosition = _globeBrowsingModule->cartesianCoordinatesFromGeo( + *_globe, + lEntry.latitude, + lEntry.longitude, + lEntry.diameter + ); + + _labels.labelsArray.push_back(lEntry); + + } + + return true; + } + catch (const std::fstream::failure& e) { + LERROR(fmt::format("Failed reading labels file '{}'", file)); + LERROR(e.what()); + return false; + } +} + +bool GlobeLabelsComponent::loadCachedFile(const std::string& file) { + std::ifstream fileStream(file, std::ifstream::binary); + if (!fileStream.good()) { + LERROR(fmt::format("Error opening file '{}' for loading cache file", file)); + return false; + } + + int8_t version = 0; + fileStream.read(reinterpret_cast(&version), sizeof(int8_t)); + if (version != CurrentCacheVersion) { + LINFO("The format of the cached file has changed: deleting old cache"); + fileStream.close(); + FileSys.deleteFile(file); + return false; + } + + int32_t nValues = 0; + fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); + _labels.labelsArray.resize(nValues); + + fileStream.read( + reinterpret_cast(_labels.labelsArray.data()), + nValues * sizeof(_labels.labelsArray[0]) + ); + + return fileStream.good(); +} + +bool GlobeLabelsComponent::saveCachedFile(const std::string& file) const { + std::ofstream fileStream(file, std::ofstream::binary); + if (!fileStream.good()) { + LERROR(fmt::format("Error opening file '{}' for save cache file", file)); + return false; + } + fileStream.write(reinterpret_cast(&CurrentCacheVersion), + sizeof(int8_t)); + + int32_t nValues = static_cast(_labels.labelsArray.size()); + if (nValues == 0) { + LERROR("Error writing cache: No values were loaded"); + return false; + } + fileStream.write(reinterpret_cast(&nValues), sizeof(int32_t)); + + size_t nBytes = nValues * sizeof(_labels.labelsArray[0]); + fileStream.write(reinterpret_cast(&_labels.labelsArray[0]), nBytes); + + return fileStream.good(); +} + +void GlobeLabelsComponent::draw(const RenderData& data) { + if (!_labelsEnabled) { + return; + } + + // Calculate the MVP matrix + glm::dmat4 viewTransform = glm::dmat4(data.camera.combinedViewMatrix()); + glm::dmat4 vp = glm::dmat4(data.camera.sgctInternal.projectionMatrix()) * + viewTransform; + glm::dmat4 mvp = vp * _globe->modelTransform(); + + glm::dvec3 globePositionWorld = glm::dvec3(_globe->modelTransform() * + glm::vec4(0.f, 0.f, 0.f, 1.f)); + glm::dvec3 cameraToGlobeDistanceWorld = globePositionWorld - + data.camera.positionVec3(); + double distanceCameraGlobeWorld = glm::length(cameraToGlobeDistanceWorld); + + float varyingOpacity = 1.f; + if (_labelsFadeInEnabled) { + double averageRadius = ( + _globe->ellipsoid().radii().x + _globe->ellipsoid().radii().y + + _globe->ellipsoid().radii().z + ) / 3.0; + glm::dvec2 fadeRange = glm::dvec2( + averageRadius + _labelsMinHeight + ); + fadeRange.x += _labelsFadeInDist; + double a = 1.0 / (fadeRange.y - fadeRange.x); + double b = -(fadeRange.x / (fadeRange.y - fadeRange.x)); + double funcValue = a * distanceCameraGlobeWorld + b; + varyingOpacity *= static_cast(std::min(funcValue, 1.0)); + + if (varyingOpacity < minTransparencyValueConst) { + return; + } + } + + if (_labelsFadeOutEnabled) { + double averageRadius = ( + _globe->ellipsoid().radii().x + _globe->ellipsoid().radii().y + + _globe->ellipsoid().radii().z + ) / 3.0; + + glm::dvec2 fadeRange = glm::dvec2( + averageRadius + _labelsMinHeight + labelFadeRangeConst + ); + fadeRange.x += _labelsFadeOutDist; + double a = rangeAngularCoefConst / (fadeRange.x - fadeRange.y); + double b = -(fadeRange.y / (fadeRange.x - fadeRange.y)); + double funcValue = a * distanceCameraGlobeWorld + b; + varyingOpacity *= static_cast(std::min(funcValue, 1.0)); + + if (varyingOpacity < minTransparencyValueConst) { + return; + } + } + + renderLabels(data, mvp, static_cast(distanceCameraGlobeWorld), varyingOpacity); +} + +void GlobeLabelsComponent::renderLabels(const RenderData& data, + const glm::dmat4& modelViewProjectionMatrix, + float distToCamera, + float fadeInVariable +) { + constexpr double DIST_EPS = 6000.0; + constexpr double SIN_EPS = 0.001; + + glm::vec4 textColor = _labelsColor; + textColor.a *= fadeInVariable; + + glm::dmat4 invMP = glm::inverse(_globe->modelTransform()); + glm::dmat4 invCombinedView = glm::inverse(data.camera.combinedViewMatrix()); + + glm::dvec4 cameraPosWorld = invCombinedView * glm::dvec4(0.0, 0.0, 0.0, 1.0); + glm::dvec3 cameraPosObj = glm::dvec3(invMP * cameraPosWorld); + glm::dvec4 cameraUpVecWorld = glm::dvec4(data.camera.lookUpVectorWorldSpace(), 0.0); + glm::dvec3 cameraLookUpObj = glm::dvec3(invMP * cameraUpVecWorld); + + glm::dmat4 VP = glm::dmat4(data.camera.sgctInternal.projectionMatrix()) * + data.camera.combinedViewMatrix(); + + glm::dmat4 invModelMatrix = glm::inverse(_globe->modelTransform()); + + glm::dvec3 cameraViewDirectionObj = glm::dvec3( + invModelMatrix * glm::dvec4(data.camera.viewDirectionWorldSpace(), 0.0) + ); + glm::dvec3 cameraUpDirectionObj = glm::dvec3( + invModelMatrix * glm::dvec4(data.camera.lookUpVectorWorldSpace(), 0.0) + ); + glm::dvec3 orthoRight = glm::normalize( + glm::cross(cameraViewDirectionObj, cameraUpDirectionObj) + ); + if (orthoRight == glm::dvec3(0.0)) { + glm::dvec3 otherVector( + cameraUpDirectionObj.y, + cameraUpDirectionObj.x, + cameraUpDirectionObj.z + ); + orthoRight = glm::normalize(glm::cross(otherVector, cameraViewDirectionObj)); + } + glm::dvec3 orthoUp = glm::normalize(glm::cross(orthoRight, cameraViewDirectionObj)); + + for (const LabelEntry& lEntry : _labels.labelsArray) { + glm::vec3 position = lEntry.geoPosition; + glm::dvec3 locationPositionWorld = + glm::dvec3(_globe->modelTransform() * glm::dvec4(position, 1.0)); + double distanceCameraToLabelWorld = + glm::length(locationPositionWorld - data.camera.positionVec3()); + + if (_labelsDisableCullingEnabled || + ((distToCamera > (distanceCameraToLabelWorld + _labelsDistaneEPS)) && + isLabelInFrustum(VP, locationPositionWorld))) + { + if (_labelAlignmentOption == Circularly) { + glm::dvec3 labelNormalObj = glm::dvec3( + invModelMatrix * glm::dvec4(data.camera.positionVec3(), 1.0) + ) - glm::dvec3(position); + + glm::dvec3 labelUpDirectionObj = glm::dvec3(position); + + orthoRight = glm::normalize( + glm::cross(labelUpDirectionObj, labelNormalObj) + ); + if (orthoRight == glm::dvec3(0.0)) { + glm::dvec3 otherVector( + labelUpDirectionObj.y, + labelUpDirectionObj.x, + labelUpDirectionObj.z + ); + orthoRight = glm::normalize(glm::cross(otherVector, labelNormalObj)); + } + orthoUp = glm::normalize(glm::cross(labelNormalObj, orthoRight)); + } + + position += _labelsMinHeight; + + ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; + labelInfo.orthoRight = orthoRight; + labelInfo.orthoUp = orthoUp; + labelInfo.minSize = _labelsMinSize; + labelInfo.maxSize = _labelsMaxSize; + labelInfo.cameraPos = data.camera.positionVec3(); + labelInfo.cameraLookUp = data.camera.lookUpVectorWorldSpace(); + labelInfo.renderType = 0; + labelInfo.mvpMatrix = modelViewProjectionMatrix; + labelInfo.scale = powf(2.f, _labelsSize); + labelInfo.enableDepth = true; + labelInfo.enableFalseDepth = true; + labelInfo.disableTransmittance = true; + + // Testing + glm::dmat4 modelviewTransform = glm::dmat4(data.camera.combinedViewMatrix()) * + _globe->modelTransform(); + labelInfo.modelViewMatrix = modelviewTransform; + labelInfo.projectionMatrix = glm::dmat4( + data.camera.sgctInternal.projectionMatrix() + ); + + ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( + *_font, + position, + lEntry.feature, + textColor, + labelInfo + ); + } + } +} + +bool GlobeLabelsComponent::isLabelInFrustum(const glm::dmat4& MVMatrix, + const glm::dvec3& position) const +{ + + // Frustum Planes + glm::dvec3 col1(MVMatrix[0][0], MVMatrix[1][0], MVMatrix[2][0]); + glm::dvec3 col2(MVMatrix[0][1], MVMatrix[1][1], MVMatrix[2][1]); + glm::dvec3 col3(MVMatrix[0][2], MVMatrix[1][2], MVMatrix[2][2]); + glm::dvec3 col4(MVMatrix[0][3], MVMatrix[1][3], MVMatrix[2][3]); + + glm::dvec3 leftNormal = col4 + col1; + glm::dvec3 rightNormal = col4 - col1; + glm::dvec3 bottomNormal = col4 + col2; + glm::dvec3 topNormal = col4 - col2; + glm::dvec3 nearNormal = col3 + col4; + glm::dvec3 farNormal = col4 - col3; + + // Plane Distances + double leftDistance = MVMatrix[3][3] + MVMatrix[3][0]; + double rightDistance = MVMatrix[3][3] - MVMatrix[3][0]; + double bottomDistance = MVMatrix[3][3] + MVMatrix[3][1]; + double topDistance = MVMatrix[3][3] - MVMatrix[3][1]; + double nearDistance = MVMatrix[3][3] + MVMatrix[3][2]; + double farDistance = MVMatrix[3][3] - MVMatrix[3][2]; + + // Normalize Planes + double invMag = 1.0 / glm::length(leftNormal); + leftNormal *= invMag; + leftDistance *= invMag; + + invMag = 1.0 / glm::length(rightNormal); + rightNormal *= invMag; + rightDistance *= invMag; + + invMag = 1.0 / glm::length(bottomNormal); + bottomNormal *= invMag; + bottomDistance *= invMag; + + invMag = 1.0 / glm::length(topNormal); + topNormal *= invMag; + topDistance *= invMag; + + invMag = 1.0 / glm::length(nearNormal); + nearNormal *= invMag; + nearDistance *= invMag; + + invMag = 1.0 / glm::length(farNormal); + farNormal *= invMag; + farDistance *= invMag; + + float radius = 1.0; + + if ((glm::dot(leftNormal, position) + leftDistance) < -radius) { + return false; + } + else if ((glm::dot(rightNormal, position) + rightDistance) < -radius) { + return false; + } + else if ((glm::dot(bottomNormal, position) + bottomDistance) < -radius) { + return false; + } + else if ((glm::dot(topNormal, position) + topDistance) < -radius) { + return false; + } + else if ((glm::dot(nearNormal, position) + nearDistance) < -radius) { + return false; + } + // The far plane testing is disabled because the atm has no depth. + /*else if ((glm::dot(farNormal, position) + farDistance) < -radius) { + return false; + }*/ + + return true; +} + +} // namespace openspace diff --git a/modules/globebrowsing/src/globelabelscomponent.h b/modules/globebrowsing/src/globelabelscomponent.h new file mode 100644 index 0000000000..10b732626b --- /dev/null +++ b/modules/globebrowsing/src/globelabelscomponent.h @@ -0,0 +1,121 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2019 * +* * +* 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___GLOBELABELSCOMPONENT___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___GLOBELABELSCOMPONENT___H__ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace ghoul { class Dictionary;} +namespace ghoul::opengl { class ProgramObject; } + +namespace openspace { + +struct RenderData; + +namespace documentation { struct Documentation; } +namespace globebrowsing { class RenderableGlobe; } + +class GlobeLabelsComponent : public properties::PropertyOwner { +public: + enum LabelRenderingAlignmentType { + Horizontally = 0, + Circularly + }; + + // Labels Structures + struct LabelEntry { + char feature[256]; + float diameter; + float latitude; + float longitude; + glm::vec3 geoPosition; + }; + struct Labels { + std::string filename; + std::vector labelsArray; + }; + + const double labelFadeRangeConst = 1500.0; + const double rangeAngularCoefConst = 0.8; + const float minTransparencyValueConst = 0.009f; + + GlobeLabelsComponent(); + ~GlobeLabelsComponent() = default; + + void initialize(const ghoul::Dictionary& dictionary, + globebrowsing::RenderableGlobe* globe); + + void initializeFonts(); + + static documentation::Documentation Documentation(); + + void draw(const RenderData& data); + +private: + bool loadLabelsData(const std::string& file); + bool readLabelsFile(const std::string& file); + bool loadCachedFile(const std::string& file); + bool saveCachedFile(const std::string& file) const; + void renderLabels(const RenderData& data, const glm::dmat4& modelViewProjectionMatrix, + float distToCamera, float fadeInVariable); + bool isLabelInFrustum(const glm::dmat4& MVMatrix, const glm::dvec3& position) const; + +protected: + properties::BoolProperty _labelsEnabled; + properties::FloatProperty _labelsFontSize; + properties::IntProperty _labelsMaxSize; + properties::IntProperty _labelsMinSize; + properties::FloatProperty _labelsSize; + properties::FloatProperty _labelsMinHeight; + properties::Vec4Property _labelsColor; + properties::FloatProperty _labelsFadeInDist; + properties::FloatProperty _labelsFadeOutDist; + properties::BoolProperty _labelsFadeInEnabled; + properties::BoolProperty _labelsFadeOutEnabled; + properties::BoolProperty _labelsDisableCullingEnabled; + properties::FloatProperty _labelsDistaneEPS; + properties::OptionProperty _labelAlignmentOption; + +private: + Labels _labels; + + // Font + std::shared_ptr _font; + + // Globe + openspace::globebrowsing::RenderableGlobe* _globe; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___GLOBELABELSCOMPONENT___H__ diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 4c8a92fe5e..c9bca464c8 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -69,6 +69,8 @@ namespace { constexpr const char* KeyShadowGroup = "ShadowGroup"; constexpr const char* KeyShadowSource = "Source"; constexpr const char* KeyShadowCaster = "Caster"; + constexpr const char* KeyLabels = "Labels"; + const openspace::globebrowsing::AABB3 CullingFrustum{ glm::vec3(-1.f, -1.f, 0.f), glm::vec3( 1.f, 1.f, 1e35) @@ -541,8 +543,8 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) std::vector shadowConfArray; if (!disableShadows && (!sourceArray.empty() && !casterArray.empty())) { - for (const auto & source : sourceArray) { - for (const auto & caster : casterArray) { + for (const std::pair& source : sourceArray) { + for (const std::pair& caster : casterArray) { Ellipsoid::ShadowConfiguration sc; sc.source = source; sc.caster = caster; @@ -553,9 +555,17 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) } } } + + // Labels Dictionary + if (dictionary.hasKeyAndValue(KeyLabels)) { + _labelsDictionary = dictionary.value(KeyLabels); + } } void RenderableGlobe::initializeGL() { + _globeLabelsComponent.initialize(_labelsDictionary, this); + addPropertySubOwner(_globeLabelsComponent); + _layerManager.update(); _grid.initializeGL(); @@ -603,11 +613,10 @@ void RenderableGlobe::render(const RenderData& data, RendererTasks& rendererTask if (distanceToCamera < distance) { try { renderChunks(data, rendererTask); + _globeLabelsComponent.draw(data); } catch (const ghoul::opengl::TextureUnit::TextureUnitError&) { - std::string layer = _lastChangedLayer ? - _lastChangedLayer->guiName() : - ""; + std::string layer = _lastChangedLayer ? _lastChangedLayer->guiName() : ""; LWARNINGC( guiName(), diff --git a/modules/globebrowsing/src/renderableglobe.h b/modules/globebrowsing/src/renderableglobe.h index 48e4721536..7f6a6f7948 100644 --- a/modules/globebrowsing/src/renderableglobe.h +++ b/modules/globebrowsing/src/renderableglobe.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -247,6 +248,10 @@ private: bool _chunkCornersDirty = true; bool _nLayersIsDirty = true; Layer* _lastChangedLayer = nullptr; + + // Labels + GlobeLabelsComponent _globeLabelsComponent; + ghoul::Dictionary _labelsDictionary; }; } // namespace openspace::globebrowsing