From c7f77e3286b83f012f6b53fec3d639b37af5a9e4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Mar 2021 15:49:30 +0100 Subject: [PATCH 01/12] Add property to determine whether the date should be added to the screenshot folder (#1535) closes #1528 --- apps/OpenSpace/ext/sgct | 2 +- apps/OpenSpace/main.cpp | 30 +++++---------- include/openspace/rendering/renderengine.h | 1 + src/rendering/renderengine.cpp | 43 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/apps/OpenSpace/ext/sgct b/apps/OpenSpace/ext/sgct index eefd275cce..c651841a49 160000 --- a/apps/OpenSpace/ext/sgct +++ b/apps/OpenSpace/ext/sgct @@ -1 +1 @@ -Subproject commit eefd275ccec15c9316e4bc91d7232f3beea083a2 +Subproject commit c651841a49c468f2371713f23e90561f0c080d52 diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 6330167b4a..0851418cb3 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -218,6 +218,15 @@ void mainInitFunc(GLFWwindow*) { LTRACE("main::mainInitFunc(begin)"); + // + // Screenshots + // + // We save the startup value of the screenshots just in case we want to add a date + // to them later in the RenderEngine + std::string screenshotPath = absPath("${SCREENSHOTS}"); + FileSys.registerPathToken("${STARTUP_SCREENSHOT}", screenshotPath); + Settings::instance().setCapturePath(screenshotPath); + LDEBUG("Initializing OpenSpace Engine started"); global::openSpaceEngine->initialize(); LDEBUG("Initializing OpenSpace Engine finished"); @@ -321,27 +330,6 @@ void mainInitFunc(GLFWwindow*) { #endif // OPENSPACE_HAS_SPOUT } - - // - // Screenshots - // - std::string screenshotPath = "${SCREENSHOTS}"; - if (global::configuration->shouldUseScreenshotDate) { - std::time_t now = std::time(nullptr); - std::tm* nowTime = std::localtime(&now); - char mbstr[128]; - strftime(mbstr, sizeof(mbstr), "%Y-%m-%d-%H-%M", nowTime); - - FileSys.registerPathToken( - "${SCREENSHOTS}", - absPath(screenshotPath + '/' + std::string(mbstr)), - ghoul::filesystem::FileSystem::Override::Yes - ); - } - - Settings::instance().setCapturePath(absPath(screenshotPath)); - - LTRACE("main::mainInitFunc(end)"); } diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 000595f69e..f698af2248 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -201,6 +201,7 @@ private: properties::BoolProperty _showCameraInfo; properties::BoolProperty _applyWarping; + properties::BoolProperty _screenshotUseDate; properties::BoolProperty _showFrameInformation; properties::BoolProperty _disableMasterRendering; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 7130840e05..38b5c6c9f4 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -134,6 +134,13 @@ namespace { "interface." }; + constexpr openspace::properties::Property::PropertyInfo ScreenshotUseDateInfo = { + "ScreenshotUseDate", + "Screenshot Folder uses Date", + "If this value is set to 'true', screenshots will be saved to a folder that " + "contains the time at which this value was enabled" + }; + constexpr openspace::properties::Property::PropertyInfo ShowFrameNumberInfo = { "ShowFrameInformation", "Show Frame Information", @@ -255,6 +262,7 @@ RenderEngine::RenderEngine() , _showVersionInfo(ShowVersionInfo, true) , _showCameraInfo(ShowCameraInfo, true) , _applyWarping(ApplyWarpingInfo, false) + , _screenshotUseDate(ScreenshotUseDateInfo, false) , _showFrameInformation(ShowFrameNumberInfo, false) , _disableMasterRendering(DisableMasterInfo, false) , _globalBlackOutFactor(GlobalBlackoutFactorInfo, 1.f, 0.f, 1.f) @@ -348,6 +356,39 @@ RenderEngine::RenderEngine() addProperty(_globalBlackOutFactor); addProperty(_applyWarping); + _screenshotUseDate.onChange([this]() { + if (_screenshotUseDate) { + // Going from 'false' -> 'true' + // We might need to create the folder first + + std::time_t now = std::time(nullptr); + std::tm* nowTime = std::localtime(&now); + char date[128]; + strftime(date, sizeof(date), "%Y-%m-%d-%H-%M", nowTime); + + std::string newFolder = absPath("${STARTUP_SCREENSHOT}/" + std::string(date)); + if (!FileSys.directoryExists(newFolder)) { + FileSys.createDirectory(newFolder); + } + FileSys.registerPathToken( + "${SCREENSHOTS}", + newFolder, + ghoul::filesystem::FileSystem::Override::Yes + ); + } + else { + // Going from 'true' -> 'false' + // We reset the screenshot folder back to what it was in the beginning + FileSys.registerPathToken( + "${SCREENSHOTS}", + absPath("${STARTUP_SCREENSHOT}"), + ghoul::filesystem::FileSystem::Override::Yes + ); + } + global::windowDelegate->setScreenshotFolder(absPath("${SCREENSHOTS}")); + }); + addProperty(_screenshotUseDate); + _horizFieldOfView.onChange([this]() { if (global::windowDelegate->isMaster()) { global::windowDelegate->setHorizFieldOfView(_horizFieldOfView); @@ -447,6 +488,8 @@ void RenderEngine::initialize() { ); } } + + _screenshotUseDate = global::configuration->shouldUseScreenshotDate; } void RenderEngine::initializeGL() { From dfc42e94679a08669cff8dbb595168d13f83202b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Mar 2021 23:17:42 +0100 Subject: [PATCH 02/12] Rename createSingeColorImage -> createSingleColorImage --- ext/ghoul | 2 +- include/openspace/engine/openspaceengine.h | 2 +- src/engine/openspaceengine.cpp | 4 ++-- src/engine/openspaceengine_lua.inl | 6 +++--- tests/test_lua_createsinglecolorimage.cpp | 16 ++++++++-------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/ghoul b/ext/ghoul index 1625701baa..14896cdcf1 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 1625701baa17a568163d96a3532489d306d18e0e +Subproject commit 14896cdcf1e22f6635a9fa90eaf2fb1e59f266c3 diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 68843243ca..473641efb3 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -144,7 +144,7 @@ private: // Lua functions - exposed for testing namespace openspace::luascriptfunctions { -int createSingeColorImage(lua_State* L); +int createSingleColorImage(lua_State* L); } // openspace::luascriptfunctions diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 7c306ddc2d..2eca8e12f4 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -1609,8 +1609,8 @@ scripting::LuaLibrary OpenSpaceEngine::luaLibrary() { "Removes a tag (second argument) from a scene graph node (first argument)" }, { - "createSingeColorImage", - &luascriptfunctions::createSingeColorImage, + "createSingleColorImage", + &luascriptfunctions::createSingleColorImage, {}, "string, vec3", "Creates a 1 pixel image with a certain color in the cache folder and " diff --git a/src/engine/openspaceengine_lua.inl b/src/engine/openspaceengine_lua.inl index c52a1dae18..62a99f114e 100644 --- a/src/engine/openspaceengine_lua.inl +++ b/src/engine/openspaceengine_lua.inl @@ -294,11 +294,11 @@ int downloadFile(lua_State* L) { /** * \ingroup LuaScripts -* createSingeColorImage(): +* createSingleColorImage(): * Creates a one pixel image with a given color and returns the path to the cached file */ -int createSingeColorImage(lua_State* L) { - ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::createSingeColorImage"); +int createSingleColorImage(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 2, "lua::createSingleColorImage"); const std::string& name = ghoul::lua::value(L, 1); const ghoul::Dictionary& d = ghoul::lua::value(L, 2); diff --git a/tests/test_lua_createsinglecolorimage.cpp b/tests/test_lua_createsinglecolorimage.cpp index 612b7b6af9..88b98d68ab 100644 --- a/tests/test_lua_createsinglecolorimage.cpp +++ b/tests/test_lua_createsinglecolorimage.cpp @@ -38,7 +38,7 @@ TEST_CASE("CreateSingleColorImage: Create image and check return value", ghoul::lua::push(L, "colorFile"); ghoul::lua::push(L, std::vector{ 1.0, 0.0, 0.0 }); - int res = openspace::luascriptfunctions::createSingeColorImage(L); + int res = openspace::luascriptfunctions::createSingleColorImage(L); // One return value CHECK(res == 1); @@ -58,7 +58,7 @@ TEST_CASE("CreateSingleColorImage: Faulty 1st input type", "[createsinglecolorim ghoul::lua::push(L, std::vector{ 1.0, 0.0, 0.0 }); CHECK_THROWS_WITH( - openspace::luascriptfunctions::createSingeColorImage(L), + openspace::luascriptfunctions::createSingleColorImage(L), Catch::Matchers::Contains("parameter 1 was not the expected type") ); } @@ -69,7 +69,7 @@ TEST_CASE("CreateSingleColorImage: Faulty 2nd input type", "[createsinglecolorim ghoul::lua::push(L, "not a vector"); CHECK_THROWS_WITH( - openspace::luascriptfunctions::createSingeColorImage(L), + openspace::luascriptfunctions::createSingleColorImage(L), Catch::Matchers::Contains("parameter 2 was not the expected type") ); } @@ -80,7 +80,7 @@ TEST_CASE("CreateSingleColorImage: Invalid number of inputs", "[createsinglecolo ghoul::lua::push(L, std::vector{ 1.0, 0.0, 0.0 }); CHECK_THROWS_WITH( - openspace::luascriptfunctions::createSingeColorImage(L), + openspace::luascriptfunctions::createSingleColorImage(L), Catch::Matchers::Contains("Expected 2 arguments, got 1") ); } @@ -93,7 +93,7 @@ TEST_CASE("CreateSingleColorImage: Faulty color value (vec4)", ghoul::lua::push(L, std::vector{ 1.0, 0.0, 0.0, 0.0 }); CHECK_THROWS_WITH( - openspace::luascriptfunctions::createSingeColorImage(L), + openspace::luascriptfunctions::createSingleColorImage(L), Catch::Matchers::Contains( "Invalid color. Expected three double values {r, g, b} in range 0 to 1" ) @@ -108,7 +108,7 @@ TEST_CASE("CreateSingleColorImage: Faulty color value (invalid values)", ghoul::lua::push(L, std::vector{ 255.0, 0.0, 0.0 }); // not a valid color CHECK_THROWS_WITH( - openspace::luascriptfunctions::createSingeColorImage(L), + openspace::luascriptfunctions::createSingleColorImage(L), Catch::Matchers::Contains( "Invalid color. Expected three double values {r, g, b} in range 0 to 1" ) @@ -122,7 +122,7 @@ TEST_CASE("CreateSingleColorImage: Check if file was created", ghoul::lua::push(L, "colorFile2"); ghoul::lua::push(L, std::vector{ 0.0, 1.0, 0.0 }); - int res = openspace::luascriptfunctions::createSingeColorImage(L); + int res = openspace::luascriptfunctions::createSingleColorImage(L); CHECK(res == 1); std::string path = ghoul::lua::value(L, 1); @@ -135,7 +135,7 @@ TEST_CASE("CreateSingleColorImage: Load created image", "[createsinglecolorimage ghoul::lua::push(L, std::vector{ 1.0, 0.0, 0.0 }); // Loads the same file that was created in a previous test case - int res = openspace::luascriptfunctions::createSingeColorImage(L); + int res = openspace::luascriptfunctions::createSingleColorImage(L); CHECK(res == 1); CHECK(lua_gettop(L) == 1); From 2f36c7d88c47d35872abc7ad83a67f10b0badede Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Mon, 22 Mar 2021 23:40:47 +0100 Subject: [PATCH 03/12] Use codegen in LogFactory --- src/engine/logfactory.cpp | 259 +++++++++++++++----------------------- 1 file changed, 102 insertions(+), 157 deletions(-) diff --git a/src/engine/logfactory.cpp b/src/engine/logfactory.cpp index a1f65f8898..e07c794afa 100644 --- a/src/engine/logfactory.cpp +++ b/src/engine/logfactory.cpp @@ -32,186 +32,131 @@ #include #include #include +#include namespace { - constexpr const char* KeyType = "Type"; - constexpr const char* KeyFilename = "File"; - constexpr const char* KeyAppend = "Append"; - constexpr const char* KeyTimeStamping = "TimeStamping"; - constexpr const char* KeyDateStamping = "DateStamping"; - constexpr const char* KeyCategoryStamping = "CategoryStamping"; - constexpr const char* KeyLogLevelStamping = "LogLevelStamping"; - constexpr const char* KeyLogLevel = "LogLevel"; - - constexpr const char* ValueHtmlLog = "html"; - constexpr const char* ValueTextLog = "Text"; - constexpr const char* BootstrapPath = "${WEB}/common/bootstrap.min.css"; constexpr const char* CssPath = "${WEB}/log/style.css"; constexpr const char* JsPath = "${WEB}/log/script.js"; + + struct [[codegen::Dictionary(LogFactory)]] Parameters { + enum class Type { + Html [[codegen::key("html")]], + Text + }; + // The type of the new log to be generated + Type type; + + // The filename to which the log will be written + std::string file; + + // Determines whether the file will be cleared at startup or if the contents will + // be appended to previous runs + std::optional append; + + // Determines whether the log entires should be stamped with the time at which the + // message was logged + std::optional timeStamping; + + // Determines whether the log entries should be stamped with the date at which the + // message was logged + std::optional dateStamping; + + // Determines whether the log entries should be stamped with the category that + // creates the log message + std::optional categoryStamping; + + // Determines whether the log entries should be stamped with the log level that + // was used to create the log message + std::optional logLevelStamping; + + enum class LogLevel { + AllLogging, + Trace, + Debug, + Info, + Warning, + Error, + Fatal, + NoLogging + }; + // The log level for this specific text-based log + std::optional logLevel; + }; +#include "logfactory_codegen.cpp" } // namespace namespace openspace { documentation::Documentation LogFactoryDocumentation() { - using namespace documentation; - - return { - "LogFactory", - "core_logfactory", - { - { - KeyType, - new StringInListVerifier({ - // List from createLog - ValueTextLog, ValueHtmlLog - }), - Optional::No, - "The type of the new log to be generated." - }, - { - KeyFilename, - new StringVerifier, - Optional::No, - "The filename to which the log will be written." - }, - { - KeyAppend, - new BoolVerifier, - Optional::Yes, - "Determines whether the file will be cleared at startup or if the " - "contents will be appended to previous runs." - }, - { - KeyTimeStamping, - new BoolVerifier, - Optional::Yes, - "Determines whether the log entires should be stamped with the time at " - "which the message was logged." - }, - { - KeyDateStamping, - new BoolVerifier, - Optional::Yes, - "Determines whether the log entries should be stamped with the date at " - "which the message was logged." - }, - { - KeyCategoryStamping, - new BoolVerifier, - Optional::Yes, - "Determines whether the log entries should be stamped with the " - "category that creates the log message." - }, - { - KeyLogLevelStamping, - new BoolVerifier, - Optional::Yes, - "Determines whether the log entries should be stamped with the log level " - "that was used to create the log message." - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "core_logfactory"; + return doc; } std::unique_ptr createLog(const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - LogFactoryDocumentation(), - dictionary, - "LogFactory" - ); + const Parameters p = codegen::bake(dictionary); - // 'type' and 'filename' are required keys - const std::string& type = dictionary.value(KeyType); - const std::string& filename = absPath(dictionary.value(KeyFilename)); - - // the rest are optional - bool append = true; - if (dictionary.hasValue(KeyAppend)) { - append = dictionary.value(KeyAppend); - } - bool timeStamp = true; - if (dictionary.hasValue(KeyTimeStamping)) { - timeStamp = dictionary.value(KeyTimeStamping); - } - bool dateStamp = true; - if (dictionary.hasValue(KeyDateStamping)) { - dateStamp = dictionary.value(KeyDateStamping); - } - bool categoryStamp = true; - if (dictionary.hasValue(KeyCategoryStamping)) { - categoryStamp = dictionary.value(KeyCategoryStamping); - } - bool logLevelStamp = true; - if (dictionary.hasValue(KeyLogLevelStamping)) { - logLevelStamp = dictionary.value(KeyLogLevelStamping); - } - std::string logLevel; - if (dictionary.hasValue(KeyLogLevel)) { - logLevel = dictionary.value(KeyLogLevel); - } - - using Append = ghoul::logging::TextLog::Append; - using TimeStamping = ghoul::logging::Log::TimeStamping; - using DateStamping = ghoul::logging::Log::DateStamping; - using CategoryStamping = ghoul::logging::Log::CategoryStamping; - using LogLevelStamping = ghoul::logging::Log::LogLevelStamping; - - if (type == ValueHtmlLog) { - std::vector cssFiles{absPath(BootstrapPath), absPath(CssPath)}; - std::vector jsFiles{absPath(JsPath)}; - - if (logLevel.empty()) { - return std::make_unique( - filename, - Append(append), - TimeStamping(timeStamp), - DateStamping(dateStamp), - CategoryStamping(categoryStamp), - LogLevelStamping(logLevelStamp), - cssFiles, - jsFiles - ); + std::string filename = absPath(p.file); + bool append = p.append.value_or(true); + bool timeStamp = p.timeStamping.value_or(true); + bool dateStamp = p.dateStamping.value_or(true); + bool categoryStamp = p.categoryStamping.value_or(true); + bool logLevelStamp = p.logLevelStamping.value_or(true); + Parameters::LogLevel logLevel = p.logLevel.value_or(Parameters::LogLevel::AllLogging); + ghoul::logging::LogLevel level = [](Parameters::LogLevel l) { + switch (l) { + case Parameters::LogLevel::AllLogging: + return ghoul::logging::LogLevel::AllLogging; + case Parameters::LogLevel::Trace: + return ghoul::logging::LogLevel::Trace; + case Parameters::LogLevel::Debug: + return ghoul::logging::LogLevel::Debug; + case Parameters::LogLevel::Info: + return ghoul::logging::LogLevel::Info; + case Parameters::LogLevel::Warning: + return ghoul::logging::LogLevel::Warning; + case Parameters::LogLevel::Error: + return ghoul::logging::LogLevel::Error; + case Parameters::LogLevel::Fatal: + return ghoul::logging::LogLevel::Fatal; + case Parameters::LogLevel::NoLogging: + return ghoul::logging::LogLevel::NoLogging; + default: + throw ghoul::MissingCaseException(); } - else { + }(p.logLevel.value_or(Parameters::LogLevel::AllLogging)); + + switch (p.type) { + case Parameters::Type::Html: + { + std::vector cssFiles{ absPath(BootstrapPath), absPath(CssPath) }; + std::vector jsFiles{ absPath(JsPath) }; + return std::make_unique( filename, - Append(append), - TimeStamping(timeStamp), - DateStamping(dateStamp), - CategoryStamping(categoryStamp), - LogLevelStamping(logLevelStamp), + ghoul::logging::TextLog::Append(append), + ghoul::logging::Log::TimeStamping(timeStamp), + ghoul::logging::Log::DateStamping(dateStamp), + ghoul::logging::Log::CategoryStamping(categoryStamp), + ghoul::logging::Log::LogLevelStamping(logLevelStamp), cssFiles, jsFiles, - ghoul::from_string(logLevel) - ); + level + ); } - } - else if (type == ValueTextLog) { - if (logLevel.empty()) { + case Parameters::Type::Text: return std::make_unique( filename, - Append(append), - TimeStamping(timeStamp), - DateStamping(dateStamp), - CategoryStamping(categoryStamp), - LogLevelStamping(logLevelStamp) + ghoul::logging::TextLog::Append(append), + ghoul::logging::Log::TimeStamping(timeStamp), + ghoul::logging::Log::DateStamping(dateStamp), + ghoul::logging::Log::CategoryStamping(categoryStamp), + ghoul::logging::Log::LogLevelStamping(logLevelStamp), + level ); - } - else { - return std::make_unique( - filename, - Append(append), - TimeStamping(timeStamp), - DateStamping(dateStamp), - CategoryStamping(categoryStamp), - LogLevelStamping(logLevelStamp), - ghoul::from_string(logLevel) - ); - } - } - else { - throw ghoul::MissingCaseException(); + default: + throw new ghoul::MissingCaseException(); } } From 45b2d0e0144721a38cc569b2d0794e6e7dab87bc Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 23 Mar 2021 00:08:35 +0100 Subject: [PATCH 04/12] Update Ghoul with an update to AssImp to fix FBX loading bug --- ext/ghoul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ghoul b/ext/ghoul index 14896cdcf1..1ce3299b7e 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 14896cdcf1e22f6635a9fa90eaf2fb1e59f266c3 +Subproject commit 1ce3299b7e5e835e52d1758406727d02b080916b From 196a98b0c6967591f1119c110fb15d7f962a7f4c Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 23 Mar 2021 00:09:02 +0100 Subject: [PATCH 05/12] Add ability to StringVerifier to test for non-emptiness --- include/openspace/documentation/verifier.h | 10 ++++++++ src/documentation/verifier.cpp | 27 ++++++++++++++++++++++ src/engine/logfactory.cpp | 1 - 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/openspace/documentation/verifier.h b/include/openspace/documentation/verifier.h index 47f61427b2..7cde68a2aa 100644 --- a/include/openspace/documentation/verifier.h +++ b/include/openspace/documentation/verifier.h @@ -156,7 +156,17 @@ struct IntVerifier : public TemplateVerifier { * std::string. No implicit conversion is considered in this testing. */ struct StringVerifier : public TemplateVerifier { + StringVerifier(bool mustBeNotEmpty = false); + + TestResult operator()(const ghoul::Dictionary& dictionary, + const std::string& key) const override; + std::string type() const override; + + bool mustBeNotEmpty() const; + +private: + bool _mustBeNotEmpty = false; }; /** diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index 9de0012f2c..ab6377f91f 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -174,6 +174,33 @@ std::string IntVerifier::type() const { return "Integer"; } +StringVerifier::StringVerifier(bool mustBeNotEmpty) + : TemplateVerifier() + , _mustBeNotEmpty(mustBeNotEmpty) +{} + +TestResult StringVerifier::operator()(const ghoul::Dictionary& dictionary, + const std::string& key) const +{ + TestResult res = TemplateVerifier::operator()(dictionary, key); + if (!res.success) { + return res; + } + + std::string value = dictionary.value(key); + if (value.empty() && _mustBeNotEmpty) { + res.success = false; + res.offenses.push_back({ + key, TestResult::Offense::Reason::Verification, "value must not be empty" + }); + } + return res; +} + +bool StringVerifier::mustBeNotEmpty() const { + return _mustBeNotEmpty; +} + std::string StringVerifier::type() const { return "String"; } diff --git a/src/engine/logfactory.cpp b/src/engine/logfactory.cpp index e07c794afa..90633d26e6 100644 --- a/src/engine/logfactory.cpp +++ b/src/engine/logfactory.cpp @@ -103,7 +103,6 @@ std::unique_ptr createLog(const ghoul::Dictionary& dictiona bool dateStamp = p.dateStamping.value_or(true); bool categoryStamp = p.categoryStamping.value_or(true); bool logLevelStamp = p.logLevelStamping.value_or(true); - Parameters::LogLevel logLevel = p.logLevel.value_or(Parameters::LogLevel::AllLogging); ghoul::logging::LogLevel level = [](Parameters::LogLevel l) { switch (l) { case Parameters::LogLevel::AllLogging: From 97c10661490a42692b7e2eaec098d8f466288dbe Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 23 Mar 2021 11:27:55 +0100 Subject: [PATCH 06/12] Fix labels not facing the camera due to static rotation (closes #1542) Bring back old transformation matrices --- data/assets/scene/digitaluniverse/grids.asset | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/data/assets/scene/digitaluniverse/grids.asset b/data/assets/scene/digitaluniverse/grids.asset index 16f4afd20b..55dc314238 100644 --- a/data/assets/scene/digitaluniverse/grids.asset +++ b/data/assets/scene/digitaluniverse/grids.asset @@ -104,12 +104,6 @@ local ecliptic = { local eclipticLabels = { Identifier = "EclipticSphereLabels", Parent = transforms.SolarSystemBarycenter.Name, - Transform = { - Rotation = { - Type = "StaticRotation", - Rotation = eclipticRotationMatrix - } - }, Renderable = { Type = "RenderableBillboardsCloud", Enabled = false, @@ -122,6 +116,12 @@ local eclipticLabels = { TextMinSize = 1.3, TextMaxSize = 50.0, Unit = "pc", + TransformationMatrix = { + -0.05487554, 0.4941095, -0.8676661, 0.0, + -0.9938214 , -0.1109906, -0.0003515167, 0.0, + -0.09647644, 0.8622859, 0.4971472, 0.0, + 0.0, 0.0, 0.0, 1.0 + } }, GUI = { Name = "Ecliptic Sphere Labels", @@ -158,12 +158,6 @@ local equatorial = { local equatorialLabels = { Identifier = "EquatorialSphereLabels", Parent = transforms.SolarSystemBarycenter.Name, - Transform = { - Rotation = { - Type = "StaticRotation", - Rotation = equatorialRotationMatrix - } - }, Renderable = { Type = "RenderableBillboardsCloud", Enabled = false, @@ -176,6 +170,12 @@ local equatorialLabels = { TextMinSize = 1.7, TextMaxSize = 70.0, Unit = "pc", + TransformationMatrix = { + -0.05487554, 0.4941095, -0.8676661, 0.0, + -0.8734371 , -0.4448296, -0.1980764, 0.0, + -0.483835 , 0.7469823, 0.4559838, 0.0, + 0.0 , 0.0 , 0.0 , 1.0 + } }, GUI = { Name = "Equatorial Sphere Labels", From 0ea0e781da1e9a062c5ca39016e7d4cde2d433ce Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 23 Mar 2021 11:40:34 +0100 Subject: [PATCH 07/12] Prevent crash when reloading renderable that only has labels and no data Occurred when unchecking and rechecking the "Draw elements" property for example the "Eclipltic Sphere Labels" --- .../digitaluniverse/rendering/renderablebillboardscloud.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp index d49e7682f8..6bc06860c8 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp @@ -1492,6 +1492,11 @@ void RenderableBillboardsCloud::createDataSlice() { ZoneScoped _slicedData.clear(); + + if (_fullData.empty() || _nValuesPerAstronomicalObject == 0) { + return; + } + if (_hasColorMapFile) { _slicedData.reserve(8 * (_fullData.size() / _nValuesPerAstronomicalObject)); } From 48e2f35aa1647ebf55515af713fdae9cb2e75eaf Mon Sep 17 00:00:00 2001 From: Micah Acinapura Date: Tue, 23 Mar 2021 08:56:23 -0400 Subject: [PATCH 08/12] added dialog for choosing scripts from the script log (#1539) * added dialog for choosing scripts from the script log --- apps/OpenSpace/ext/launcher/CMakeLists.txt | 3 + .../include/profile/keybindingsdialog.h | 9 ++ .../include/profile/scriptlogdialog.h | 53 ++++++++++ .../src/profile/keybindingsdialog.cpp | 20 ++++ .../launcher/src/profile/scriptlogdialog.cpp | 99 +++++++++++++++++++ 5 files changed, 184 insertions(+) create mode 100644 apps/OpenSpace/ext/launcher/include/profile/scriptlogdialog.h create mode 100644 apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp diff --git a/apps/OpenSpace/ext/launcher/CMakeLists.txt b/apps/OpenSpace/ext/launcher/CMakeLists.txt index 460f486412..f8cbd26db7 100644 --- a/apps/OpenSpace/ext/launcher/CMakeLists.txt +++ b/apps/OpenSpace/ext/launcher/CMakeLists.txt @@ -34,6 +34,7 @@ set(HEADER_FILES include/profile/cameradialog.h include/profile/deltatimesdialog.h include/profile/keybindingsdialog.h + include/profile/scriptlogdialog.h include/profile/line.h include/profile/marknodesdialog.h include/profile/metadialog.h @@ -53,6 +54,7 @@ set(SOURCE_FILES src/profile/cameradialog.cpp src/profile/deltatimesdialog.cpp src/profile/keybindingsdialog.cpp + src/profile/scriptlogdialog.cpp src/profile/line.cpp src/profile/marknodesdialog.cpp src/profile/metadialog.cpp @@ -74,6 +76,7 @@ qt5_wrap_cpp( include/profile/cameradialog.h include/profile/deltatimesdialog.h include/profile/keybindingsdialog.h + include/profile/scriptlogdialog.h include/profile/marknodesdialog.h include/profile/metadialog.h include/profile/modulesdialog.h diff --git a/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h index f242f19e42..488b560155 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h @@ -58,6 +58,13 @@ public: */ virtual void keyPressEvent(QKeyEvent* evt) override; + /** + * Adds scripts to the _scriptEdit from outside dialogs + * + * \param scripts #std::string scripts to be appended + */ + void appendScriptsToKeybind(const std::string& scripts); + private slots: void listItemSelected(); void listItemAdded(); @@ -66,6 +73,7 @@ private slots: void listItemCancelSave(); void transitionToEditMode(); void parseSelections(); + void chooseScripts(); void keySelected(int index); private: @@ -99,6 +107,7 @@ private: QPushButton* _addButton = nullptr; QPushButton* _removeButton = nullptr; + QPushButton* _chooseScriptsButton = nullptr; QPushButton* _saveButton = nullptr; QPushButton* _cancelButton = nullptr; QDialogButtonBox* _buttonBox = nullptr; diff --git a/apps/OpenSpace/ext/launcher/include/profile/scriptlogdialog.h b/apps/OpenSpace/ext/launcher/include/profile/scriptlogdialog.h new file mode 100644 index 0000000000..49de2deee8 --- /dev/null +++ b/apps/OpenSpace/ext/launcher/include/profile/scriptlogdialog.h @@ -0,0 +1,53 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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_UI_LAUNCHER___SCRIPTLOG___H__ +#define __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__ + +#include "profile/keybindingsdialog.h" +#include +#include + +class ScriptlogDialog : public QDialog { +Q_OBJECT +public: + /** + * Constructor for ScriptlogDialog class + * + * \param bindingDialog keybindingDialog that openend this window. + * \param parent Pointer to parent Qt widget + */ + ScriptlogDialog(KeybindingsDialog* bindingDialog, QWidget* parent); + +private slots: + void saveChosenScripts(); + +private: + void createWidgets(); + + KeybindingsDialog* _bindingDialog = nullptr; + QListWidget* _scriptlogList = nullptr; +}; + +#endif // __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__ diff --git a/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp index 8ea5dcf272..dc373a6a01 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp @@ -25,6 +25,8 @@ #include "profile/keybindingsdialog.h" #include "profile/line.h" +#include "profile/scriptlogdialog.h" + #include #include #include @@ -106,6 +108,10 @@ KeybindingsDialog::KeybindingsDialog(Profile& profile, QWidget *parent) transitionFromEditMode(); } +void KeybindingsDialog::appendScriptsToKeybind(const std::string& scripts) { + _scriptEdit->append(QString::fromStdString(scripts)); +} + void KeybindingsDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); { @@ -220,6 +226,14 @@ void KeybindingsDialog::createWidgets() { _scriptLabel = new QLabel("Script"); box->addWidget(_scriptLabel, 6, 0, 1, 2); + + _chooseScriptsButton = new QPushButton("Choose Scripts"); + connect( + _chooseScriptsButton, &QPushButton::clicked, + this, &KeybindingsDialog::chooseScripts + ); + box->addWidget(_chooseScriptsButton, 6, 1, 1, 1); + _scriptEdit = new QTextEdit; _scriptEdit->setAcceptRichText(false); _scriptEdit->setToolTip("Command(s) to execute at keypress event"); @@ -497,6 +511,7 @@ void KeybindingsDialog::editBoxDisabled(bool disabled) { _scriptEdit->setDisabled(disabled); _cancelButton->setDisabled(disabled); _saveButton->setDisabled(disabled); + _chooseScriptsButton->setDisabled(disabled); } void KeybindingsDialog::parseSelections() { @@ -508,6 +523,11 @@ void KeybindingsDialog::parseSelections() { accept(); } +void KeybindingsDialog::chooseScripts() { + _errorMsg->clear(); + ScriptlogDialog(this, this).exec(); +} + void KeybindingsDialog::keyPressEvent(QKeyEvent* evt) { if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) { return; diff --git a/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp new file mode 100644 index 0000000000..dab9689dd8 --- /dev/null +++ b/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp @@ -0,0 +1,99 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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 "profile/scriptlogdialog.h" +#include "profile/line.h" +#include +#include +#include +#include +#include +#include +#include +#include + +ScriptlogDialog::ScriptlogDialog(KeybindingsDialog* bindingDialog, + QWidget* parent) + : QDialog(parent) + , _bindingDialog(bindingDialog) +{ + setWindowTitle("Scriptlog"); + createWidgets(); + + QFile file(QString::fromStdString(absPath("${LOGS}/scriptLog.txt"))); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine(); + // removing return from a few statments + // these are usually generated by gui panels + line.remove(QRegularExpression("^return ")); + if (!line.isEmpty()) { + _scriptlogList->addItem(line); + } + } + } +} + +void ScriptlogDialog::createWidgets() { + QBoxLayout* layout = new QVBoxLayout(this); + { + QLabel* heading = new QLabel("Choose commands from log/scriptLog.txt"); + heading->setObjectName("heading"); + layout->addWidget(heading); + } + + _scriptlogList = new QListWidget; + _scriptlogList->setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection); + layout->addWidget(_scriptlogList); + + layout->addWidget(new Line); + { + QDialogButtonBox* buttons = new QDialogButtonBox; + buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); + connect( + buttons, &QDialogButtonBox::accepted, + this, &ScriptlogDialog::saveChosenScripts + ); + connect( + buttons, &QDialogButtonBox::rejected, + this, &ScriptlogDialog::reject + ); + layout->addWidget(buttons); + } +} + +void ScriptlogDialog::saveChosenScripts() { + std::string chosenScripts; + QList itemList = _scriptlogList->selectedItems(); + for (int i = 0; i < itemList.size(); ++i) { + chosenScripts += itemList.at(i)->text().toStdString(); + if (i < itemList.size()) { + chosenScripts += "\n"; + } + } + _bindingDialog->appendScriptsToKeybind(chosenScripts); + + accept(); +} From 0d82e1848f671c826202f72cd41d5560e4910a67 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 23 Mar 2021 15:18:55 +0100 Subject: [PATCH 09/12] Feature/configuration codegen (#1544) Convert configuration document over to codegen --- apps/OpenSpace/main.cpp | 19 +- include/openspace/engine/configuration.h | 10 +- src/CMakeLists.txt | 1 - src/engine/configuration.cpp | 820 +++++++++++++++-------- src/engine/configuration_doc.inl | 450 ------------- tests/CMakeLists.txt | 1 + tests/main.cpp | 15 +- tests/test_configuration.cpp | 640 ++++++++++++++++++ 8 files changed, 1205 insertions(+), 751 deletions(-) delete mode 100644 src/engine/configuration_doc.inl create mode 100644 tests/test_configuration.cpp diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 0851418cb3..9adc728a07 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -1105,22 +1105,17 @@ int main(int argc, char* argv[]) { } LINFO(fmt::format("Configuration Path: '{}'", configurationFilePath)); + // Register the base path as the directory where the configuration file lives + std::string base = ghoul::filesystem::File(configurationFilePath).directoryName(); + constexpr const char* BasePathToken = "${BASE}"; + FileSys.registerPathToken(BasePathToken, base); + // Loading configuration from disk LDEBUG("Loading configuration from disk"); *global::configuration = configuration::loadConfigurationFromFile( - configurationFilePath + configurationFilePath, + commandlineArguments.configurationOverride ); - // If the user requested a commandline-based configuration script that should - // overwrite some of the values, this is the time to do it - if (!commandlineArguments.configurationOverride.empty()) { - LDEBUG("Executing Lua script passed through the commandline:"); - LDEBUG(commandlineArguments.configurationOverride); - ghoul::lua::runScript( - global::configuration->state, - commandlineArguments.configurationOverride - ); - parseLuaState(*global::configuration); - } // Determining SGCT configuration file LDEBUG("SGCT Configuration file: " + global::configuration->windowConfiguration); diff --git a/include/openspace/engine/configuration.h b/include/openspace/engine/configuration.h index 5ba443cf7e..98fbb8518c 100644 --- a/include/openspace/engine/configuration.h +++ b/include/openspace/engine/configuration.h @@ -43,7 +43,6 @@ struct Configuration { Configuration& operator=(Configuration&&) = default; std::string windowConfiguration = "${CONFIG}/single.xml"; - std::string sgctConfigNameInitialized; std::string asset; std::string profile; std::vector readOnlyProfiles; @@ -93,7 +92,6 @@ struct Configuration { glm::dvec3 screenSpaceRotation = glm::dvec3(0.0); glm::dvec3 masterRotation = glm::dvec3(0.0); bool isConsoleDisabled = false; - bool usingProfile = false; bool bypassLauncher = false; std::map moduleConfigurations; @@ -123,6 +121,9 @@ struct Configuration { }; HTTPProxy httpProxy; + // Values not read from the openspace.cfg file + bool usingProfile = false; + std::string sgctConfigNameInitialized; static documentation::Documentation Documentation; ghoul::lua::LuaState state; @@ -130,9 +131,8 @@ struct Configuration { std::string findConfiguration(const std::string& filename = "openspace.cfg"); -Configuration loadConfigurationFromFile(const std::string& filename); - -void parseLuaState(Configuration& configuration); +Configuration loadConfigurationFromFile(const std::string& filename, + const std::string& overrideScript); } // namespace openspace::configuration diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6f62fad495..c68ddecfc6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,6 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/documentation/documentationgenerator.cpp ${OPENSPACE_BASE_DIR}/src/documentation/verifier.cpp ${OPENSPACE_BASE_DIR}/src/engine/configuration.cpp - ${OPENSPACE_BASE_DIR}/src/engine/configuration_doc.inl ${OPENSPACE_BASE_DIR}/src/engine/downloadmanager.cpp ${OPENSPACE_BASE_DIR}/src/engine/globals.cpp ${OPENSPACE_BASE_DIR}/src/engine/globalscallbacks.cpp diff --git a/src/engine/configuration.cpp b/src/engine/configuration.cpp index 35727a3790..ee4b6b7f07 100644 --- a/src/engine/configuration.cpp +++ b/src/engine/configuration.cpp @@ -27,287 +27,304 @@ #include #include #include +#include #include #include #include +#include namespace { - constexpr const char* BasePathToken = "${BASE}"; // We can't use ${SCRIPTS} here as that hasn't been defined by this point constexpr const char* InitialConfigHelper = "${BASE}/scripts/configuration_helper.lua"; - // Variable names for the openspace.cfg file - // These are also used in the _doc include file - constexpr const char* KeySGCTConfig = "SGCTConfig"; - constexpr const char* KeyAsset = "Asset"; - constexpr const char* KeyProfile = "Profile"; - constexpr const char* KeyGlobalCustomizationScripts = "GlobalCustomizationScripts"; - constexpr const char* KeyPaths = "Paths"; - constexpr const char* KeyFonts = "Fonts"; - constexpr const char* KeyLogging = "Logging"; - constexpr const char* KeyLogDir = "LogDir"; - constexpr const char* KeyPerformancePrefix = "PerformancePrefix"; - constexpr const char* KeyLogLevel = "LogLevel"; - constexpr const char* KeyImmediateFlush = "ImmediateFlush"; - constexpr const char* KeyLogs = "Logs"; - constexpr const char* KeyCapabilitiesVerbosity = "CapabilitiesVerbosity"; - constexpr const char* KeyDocumentationPath = "Path"; - constexpr const char* KeyDocumentation = "Documentation"; - constexpr const char* KeyScriptLog = "ScriptLog"; - constexpr const char* KeyShutdownCountdown = "ShutdownCountdown"; - constexpr const char* KeyPerSceneCache = "PerSceneCache"; - constexpr const char* KeyOnScreenTextScaling = "OnScreenTextScaling"; - constexpr const char* KeyRenderingMethod = "RenderingMethod"; - constexpr const char* KeyDisableRenderingOnMaster = "DisableRenderingOnMaster"; - constexpr const char* KeyGlobalRotation = "GlobalRotation"; - constexpr const char* KeyScreenSpaceRotation = "ScreenSpaceRotation"; - constexpr const char* KeyMasterRotation = "MasterRotation"; - constexpr const char* KeyDisableInGameConsole = "DisableInGameConsole"; - constexpr const char* KeyScreenshotUseDate = "ScreenshotUseDate"; - constexpr const char* KeyHttpProxy = "HttpProxy"; - constexpr const char* KeyAddress = "Address"; - constexpr const char* KeyPort = "Port"; - constexpr const char* KeyAuthentication = "Authentication"; - constexpr const char* KeyUser = "User"; - constexpr const char* KeyPassword = "Password"; - constexpr const char* KeyOpenGLDebugContext = "OpenGLDebugContext"; - constexpr const char* KeyActivate = "Activate"; - constexpr const char* KeySynchronous = "Synchronous"; - constexpr const char* KeyFilterIdentifier = "FilterIdentifier"; - constexpr const char* KeyIdentifier = "Identifier"; - constexpr const char* KeySource = "Source"; - constexpr const char* KeyType = "Type"; - constexpr const char* KeyFilterSeverity = "FilterSeverity"; - constexpr const char* KeyCheckOpenGLState = "CheckOpenGLState"; - constexpr const char* KeyLogEachOpenGLCall = "LogEachOpenGLCall"; - constexpr const char* KeyVersionCheckUrl = "VersionCheckUrl"; - constexpr const char* KeyUseMultithreadedInitialization = - "UseMultithreadedInitialization"; - constexpr const char* KeyLoadingScreen = "LoadingScreen"; - constexpr const char* KeyShowMessage = "ShowMessage"; - constexpr const char* KeyShowNodeNames = "ShowNodeNames"; - constexpr const char* KeyShowProgressbar = "ShowProgressbar"; - constexpr const char* KeyModuleConfigurations = "ModuleConfigurations"; + struct [[codegen::Dictionary(Configuration)]] Parameters { + // The SGCT configuration file that determines the window and view frustum + // settings that are being used when OpenSpace is started + std::optional windowConfiguration [[codegen::key("SGCTConfig")]]; - constexpr const char* KeySgctConfigNameInitialized = "sgctconfiginitializeString"; - constexpr const char* KeyReadOnlyProfiles = "ReadOnlyProfiles"; - constexpr const char* KeyBypassLauncher = "BypassLauncher"; + // The scene description that is used to populate the application after startup. + // The scene determines which objects are loaded, the startup time and other + // scene-specific settings. More information is provided in the Scene + // documentation. If the 'Asset' and the 'Profile' values are specified, the asset + // is silently ignored + std::optional asset; - template - void getValue(ghoul::lua::LuaState& L, const char* name, T& value) { - using namespace openspace::configuration; + // The profile that should be loaded at the startup. The profile determines which + // assets are loaded, the startup time, keyboard shortcuts, and other settings. + std::optional profile; - auto it = std::find_if( - Configuration::Documentation.entries.begin(), - Configuration::Documentation.entries.end(), - [name](const openspace::documentation::DocumentationEntry& e) { - return e.key == name; - } - ); + // This value names a list of scripts that get executed after initialization of + // any scene. These scripts can be used for user-specific customization, such as a + // global rebinding of keys from the default + std::optional> globalCustomizationScripts; - bool isOptional = - it != Configuration::Documentation.entries.end() - ? it->optional : - true; + // A list of paths that are automatically registered with the file system. If a + // key X is used in the table, it is then useable by referencing ${X} in all other + // configuration files or scripts + std::map paths; - lua_getglobal(L, name); - if (isOptional && lua_isnil(L, -1)) { - return; - } + // A list of all fonts that will automatically be loaded on startup. Each + // key-value pair contained in the table will become the name and the file for a + // font + std::optional> fonts; - if (!isOptional && lua_isnil(L, -1)) { - openspace::documentation::TestResult testResult = { - false, - { { - name, - openspace::documentation::TestResult::Offense::Reason::MissingKey - }}, - {} + struct Logging { + // List from logmanager.cpp::levelFromString + enum class Level { + Trace, + Debug, + Info, + Warning, + Error, + Fatal, + None }; - throw openspace::documentation::SpecificationError( - std::move(testResult), - "Configuration" - ); - } + // The severity of log messages that will be displayed. Only messages of the + // selected level or higher will be displayed. All levels below will be + // silently discarded. The order of severities is: + // Debug < Info < Warning < Error < Fatal < None. + std::optional logLevel; - if constexpr (std::is_same_v) { - ghoul::Dictionary d = ghoul::lua::value(L); - glm::dvec3 res; - res.x = d.value("1"); - res.y = d.value("2"); - res.z = d.value("3"); - value = res; - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v>) { - ghoul::Dictionary d = ghoul::lua::value(L); + // Determines whether error messages will be displayed immediately or if it is + // acceptable to have a short delay, but being more performant. If the delay + // is allowed ('true'), messages might get lost if the application crashes + // shortly after a message was logged + std::optional immediateFlush; - std::vector res; - for (size_t i = 1; i <= d.size(); ++i) { - res.push_back(d.value(std::to_string(i))); - } - value = std::move(res); - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v>) { - ghoul::Dictionary d = ghoul::lua::value(L); + // Per default, log messages are written to the console, the onscreen text, + // and (if available) the Visual Studio output window. This table can define + // other logging methods that will be used additionally + std::optional> logs + [[codegen::reference("core_logfactory")]]; - std::map res; - std::vector keys = d.keys(); - for (size_t i = 0; i < d.size(); ++i) { - std::string_view key = keys[i]; - std::string v = d.value(key); - res[std::string(key)] = std::move(v); - } - value = std::move(res); - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v>) { - ghoul::Dictionary d = ghoul::lua::value(L); + // List from OpenspaceEngine::initialize + enum class Verbosity { + None, + Minimal, + Default, + Full + }; + // At startup, a list of system capabilities is created and logged. This value + // determines how verbose this listing should be + std::optional capabilitiesVerbosity; + }; + // Configurations for the logging of messages that are generated throughout the + // code and are useful for debugging potential errors or other information + std::optional logging; - std::map res; - std::vector keys = d.keys(); - for (size_t i = 0; i < d.size(); ++i) { - std::string_view key = keys[i]; - ghoul::Dictionary v = d.value(key); - res[std::string(key)] = std::move(v); - } - value = std::move(res); - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v) { - Configuration::Logging& v = static_cast(value); - ghoul::Dictionary d = ghoul::lua::value(L); + // The file that will be created on startup containing the log of all Lua scripts + // that are executed in the last session. Any existing file (including the results + // from previous runs) will be silently overwritten + std::optional scriptLog; - if (d.hasValue(KeyLogLevel)) { - v.level = d.value(KeyLogLevel); - } - if (d.hasValue(KeyImmediateFlush)) { - v.forceImmediateFlush = d.value(KeyImmediateFlush); - } - if (d.hasValue(KeyCapabilitiesVerbosity)) { - v.capabilitiesVerbosity = d.value(KeyCapabilitiesVerbosity); - } + struct Documentation { + // The path where the documentation files will be stored + std::optional path; + }; + // Right now only contains the path where the documentation is written to + std::optional documentation; - if (d.hasValue(KeyLogs)) { - ghoul::Dictionary l = d.value(KeyLogs); - std::vector res; - for (size_t i = 1; i <= l.size(); ++i) { - res.push_back(l.value(std::to_string(i))); - } - v.logs = res; - } - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v) { - Configuration::DocumentationInfo& v = - static_cast(value); - ghoul::Dictionary d = ghoul::lua::value(L); - if (d.hasValue(KeyDocumentationPath)) { - v.path = d.value(KeyDocumentationPath); - } - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v) { - Configuration::LoadingScreen& v = - static_cast(value); - ghoul::Dictionary d = ghoul::lua::value(L); - if (d.hasValue(KeyShowMessage)) { - v.isShowingMessages = d.value(KeyShowMessage); - } - if (d.hasValue(KeyShowNodeNames)) { - v.isShowingNodeNames = d.value(KeyShowNodeNames); - } - if (d.hasValue(KeyShowProgressbar)) { - v.isShowingProgressbar = d.value(KeyShowProgressbar); - } - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v) { - Configuration::OpenGLDebugContext& v = - static_cast(value); - ghoul::Dictionary d = ghoul::lua::value(L); + // The countdown that the application will wait between pressing ESC and actually + // shutting down. If ESC is pressed again in this time, the shutdown is aborted + std::optional shutdownCountdown [[codegen::greater(0.0)]]; - if (d.hasValue(KeyActivate)) { - v.isActive = d.value(KeyActivate); - } - if (d.hasValue(KeySynchronous)) { - v.isSynchronous = d.value(KeySynchronous); - } + // If this is set to 'true', the name of the scene will be appended to the cache + // directory, thus not reusing the same directory. This is useful in cases where + // the same instance of OpenSpace is run with multiple scenes, but the caches + // should be retained. This value defaults to 'false' + std::optional perSceneCache; - if (d.hasValue(KeyFilterIdentifier)) { - ghoul::Dictionary f = d.value(KeyFilterIdentifier); + enum class Scaling { + Window [[codegen::key("window")]], + Framebuffer [[codegen::key("framebuffer")]] + }; + // The method for scaling the onscreen text in the window. As the resolution of + // the rendering can be different from the size of the window, the onscreen text + // can either be scaled according to the window size ('window'), or the rendering + // resolution ('framebuffer'). This value defaults to 'window' + std::optional onScreenTextScaling; - std::vector res; - for (size_t i = 1; i <= f.size(); ++i) { - Configuration::OpenGLDebugContext::IdentifierFilter filter; - ghoul::Dictionary fi = f.value(std::to_string(i)); + // List from RenderEngine::setRendererFromString + enum class RenderingMethod { + Framebuffer, + ABuffer + }; + // The renderer that is use after startup. The renderer 'ABuffer' requires support + // for at least OpenGL 4.3 + std::optional renderingMethod; - if (fi.hasValue(KeyIdentifier)) { - filter.identifier = static_cast( - fi.value(KeyIdentifier) - ); - } - if (fi.hasValue(KeySource)) { - filter.source = fi.value(KeySource); - } - if (fi.hasValue(KeyType)) { - filter.type = fi.value(KeyType); - } + // Toggles whether the master in a multi-application setup should be rendering or + // just managing the state of the network. This is desired in cases where the + // master computer does not have the resources to render a scene + std::optional disableRenderingOnMaster; - auto fff = filter; - const auto ffg = filter; + // Applies a global view rotation. Use this to rotate the position of the focus + // node away from the default location on the screen. This setting persists even + // when a new focus node is selected. Defined using roll, pitch, yaw in radians + std::optional globalRotation; - res.push_back(std::move(filter)); - } + // Applies a view rotation for only the master node, defined using roll, pitch yaw + // in radians. This can be used to compensate the master view direction for tilted + // display systems in clustered immersive environments + std::optional masterRotation; - v.identifierFilters = res; - } + // Applies a global rotation for all screenspace renderables. Defined using roll, + // pitch, yaw in radians + std::optional screenSpaceRotation; - if (d.hasValue(KeyFilterSeverity)) { - ghoul::Dictionary f = d.value(KeyFilterSeverity); + // If this value is set to 'true' the ingame console is disabled, locking the + // system down against random access + std::optional disableInGameConsole; - std::vector res; - for (size_t i = 1; i <= f.size(); ++i) { - res.push_back(f.value(std::to_string(i))); - } - v.severityFilters = res; - } - } - // NOLINTNEXTLINE - else if constexpr (std::is_same_v) { - Configuration::HTTPProxy& v = static_cast(value); - ghoul::Dictionary d = ghoul::lua::value(L); + // Toggles whether screenshots generated by OpenSpace contain the date when the + // concrete OpenSpace instance was started. This value is enabled by default, but + // it is advised to disable this value if rendering sessions of individual frames + // pass beyond local midnight + std::optional screenshotUseDate; - if (d.hasValue(KeyActivate)) { - v.usingHttpProxy = d.value(KeyActivate); - } - if (d.hasValue(KeyAddress)) { - v.address = d.value(KeyAddress); - } - if (d.hasValue(KeyPort)) { - v.port = static_cast(d.value(KeyPort)); - } - if (d.hasValue(KeyAuthentication)) { - v.authentication = d.value(KeyAuthentication); - } - if (d.hasValue(KeyUser)) { - v.user = d.value(KeyUser); - } - if (d.hasValue(KeyPassword)) { - v.password = d.value(KeyPassword); - } - } - else { - value = ghoul::lua::value(L); - } - } + struct HttpProxy { + // Determines whether the proxy is being used + std::optional activate; + // The address of the http proxy + std::string address; + + // The port of the http proxy + int port [[codegen::inrange(0, 65536)]]; + + enum class Authentication { + Basic [[codegen::key("basic")]], + Ntlm [[codegen::key("ntlm")]], + Digest [[codegen::key("digest")]], + Any [[codegen::key("any")]] + }; + // The authentication method of the http proxy + std::optional authentication; + + // The user of the http proxy + std::optional user; + + // The password of the http proxy + std::optional password; + }; + // This defines the use for a proxy when fetching data over http. No proxy will be + // used if this is left out + std::optional httpProxy; + + struct OpenGLDebugContext { + // Determines whether the OpenGL context should be a debug context + bool activate; + + // Determines whether the OpenGL debug callbacks are performed synchronously. + // If set to 'true' the callbacks are in the same thread as the context and in + // the scope of the OpenGL function that triggered the message. The default + // value is 'true' + std::optional synchronous; + + // Individual OpenGL debug message identifiers + struct Filter { + // The identifier that is to be filtered + int identifier; + + // Taken from ghoul::debugcontext.cpp + enum class Source { + API, + WindowSystem [[codegen::key("Window System")]], + ShaderCompiler [[codegen::key("Shader Compiler")]], + ThirdParty [[codegen::key("Third Party")]], + Application, + Other, + DontCare [[codegen::key("Don't care")]] + }; + // The source of the identifier to be filtered + Source source; + + // Taken from ghoul::debugcontext.cpp + enum class Type { + Error, + Deprecated, + Undefined, + Portability, + Performance, + Marker, + PushGroup [[codegen::key("Push group")]], + PopGroup [[codegen::key("Pop group")]], + Other, + DontCare [[codegen::key("Don't care")]] + }; + // The type of the identifier to be filtered + Type type; + }; + // A list of OpenGL debug messages identifiers that are filtered + std::optional> filterIdentifier; + + // A list of severities that can are filtered out + enum class Severity { + High, + Medium, + Low, + Notification + }; + // Determines the settings for the creation of an OpenGL debug context + std::optional> filterSeverity; + }; + // Determines the settings for the creation of an OpenGL debug context + std::optional openGLDebugContext; + + // Determines whether the OpenGL state is checked after each OpenGL function call. + // This will dramatically slow down the rendering, but will make finding OpenGL + // errors easier. This defaults to 'false' + std::optional checkOpenGLState; + + // Determines whether each OpenGL call that happens should be logged using the + // 'TRACE' loglevel. This will bring the rendering to a crawl but provides useful + // debugging features for the order in which OpenGL calls occur. This defaults to + // 'false' + std::optional logEachOpenGLCall; + + // This value determines whether the initialization of the scene graph should + // occur multithreaded, that is, whether multiple scene graph nodes should + // initialize in parallel. The only use for this value is to disable it for + // debugging support + std::optional useMultithreadedInitialization; + + // If this value is set to 'true', the launcher will not be shown and OpenSpace + // will start with the provided configuration options directly. Useful in + // multiprojector setups where a launcher window would be undesired + std::optional bypassLauncher; + + // The URL that is pinged to check which version of OpenSpace is the most current + // if you don't want this request to happen, this value should not be set at all + std::optional versionCheckUrl; + + struct LoadingScreen { + // If this value is set to 'true', the loading screen will display a message + // information about the current phase the loading is in + std::optional showMessage; + + // If this value is set to 'true', the loading screen will display a list of + // all of the nodes with their respective status (created, loaded, + // initialized) + std::optional showNodeNames; + + // If this value is set to 'true', the loading screen will contain a progress + // bar that gives an estimate of the loading progression + std::optional showProgressbar; + }; + // Values in this table describe the behavior of the loading screen that is + // displayed while the scene graph is created and initialized + std::optional loadingScreen; + + // List of profiles that cannot be overwritten by user + std::optional> readOnlyProfiles; + + // Configurations for each module + std::optional> moduleConfigurations; + }; +#include "configuration_codegen.cpp" } // namespace -#include "configuration_doc.inl" - namespace openspace::configuration { void parseLuaState(Configuration& configuration) { @@ -317,40 +334,277 @@ void parseLuaState(Configuration& configuration) { Configuration& c = configuration; LuaState& s = c.state; - getValue(s, KeySGCTConfig, c.windowConfiguration); - getValue(s, KeyAsset, c.asset); - getValue(s, KeyProfile, c.profile); - getValue(s, KeyGlobalCustomizationScripts, c.globalCustomizationScripts); - getValue(s, KeyPaths, c.pathTokens); - getValue(s, KeyFonts, c.fonts); - getValue(s, KeyScriptLog, c.scriptLog); - getValue(s, KeyVersionCheckUrl, c.versionCheckUrl); - getValue(s, KeyUseMultithreadedInitialization, c.useMultithreadedInitialization); - getValue(s, KeyCheckOpenGLState, c.isCheckingOpenGLState); - getValue(s, KeyLogEachOpenGLCall, c.isLoggingOpenGLCalls); - getValue(s, KeyShutdownCountdown, c.shutdownCountdown); - getValue(s, KeyScreenshotUseDate, c.shouldUseScreenshotDate); - getValue(s, KeyOnScreenTextScaling, c.onScreenTextScaling); - getValue(s, KeyPerSceneCache, c.usePerSceneCache); - getValue(s, KeyDisableRenderingOnMaster, c.isRenderingOnMasterDisabled); + // The sgctConfigNameInitialized is a bit special + lua_getglobal(s, "sgctconfiginitializeString"); + c.sgctConfigNameInitialized = ghoul::lua::value( + s, + ghoul::lua::PopValue::Yes + ); - getValue(s, KeyGlobalRotation, c.globalRotation); - getValue(s, KeyScreenSpaceRotation, c.screenSpaceRotation); - getValue(s, KeyMasterRotation, c.masterRotation); - getValue(s, KeyDisableInGameConsole, c.isConsoleDisabled); - getValue(s, KeyRenderingMethod, c.renderingMethod); - getValue(s, KeyLogging, c.logging); - getValue(s, KeyDocumentation, c.documentation); - getValue(s, KeyLoadingScreen, c.loadingScreen); - getValue(s, KeyModuleConfigurations, c.moduleConfigurations); - getValue(s, KeyOpenGLDebugContext, c.openGLDebugContext); - getValue(s, KeyHttpProxy, c.httpProxy); - getValue(s, KeySgctConfigNameInitialized, c.sgctConfigNameInitialized); - getValue(s, KeyReadOnlyProfiles, c.readOnlyProfiles); - getValue(s, KeyBypassLauncher, c.bypassLauncher); + // The configuration file sets all values as global variables, so we need to pull them + // into a table first so that we can pass that table to the dictionary constructor + lua_newtable(s); + + // We go through all of the entries and lift them from global scope into the table on + // the stack so that we can create a ghoul::Dictionary from this new table + documentation::Documentation doc = codegen::doc(); + for (const documentation::DocumentationEntry& e : doc.entries) { + lua_pushstring(s, e.key.c_str()); + lua_getglobal(s, e.key.c_str()); + lua_settable(s, -3); + } + + + ghoul::Dictionary d; + ghoul::lua::luaDictionaryFromState(s, d); + lua_settop(s, 0); + const Parameters p = codegen::bake(d); + + c.windowConfiguration = p.windowConfiguration.value_or(c.windowConfiguration); + c.asset = p.asset.value_or(c.asset); + c.profile = p.profile.value_or(c.profile); + c.globalCustomizationScripts = + p.globalCustomizationScripts.value_or(c.globalCustomizationScripts); + c.pathTokens = p.paths; + c.fonts = p.fonts.value_or(c.fonts); + c.scriptLog = p.scriptLog.value_or(c.scriptLog); + c.versionCheckUrl = p.versionCheckUrl.value_or(c.versionCheckUrl); + c.useMultithreadedInitialization = + p.useMultithreadedInitialization.value_or(c.useMultithreadedInitialization); + c.isCheckingOpenGLState = p.checkOpenGLState.value_or(c.isCheckingOpenGLState); + c.isLoggingOpenGLCalls = p.logEachOpenGLCall.value_or(c.isLoggingOpenGLCalls); + c.shutdownCountdown = p.shutdownCountdown.value_or(c.shutdownCountdown); + c.shouldUseScreenshotDate = p.screenshotUseDate.value_or(c.shouldUseScreenshotDate); + if (p.onScreenTextScaling.has_value()) { + switch (*p.onScreenTextScaling) { + case Parameters::Scaling::Window: + c.onScreenTextScaling = "window"; + break; + case Parameters::Scaling::Framebuffer: + c.onScreenTextScaling = "framebuffer"; + break; + default: + throw ghoul::MissingCaseException(); + } + } + c.usePerSceneCache = p.perSceneCache.value_or(c.usePerSceneCache); + c.isRenderingOnMasterDisabled = + p.disableRenderingOnMaster.value_or(c.isRenderingOnMasterDisabled); + c.globalRotation = p.globalRotation.value_or(c.globalRotation); + c.masterRotation = p.masterRotation.value_or(c.masterRotation); + c.screenSpaceRotation = p.screenSpaceRotation.value_or(c.screenSpaceRotation); + if (p.renderingMethod.has_value()) { + switch (*p.renderingMethod) { + case Parameters::RenderingMethod::Framebuffer: + c.renderingMethod = "Framebuffer"; + break; + case Parameters::RenderingMethod::ABuffer: + c.renderingMethod = "ABuffer"; + break; + default: + throw ghoul::MissingCaseException(); + } + } + c.isConsoleDisabled = p.disableInGameConsole.value_or(c.isConsoleDisabled); + if (p.logging.has_value()) { + if (p.logging->logLevel.has_value()) { + switch (*p.logging->logLevel) { + case Parameters::Logging::Level::Trace: + c.logging.level = "Trace"; + break; + case Parameters::Logging::Level::Debug: + c.logging.level = "Debug"; + break; + case Parameters::Logging::Level::Info: + c.logging.level = "Info"; + break; + case Parameters::Logging::Level::Warning: + c.logging.level = "Warning"; + break; + case Parameters::Logging::Level::Error: + c.logging.level = "Error"; + break; + case Parameters::Logging::Level::Fatal: + c.logging.level = "Fatal"; + break; + case Parameters::Logging::Level::None: + c.logging.level = "None"; + break; + default: + throw ghoul::MissingCaseException(); + } + } + + c.logging.forceImmediateFlush = + p.logging->immediateFlush.value_or(c.logging.forceImmediateFlush); + c.logging.logs = p.logging->logs.value_or(c.logging.logs); + if (p.logging->capabilitiesVerbosity.has_value()) { + switch (*p.logging->capabilitiesVerbosity) { + case Parameters::Logging::Verbosity::None: + c.logging.capabilitiesVerbosity = "None"; + break; + case Parameters::Logging::Verbosity::Minimal: + c.logging.capabilitiesVerbosity = "Minimal"; + break; + case Parameters::Logging::Verbosity::Default: + c.logging.capabilitiesVerbosity = "Default"; + break; + case Parameters::Logging::Verbosity::Full: + c.logging.capabilitiesVerbosity = "Full"; + break; + default: + throw ghoul::MissingCaseException(); + } + } + } + + if (p.documentation.has_value()) { + c.documentation.path = p.documentation->path.value_or(c.documentation.path); + } + + if (p.loadingScreen.has_value()) { + const Parameters::LoadingScreen& l = *p.loadingScreen; + c.loadingScreen.isShowingMessages = + l.showMessage.value_or(c.loadingScreen.isShowingMessages); + c.loadingScreen.isShowingProgressbar = + l.showProgressbar.value_or(c.loadingScreen.isShowingProgressbar); + c.loadingScreen.isShowingNodeNames = + l.showNodeNames.value_or(c.loadingScreen.isShowingNodeNames); + } + + c.moduleConfigurations = p.moduleConfigurations.value_or(c.moduleConfigurations); + + if (p.openGLDebugContext.has_value()) { + const Parameters::OpenGLDebugContext& l = *p.openGLDebugContext; + c.openGLDebugContext.isActive = l.activate; + c.openGLDebugContext.isSynchronous = l.synchronous.value_or( + c.openGLDebugContext.isSynchronous + ); + if (l.filterIdentifier.has_value()) { + for (const Parameters::OpenGLDebugContext::Filter& f : *l.filterIdentifier) { + Configuration::OpenGLDebugContext::IdentifierFilter filter; + filter.identifier = static_cast(f.identifier); + switch (f.source) { + case Parameters::OpenGLDebugContext::Filter::Source::API: + filter.source = "API"; + break; + case Parameters::OpenGLDebugContext::Filter::Source::WindowSystem: + filter.source = "Window System"; + break; + case Parameters::OpenGLDebugContext::Filter::Source::ShaderCompiler: + filter.source = "Shader Compiler"; + break; + case Parameters::OpenGLDebugContext::Filter::Source::ThirdParty: + filter.source = "Third Party"; + break; + case Parameters::OpenGLDebugContext::Filter::Source::Application: + filter.source = "Application"; + break; + case Parameters::OpenGLDebugContext::Filter::Source::Other: + filter.source = "Other"; + break; + case Parameters::OpenGLDebugContext::Filter::Source::DontCare: + filter.source = "Don't care"; + break; + default: + throw ghoul::MissingCaseException(); + } + switch (f.type) { + case Parameters::OpenGLDebugContext::Filter::Type::Error: + filter.type = "Error"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::Deprecated: + filter.type = "Deprecated"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::Undefined: + filter.type = "Undefined"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::Portability: + filter.type = "Portability"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::Performance: + filter.type = "Performance"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::Marker: + filter.type = "Marker"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::PushGroup: + filter.type = "Push group"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::PopGroup: + filter.type = "Pop group"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::Other: + filter.type = "Other"; + break; + case Parameters::OpenGLDebugContext::Filter::Type::DontCare: + filter.type = "Don't care"; + break; + default: + throw ghoul::MissingCaseException(); + } + + c.openGLDebugContext.identifierFilters.push_back(filter); + } + } + if (l.filterSeverity.has_value()) { + for (Parameters::OpenGLDebugContext::Severity sev : *l.filterSeverity) { + std::string severity; + switch (sev) { + case Parameters::OpenGLDebugContext::Severity::High: + severity = "High"; + break; + case Parameters::OpenGLDebugContext::Severity::Medium: + severity = "Medium"; + break; + case Parameters::OpenGLDebugContext::Severity::Low: + severity = "Low"; + break; + case Parameters::OpenGLDebugContext::Severity::Notification: + severity = "Notification"; + break; + default: + throw ghoul::MissingCaseException(); + } + c.openGLDebugContext.severityFilters.push_back(severity); + } + } + } + + if (p.httpProxy.has_value()) { + c.httpProxy.usingHttpProxy = + p.httpProxy->activate.value_or(c.httpProxy.usingHttpProxy); + c.httpProxy.address = p.httpProxy->address; + c.httpProxy.port = static_cast(p.httpProxy->port); + if (p.httpProxy->authentication.has_value()) { + switch (*p.httpProxy->authentication) { + case Parameters::HttpProxy::Authentication::Basic: + c.httpProxy.authentication = "basic"; + break; + case Parameters::HttpProxy::Authentication::Ntlm: + c.httpProxy.authentication = "ntlm"; + break; + case Parameters::HttpProxy::Authentication::Digest: + c.httpProxy.authentication = "digest"; + break; + case Parameters::HttpProxy::Authentication::Any: + c.httpProxy.authentication = "any"; + break; + default: + throw ghoul::MissingCaseException(); + } + } + c.httpProxy.user = p.httpProxy->user.value_or(c.httpProxy.user); + c.httpProxy.password = p.httpProxy->password.value_or(c.httpProxy.password); + } + + c.readOnlyProfiles = p.readOnlyProfiles.value_or(c.readOnlyProfiles); + c.bypassLauncher = p.bypassLauncher.value_or(c.bypassLauncher); } +documentation::Documentation Configuration::Documentation = codegen::doc(); + std::string findConfiguration(const std::string& filename) { using ghoul::filesystem::Directory; @@ -383,16 +637,14 @@ std::string findConfiguration(const std::string& filename) { } } -Configuration loadConfigurationFromFile(const std::string& filename) { +Configuration loadConfigurationFromFile(const std::string& filename, + const std::string& overrideScript) +{ ghoul_assert(!filename.empty(), "Filename must not be empty"); ghoul_assert(FileSys.fileExists(filename), "File must exist"); Configuration result; - // Register the base path as the directory where 'filename' lives - std::string basePath = ghoul::filesystem::File(filename).directoryName(); - FileSys.registerPathToken(BasePathToken, basePath); - // If there is an initial config helper file, load it into the state if (FileSys.fileExists(absPath(InitialConfigHelper))) { ghoul::lua::runScriptFile(result.state, absPath(InitialConfigHelper)); @@ -401,6 +653,12 @@ Configuration loadConfigurationFromFile(const std::string& filename) { // Load the configuration file into the state ghoul::lua::runScriptFile(result.state, filename); + if (!overrideScript.empty()) { + LDEBUGC("Configuration", "Executing Lua script passed through the commandline:"); + LDEBUGC("Configuration", overrideScript); + ghoul::lua::runScript(result.state, overrideScript); + } + parseLuaState(result); return result; diff --git a/src/engine/configuration_doc.inl b/src/engine/configuration_doc.inl deleted file mode 100644 index 16430a7c0c..0000000000 --- a/src/engine/configuration_doc.inl +++ /dev/null @@ -1,450 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2021 * - * * - * 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 - -namespace openspace::configuration { - -using namespace documentation; -documentation::Documentation Configuration::Documentation = { - "OpenSpace Configuration", - "openspace_configuraion", - { - { - KeySGCTConfig, - new StringAnnotationVerifier("A valid SGCT configuration file"), - Optional::Yes, - "The SGCT configuration file that determines the window and view frustum " - "settings that are being used when OpenSpace is started." - }, - { - KeySgctConfigNameInitialized, - new StringAnnotationVerifier("The type of SGCT autogen function (if called)"), - Optional::Yes, - "The SGCT configuration can be defined from an .xml file, or auto-generated " - "by an sgct.config.* lua function. If a lua function is used to generate the " - "SGCT configuration, then this key contains the name of the function, " - "otherwise is blank." - }, - { - KeyAsset, - new StringAnnotationVerifier( - "A valid scene file as described in the Scene documentation" - ), - Optional::Yes, - "The scene description that is used to populate the application after " - "startup. The scene determines which objects are loaded, the startup " - "time and other scene-specific settings. More information is provided in " - "the Scene documentation." - }, - { - KeyGlobalCustomizationScripts, - new StringListVerifier, - Optional::Yes, - "This value names a list of scripts that get executed after initialization " - "of any scene. These scripts can be used for user-specific customization, " - "such as a global rebinding of keys from the default." - }, - { - KeyPaths, - new StringListVerifier, - Optional::No, - "A list of paths that are automatically registered with the file system. " - "If a key X is used in the table, it is then useable by referencing ${X} " - "in all other configuration files or scripts." - }, - { - KeyFonts, - new StringListVerifier("Font paths loadable by FreeType"), - Optional::Yes, - "A list of all fonts that will automatically be loaded on startup. Each " - "key-value pair contained in the table will become the name and the file " - "for a font." - }, - { - KeyLogging, - new TableVerifier({ - { - KeyLogDir, - new StringVerifier, - Optional::Yes, - "The directory for logs. Default value is \"${BASE}\"" - }, - { - KeyPerformancePrefix, - new StringVerifier, - Optional::Yes, - "A string to prefix PerformanceMeasurement logfiles." - "Default value is \"PM-\"" - }, - { - KeyLogLevel, - new StringInListVerifier( - // List from logmanager.cpp::levelFromString - { "Trace", "Debug", "Info", "Warning", "Error", "Fatal", "None" } - ), - Optional::Yes, - "The severity of log messages that will be displayed. Only " - "messages of the selected level or higher will be displayed. All " - "levels below will be silently discarded. The order of " - "severities is: Debug < Info < Warning < Error < Fatal < None." - }, - { - KeyImmediateFlush, - new BoolVerifier, - Optional::Yes, - "Determines whether error messages will be displayed immediately " - "or if it is acceptable to have a short delay, but being more " - "performant. If the delay is allowed ('true'), messages might " - "get lost if the application crashes shortly after a message was " - "logged." - }, - { - KeyLogs, - new TableVerifier({ - { - "*", - new ReferencingVerifier("core_logfactory"), - Optional::No, - "Additional log files" - } - }), - Optional::Yes, - "Per default, log messages are written to the console, the " - "onscreen text, and (if available) the Visual Studio output " - "window. This table can define other logging methods that will " - "be used additionally." - }, - { - KeyCapabilitiesVerbosity, - new StringInListVerifier( - // List from OpenspaceEngine::initialize - { "None", "Minimal", "Default", "Full" } - ), - Optional::Yes, - "At startup, a list of system capabilities is created and logged." - "This value determines how verbose this listing should be." - } - }), - Optional::Yes, - "Configurations for the logging of messages that are generated " - "throughout the code and are useful for debugging potential errors or " - "other information." - }, - { - KeyScriptLog, - new StringVerifier, - Optional::Yes, - "The file that will be created on startup containing the log of all Lua " - "scripts that are executed in the last session. Any existing file (including " - "the results from previous runs) will be silently overwritten." - }, - { - KeyDocumentationPath, - new TableVerifier({ - { - KeyDocumentationPath, - new StringVerifier, - Optional::Yes, - "The path where the documentation files will be stored." - }, - }), - Optional::Yes, - "Right now only contains the path where the documentation is written to." - }, - { - KeyShutdownCountdown, - new DoubleGreaterEqualVerifier(0.0), - Optional::Yes, - "The countdown that the application will wait between pressing ESC and " - "actually shutting down. If ESC is pressed again in this time, the " - "shutdown is aborted." - }, - { - KeyPerSceneCache, - new BoolVerifier, - Optional::Yes, - "If this is set to 'true', the name of the scene will be appended to the " - "cache directory, thus not reusing the same directory. This is useful in " - "cases where the same instance of OpenSpace is run with multiple scenes, but " - "the caches should be retained. This value defaults to 'false'." - }, - { - KeyOnScreenTextScaling, - new StringInListVerifier({ - // Values from RenderEngine:updateRenderer - "window", "framebuffer" - }), - Optional::Yes, - "The method for scaling the onscreen text in the window. As the resolution " - "of the rendering can be different from the size of the window, the onscreen " - "text can either be scaled according to the window size ('window'), or the " - "rendering resolution ('framebuffer'). This value defaults to 'window'." - }, - { - KeyRenderingMethod, - new StringInListVerifier( - // List from RenderEngine::setRendererFromString - { "Framebuffer", "ABuffer" } - ), - Optional::Yes, - "The renderer that is use after startup. The renderer 'ABuffer' requires " - "support for at least OpenGL 4.3" - }, - { - KeyDisableRenderingOnMaster, - new BoolVerifier, - Optional::Yes, - "Toggles whether the master in a multi-application setup should be rendering " - "or just managing the state of the network. This is desired in cases where " - "the master computer does not have the resources to render a scene." - }, - { - KeyGlobalRotation, - new DoubleVector3Verifier, - Optional::Yes, - "Applies a global view rotation. Use this to rotate the position of the " - "focus node away from the default location on the screen. This setting " - "persists even when a new focus node is selected. Defined using roll, pitch, " - "yaw in radians" - }, - { - KeyMasterRotation, - new DoubleVector3Verifier, - Optional::Yes, - "Applies a view rotation for only the master node, defined using " - "roll, pitch yaw in radians. This can be used to compensate the master view " - "direction for tilted display systems in clustered immersive environments." - }, - { - KeyScreenSpaceRotation, - new DoubleVector3Verifier, - Optional::Yes, - "Applies a global rotation for all screenspace renderables. Defined using " - "roll, pitch, yaw in radians." - }, - { - KeyScreenshotUseDate, - new BoolVerifier, - Optional::Yes, - "Toggles whether screenshots generated by OpenSpace contain the date when " - "the concrete OpenSpace instance was started. This value is enabled by " - "default, but it is advised to disable this value if rendering sessions of " - "individual frames pass beyond local midnight." - }, - { - KeyHttpProxy, - new TableVerifier({ - { - KeyActivate, - new BoolVerifier, - Optional::Yes, - "Determines whether the proxy is being used" - }, - { - KeyAddress, - new StringVerifier, - Optional::No, - "The address of the http proxy" - }, - { - KeyPort, - new IntVerifier, - Optional::No, - "The port of the http proxy" - }, - { - KeyAuthentication, - new StringInListVerifier( - { "basic", "ntlm", "digest", "any" } - ), - Optional::Yes, - "The authentication method of the http proxy" - }, - { - KeyUser, - new StringVerifier, - Optional::Yes, - "The user of the http proxy" - }, - { - KeyPassword, - new StringVerifier, - Optional::Yes, - "The password of the http proxy" - } - }), - Optional::Yes, - "This defines the use for a proxy when fetching data over http." - "No proxy will be used if this is left out." - }, - { - KeyOpenGLDebugContext, - new TableVerifier({ - { - KeyActivate, - new BoolVerifier, - Optional::No, - "Determines whether the OpenGL context should be a debug context" - }, - { - KeySynchronous, - new BoolVerifier, - Optional::Yes, - "Determines whether the OpenGL debug callbacks are performed " - "synchronously. If set to the callbacks are in the same " - "thread as the context and in the scope of the OpenGL function that " - "triggered the message. The default value is ." - }, - { - KeyFilterIdentifier, - new TableVerifier({{ - "*", - new TableVerifier({ - { - KeyIdentifier, - new IntVerifier, - Optional::No, - "The identifier that is to be filtered" - }, - { - KeySource, - new StringInListVerifier({ - // Taken from ghoul::debugcontext.cpp - "API", "Window System", "Shader Compiler", - "Third Party", "Application", "Other", "Don't care" - }), - Optional::No, - "The source of the identifier to be filtered" - }, - { - KeyType, - new StringInListVerifier({ - // Taken from ghoul::debugcontext.cpp - "Error", "Deprecated", "Undefined", "Portability", - "Performance", "Marker", "Push group", "Pop group", - "Other", "Don't care" - }), - Optional::No, - "The type of the identifier to be filtered" - } - }), - Optional::No, - "Individual OpenGL debug message identifiers" - }}), - Optional::Yes, - "A list of OpenGL debug messages identifiers that are filtered" - }, - { - KeyFilterSeverity, - new TableVerifier({ - { - "*", - new StringInListVerifier( - // ghoul::debugcontext.cpp - { "High", "Medium", "Low", "Notification" } - ), - Optional::No - } - }), - Optional::Yes, - "A list of severities that can are filtered out" - } - }), - Optional::Yes, - "Determines the settings for the creation of an OpenGL debug context.", - }, - { - KeyCheckOpenGLState, - new BoolVerifier, - Optional::Yes, - "Determines whether the OpenGL state is checked after each OpenGL function " - "call. This will dramatically slow down the rendering, but will make finding " - "OpenGL errors easier. This defaults to 'false'." - }, - { - KeyLogEachOpenGLCall, - new BoolVerifier, - Optional::Yes, - "Determines whether each OpenGL call that happens should be logged using the " - "'TRACE' loglevel. This will bring the rendering to a crawl but provides " - "useful debugging features for the order in which OpenGL calls occur. This " - "defaults to 'false'." - }, - { - KeyUseMultithreadedInitialization, - new BoolVerifier, - Optional::Yes, - "This value determines whether the initialization of the scene graph should " - "occur multithreaded, that is, whether multiple scene graph nodes should " - "initialize in parallel. The only use for this value is to disable it for " - "debugging support." - }, - { - KeyLoadingScreen, - new TableVerifier({ - { - KeyShowMessage, - new BoolVerifier, - Optional::Yes, - "If this value is set to 'true', the loading screen will display a " - "message information about the current phase the loading is in." - }, - { - KeyShowNodeNames, - new BoolVerifier, - Optional::Yes, - "If this value is set to 'true', the loading screen will display a " - "list of all of the nodes with their respective status (created, " - "loaded, initialized)." - }, - { - KeyShowProgressbar, - new BoolVerifier, - Optional::Yes, - "If this value is set to 'true', the loading screen will contain a " - "progress bar that gives an estimate of the loading progression." - } - }), - Optional::Yes, - "Values in this table describe the behavior of the loading screen that is " - "displayed while the scene graph is created and initialized." - }, - { - KeyModuleConfigurations, - new TableVerifier, - Optional::Yes, - "Configurations for each module" - }, - { - KeyReadOnlyProfiles, - new StringListVerifier, - Optional::Yes, - "List of profiles that cannot be overwritten by user" - } - } -}; - -} // namespace openspace::configuration diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 522b4509c8..03d0883df7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable( test_assetloader.cpp test_concurrentjobmanager.cpp test_concurrentqueue.cpp + test_configuration.cpp test_documentation.cpp test_iswamanager.cpp test_latlonpatch.cpp diff --git a/tests/main.cpp b/tests/main.cpp index 788f7cf5f8..578dc62cb1 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -44,7 +44,7 @@ int main(int argc, char** argv) { using namespace openspace; ghoul::logging::LogManager::initialize( - ghoul::logging::LogLevel::Debug, + ghoul::logging::LogLevel::Info, ghoul::logging::LogManager::ImmediateFlush::Yes ); ghoul::initialize(); @@ -59,10 +59,21 @@ int main(int argc, char** argv) { ); std::string configFile = configuration::findConfiguration(); - *global::configuration = configuration::loadConfigurationFromFile(configFile); + // Register the base path as the directory where 'filename' lives + std::string base = ghoul::filesystem::File(configFile).directoryName(); + constexpr const char* BasePathToken = "${BASE}"; + FileSys.registerPathToken(BasePathToken, base); + + *global::configuration = configuration::loadConfigurationFromFile(configFile, ""); global::openSpaceEngine->registerPathTokens(); global::openSpaceEngine->initialize(); + ghoul::logging::LogManager::deinitialize(); + ghoul::logging::LogManager::initialize( + ghoul::logging::LogLevel::Info, + ghoul::logging::LogManager::ImmediateFlush::Yes + ); + FileSys.registerPathToken("${TESTDIR}", "${BASE}/tests"); // All of the relevant tests initialize the SpiceManager diff --git a/tests/test_configuration.cpp b/tests/test_configuration.cpp new file mode 100644 index 0000000000..f7c031d251 --- /dev/null +++ b/tests/test_configuration.cpp @@ -0,0 +1,640 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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 "catch2/catch.hpp" + +#include +#include +#include +#include + +using namespace openspace::configuration; + +namespace { + std::string MinimalConfig = R"( +Paths = {} +)"; + + void writeConfig(const std::string& filename, const std::string& content) { + std::string config = MinimalConfig + content; + std::ofstream f(filename); + f << MinimalConfig << '\n' << content; + } + + Configuration loadConfiguration(const std::string& tag, const std::string& content) { + std::string filename = fmt::format("test_configuration_{}.cfg", tag); + std::filesystem::path path = std::filesystem::temp_directory_path(); + std::string configFile = (path / filename).string(); + writeConfig(configFile, content); + Configuration conf = loadConfigurationFromFile(configFile, content); + std::filesystem::remove(configFile); + return conf; + + } +} // namespace + +TEST_CASE("Configuration: minimal", "[configuration]") { + loadConfiguration("minimal", ""); +} + +TEST_CASE("Configuration: windowConfiguration", "[configuration]") { + constexpr const char Extra[] = R"(SGCTConfig = "foobar")"; + const Configuration c = loadConfiguration("windowConfiguration", Extra); + REQUIRE(c.windowConfiguration == "foobar"); +} + +TEST_CASE("Configuration: asset", "[configuration]") { + constexpr const char Extra[] = R"(Asset = "foobar")"; + const Configuration c = loadConfiguration("asset", Extra); + REQUIRE(c.asset == "foobar"); +} + +TEST_CASE("Configuration: profile", "[configuration]") { + constexpr const char Extra[] = R"(Profile = "foobar")"; + const Configuration c = loadConfiguration("profile", Extra); + REQUIRE(c.profile == "foobar"); +} + +TEST_CASE("Configuration: globalCustomizationScripts", "[configuration]") { + constexpr const char Extra[] = R"(GlobalCustomizationScripts = { "foo", "bar" })"; + const Configuration c = loadConfiguration("globalCustomization", Extra); + REQUIRE(c.globalCustomizationScripts.size() == 2); + CHECK(c.globalCustomizationScripts == std::vector{ "foo", "bar" }); +} + +TEST_CASE("Configuration: paths", "[configuration]") { + constexpr const char Extra[] = R"(Paths = { foo = "1", bar = "2" })"; + const Configuration c = loadConfiguration("paths", Extra); + REQUIRE(c.pathTokens.size() == 2); + CHECK( + c.pathTokens == + std::map{ { "foo", "1" }, { "bar", "2" } } + ); +} + +TEST_CASE("Configuration: fonts", "[configuration]") { + constexpr const char Extra[] = R"(Fonts = { foo = "1", bar = "2" })"; + const Configuration c = loadConfiguration("fonts", Extra); + REQUIRE(c.fonts.size() == 2); + CHECK( + c.fonts == + std::map{ { "foo", "1" }, { "bar", "2" } } + ); +} + +TEST_CASE("Configuration: logging", "[configuration]") { + Configuration defaultConf; + { + // Empty + constexpr const char Extra[] = R"(Logging = {})"; + const Configuration c = loadConfiguration("logging1", Extra); + + CHECK(c.logging.level == defaultConf.logging.level); + CHECK(c.logging.forceImmediateFlush == defaultConf.logging.forceImmediateFlush); + CHECK( + c.logging.capabilitiesVerbosity == + defaultConf.logging.capabilitiesVerbosity + ); + CHECK(c.logging.logs == defaultConf.logging.logs); + } + { + // level + constexpr const char Extra[] = R"(Logging = { LogLevel = "Fatal" })"; + const Configuration c = loadConfiguration("logging2", Extra); + + CHECK(c.logging.level == "Fatal"); + CHECK(c.logging.forceImmediateFlush == defaultConf.logging.forceImmediateFlush); + CHECK( + c.logging.capabilitiesVerbosity == + defaultConf.logging.capabilitiesVerbosity + ); + CHECK(c.logging.logs == defaultConf.logging.logs); + } + { + // forceimmediate + constexpr const char Extra[] = R"(Logging = { ImmediateFlush = false })"; + const Configuration c = loadConfiguration("logging3", Extra); + + CHECK(c.logging.level == defaultConf.logging.level); + CHECK(c.logging.forceImmediateFlush == false); + CHECK( + c.logging.capabilitiesVerbosity == + defaultConf.logging.capabilitiesVerbosity + ); + CHECK(c.logging.logs == defaultConf.logging.logs); + } + { + // logs + constexpr const char Extra[] = R"( +Logging = { + Logs = { + { Type = "html", File = "foobar", Append = false } + } +} +)"; + const Configuration c = loadConfiguration("logging4", Extra); + + CHECK(c.logging.level == defaultConf.logging.level); + CHECK(c.logging.forceImmediateFlush == defaultConf.logging.forceImmediateFlush); + CHECK( + c.logging.capabilitiesVerbosity == + defaultConf.logging.capabilitiesVerbosity + ); + REQUIRE(c.logging.logs.size() == 1); + const ghoul::Dictionary& d = c.logging.logs[0]; + REQUIRE(d.hasValue("Type")); + CHECK(d.value("Type") == "html"); + REQUIRE(d.hasValue("File")); + CHECK(d.value("File") == "foobar"); + REQUIRE(d.hasValue("Append")); + CHECK(d.value("Append") == false); + } + { + // capabilities verbosity + constexpr const char Extra[] = R"(Logging = { CapabilitiesVerbosity = "Full" })"; + const Configuration c = loadConfiguration("logging5", Extra); + + CHECK(c.logging.level == defaultConf.logging.level); + CHECK(c.logging.forceImmediateFlush == defaultConf.logging.forceImmediateFlush); + CHECK(c.logging.capabilitiesVerbosity == "Full"); + CHECK(c.logging.logs == defaultConf.logging.logs); + } +} + +TEST_CASE("Configuration: scriptlog", "[configuration]") { + constexpr const char Extra[] = R"(ScriptLog = "foobar")"; + const Configuration c = loadConfiguration("scriptlog", Extra); + CHECK(c.scriptLog == "foobar"); +} + +TEST_CASE("Configuration: documentationpath", "[configuration]") { + constexpr const char Extra[] = R"(Documentation = { Path = "foobar" })"; + const Configuration c = loadConfiguration("documentationpath", Extra); + CHECK(c.documentation.path == "foobar"); +} + +TEST_CASE("Configuration: versioncheckurl", "[configuration]") { + constexpr const char Extra[] = R"(VersionCheckUrl = "foobar")"; + const Configuration c = loadConfiguration("versioncheckurl", Extra); + CHECK(c.versionCheckUrl == "foobar"); +} + +TEST_CASE("Configuration: useMultithreadedInit", "[configuration]") { + constexpr const char Extra[] = R"(UseMultithreadedInitialization = true)"; + const Configuration c = loadConfiguration("useMultithreadedInit", Extra); + CHECK(c.useMultithreadedInitialization == true); +} + +TEST_CASE("Configuration: loadingscreen", "[configuration]") { + Configuration defaultConf; + + { + // empty + constexpr const char Extra[] = R"(LoadingScreen = {})"; + const Configuration c = loadConfiguration("loadingscreen1", Extra); + CHECK( + c.loadingScreen.isShowingMessages == + defaultConf.loadingScreen.isShowingMessages + ); + CHECK( + c.loadingScreen.isShowingProgressbar == + defaultConf.loadingScreen.isShowingProgressbar + ); + CHECK( + c.loadingScreen.isShowingNodeNames == + defaultConf.loadingScreen.isShowingNodeNames + ); + } + { + // isShowingMessages + constexpr const char Extra[] = R"(LoadingScreen = { ShowMessage = true })"; + const Configuration c = loadConfiguration("loadingscreen2", Extra); + CHECK(c.loadingScreen.isShowingMessages == true); + CHECK( + c.loadingScreen.isShowingProgressbar == + defaultConf.loadingScreen.isShowingProgressbar + ); + CHECK( + c.loadingScreen.isShowingNodeNames == + defaultConf.loadingScreen.isShowingNodeNames + ); + } + { + // isShowingProgressbar + constexpr const char Extra[] = R"(LoadingScreen = { ShowProgressbar = true })"; + const Configuration c = loadConfiguration("loadingscreen3", Extra); + CHECK( + c.loadingScreen.isShowingMessages == + defaultConf.loadingScreen.isShowingMessages + ); + CHECK(c.loadingScreen.isShowingProgressbar == true); + CHECK( + c.loadingScreen.isShowingNodeNames == + defaultConf.loadingScreen.isShowingNodeNames + ); + } + { + // isShowingNodeNames + constexpr const char Extra[] = R"(LoadingScreen = { ShowNodeNames = true })"; + const Configuration c = loadConfiguration("loadingscreen4", Extra); + CHECK( + c.loadingScreen.isShowingMessages == + defaultConf.loadingScreen.isShowingMessages + ); + CHECK( + c.loadingScreen.isShowingProgressbar == + defaultConf.loadingScreen.isShowingProgressbar + ); + CHECK(c.loadingScreen.isShowingNodeNames == true); + } +} + +TEST_CASE("Configuration: isCheckingOpenGLState", "[configuration]") { + constexpr const char Extra[] = R"(CheckOpenGLState = true)"; + const Configuration c = loadConfiguration("isCheckingOpenGLState", Extra); + CHECK(c.isCheckingOpenGLState == true); +} + +TEST_CASE("Configuration: isLoggingOpenGLCalls", "[configuration]") { + constexpr const char Extra[] = R"(LogEachOpenGLCall = true)"; + const Configuration c = loadConfiguration("isLoggingOpenGLCalls", Extra); + CHECK(c.isLoggingOpenGLCalls == true); +} + +TEST_CASE("Configuration: shutdownCountdown", "[configuration]") { + constexpr const char Extra[] = R"(ShutdownCountdown = 0.5)"; + const Configuration c = loadConfiguration("shutdownCountdown", Extra); + CHECK(c.shutdownCountdown == 0.5f); +} + +TEST_CASE("Configuration: shouldUseScreenshotDate", "[configuration]") { + constexpr const char Extra[] = R"(ScreenshotUseDate = true)"; + const Configuration c = loadConfiguration("shouldUseScreenshotDate", Extra); + CHECK(c.shouldUseScreenshotDate == true); +} + +TEST_CASE("Configuration: onScreenTextScaling", "[configuration]") { + constexpr const char Extra[] = R"(OnScreenTextScaling = "framebuffer")"; + const Configuration c = loadConfiguration("onScreenTextScaling", Extra); + CHECK(c.onScreenTextScaling == "framebuffer"); +} + +TEST_CASE("Configuration: usePerSceneCache", "[configuration]") { + constexpr const char Extra[] = R"(PerSceneCache = true)"; + const Configuration c = loadConfiguration("usePerSceneCache", Extra); + CHECK(c.usePerSceneCache == true); +} + +TEST_CASE("Configuration: isRenderingOnMasterDisabled", "[configuration]") { + constexpr const char Extra[] = R"(DisableRenderingOnMaster = true)"; + const Configuration c = loadConfiguration("isRenderingOnMasterDisabled", Extra); + CHECK(c.isRenderingOnMasterDisabled == true); +} + +TEST_CASE("Configuration: globalRotation", "[configuration]") { + constexpr const char Extra[] = R"(GlobalRotation = { 1.0, 2.0, 3.0 })"; + const Configuration c = loadConfiguration("globalRotation", Extra); + CHECK(c.globalRotation == glm::dvec3(1.0, 2.0, 3.0)); +} + +TEST_CASE("Configuration: screenSpaceRotation", "[configuration]") { + constexpr const char Extra[] = R"(ScreenSpaceRotation = { 1.0, 2.0, 3.0 })"; + const Configuration c = loadConfiguration("screenSpaceRotation", Extra); + CHECK(c.screenSpaceRotation == glm::dvec3(1.0, 2.0, 3.0)); +} + +TEST_CASE("Configuration: masterRotation", "[configuration]") { + constexpr const char Extra[] = R"(MasterRotation = { 1.0, 2.0, 3.0 })"; + const Configuration c = loadConfiguration("masterRotation", Extra); + CHECK(c.masterRotation == glm::dvec3(1.0, 2.0, 3.0)); +} + +TEST_CASE("Configuration: isConsoleDisabled", "[configuration]") { + constexpr const char Extra[] = R"(DisableInGameConsole = true)"; + const Configuration c = loadConfiguration("isConsoleDisabled", Extra); + CHECK(c.isConsoleDisabled == true); +} + +TEST_CASE("Configuration: bypassLauncher", "[configuration]") { + constexpr const char Extra[] = R"(BypassLauncher = true)"; + const Configuration c = loadConfiguration("bypassLauncher", Extra); + CHECK(c.bypassLauncher == true); +} + +TEST_CASE("Configuration: moduleConfigurations", "[configuration]") { + { + // empty + constexpr const char Extra[] = R"(ModuleConfigurations = {})"; + const Configuration c = loadConfiguration("moduleConfigurations", Extra); + CHECK(c.moduleConfigurations.empty()); + } + { + // values + constexpr const char Extra[] = R"( +ModuleConfigurations = { + Foo = { + Foo2 = 1.0, + Foo3 = "abc" + }, + Bar = { + Bar2 = true, + Bar3 = { 1.0, 2.0, 3.0 } + } +} +)"; + const Configuration c = loadConfiguration("moduleConfigurations", Extra); + REQUIRE(c.moduleConfigurations.size() == 2); + ghoul::Dictionary foo = c.moduleConfigurations.at("Foo"); + REQUIRE(foo.size() == 2); + REQUIRE(foo.hasValue("Foo2")); + CHECK(foo.value("Foo2") == 1.0); + REQUIRE(foo.hasValue("Foo3")); + CHECK(foo.value("Foo3") == std::string("abc")); + + ghoul::Dictionary bar = c.moduleConfigurations.at("Bar"); + REQUIRE(bar.size() == 2); + REQUIRE(bar.hasValue("Bar2")); + CHECK(bar.value("Bar2") == true); + REQUIRE(bar.hasValue("Bar3")); + CHECK(bar.value("Bar3") == glm::dvec3(1.0, 2.0, 3.0)); + } +} + +TEST_CASE("Configuration: renderingMethod", "[configuration]") { + constexpr const char Extra[] = R"(RenderingMethod = "ABuffer")"; + const Configuration c = loadConfiguration("renderingMethod", Extra); + CHECK(c.renderingMethod == "ABuffer"); +} + +TEST_CASE("Configuration: openGLDebugContext", "[configuration]") { + Configuration defaultConf; + { + // empty-ish / activate + constexpr const char Extra[] = R"(OpenGLDebugContext = { Activate = true })"; + const Configuration c = loadConfiguration("openGLDebugContext1", Extra); + CHECK(c.openGLDebugContext.isActive == true); + CHECK( + c.openGLDebugContext.isSynchronous == + defaultConf.openGLDebugContext.isSynchronous + ); + REQUIRE( + c.openGLDebugContext.identifierFilters.size() == + defaultConf.openGLDebugContext.identifierFilters.size() + ); + for (size_t i = 0; i < c.openGLDebugContext.identifierFilters.size(); i += 1) { + CHECK( + c.openGLDebugContext.identifierFilters[i].identifier == + defaultConf.openGLDebugContext.identifierFilters[i].identifier + ); + CHECK( + c.openGLDebugContext.identifierFilters[i].source == + defaultConf.openGLDebugContext.identifierFilters[i].source + ); + CHECK( + c.openGLDebugContext.identifierFilters[i].type == + defaultConf.openGLDebugContext.identifierFilters[i].type + ); + } + CHECK( + c.openGLDebugContext.severityFilters == + defaultConf.openGLDebugContext.severityFilters + ); + } + { + // isSynchronous + constexpr const char Extra[] = R"( +OpenGLDebugContext = { Activate = true, Synchronous = true } +)"; + const Configuration c = loadConfiguration("openGLDebugContext2", Extra); + CHECK(c.openGLDebugContext.isActive == true); + CHECK(c.openGLDebugContext.isSynchronous == true); + REQUIRE( + c.openGLDebugContext.identifierFilters.size() == + defaultConf.openGLDebugContext.identifierFilters.size() + ); + for (size_t i = 0; i < c.openGLDebugContext.identifierFilters.size(); i += 1) { + CHECK( + c.openGLDebugContext.identifierFilters[i].identifier == + defaultConf.openGLDebugContext.identifierFilters[i].identifier + ); + CHECK( + c.openGLDebugContext.identifierFilters[i].source == + defaultConf.openGLDebugContext.identifierFilters[i].source + ); + CHECK( + c.openGLDebugContext.identifierFilters[i].type == + defaultConf.openGLDebugContext.identifierFilters[i].type + ); + } + CHECK( + c.openGLDebugContext.severityFilters == + defaultConf.openGLDebugContext.severityFilters + ); + } + { + // identifierFilters + constexpr const char Extra[] = R"( +OpenGLDebugContext = { + Activate = true, + FilterIdentifier = { + { Identifier = 1, Source = "API", Type = "Error" }, + { Identifier = 2, Source = "Window System", Type = "Deprecated" }, + { Identifier = 3, Source = "Shader Compiler", Type = "Undefined" }, + { Identifier = 4, Source = "Third Party", Type = "Portability" }, + { Identifier = 5, Source = "Application", Type = "Performance" }, + { Identifier = 6, Source = "Other", Type = "Marker" }, + { Identifier = 7, Source = "Don't care", Type = "Push group" }, + { Identifier = 8, Source = "API", Type = "Pop group" }, + { Identifier = 9, Source = "Window System", Type = "Other" }, + { Identifier = 10, Source = "Shader Compiler", Type = "Don't care" } + } +} +)"; + const Configuration c = loadConfiguration("openGLDebugContext3", Extra); + CHECK(c.openGLDebugContext.isActive == true); + CHECK( + c.openGLDebugContext.isSynchronous == + defaultConf.openGLDebugContext.isSynchronous + ); + REQUIRE(c.openGLDebugContext.identifierFilters.size() == 10); + CHECK(c.openGLDebugContext.identifierFilters[0].identifier == 1); + CHECK(c.openGLDebugContext.identifierFilters[0].source == "API"); + CHECK(c.openGLDebugContext.identifierFilters[0].type == "Error"); + CHECK(c.openGLDebugContext.identifierFilters[1].identifier == 2); + CHECK(c.openGLDebugContext.identifierFilters[1].source == "Window System"); + CHECK(c.openGLDebugContext.identifierFilters[1].type == "Deprecated"); + CHECK(c.openGLDebugContext.identifierFilters[2].identifier == 3); + CHECK(c.openGLDebugContext.identifierFilters[2].source == "Shader Compiler"); + CHECK(c.openGLDebugContext.identifierFilters[2].type == "Undefined"); + CHECK(c.openGLDebugContext.identifierFilters[3].identifier == 4); + CHECK(c.openGLDebugContext.identifierFilters[3].source == "Third Party"); + CHECK(c.openGLDebugContext.identifierFilters[3].type == "Portability"); + CHECK(c.openGLDebugContext.identifierFilters[4].identifier == 5); + CHECK(c.openGLDebugContext.identifierFilters[4].source == "Application"); + CHECK(c.openGLDebugContext.identifierFilters[4].type == "Performance"); + CHECK(c.openGLDebugContext.identifierFilters[5].identifier == 6); + CHECK(c.openGLDebugContext.identifierFilters[5].source == "Other"); + CHECK(c.openGLDebugContext.identifierFilters[5].type == "Marker"); + CHECK(c.openGLDebugContext.identifierFilters[6].identifier == 7); + CHECK(c.openGLDebugContext.identifierFilters[6].source == "Don't care"); + CHECK(c.openGLDebugContext.identifierFilters[6].type == "Push group"); + CHECK(c.openGLDebugContext.identifierFilters[7].identifier == 8); + CHECK(c.openGLDebugContext.identifierFilters[7].source == "API"); + CHECK(c.openGLDebugContext.identifierFilters[7].type == "Pop group"); + CHECK(c.openGLDebugContext.identifierFilters[8].identifier == 9); + CHECK(c.openGLDebugContext.identifierFilters[8].source == "Window System"); + CHECK(c.openGLDebugContext.identifierFilters[8].type == "Other"); + CHECK(c.openGLDebugContext.identifierFilters[9].identifier == 10); + CHECK(c.openGLDebugContext.identifierFilters[9].source == "Shader Compiler"); + CHECK(c.openGLDebugContext.identifierFilters[9].type == "Don't care"); + + CHECK( + c.openGLDebugContext.severityFilters == + defaultConf.openGLDebugContext.severityFilters + ); + } + { + // filterSeverity + constexpr const char Extra[] = R"( +OpenGLDebugContext = { Activate = true, FilterSeverity = { "High", "Medium" } } +)"; + const Configuration c = loadConfiguration("openGLDebugContext4", Extra); + CHECK(c.openGLDebugContext.isActive == true); + CHECK( + c.openGLDebugContext.isSynchronous == + defaultConf.openGLDebugContext.isSynchronous + ); + REQUIRE( + c.openGLDebugContext.identifierFilters.size() == + defaultConf.openGLDebugContext.identifierFilters.size() + ); + for (size_t i = 0; i < c.openGLDebugContext.identifierFilters.size(); i += 1) { + CHECK( + c.openGLDebugContext.identifierFilters[i].identifier == + defaultConf.openGLDebugContext.identifierFilters[i].identifier + ); + CHECK( + c.openGLDebugContext.identifierFilters[i].source == + defaultConf.openGLDebugContext.identifierFilters[i].source + ); + CHECK( + c.openGLDebugContext.identifierFilters[i].type == + defaultConf.openGLDebugContext.identifierFilters[i].type + ); + } + REQUIRE(c.openGLDebugContext.severityFilters.size() == 2); + CHECK( + c.openGLDebugContext.severityFilters == + std::vector{ "High", "Medium" } + ); + } +} + +TEST_CASE("Configuration: httpProxy", "[configuration]") { + Configuration defaultConf; + { + // empty-ish / address + port + constexpr const char Extra[] = R"( +HttpProxy = { + Address = "foobar", + Port = 1234 +} +)"; + const Configuration c = loadConfiguration("httpProxy1", Extra); + CHECK(c.httpProxy.usingHttpProxy == defaultConf.httpProxy.usingHttpProxy); + CHECK(c.httpProxy.address == "foobar"); + CHECK(c.httpProxy.port == 1234); + CHECK(c.httpProxy.authentication == defaultConf.httpProxy.authentication); + CHECK(c.httpProxy.user == defaultConf.httpProxy.user); + CHECK(c.httpProxy.password == defaultConf.httpProxy.password); + } + { + // activate + constexpr const char Extra[] = R"( +HttpProxy = { + Activate = true, + Address = "foobar", + Port = 1234 +} +)"; + const Configuration c = loadConfiguration("httpProxy2", Extra); + CHECK(c.httpProxy.usingHttpProxy == true); + CHECK(c.httpProxy.address == "foobar"); + CHECK(c.httpProxy.port == 1234); + CHECK(c.httpProxy.authentication == defaultConf.httpProxy.authentication); + CHECK(c.httpProxy.user == defaultConf.httpProxy.user); + CHECK(c.httpProxy.password == defaultConf.httpProxy.password); + } + { + // authentication + constexpr const char Extra[] = R"( +HttpProxy = { + Address = "foobar", + Port = 1234, + Authentication = "ntlm" +} +)"; + const Configuration c = loadConfiguration("httpProxy3", Extra); + CHECK(c.httpProxy.usingHttpProxy == defaultConf.httpProxy.usingHttpProxy); + CHECK(c.httpProxy.address == "foobar"); + CHECK(c.httpProxy.port == 1234); + CHECK(c.httpProxy.authentication == "ntlm"); + CHECK(c.httpProxy.user == defaultConf.httpProxy.user); + CHECK(c.httpProxy.password == defaultConf.httpProxy.password); + } + { + // user + constexpr const char Extra[] = R"( +HttpProxy = { + Address = "foobar", + Port = 1234, + User = "user-bar" +} +)"; + const Configuration c = loadConfiguration("httpProxy4", Extra); + CHECK(c.httpProxy.usingHttpProxy == defaultConf.httpProxy.usingHttpProxy); + CHECK(c.httpProxy.address == "foobar"); + CHECK(c.httpProxy.port == 1234); + CHECK(c.httpProxy.authentication == defaultConf.httpProxy.authentication); + CHECK(c.httpProxy.user == "user-bar"); + CHECK(c.httpProxy.password == defaultConf.httpProxy.password); + } + { + // password + constexpr const char Extra[] = R"( +HttpProxy = { + Address = "foobar", + Port = 1234, + Password = "password-bar" +} +)"; + const Configuration c = loadConfiguration("httpProxy5", Extra); + CHECK(c.httpProxy.usingHttpProxy == defaultConf.httpProxy.usingHttpProxy); + CHECK(c.httpProxy.address == "foobar"); + CHECK(c.httpProxy.port == 1234); + CHECK(c.httpProxy.authentication == defaultConf.httpProxy.authentication); + CHECK(c.httpProxy.user == defaultConf.httpProxy.user); + CHECK(c.httpProxy.password == "password-bar"); + } +} From 4b25f5c074641c5af71f9ed1646c41f89478a21b Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 24 Mar 2021 09:57:27 +0100 Subject: [PATCH 10/12] Move default_dashboard asset from util folder to new dashboard folder that also includes the individual dashboard items --- .../launcher/src/profile/assettreemodel.cpp | 4 ++- data/assets/base_blank.asset | 2 +- data/assets/dashboard/date.asset | 13 +++++++ data/assets/dashboard/default_dashboard.asset | 8 +++++ data/assets/dashboard/distance.asset | 13 +++++++ data/assets/dashboard/framerate.asset | 13 +++++++ data/assets/dashboard/globelocation.asset | 13 +++++++ .../assets/dashboard/parallelconnection.asset | 13 +++++++ .../dashboard/simulationincrement.asset | 13 +++++++ data/assets/dashboard/velocity.asset | 14 ++++++++ data/assets/util/default_dashboard.asset | 34 ------------------- 11 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 data/assets/dashboard/date.asset create mode 100644 data/assets/dashboard/default_dashboard.asset create mode 100644 data/assets/dashboard/distance.asset create mode 100644 data/assets/dashboard/framerate.asset create mode 100644 data/assets/dashboard/globelocation.asset create mode 100644 data/assets/dashboard/parallelconnection.asset create mode 100644 data/assets/dashboard/simulationincrement.asset create mode 100644 data/assets/dashboard/velocity.asset delete mode 100644 data/assets/util/default_dashboard.asset diff --git a/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp b/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp index b33b9c216f..53521995f6 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp @@ -149,7 +149,9 @@ void AssetTreeModel::importModelData(const std::string& assetBasePath, const std::string& userAssetBasePath) { FileSystemAccess assets( ".asset", - { "scene", "global", "customization", "examples", "util" }, + // @TODO (abock, 2021-03-24) We need some better solution for this; what is the + // problem of just including all subfolders instead? + { "scene", "global", "customization", "dashboard", "examples", "util" }, true, true ); diff --git a/data/assets/base_blank.asset b/data/assets/base_blank.asset index 46736f0e37..41720dc915 100644 --- a/data/assets/base_blank.asset +++ b/data/assets/base_blank.asset @@ -8,8 +8,8 @@ local propertyHelper = asset.require('util/property_helper') asset.require('spice/base') -- Load default key bindings applicable to most scenes +asset.require('dashboard/default_dashboard') asset.require('util/default_keybindings') -asset.require('util/default_dashboard') asset.require('util/default_joystick') -- Load web gui diff --git a/data/assets/dashboard/date.asset b/data/assets/dashboard/date.asset new file mode 100644 index 0000000000..d92dec9632 --- /dev/null +++ b/data/assets/dashboard/date.asset @@ -0,0 +1,13 @@ +local item = { + Type = "DashboardItemDate", + Identifier = "Date", + GuiName = "Date" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/dashboard/default_dashboard.asset b/data/assets/dashboard/default_dashboard.asset new file mode 100644 index 0000000000..af524db703 --- /dev/null +++ b/data/assets/dashboard/default_dashboard.asset @@ -0,0 +1,8 @@ +local assetHelper = asset.require('util/asset_helper') + +asset.require('./date') +asset.require('./simulationincrement') +asset.require('./distance') +asset.require('./framerate') +asset.require('./parallelconnection') +asset.require('./globelocation') diff --git a/data/assets/dashboard/distance.asset b/data/assets/dashboard/distance.asset new file mode 100644 index 0000000000..b05f79520d --- /dev/null +++ b/data/assets/dashboard/distance.asset @@ -0,0 +1,13 @@ +local item = { + Type = "DashboardItemDistance", + Identifier = "Distance", + GuiName = "Distance" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/dashboard/framerate.asset b/data/assets/dashboard/framerate.asset new file mode 100644 index 0000000000..ea95484f7d --- /dev/null +++ b/data/assets/dashboard/framerate.asset @@ -0,0 +1,13 @@ +local item = { + Type = "DashboardItemFramerate", + Identifier = "Framerate", + GuiName = "Framerate" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/dashboard/globelocation.asset b/data/assets/dashboard/globelocation.asset new file mode 100644 index 0000000000..da36c97a46 --- /dev/null +++ b/data/assets/dashboard/globelocation.asset @@ -0,0 +1,13 @@ +local item = { + Type = "DashboardItemGlobeLocation", + Identifier = "GlobeLocation", + GuiName = "Globe Location" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/dashboard/parallelconnection.asset b/data/assets/dashboard/parallelconnection.asset new file mode 100644 index 0000000000..6bc13dc818 --- /dev/null +++ b/data/assets/dashboard/parallelconnection.asset @@ -0,0 +1,13 @@ +local item = { + Type = "DashboardItemParallelConnection", + Identifier = "ParallelConnection", + GuiName = "Parallel Connection" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/dashboard/simulationincrement.asset b/data/assets/dashboard/simulationincrement.asset new file mode 100644 index 0000000000..6431e403bd --- /dev/null +++ b/data/assets/dashboard/simulationincrement.asset @@ -0,0 +1,13 @@ +local item = { + Type = "DashboardItemSimulationIncrement", + Identifier = "SimulationIncrement", + GuiName = "Simulation Increment" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/dashboard/velocity.asset b/data/assets/dashboard/velocity.asset new file mode 100644 index 0000000000..89655ce60f --- /dev/null +++ b/data/assets/dashboard/velocity.asset @@ -0,0 +1,14 @@ +local item = { + Type = "DashboardItemVelocity", + Identifier = "GlobeLocation", + Simplification = true, + GuiName = "Velocity" +} + +asset.onInitialize(function() + openspace.dashboard.addDashboardItem(item) +end) + +asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(item.Identifier) +end) diff --git a/data/assets/util/default_dashboard.asset b/data/assets/util/default_dashboard.asset deleted file mode 100644 index 935aae0956..0000000000 --- a/data/assets/util/default_dashboard.asset +++ /dev/null @@ -1,34 +0,0 @@ -local assetHelper = asset.require('util/asset_helper') - -assetHelper.registerDashboardItems(asset, { - { - Type = "DashboardItemDate", - Identifier = "Date", - GuiName = "Date" - }, - { - Type = "DashboardItemSimulationIncrement", - Identifier = "SimulationIncrement", - GuiName = "Simulation Increment" - }, - { - Type = "DashboardItemDistance", - Identifier = "Distance", - GuiName = "Distance" - }, - { - Type = "DashboardItemFramerate", - Identifier = "Framerate", - GuiName = "Framerate" - }, - { - Type = "DashboardItemParallelConnection", - Identifier = "ParallelConnection", - GuiName = "Parallel Connection" - }, - { - Type = "DashboardItemGlobeLocation", - Identifier = "GlobeLocation", - GuiName = "Globe Location" - } -}) From 38f5a12c24af0ff2565f9e6302d6dfce934e8918 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 24 Mar 2021 14:02:05 +0100 Subject: [PATCH 11/12] Avoid crash in RenderEngine if screenshot token has not been registered This is the case in for example the test application --- src/rendering/renderengine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 38b5c6c9f4..fdc278b9bd 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -357,6 +357,11 @@ RenderEngine::RenderEngine() addProperty(_applyWarping); _screenshotUseDate.onChange([this]() { + // If there is no screenshot folder, don't bother with handling the change + if (!FileSys.hasRegisteredToken("${STARTUP_SCREENSHOT}")) { + return; + } + if (_screenshotUseDate) { // Going from 'false' -> 'true' // We might need to create the folder first From a6bb8cdecb560d2940e0e134888577c6243a6c78 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Wed, 24 Mar 2021 17:46:13 +0100 Subject: [PATCH 12/12] Split DashboardTextItem from DashboardItem --- include/openspace/rendering/dashboarditem.h | 19 --- .../openspace/rendering/dashboardtextitem.h | 56 ++++++++ modules/base/dashboard/dashboarditemangle.h | 2 +- modules/base/dashboard/dashboarditemdate.h | 2 +- .../base/dashboard/dashboarditemdistance.h | 2 +- .../base/dashboard/dashboarditemframerate.h | 2 +- modules/base/dashboard/dashboarditemmission.h | 2 +- .../dashboarditemparallelconnection.h | 2 +- .../dashboard/dashboarditempropertyvalue.h | 2 +- .../dashboarditemsimulationincrement.h | 2 +- modules/base/dashboard/dashboarditemtext.h | 2 +- .../base/dashboard/dashboarditemvelocity.h | 2 +- src/CMakeLists.txt | 2 + src/rendering/dashboarditem.cpp | 133 +++--------------- src/rendering/dashboardtextitem.cpp | 88 ++++++++++++ 15 files changed, 176 insertions(+), 142 deletions(-) create mode 100644 include/openspace/rendering/dashboardtextitem.h create mode 100644 src/rendering/dashboardtextitem.cpp diff --git a/include/openspace/rendering/dashboarditem.h b/include/openspace/rendering/dashboarditem.h index 11013a5d28..6e5d28060d 100644 --- a/include/openspace/rendering/dashboarditem.h +++ b/include/openspace/rendering/dashboarditem.h @@ -28,12 +28,9 @@ #include #include -#include -#include #include namespace ghoul { class Dictionary; } -namespace ghoul::fontrendering { class Font; } namespace openspace { @@ -58,22 +55,6 @@ protected: properties::BoolProperty _isEnabled; }; - - -class DashboardTextItem : public DashboardItem { -public: - static documentation::Documentation Documentation(); - - DashboardTextItem(const ghoul::Dictionary& dictionary, float fontSize = 10.f, - const std::string& fontName = "Mono"); - -protected: - properties::StringProperty _fontName; - properties::FloatProperty _fontSize; - - std::shared_ptr _font; -}; - } // openspace #endif // __OPENSPACE_CORE___DASHBOARDITEM___H__ diff --git a/include/openspace/rendering/dashboardtextitem.h b/include/openspace/rendering/dashboardtextitem.h new file mode 100644 index 0000000000..5dbeb00de9 --- /dev/null +++ b/include/openspace/rendering/dashboardtextitem.h @@ -0,0 +1,56 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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_CORE___DASHBOARDTEXTITEM___H__ +#define __OPENSPACE_CORE___DASHBOARDTEXTITEM___H__ + +#include + +#include +#include + +namespace ghoul { class Dictionary; } +namespace ghoul::fontrendering { class Font; } + +namespace openspace { + +namespace documentation { struct Documentation; } + +class DashboardTextItem : public DashboardItem { +public: + static documentation::Documentation Documentation(); + + DashboardTextItem(const ghoul::Dictionary& dictionary, float fontSize = 10.f, + const std::string& fontName = "Mono"); + +protected: + properties::StringProperty _fontName; + properties::FloatProperty _fontSize; + + std::shared_ptr _font; +}; + +} // openspace + +#endif // __OPENSPACE_CORE___DASHBOARDITEM___H__ diff --git a/modules/base/dashboard/dashboarditemangle.h b/modules/base/dashboard/dashboarditemangle.h index b6d234d862..69c7e41a65 100644 --- a/modules/base/dashboard/dashboarditemangle.h +++ b/modules/base/dashboard/dashboarditemangle.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMANGLE___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMANGLE___H__ -#include +#include #include #include diff --git a/modules/base/dashboard/dashboarditemdate.h b/modules/base/dashboard/dashboarditemdate.h index 602de7b2c9..6e2acd9c43 100644 --- a/modules/base/dashboard/dashboarditemdate.h +++ b/modules/base/dashboard/dashboarditemdate.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMDATE___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMDATE___H__ -#include +#include #include diff --git a/modules/base/dashboard/dashboarditemdistance.h b/modules/base/dashboard/dashboarditemdistance.h index bb3b7037ef..dc0cd2a64c 100644 --- a/modules/base/dashboard/dashboarditemdistance.h +++ b/modules/base/dashboard/dashboarditemdistance.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMDISTANCE___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMDISTANCE___H__ -#include +#include #include #include diff --git a/modules/base/dashboard/dashboarditemframerate.h b/modules/base/dashboard/dashboarditemframerate.h index 17038cc572..226d488dd8 100644 --- a/modules/base/dashboard/dashboarditemframerate.h +++ b/modules/base/dashboard/dashboarditemframerate.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMFRAMERATE___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMFRAMERATE___H__ -#include +#include #include #include diff --git a/modules/base/dashboard/dashboarditemmission.h b/modules/base/dashboard/dashboarditemmission.h index bb02551fdf..1156cebe4f 100644 --- a/modules/base/dashboard/dashboarditemmission.h +++ b/modules/base/dashboard/dashboarditemmission.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMMISSION___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMMISSION___H__ -#include +#include namespace openspace { diff --git a/modules/base/dashboard/dashboarditemparallelconnection.h b/modules/base/dashboard/dashboarditemparallelconnection.h index 7e13459850..687eb3734b 100644 --- a/modules/base/dashboard/dashboarditemparallelconnection.h +++ b/modules/base/dashboard/dashboarditemparallelconnection.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMPARALLELCONNECTION___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMPARALLELCONNECTION___H__ -#include +#include namespace openspace { diff --git a/modules/base/dashboard/dashboarditempropertyvalue.h b/modules/base/dashboard/dashboarditempropertyvalue.h index 2f14a3aeff..73f2191ba1 100644 --- a/modules/base/dashboard/dashboarditempropertyvalue.h +++ b/modules/base/dashboard/dashboarditempropertyvalue.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMPROPERTYVALUE___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMPROPERTYVALUE___H__ -#include +#include #include diff --git a/modules/base/dashboard/dashboarditemsimulationincrement.h b/modules/base/dashboard/dashboarditemsimulationincrement.h index 7b3367811f..2f214de3f9 100644 --- a/modules/base/dashboard/dashboarditemsimulationincrement.h +++ b/modules/base/dashboard/dashboarditemsimulationincrement.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMSIMULATIONINCREMENT___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMSIMULATIONINCREMENT___H__ -#include +#include #include #include diff --git a/modules/base/dashboard/dashboarditemtext.h b/modules/base/dashboard/dashboarditemtext.h index 648b501805..a5be0355fd 100644 --- a/modules/base/dashboard/dashboarditemtext.h +++ b/modules/base/dashboard/dashboarditemtext.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMTEXT___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMTEXT___H__ -#include +#include #include diff --git a/modules/base/dashboard/dashboarditemvelocity.h b/modules/base/dashboard/dashboarditemvelocity.h index fa2664b2cf..827ab6dc91 100644 --- a/modules/base/dashboard/dashboarditemvelocity.h +++ b/modules/base/dashboard/dashboarditemvelocity.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMVELOCITY___H__ #define __OPENSPACE_MODULE_BASE___DASHBOARDITEMVELOCITY___H__ -#include +#include #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c68ddecfc6..1efeee4a4d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -132,6 +132,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/rendering/dashboard.cpp ${OPENSPACE_BASE_DIR}/src/rendering/dashboard_lua.inl ${OPENSPACE_BASE_DIR}/src/rendering/dashboarditem.cpp + ${OPENSPACE_BASE_DIR}/src/rendering/dashboardtextitem.cpp ${OPENSPACE_BASE_DIR}/src/rendering/framebufferrenderer.cpp ${OPENSPACE_BASE_DIR}/src/rendering/deferredcastermanager.cpp ${OPENSPACE_BASE_DIR}/src/rendering/helper.cpp @@ -322,6 +323,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/rendering/abufferrenderer.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/dashboard.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/dashboarditem.h + ${OPENSPACE_BASE_DIR}/include/openspace/rendering/dashboardtextitem.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/framebufferrenderer.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/deferredcaster.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/deferredcasterlistener.h diff --git a/src/rendering/dashboarditem.cpp b/src/rendering/dashboarditem.cpp index 9dbc2e3604..a0f907f11b 100644 --- a/src/rendering/dashboarditem.cpp +++ b/src/rendering/dashboarditem.cpp @@ -28,8 +28,8 @@ #include #include #include -#include #include +#include namespace { constexpr const char* KeyType = "Type"; @@ -40,12 +40,6 @@ namespace { "If this value is set to 'true' this dashboard item is shown in the dashboard" }; - constexpr openspace::properties::Property::PropertyInfo TypeInfo = { - "Type", - "Type", - "" - }; - constexpr openspace::properties::Property::PropertyInfo IdentifierInfo = { "Identifier", "Identifier", @@ -57,36 +51,25 @@ namespace { "Gui Name", "" }; + + struct [[codegen::Dictionary(DashboardItem)]] Parameters { + std::string type; + + // [[codegen::verbatim(IdentifierInfo.description)]] + std::string identifier; + + // [[codegen::verbatim(GuiNameInfo.description)]] + std::optional guiName; + }; +#include "dashboarditem_codegen.cpp" } // namespace namespace openspace { documentation::Documentation DashboardItem::Documentation() { - using namespace documentation; - return { - "DashboardItem", - "dashboarditem", - { - { - TypeInfo.identifier, - new StringVerifier, - Optional::No, - TypeInfo.description - }, - { - IdentifierInfo.identifier, - new StringVerifier, - Optional::No, - IdentifierInfo.description - }, - { - GuiNameInfo.identifier, - new StringVerifier, - Optional::Yes, - GuiNameInfo.description - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "dashboarditem"; + return doc; } std::unique_ptr DashboardItem::createFromDictionary( @@ -105,18 +88,11 @@ DashboardItem::DashboardItem(const ghoul::Dictionary& dictionary) : properties::PropertyOwner({ "", "" }) , _isEnabled(EnabledInfo, true) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "DashboardItem" - ); - - std::string identifier = dictionary.value(IdentifierInfo.identifier); - setIdentifier(std::move(identifier)); - - if (dictionary.hasValue(GuiNameInfo.identifier)) { - std::string guiName = dictionary.value(GuiNameInfo.identifier); - setGuiName(std::move(guiName)); + const Parameters p = codegen::bake(dictionary); + + setIdentifier(p.identifier); + if (p.guiName.has_value()) { + setGuiName(*p.guiName); } addProperty(_isEnabled); @@ -126,73 +102,4 @@ bool DashboardItem::isEnabled() const { return _isEnabled; } - -namespace { - constexpr openspace::properties::Property::PropertyInfo FontNameInfo = { - "FontName", - "Font Name", - "This value is the name of the font that is used. It can either refer to an " - "internal name registered previously, or it can refer to a path that is used." - }; - - constexpr openspace::properties::Property::PropertyInfo FontSizeInfo = { - "FontSize", - "Font Size", - "This value determines the size of the font that is used to render the distance." - }; -} // namespace - -documentation::Documentation DashboardTextItem::Documentation() { - using namespace documentation; - return { - "DashboardTextItem", - "dashboardtextitem", - { - { - FontNameInfo.identifier, - new StringVerifier, - Optional::Yes, - FontNameInfo.description - }, - { - FontSizeInfo.identifier, - new IntVerifier, - Optional::Yes, - FontSizeInfo.description - } - } - }; -} - -DashboardTextItem::DashboardTextItem(const ghoul::Dictionary& dictionary, float fontSize, - const std::string& fontName) - : DashboardItem(dictionary) - , _fontName(FontNameInfo, fontName) - , _fontSize(FontSizeInfo, fontSize, 6.f, 144.f, 1.f) -{ - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "DashboardTextItem" - ); - - if (dictionary.hasKey(FontNameInfo.identifier)) { - _fontName = dictionary.value(FontNameInfo.identifier); - } - _fontName.onChange([this]() { - _font = global::fontManager->font(_fontName, _fontSize); - }); - addProperty(_fontName); - - if (dictionary.hasKey(FontSizeInfo.identifier)) { - _fontSize = static_cast(dictionary.value(FontSizeInfo.identifier)); - } - _fontSize.onChange([this]() { - _font = global::fontManager->font(_fontName, _fontSize); - }); - addProperty(_fontSize); - - _font = global::fontManager->font(_fontName, _fontSize); -} - } // namespace openspace diff --git a/src/rendering/dashboardtextitem.cpp b/src/rendering/dashboardtextitem.cpp new file mode 100644 index 0000000000..fae12f4403 --- /dev/null +++ b/src/rendering/dashboardtextitem.cpp @@ -0,0 +1,88 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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 + +namespace { + constexpr openspace::properties::Property::PropertyInfo FontNameInfo = { + "FontName", + "Font Name", + "This value is the name of the font that is used. It can either refer to an " + "internal name registered previously, or it can refer to a path that is used." + }; + + constexpr openspace::properties::Property::PropertyInfo FontSizeInfo = { + "FontSize", + "Font Size", + "This value determines the size of the font that is used to render the distance." + }; + + struct [[codegen::Dictionary(DashboardTextItem)]] Parameters { + // [[codegen::verbatim(FontNameInfo.description)]] + std::optional fontName; + + // [[codegen::verbatim(FontSizeInfo.description)]] + std::optional fontSize; + }; +#include "dashboardtextitem_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation DashboardTextItem::Documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "dashboardtextitem"; + return doc; +} + +DashboardTextItem::DashboardTextItem(const ghoul::Dictionary& dictionary, float fontSize, + const std::string& fontName) + : DashboardItem(dictionary) + , _fontName(FontNameInfo, fontName) + , _fontSize(FontSizeInfo, fontSize, 6.f, 144.f, 1.f) +{ + const Parameters p = codegen::bake(dictionary); + + _fontName = p.fontName.value_or(_fontName); + _fontName.onChange([this]() { + _font = global::fontManager->font(_fontName, _fontSize); + }); + addProperty(_fontName); + + _fontSize = p.fontSize.value_or(_fontSize); + _fontSize.onChange([this]() { + _font = global::fontManager->font(_fontName, _fontSize); + }); + addProperty(_fontSize); + + _font = global::fontManager->font(_fontName, _fontSize); +} + +} // namespace openspace