Merge branch 'master' into thesis/2021/skybrowser

This commit is contained in:
Ester Lindgren
2021-04-06 08:52:11 +02:00
120 changed files with 4146 additions and 4595 deletions

View File

@@ -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
@@ -133,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
@@ -161,7 +161,6 @@ set(OPENSPACE_SOURCE
${OPENSPACE_BASE_DIR}/src/scene/sceneinitializer.cpp
${OPENSPACE_BASE_DIR}/src/scene/scenelicensewriter.cpp
${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode.cpp
${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode_doc.inl
${OPENSPACE_BASE_DIR}/src/scene/timeframe.cpp
${OPENSPACE_BASE_DIR}/src/scene/translation.cpp
${OPENSPACE_BASE_DIR}/src/scripting/lualibrary.cpp
@@ -208,6 +207,10 @@ if (APPLE)
${OPENSPACE_SOURCE}
${OPENSPACE_BASE_DIR}/src/interaction/touchbar.mm
)
set_source_files_properties(
${OPENSPACE_BASE_DIR}/src/interaction/touchbar.mm
PROPERTIES SKIP_PRECOMPILE_HEADERS ON
)
endif ()
set(OPENSPACE_HEADER
${OPENSPACE_BASE_DIR}/include/openspace/json.h
@@ -323,6 +326,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

View File

@@ -140,21 +140,6 @@ namespace openspace::documentation {
const std::string DocumentationEntry::Wildcard = "*";
//std::string concatenate(const std::vector<TestResult::Offense>& offenses) {
// std::string result = "Error in specification (";
// for (const TestResult::Offense& o : offenses) {
// if (o.explanation.empty()) {
// result += fmt::format("{} ({}), ", o.offender, ghoul::to_string(o.reason));
// }
// else {
// result += fmt::format("{} ({}: {}), ", o.offender, ghoul::to_string(o.reason), o.explanation);
// }
// }
// result.pop_back();
// result.back() = ')';
// return result;
//}
SpecificationError::SpecificationError(TestResult res, std::string comp)
: ghoul::RuntimeError("Error in specification", std::move(comp))
, result(std::move(res))

View File

@@ -139,7 +139,9 @@ TestResult IntVerifier::operator()(const ghoul::Dictionary& dict,
{
if (dict.hasValue<int>(key)) {
// We have a key and the value is int, we are done
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -149,23 +151,39 @@ TestResult IntVerifier::operator()(const ghoul::Dictionary& dict,
double intPart;
bool isInt = modf(value, &intPart) == 0.0;
if (isInt) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{ { key, TestResult::Offense::Reason::WrongType } },
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
// If we don't have a double value, we cannot have an int value
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -174,6 +192,33 @@ std::string IntVerifier::type() const {
return "Integer";
}
StringVerifier::StringVerifier(bool mustBeNotEmpty)
: TemplateVerifier<std::string>()
, _mustBeNotEmpty(mustBeNotEmpty)
{}
TestResult StringVerifier::operator()(const ghoul::Dictionary& dictionary,
const std::string& key) const
{
TestResult res = TemplateVerifier<std::string>::operator()(dictionary, key);
if (!res.success) {
return res;
}
std::string value = dictionary.value<std::string>(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";
}
@@ -237,17 +282,26 @@ TestResult Color3Verifier::operator()(const ghoul::Dictionary& dictionary,
glm::dvec3 values = dictionary.value<glm::dvec3>(key);
if (values.x < 0.0 || values.x > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".x", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".x";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values.y < 0.0 || values.y > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".y", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".y";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values.z < 0.0 || values.z > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".z", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".z";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
return res;
@@ -265,27 +319,39 @@ TestResult Color4Verifier::operator()(const ghoul::Dictionary& dictionary,
return res;
}
std::vector<double> values = dictionary.value<std::vector<double>>(key);
if (values[0] < 0.0 || values[0] > 1.0) {
glm::dvec4 values = dictionary.value<glm::dvec4>(key);
if (values.x < 0.0 || values.x > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".x", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".x";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values[1] < 0.0 || values[1] > 1.0) {
if (values.y < 0.0 || values.y > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".y", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".y";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values[2] < 0.0 || values[2] > 1.0) {
if (values.z < 0.0 || values.z > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".z", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".z";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
if (values[3] < 0.0 || values[3] > 1.0) {
if (values.w < 0.0 || values.w > 1.0) {
res.success = false;
res.offenses.push_back({ key + ".a", TestResult::Offense::Reason::Verification });
TestResult::Offense o;
o.offender = key + ".a";
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
}
return res;
}
@@ -298,7 +364,9 @@ TestResult TemplateVerifier<glm::ivec2>::operator()(const ghoul::Dictionary& dic
const std::string& key) const
{
if (dict.hasValue<glm::ivec2>(key)) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -310,22 +378,38 @@ TestResult TemplateVerifier<glm::ivec2>::operator()(const ghoul::Dictionary& dic
modf(value.y, &intPart.y) == 0.0
};
if (isInt.x && isInt.y) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{{ key, TestResult::Offense::Reason::WrongType }},
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -335,7 +419,9 @@ TestResult TemplateVerifier<glm::ivec3>::operator()(const ghoul::Dictionary& dic
const std::string& key) const
{
if (dict.hasValue<glm::ivec3>(key)) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -348,22 +434,38 @@ TestResult TemplateVerifier<glm::ivec3>::operator()(const ghoul::Dictionary& dic
modf(value.z, &intPart.z) == 0.0
};
if (isInt.x && isInt.y && isInt.z) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{{ key, TestResult::Offense::Reason::WrongType }},
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -373,7 +475,9 @@ TestResult TemplateVerifier<glm::ivec4>::operator()(const ghoul::Dictionary& dic
const std::string& key) const
{
if (dict.hasValue<glm::ivec4>(key)) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
if (dict.hasKey(key)) {
@@ -387,22 +491,38 @@ TestResult TemplateVerifier<glm::ivec4>::operator()(const ghoul::Dictionary& dic
modf(value.w, &intPart.w) == 0.0
};
if (isInt.x && isInt.y && isInt.z && isInt.w) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return {
false,
{{ key, TestResult::Offense::Reason::WrongType }},
{}
};
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
}
else {
return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -432,11 +552,22 @@ TestResult TableVerifier::operator()(const ghoul::Dictionary& dictionary,
}
else {
if (dictionary.hasKey(key)) {
return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::WrongType;
res.offenses.push_back(o);
return res;
}
else {
return { false, { { key, TestResult::Offense::Reason::MissingKey } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::MissingKey;
res.offenses.push_back(o);
return res;
}
}
}
@@ -485,11 +616,11 @@ TestResult ReferencingVerifier::operator()(const ghoul::Dictionary& dictionary,
);
if (it == docs.end()) {
res.offenses.push_back({
key,
TestResult::Offense::Reason::UnknownIdentifier
});
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::UnknownIdentifier;
res.offenses.push_back(o);
return res;
}
@@ -549,10 +680,18 @@ TestResult AndVerifier::operator()(const ghoul::Dictionary& dictionary,
);
if (success) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
return res;
}
}
@@ -618,10 +757,18 @@ TestResult OrVerifier::operator()(const ghoul::Dictionary& dictionary,
);
if (success) {
return { true, {}, {} };
TestResult res;
res.success = true;
return res;
}
else {
return { false, { { key, TestResult::Offense::Reason::Verification } }, {} };
TestResult res;
res.success = false;
TestResult::Offense o;
o.offender = key;
o.reason = TestResult::Offense::Reason::Verification;
res.offenses.push_back(o);
return res;
}
}

View File

@@ -27,287 +27,305 @@
#include <openspace/documentation/documentation.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/lua/ghoul_lua.h>
#include <ghoul/lua/lua_helper.h>
#include <ghoul/misc/assert.h>
#include <optional>
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";
constexpr const char* StrictLuaScript = "${BASE}/scripts/strict.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<std::string> 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<std::string> asset;
template <typename T>
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<std::string> 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<std::vector<std::string>> 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<std::string, std::string> 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<std::map<std::string, std::string>> 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<Level> logLevel;
if constexpr (std::is_same_v<T, glm::dvec3>) {
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(L);
glm::dvec3 res;
res.x = d.value<double>("1");
res.y = d.value<double>("2");
res.z = d.value<double>("3");
value = res;
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(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<bool> immediateFlush;
std::vector<std::string> res;
for (size_t i = 1; i <= d.size(); ++i) {
res.push_back(d.value<std::string>(std::to_string(i)));
}
value = std::move(res);
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, std::map<std::string, std::string>>) {
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(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<std::vector<ghoul::Dictionary>> logs
[[codegen::reference("core_logfactory")]];
std::map<std::string, std::string> res;
std::vector<std::string_view> keys = d.keys();
for (size_t i = 0; i < d.size(); ++i) {
std::string_view key = keys[i];
std::string v = d.value<std::string>(key);
res[std::string(key)] = std::move(v);
}
value = std::move(res);
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, std::map<std::string, ghoul::Dictionary>>) {
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(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<Verbosity> 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> logging;
std::map<std::string, ghoul::Dictionary> res;
std::vector<std::string_view> keys = d.keys();
for (size_t i = 0; i < d.size(); ++i) {
std::string_view key = keys[i];
ghoul::Dictionary v = d.value<ghoul::Dictionary>(key);
res[std::string(key)] = std::move(v);
}
value = std::move(res);
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, Configuration::Logging>) {
Configuration::Logging& v = static_cast<Configuration::Logging&>(value);
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(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<std::string> scriptLog;
if (d.hasValue<std::string>(KeyLogLevel)) {
v.level = d.value<std::string>(KeyLogLevel);
}
if (d.hasValue<bool>(KeyImmediateFlush)) {
v.forceImmediateFlush = d.value<bool>(KeyImmediateFlush);
}
if (d.hasValue<std::string>(KeyCapabilitiesVerbosity)) {
v.capabilitiesVerbosity = d.value<std::string>(KeyCapabilitiesVerbosity);
}
struct Documentation {
// The path where the documentation files will be stored
std::optional<std::string> path;
};
// Right now only contains the path where the documentation is written to
std::optional<Documentation> documentation;
if (d.hasValue<ghoul::Dictionary>(KeyLogs)) {
ghoul::Dictionary l = d.value<ghoul::Dictionary>(KeyLogs);
std::vector<ghoul::Dictionary> res;
for (size_t i = 1; i <= l.size(); ++i) {
res.push_back(l.value<ghoul::Dictionary>(std::to_string(i)));
}
v.logs = res;
}
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, Configuration::DocumentationInfo>) {
Configuration::DocumentationInfo& v =
static_cast<Configuration::DocumentationInfo&>(value);
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(L);
if (d.hasValue<std::string>(KeyDocumentationPath)) {
v.path = d.value<std::string>(KeyDocumentationPath);
}
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, Configuration::LoadingScreen>) {
Configuration::LoadingScreen& v =
static_cast<Configuration::LoadingScreen&>(value);
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(L);
if (d.hasValue<bool>(KeyShowMessage)) {
v.isShowingMessages = d.value<bool>(KeyShowMessage);
}
if (d.hasValue<bool>(KeyShowNodeNames)) {
v.isShowingNodeNames = d.value<bool>(KeyShowNodeNames);
}
if (d.hasValue<bool>(KeyShowProgressbar)) {
v.isShowingProgressbar = d.value<bool>(KeyShowProgressbar);
}
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, Configuration::OpenGLDebugContext>) {
Configuration::OpenGLDebugContext& v =
static_cast<Configuration::OpenGLDebugContext&>(value);
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(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<float> shutdownCountdown [[codegen::greater(0.0)]];
if (d.hasValue<bool>(KeyActivate)) {
v.isActive = d.value<bool>(KeyActivate);
}
if (d.hasValue<bool>(KeySynchronous)) {
v.isSynchronous = d.value<bool>(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<bool> perSceneCache;
if (d.hasValue<ghoul::Dictionary>(KeyFilterIdentifier)) {
ghoul::Dictionary f = d.value<ghoul::Dictionary>(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<Scaling> onScreenTextScaling;
std::vector<Configuration::OpenGLDebugContext::IdentifierFilter> res;
for (size_t i = 1; i <= f.size(); ++i) {
Configuration::OpenGLDebugContext::IdentifierFilter filter;
ghoul::Dictionary fi = f.value<ghoul::Dictionary>(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> renderingMethod;
if (fi.hasValue<double>(KeyIdentifier)) {
filter.identifier = static_cast<unsigned int>(
fi.value<double>(KeyIdentifier)
);
}
if (fi.hasValue<std::string>(KeySource)) {
filter.source = fi.value<std::string>(KeySource);
}
if (fi.hasValue<std::string>(KeyType)) {
filter.type = fi.value<std::string>(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<bool> 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<glm::dvec3> 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<glm::dvec3> masterRotation;
v.identifierFilters = res;
}
// Applies a global rotation for all screenspace renderables. Defined using roll,
// pitch, yaw in radians
std::optional<glm::dvec3> screenSpaceRotation;
if (d.hasValue<ghoul::Dictionary>(KeyFilterSeverity)) {
ghoul::Dictionary f = d.value<ghoul::Dictionary>(KeyFilterSeverity);
// If this value is set to 'true' the ingame console is disabled, locking the
// system down against random access
std::optional<bool> disableInGameConsole;
std::vector<std::string> res;
for (size_t i = 1; i <= f.size(); ++i) {
res.push_back(f.value<std::string>(std::to_string(i)));
}
v.severityFilters = res;
}
}
// NOLINTNEXTLINE
else if constexpr (std::is_same_v<T, Configuration::HTTPProxy>) {
Configuration::HTTPProxy& v = static_cast<Configuration::HTTPProxy&>(value);
ghoul::Dictionary d = ghoul::lua::value<ghoul::Dictionary>(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<bool> screenshotUseDate;
if (d.hasValue<bool>(KeyActivate)) {
v.usingHttpProxy = d.value<bool>(KeyActivate);
}
if (d.hasValue<std::string>(KeyAddress)) {
v.address = d.value<std::string>(KeyAddress);
}
if (d.hasValue<double>(KeyPort)) {
v.port = static_cast<unsigned int>(d.value<double>(KeyPort));
}
if (d.hasValue<std::string>(KeyAuthentication)) {
v.authentication = d.value<std::string>(KeyAuthentication);
}
if (d.hasValue<std::string>(KeyUser)) {
v.user = d.value<std::string>(KeyUser);
}
if (d.hasValue<std::string>(KeyPassword)) {
v.password = d.value<std::string>(KeyPassword);
}
}
else {
value = ghoul::lua::value<T>(L);
}
}
struct HttpProxy {
// Determines whether the proxy is being used
std::optional<bool> 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> authentication;
// The user of the http proxy
std::optional<std::string> user;
// The password of the http proxy
std::optional<std::string> 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> 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<bool> 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<std::vector<Filter>> 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<std::vector<Severity>> filterSeverity;
};
// Determines the settings for the creation of an OpenGL debug context
std::optional<OpenGLDebugContext> 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<bool> 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<bool> 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<bool> 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<bool> 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<std::string> 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<bool> 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<bool> 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<bool> 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> loadingScreen;
// List of profiles that cannot be overwritten by user
std::optional<std::vector<std::string>> readOnlyProfiles;
// Configurations for each module
std::optional<std::map<std::string, ghoul::Dictionary>> moduleConfigurations;
};
#include "configuration_codegen.cpp"
} // namespace
#include "configuration_doc.inl"
namespace openspace::configuration {
void parseLuaState(Configuration& configuration) {
@@ -317,40 +335,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<std::string>(
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<Parameters>();
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<Parameters>(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<unsigned int>(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<unsigned int>(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<Parameters>();
std::string findConfiguration(const std::string& filename) {
using ghoul::filesystem::Directory;
@@ -383,24 +638,31 @@ 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));
}
if (FileSys.fileExists(absPath(StrictLuaScript))) {
ghoul::lua::runScriptFile(result.state, absPath(StrictLuaScript));
}
// 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;

View File

@@ -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 <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
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 <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>."
},
{
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

View File

@@ -32,186 +32,130 @@
#include <ghoul/logging/textlog.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/misc/exception.h>
#include <optional>
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<bool> append;
// Determines whether the log entires should be stamped with the time at which the
// message was logged
std::optional<bool> timeStamping;
// Determines whether the log entries should be stamped with the date at which the
// message was logged
std::optional<bool> dateStamping;
// Determines whether the log entries should be stamped with the category that
// creates the log message
std::optional<bool> categoryStamping;
// Determines whether the log entries should be stamped with the log level that
// was used to create the log message
std::optional<bool> logLevelStamping;
enum class LogLevel {
AllLogging,
Trace,
Debug,
Info,
Warning,
Error,
Fatal,
NoLogging
};
// The log level for this specific text-based log
std::optional<LogLevel> 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<Parameters>();
doc.id = "core_logfactory";
return doc;
}
std::unique_ptr<ghoul::logging::Log> createLog(const ghoul::Dictionary& dictionary) {
documentation::testSpecificationAndThrow(
LogFactoryDocumentation(),
dictionary,
"LogFactory"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
// 'type' and 'filename' are required keys
const std::string& type = dictionary.value<std::string>(KeyType);
const std::string& filename = absPath(dictionary.value<std::string>(KeyFilename));
// the rest are optional
bool append = true;
if (dictionary.hasValue<bool>(KeyAppend)) {
append = dictionary.value<bool>(KeyAppend);
}
bool timeStamp = true;
if (dictionary.hasValue<bool>(KeyTimeStamping)) {
timeStamp = dictionary.value<bool>(KeyTimeStamping);
}
bool dateStamp = true;
if (dictionary.hasValue<bool>(KeyDateStamping)) {
dateStamp = dictionary.value<bool>(KeyDateStamping);
}
bool categoryStamp = true;
if (dictionary.hasValue<bool>(KeyCategoryStamping)) {
categoryStamp = dictionary.value<bool>(KeyCategoryStamping);
}
bool logLevelStamp = true;
if (dictionary.hasValue<bool>(KeyLogLevelStamping)) {
logLevelStamp = dictionary.value<bool>(KeyLogLevelStamping);
}
std::string logLevel;
if (dictionary.hasValue<std::string>(KeyLogLevel)) {
logLevel = dictionary.value<std::string>(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<std::string> cssFiles{absPath(BootstrapPath), absPath(CssPath)};
std::vector<std::string> jsFiles{absPath(JsPath)};
if (logLevel.empty()) {
return std::make_unique<ghoul::logging::HTMLLog>(
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);
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<std::string> cssFiles{ absPath(BootstrapPath), absPath(CssPath) };
std::vector<std::string> jsFiles{ absPath(JsPath) };
return std::make_unique<ghoul::logging::HTMLLog>(
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<ghoul::logging::LogLevel>(logLevel)
);
level
);
}
}
else if (type == ValueTextLog) {
if (logLevel.empty()) {
case Parameters::Type::Text:
return std::make_unique<ghoul::logging::TextLog>(
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<ghoul::logging::TextLog>(
filename,
Append(append),
TimeStamping(timeStamp),
DateStamping(dateStamp),
CategoryStamping(categoryStamp),
LogLevelStamping(logLevelStamp),
ghoul::from_string<ghoul::logging::LogLevel>(logLevel)
);
}
}
else {
throw ghoul::MissingCaseException();
default:
throw new ghoul::MissingCaseException();
}
}

View File

@@ -63,7 +63,6 @@ void ModuleEngine::initialize(
m->initialize(configuration);
}
catch (const documentation::SpecificationError& e) {
//LFATALC(e.component, e.message);
for (const documentation::TestResult::Offense& o : e.result.offenses) {
LERRORC(e.component, o.offender + ": " + ghoul::to_string(o.reason));
}

View File

@@ -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 "

View File

@@ -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<std::string>(L, 1);
const ghoul::Dictionary& d = ghoul::lua::value<ghoul::Dictionary>(L, 2);
@@ -306,11 +306,10 @@ int createSingeColorImage(lua_State* L) {
// @TODO (emmbr 2020-12-18) Verify that the input dictionary is a vec3
// Would like to clean this up with a more direct use of the Verifier in the future
using namespace openspace::documentation;
const std::string& key = "color";
ghoul::Dictionary colorDict;
colorDict.setValue(key, d);
TestResult res = Color3Verifier()(colorDict, key);
documentation::TestResult res = documentation::Color3Verifier()(colorDict, key);
if (!res.success) {
return ghoul::lua::luaError(

View File

@@ -44,13 +44,6 @@
namespace {
constexpr const char* _loggerCat = "NavigationHandler";
constexpr const char* KeyAnchor = "Anchor";
constexpr const char* KeyAim = "Aim";
constexpr const char* KeyPosition = "Position";
constexpr const char* KeyUp = "Up";
constexpr const char* KeyYaw = "Yaw";
constexpr const char* KeyPitch = "Pitch";
constexpr const char* KeyReferenceFrame = "ReferenceFrame";
const double Epsilon = 1E-7;
using namespace openspace;
@@ -73,6 +66,31 @@ namespace {
"than using the mouse interaction."
};
struct [[codegen::Dictionary(NavigationHandler)]] Parameters {
// The identifier of the anchor node
std::string anchor;
// The identifier of the aim node, if used
std::optional<std::string> aim;
// The identifier of the scene graph node to use as reference frame. If not
// specified, this will be the same as the anchor
std::optional<std::string> referenceFrame;
// The position of the camera relative to the anchor node, expressed in meters in
// the specified reference frame
glm::dvec3 position;
// The up vector expressed in the coordinate system of the reference frame
std::optional<glm::dvec3> up;
// The yaw angle in radians. Positive angle means yawing camera to the right
std::optional<double> yaw;
// The pitch angle in radians. Positive angle means pitching camera upwards
std::optional<double> pitch;
};
#include "navigationhandler_codegen.cpp"
} // namespace
#include "navigationhandler_lua.inl"
@@ -80,6 +98,14 @@ namespace {
namespace openspace::interaction {
ghoul::Dictionary NavigationHandler::NavigationState::dictionary() const {
constexpr const char* KeyAnchor = "Anchor";
constexpr const char* KeyAim = "Aim";
constexpr const char* KeyPosition = "Position";
constexpr const char* KeyUp = "Up";
constexpr const char* KeyYaw = "Yaw";
constexpr const char* KeyPitch = "Pitch";
constexpr const char* KeyReferenceFrame = "ReferenceFrame";
ghoul::Dictionary cameraDict;
cameraDict.setValue(KeyPosition, position);
cameraDict.setValue(KeyAnchor, anchor);
@@ -105,36 +131,19 @@ ghoul::Dictionary NavigationHandler::NavigationState::dictionary() const {
}
NavigationHandler::NavigationState::NavigationState(const ghoul::Dictionary& dictionary) {
const bool hasAnchor = dictionary.hasValue<std::string>(KeyAnchor);
const bool hasPosition = dictionary.hasValue<glm::dvec3>(KeyPosition);
if (!hasAnchor || !hasPosition) {
throw ghoul::RuntimeError(
"Position and Anchor need to be defined for navigation dictionary."
);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
anchor = dictionary.value<std::string>(KeyAnchor);
position = dictionary.value<glm::dvec3>(KeyPosition);
anchor = p.anchor;
position = p.position;
if (dictionary.hasValue<std::string>(KeyReferenceFrame)) {
referenceFrame = dictionary.value<std::string>(KeyReferenceFrame);
}
else {
referenceFrame = anchor;
}
if (dictionary.hasValue<std::string>(KeyAim)) {
aim = dictionary.value<std::string>(KeyAim);
}
referenceFrame = p.referenceFrame.value_or(anchor);
aim = p.aim.value_or(aim);
if (dictionary.hasValue<glm::dvec3>(KeyUp)) {
up = dictionary.value<glm::dvec3>(KeyUp);
if (p.up.has_value()) {
up = *p.up;
if (dictionary.hasValue<double>(KeyYaw)) {
yaw = dictionary.value<double>(KeyYaw);
}
if (dictionary.hasValue<double>(KeyPitch)) {
pitch = dictionary.value<double>(KeyPitch);
}
yaw = p.yaw.value_or(yaw);
pitch = p.pitch.value_or(pitch);
}
}
@@ -142,8 +151,7 @@ NavigationHandler::NavigationState::NavigationState(std::string anchor_, std::st
std::string referenceFrame_,
glm::dvec3 position_,
std::optional<glm::dvec3> up_,
double yaw_,
double pitch_)
double yaw_, double pitch_)
: anchor(std::move(anchor_))
, aim(std::move(aim_))
, referenceFrame(std::move(referenceFrame_))
@@ -545,60 +553,9 @@ std::vector<std::string> NavigationHandler::joystickButtonCommand(int button) co
}
documentation::Documentation NavigationHandler::NavigationState::Documentation() {
using namespace documentation;
return {
"Navigation State",
"core_navigation_state",
{
{
KeyAnchor,
new StringVerifier,
Optional::No,
"The identifier of the anchor node."
},
{
KeyAim,
new StringVerifier,
Optional::Yes,
"The identifier of the aim node, if used."
},
{
KeyReferenceFrame,
new StringVerifier,
Optional::Yes,
"The identifier of the scene graph node to use as reference frame. "
"If not specified, this will be the same as the anchor."
},
{
KeyPosition,
new DoubleVector3Verifier,
Optional::No,
"The position of the camera relative to the anchor node, "
"expressed in meters in the specified reference frame."
},
{
KeyUp,
new DoubleVector3Verifier,
Optional::Yes,
"The up vector expressed in the coordinate system of the reference frame."
},
{
KeyYaw,
new DoubleVerifier,
Optional::Yes,
"The yaw angle in radians. "
"Positive angle means yawing camera to the right."
},
{
KeyPitch,
new DoubleVerifier,
Optional::Yes,
"The pitch angle in radians. "
"Positive angle means pitching camera upwards."
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_navigation_state";
return doc;
}
scripting::LuaLibrary NavigationHandler::luaLibrary() {

View File

@@ -29,73 +29,47 @@
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/lua/lua_helper.h>
#include <algorithm>
#include <optional>
namespace {
constexpr const char* KeyName = "Name";
constexpr const char* KeyDescription = "Description";
constexpr const char* KeyPhases = "Phases";
constexpr const char* KeyTimeRange = "TimeRange";
struct [[codegen::Dictionary(MissionPhase)]] Parameters {
// The human readable name of this mission or mission phase that is displayed to
// the user
std::string name;
// A description of this mission or mission phase
std::optional<std::string> description;
// The time range for which this mission or mission phase is valid. If no time
// range is specified, the ranges of sub mission phases are used instead
std::optional<ghoul::Dictionary> timeRange
[[codegen::reference("core_util_timerange")]];
// The phases into which this mission or mission phase is separated
std::optional<std::vector<ghoul::Dictionary>> phases
[[codegen::reference("core_mission_mission")]];
};
#include "mission_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation MissionPhase::Documentation() {
using namespace documentation;
return {
"Missions and Mission Phases",
"core_mission_mission",
{
{
KeyName,
new StringVerifier,
Optional::No,
"The human readable name of this mission or mission phase that is "
"displayed to the user."
},
{
KeyDescription,
new StringVerifier,
Optional::Yes,
"A description of this mission or mission phase."
},
{
KeyTimeRange,
new ReferencingVerifier("core_util_timerange"),
Optional::Yes,
"The time range for which this mission or mission phase is valid. If no "
"time range is specified, the ranges of sub mission phases are used "
"instead."
},
{
KeyPhases,
new TableVerifier({
{
"*",
new ReferencingVerifier("core_mission_mission"),
Optional::Yes
}
}),
Optional::Yes,
"The phases into which this mission or mission phase is separated."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_mission_mission";
return doc;
}
MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) {
_name = dictionary.value<std::string>(KeyName);
if (dictionary.hasValue<std::string>(KeyDescription)) {
_description = dictionary.value<std::string>(KeyDescription);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
if (dictionary.hasValue<ghoul::Dictionary>(KeyPhases)) {
ghoul::Dictionary childDicts = dictionary.value<ghoul::Dictionary>(KeyPhases);
// This is a nested mission phase
_subphases.reserve(childDicts.size());
for (size_t i = 0; i < childDicts.size(); ++i) {
std::string key = std::to_string(i + 1);
_subphases.emplace_back(childDicts.value<ghoul::Dictionary>(key));
_name = p.name;
_description = p.description.value_or(_description);
if (p.phases.has_value()) {
_subphases.reserve(p.phases->size());
for (const ghoul::Dictionary& phase : *p.phases) {
_subphases.emplace_back(phase);
}
// Ensure subphases are sorted
@@ -112,15 +86,14 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) {
timeRangeSubPhases.start = _subphases[0].timeRange().start;
timeRangeSubPhases.end = _subphases.back().timeRange().end;
// user may specify an overall time range. In that case expand this timerange.
if (dictionary.hasValue<ghoul::Dictionary>(KeyTimeRange)) {
ghoul::Dictionary range = dictionary.value<ghoul::Dictionary>(KeyTimeRange);
TimeRange overallTimeRange(range);
// user may specify an overall time range. In that case expand this timerange
if (p.timeRange.has_value()) {
TimeRange overallTimeRange(*p.timeRange);
if (!overallTimeRange.includes(timeRangeSubPhases)) {
throw ghoul::RuntimeError(
throw ghoul::RuntimeError(fmt::format(
"User specified time range must at least include its subphases'",
"Mission (" + _name + ")"
);
"Mission ({})", _name
));
}
_timeRange.include(overallTimeRange);
@@ -132,16 +105,14 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dictionary) {
}
}
else {
if (dictionary.hasValue<ghoul::Dictionary>(KeyTimeRange)) {
ghoul::Dictionary timeRangeDict =
dictionary.value<ghoul::Dictionary>(KeyTimeRange);
_timeRange = TimeRange(timeRangeDict); // throws exception if unable to parse
if (p.timeRange.has_value()) {
_timeRange = TimeRange(*p.timeRange); // throws exception if unable to parse
}
else {
throw ghoul::RuntimeError(
throw ghoul::RuntimeError(fmt::format(
"If there are no subphases specified, the time range has to be specified",
"Mission (" + _name + ")"
);
"Mission ({})", _name
));
}
}
}

View File

@@ -28,8 +28,8 @@
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/util/factorymanager.h>
#include <ghoul/font/fontmanager.h>
#include <ghoul/misc/templatefactory.h>
#include <optional>
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<std::string> 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<Parameters>();
doc.id = "dashboarditem";
return doc;
}
std::unique_ptr<DashboardItem> DashboardItem::createFromDictionary(
@@ -105,18 +88,11 @@ DashboardItem::DashboardItem(const ghoul::Dictionary& dictionary)
: properties::PropertyOwner({ "", "" })
, _isEnabled(EnabledInfo, true)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"DashboardItem"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
std::string identifier = dictionary.value<std::string>(IdentifierInfo.identifier);
setIdentifier(std::move(identifier));
if (dictionary.hasValue<std::string>(GuiNameInfo.identifier)) {
std::string guiName = dictionary.value<std::string>(GuiNameInfo.identifier);
setGuiName(std::move(guiName));
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<std::string>(FontNameInfo.identifier);
}
_fontName.onChange([this]() {
_font = global::fontManager->font(_fontName, _fontSize);
});
addProperty(_fontName);
if (dictionary.hasKey(FontSizeInfo.identifier)) {
_fontSize = static_cast<float>(dictionary.value<double>(FontSizeInfo.identifier));
}
_fontSize.onChange([this]() {
_font = global::fontManager->font(_fontName, _fontSize);
});
addProperty(_fontSize);
_font = global::fontManager->font(_fontName, _fontSize);
}
} // namespace openspace

View File

@@ -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 <openspace/rendering/dashboardtextitem.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <ghoul/font/fontmanager.h>
#include <optional>
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<std::string> fontName;
// [[codegen::verbatim(FontSizeInfo.description)]]
std::optional<float> fontSize;
};
#include "dashboardtextitem_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation DashboardTextItem::Documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
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<Parameters>(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

View File

@@ -129,7 +129,7 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
registerUpdateRenderBinFromOpacity();
const Parameters p = codegen::bake<Parameters>(dictionary);
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
if (!std::get<std::string>(*p.tag).empty()) {

View File

@@ -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,44 @@ RenderEngine::RenderEngine()
addProperty(_globalBlackOutFactor);
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
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 +493,8 @@ void RenderEngine::initialize() {
);
}
}
_screenshotUseDate = global::configuration->shouldUseScreenshotDate;
}
void RenderEngine::initializeGL() {

View File

@@ -38,11 +38,10 @@
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/textureunit.h>
#include <optional>
#include <variant>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyTag = "Tag";
constexpr const std::array<const char*, 4> UniformNames = {
"Alpha", "ModelTransform", "ViewProjectionMatrix", "texture1"
};
@@ -207,109 +206,72 @@ namespace {
wrap(elevation, -glm::pi<float>(), glm::pi<float>())
);
}
struct [[codegen::Dictionary(ScreenSpaceRenderable)]] Parameters {
// The type of the Screenspace renderable that is to be created. The available
// types of Screenspace renderable depend on the configuration of the application
// and can be written to disk on application startup into the FactoryDocumentation
std::string type
[[codegen::annotation("Must name a valid Screenspace renderable")]];
// Specifies the name of this screenspace renderable. This does not have to be
// unique to the scene, but it is recommended to be
std::optional<std::string> name;
// This is the unique identifier for this screenspace renderable. It has to be
// unique amongst all existing screenspace nodes that already have been added to
// the scene. The identifier is not allowed to have any whitespace or '.' and must
// not be empty
std::optional<std::string> identifier;
// [[codegen::verbatim(EnabledInfo.description)]]
std::optional<bool> enabled;
// [[codegen::verbatim(UseRadiusAzimuthElevationInfo.description)]]
std::optional<bool> useRadiusAzimuthElevation;
// [[codegen::verbatim(FaceCameraInfo.description)]]
std::optional<bool> faceCamera;
// [[codegen::verbatim(CartesianPositionInfo.description)]]
std::optional<glm::vec3> cartesianPosition;
// [[codegen::verbatim(RadiusAzimuthElevationInfo.description)]]
std::optional<glm::vec3> radiusAzimuthElevation;
// [[codegen::verbatim(ScaleInfo.description)]]
std::optional<float> scale;
// [[codegen::verbatim(UsePerspectiveProjectionInfo.description)]]
std::optional<bool> usePerspectiveProjection;
// [codegen::verbatim(OpacityInfo.description)]]
std::optional<float> opacity [[codegen::inrange(0.f, 1.f)]];
// Defines either a single or multiple tags that apply to this
// ScreenSpaceRenderable, thus making it possible to address multiple, separate
// Renderables with a single property change
std::optional<std::variant<std::string, std::vector<std::string>>> tag;
};
#include "screenspacerenderable_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ScreenSpaceRenderable::Documentation() {
using namespace openspace::documentation;
return {
"Screenspace Renderable",
"core_screenspacerenderable",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid Screenspace renderable"),
Optional::No,
"The type of the Screenspace renderable that is to be created. The "
"available types of Screenspace renderable depend on the configuration of"
"the application and can be written to disk on application startup into "
"the FactoryDocumentation."
},
{
KeyName,
new StringVerifier,
Optional::Yes,
"Specifies the name of this screenspace renderable. This does not have "
"to be unique to the scene, but it is recommended to be."
},
{
KeyIdentifier,
new StringVerifier,
Optional::Yes,
"This is the unique identifier for this screenspace renderable. It has "
"to be unique amongst all existing screenspace nodes that already have "
"been added to the scene. The identifier is not allowed to have any "
"whitespace or '.' and must not be empty."
},
{
EnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
EnabledInfo.description
},
{
UseRadiusAzimuthElevationInfo.identifier,
new BoolVerifier,
Optional::Yes,
UseRadiusAzimuthElevationInfo.description
},
{
FaceCameraInfo.identifier,
new BoolVerifier,
Optional::Yes,
FaceCameraInfo.description
},
{
CartesianPositionInfo.identifier,
new DoubleVector3Verifier,
Optional::Yes,
CartesianPositionInfo.description
},
{
RadiusAzimuthElevationInfo.identifier,
new DoubleVector3Verifier,
Optional::Yes,
RadiusAzimuthElevationInfo.description
},
{
ScaleInfo.identifier,
new DoubleVerifier,
Optional::Yes,
ScaleInfo.description
},
{
OpacityInfo.identifier,
new DoubleVerifier,
Optional::Yes,
OpacityInfo.description
},
{
KeyTag,
new OrVerifier({ new StringVerifier, new StringListVerifier }),
Optional::Yes,
"Defines either a single or multiple tags that apply to this "
"ScreenSpaceRenderable, thus making it possible to address multiple, "
"seprate Renderables with a single property change."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_screenspacerenderable";
return doc;
}
std::unique_ptr<ScreenSpaceRenderable> ScreenSpaceRenderable::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ScreenSpaceRenderable"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
const std::string& renderableType = dictionary.value<std::string>(KeyType);
ScreenSpaceRenderable* ssr =
FactoryManager::ref().factory<ScreenSpaceRenderable>()->create(
renderableType,
p.type,
dictionary
);
return std::unique_ptr<ScreenSpaceRenderable>(ssr);
@@ -365,12 +327,14 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
, _opacity(OpacityInfo, 1.f, 0.f, 1.f)
, _delete(DeleteInfo)
{
if (dictionary.hasKey(KeyIdentifier)) {
setIdentifier(dictionary.value<std::string>(KeyIdentifier));
const Parameters p = codegen::bake<Parameters>(dictionary);
if (p.identifier.has_value()) {
setIdentifier(*p.identifier);
}
if (dictionary.hasKey(KeyName)) {
setGuiName(dictionary.value<std::string>(KeyName));
if (p.name.has_value()) {
setGuiName(*p.name);
}
addProperty(_enabled);
@@ -394,62 +358,38 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
addProperty(_opacity);
addProperty(_localRotation);
if (dictionary.hasKey(EnabledInfo.identifier)) {
_enabled = dictionary.value<bool>(EnabledInfo.identifier);
}
if (dictionary.hasKey(UseRadiusAzimuthElevationInfo.identifier)) {
_useRadiusAzimuthElevation = dictionary.value<bool>(
UseRadiusAzimuthElevationInfo.identifier
);
}
_enabled = p.enabled.value_or(_enabled);
_useRadiusAzimuthElevation =
p.useRadiusAzimuthElevation.value_or(_useRadiusAzimuthElevation);
if (_useRadiusAzimuthElevation) {
if (dictionary.hasKey(RadiusAzimuthElevationInfo.identifier)) {
_raePosition = dictionary.value<glm::dvec3>(
RadiusAzimuthElevationInfo.identifier
);
}
_raePosition = p.radiusAzimuthElevation.value_or(_raePosition);
}
else {
if (dictionary.hasKey(CartesianPositionInfo.identifier)) {
_cartesianPosition = dictionary.value<glm::dvec3>(
CartesianPositionInfo.identifier
);
_cartesianPosition = p.cartesianPosition.value_or(_cartesianPosition);
}
_scale = p.scale.value_or(_scale);
_opacity = p.opacity.value_or(_opacity);
_usePerspectiveProjection =
p.usePerspectiveProjection.value_or(_usePerspectiveProjection);
_faceCamera = p.faceCamera.value_or(_faceCamera);
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
addTag(std::get<std::string>(*p.tag));
}
}
if (dictionary.hasKey(ScaleInfo.identifier)) {
_scale = static_cast<float>(dictionary.value<double>(ScaleInfo.identifier));
}
if (dictionary.hasKey(OpacityInfo.identifier)) {
_opacity = static_cast<float>(dictionary.value<double>(OpacityInfo.identifier));
}
if (dictionary.hasKey(UsePerspectiveProjectionInfo.identifier)) {
_usePerspectiveProjection =
dictionary.value<bool>(UsePerspectiveProjectionInfo.identifier);
}
if (dictionary.hasKey(FaceCameraInfo.identifier)) {
_faceCamera = dictionary.value<bool>(FaceCameraInfo.identifier);
}
if (dictionary.hasValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty()) {
addTag(std::move(tagName));
}
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyTag)) {
const ghoul::Dictionary& tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
for (std::string_view key : tagNames.keys()) {
std::string tagName = tagNames.value<std::string>(key);
if (!tagName.empty()) {
addTag(std::move(tagName));
else if (std::holds_alternative<std::vector<std::string>>(*p.tag)) {
for (const std::string& t : std::get<std::vector<std::string>>(*p.tag)) {
if (!t.empty()) {
addTag(t);
}
}
}
else {
throw ghoul::MissingCaseException();
}
}
_delete.onChange([this](){

View File

@@ -32,17 +32,28 @@
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/misc/templatefactory.h>
#include <optional>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyIdentifier = "Identifier";
constexpr openspace::properties::Property::PropertyInfo EnabledInfo = {
"Enabled",
"Enabled",
"Whether the light source is enabled or not"
};
struct [[codegen::Dictionary(LightSource)]] Parameters {
// The type of the light source that is described in this element. The available
// types of light sources depend on the configuration of the application and can
// be written to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid LightSource type")]];
// The identifier of the light source
std::string identifier;
// [[codegen::verbatim(EnabledInfo.description)]]
std::optional<bool> enabled;
};
#include "lightsource_codegen.cpp"
} // namespace
namespace openspace {
@@ -52,49 +63,19 @@ bool LightSource::isEnabled() const {
}
documentation::Documentation LightSource::Documentation() {
using namespace openspace::documentation;
return {
"Light Source",
"core_light_source",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid LightSource type"),
Optional::No,
"The type of the light source that is described in this element. "
"The available types of light sources depend on the configuration "
"of the application and can be written to disk on "
"application startup into the FactoryDocumentation."
},
{
KeyIdentifier,
new StringVerifier,
Optional::No,
"The identifier of the light source."
},
{
EnabledInfo.identifier,
new BoolVerifier,
Optional::Yes,
EnabledInfo.description
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_light_source";
return doc;
}
std::unique_ptr<LightSource> LightSource::createFromDictionary(
const ghoul::Dictionary& dictionary)
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(Documentation(), dictionary, "LightSource");
const std::string timeFrameType = dictionary.value<std::string>(KeyType);
const Parameters p = codegen::bake<Parameters>(dictionary);
auto factory = FactoryManager::ref().factory<LightSource>();
LightSource* source = factory->create(timeFrameType, dictionary);
const std::string identifier = dictionary.value<std::string>(KeyIdentifier);
source->setIdentifier(identifier);
LightSource* source = factory->create(p.type, dictionary);
source->setIdentifier(p.identifier);
return std::unique_ptr<LightSource>(source);
}
@@ -110,10 +91,9 @@ LightSource::LightSource(const ghoul::Dictionary& dictionary)
: properties::PropertyOwner({ "LightSource" })
, _enabled(EnabledInfo, true)
{
if (dictionary.hasValue<bool>(EnabledInfo.identifier)) {
_enabled = dictionary.value<bool>(EnabledInfo.identifier);
}
const Parameters p = codegen::bake<Parameters>(dictionary);
_enabled = p.enabled.value_or(_enabled);
addProperty(_enabled);
}

View File

@@ -36,40 +36,30 @@
#include <ghoul/misc/templatefactory.h>
namespace {
constexpr const char* KeyType = "Type";
struct [[codegen::Dictionary(Rotation)]] Parameters {
// The type of the rotation that is described in this element. The available types
// of rotations depend on the configuration of the application and can be written
// to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid Rotation type")]];
};
#include "rotation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation Rotation::Documentation() {
using namespace openspace::documentation;
return {
"Transformation Rotation",
"core_transform_rotation",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid Rotation type."),
Optional::No,
"The type of the rotation that is described in this element. The "
"available types of rotations depend on the configuration of the "
"application and can be written to disk on application startup into the "
"FactoryDocumentation."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_transform_rotation";
return doc;
}
ghoul::mm_unique_ptr<Rotation> Rotation::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(Documentation(), dictionary, "Rotation");
const Parameters p = codegen::bake<Parameters>(dictionary);
const std::string& rotationType = dictionary.value<std::string>(KeyType);
auto factory = FactoryManager::ref().factory<Rotation>();
Rotation* result = factory->create(
rotationType,
Rotation* result = FactoryManager::ref().factory<Rotation>()->create(
p.type,
dictionary,
&global::memoryManager->PersistentMemory
);
@@ -78,6 +68,8 @@ ghoul::mm_unique_ptr<Rotation> Rotation::createFromDictionary(
Rotation::Rotation() : properties::PropertyOwner({ "Rotation" }) {}
// @TODO (abock, 2021-03-25) This constructor can probably die since it doesn't do any
// above the default constructor
Rotation::Rotation(const ghoul::Dictionary&)
: properties::PropertyOwner({ "Rotation" })
{}

View File

@@ -27,6 +27,8 @@
#include <modules/base/scale/staticscale.h>
#include <modules/base/rotation/staticrotation.h>
#include <modules/base/translation/statictranslation.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/rendering/renderable.h>
@@ -38,23 +40,11 @@
#include <ghoul/misc/assert.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/ghoul_gl.h>
#include "scenegraphnode_doc.inl"
#include <cmath>
#include <optional>
namespace {
constexpr const char* _loggerCat = "SceneGraphNode";
constexpr const char* KeyRenderable = "Renderable";
constexpr const char* KeyGuiName = "GUI.Name";
constexpr const char* KeyGuiPath = "GUI.Path";
constexpr const char* KeyGuiHidden = "GUI.Hidden";
constexpr const char* KeyGuiDescription = "GUI.Description";
constexpr const char* KeyTransformTranslation = "Transform.Translation";
constexpr const char* KeyTransformRotation = "Transform.Rotation";
constexpr const char* KeyTransformScale = "Transform.Scale";
constexpr const char* KeyTimeFrame = "TimeFrame";
constexpr openspace::properties::Property::PropertyInfo ComputeScreenSpaceInfo =
{
@@ -138,6 +128,90 @@ namespace {
openspace::properties::Property::Visibility::Hidden
};
struct [[codegen::Dictionary(SceneGraphNode)]] Parameters {
// The identifier of this scenegraph node. This name must be unique among all
// scene graph nodes that are loaded in a specific scene. If a duplicate is
// detected the loading of the node will fail, as will all childing that depend on
// the node. The identifier must not contain any whitespaces or '.'
std::string identifier;
// This names the parent of the currently specified scenegraph node. The parent
// must already exist in the scene graph. If not specified, the node will be
// attached to the root of the scenegraph
std::optional<std::string> parent
[[codegen::annotation(
"If specified, this must be a name for another scenegraph node"
)]];
// The renderable that is to be created for this scenegraph node. A renderable is
// a component of a scenegraph node that will lead to some visual result on the
// screen. The specifics heavily depend on the 'Type' of the renderable. If no
// Renderable is specified, this scenegraph node is an internal node and can be
// used for either group children, or apply common transformations to a group of
// children
std::optional<ghoul::Dictionary> renderable [[codegen::reference("renderable")]];
// A hard-coded bounding sphere to be used for the cases where the Renderable is
// not able to provide a reasonable bounding sphere or the calculated bounding
// sphere needs to be overwritten for some reason
std::optional<double> boundingSphere;
struct Transform {
// This node describes a translation that is applied to the scenegraph node
// and all its children. Depending on the 'Type' of the translation, this can
// either be a static translation or a time-varying one
std::optional<ghoul::Dictionary> translation
[[codegen::reference("core_transform_translation")]];
// This nodes describes a rotation that is applied to the scenegraph node and
// all its children. Depending on the 'Type' of the rotation, this can either
// be a static rotation or a time-varying one
std::optional<ghoul::Dictionary> rotation
[[codegen::reference("core_transform_rotation")]];
// This node describes a scaling that is applied to the scenegraph node and
// all its children. Depending on the 'Type' of the scaling, this can either
// be a static scaling or a time-varying one
std::optional<ghoul::Dictionary> scale
[[codegen::reference("core_transform_scaling")]];
};
// This describes a set of transformations that are applied to this scenegraph
// node and all of its children. There are only three possible values
// corresponding to a 'Translation', a 'Rotation', and a 'Scale'
std::optional<Transform> transform;
// Specifies the time frame for when this node should be active
std::optional<ghoul::Dictionary> timeFrame
[[codegen::reference("core_time_frame")]];
// A tag or list of tags that can be used to reference to a group of scenegraph
// nodes.
std::optional<std::variant<std::string, std::vector<std::string>>> tag;
struct Gui {
// An optional user-facing name for this SceneGraphNode, which does not have
// to be unique, though it is recommended, and can contain any characters
std::optional<std::string> name;
// If this value is specified, this '/' separated URI specifies the location
// of this scenegraph node in a GUI representation, for instance
// '/SolarSystem/Earth/Moon'
std::optional<std::string> path;
// A user-facing description about this scene graph node
std::optional<std::string> description;
// If this value is specified, GUI applications are incouraged to ignore this
// scenegraph node. This is most useful to trim collective lists of nodes and
// not display, for example, barycenters
std::optional<bool> hidden;
};
// Additional information that is passed to GUI applications. These are all hints
// and do not have any impact on the actual function of the scenegraph node
std::optional<Gui> gui [[codegen::key("GUI")]];
};
#include "scenegraphnode_codegen.cpp"
} // namespace
namespace openspace {
@@ -149,11 +223,7 @@ int SceneGraphNode::nextIndex = 0;
ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
openspace::documentation::testSpecificationAndThrow(
SceneGraphNode::Documentation(),
dictionary,
"SceneGraphNode"
);
const Parameters p = codegen::bake<Parameters>(dictionary);
SceneGraphNode* n = global::memoryManager->PersistentMemory.alloc<SceneGraphNode>();
ghoul::mm_unique_ptr<SceneGraphNode> result = ghoul::mm_unique_ptr<SceneGraphNode>(n);
@@ -162,89 +232,102 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
result->index = nextIndex++;
#endif // Debugging_Core_SceneGraphNode_Indices
std::string identifier = dictionary.value<std::string>(KeyIdentifier);
result->setIdentifier(std::move(identifier));
result->setIdentifier(p.identifier);
if (dictionary.hasKey(KeyGuiName)) {
result->setGuiName(dictionary.value<std::string>(KeyGuiName));
result->_guiDisplayName = result->guiName();
result->addProperty(result->_guiDisplayName);
if (p.gui.has_value()) {
if (p.gui->name.has_value()) {
result->setGuiName(*p.gui->name);
result->_guiDisplayName = result->guiName();
result->addProperty(result->_guiDisplayName);
}
if (p.gui->description.has_value()) {
result->setDescription(*p.gui->description);
result->_guiDescription = result->description();
result->addProperty(result->_guiDescription);
}
if (p.gui->hidden.has_value()) {
result->_guiHidden = *p.gui->hidden;
result->addProperty(result->_guiHidden);
}
if (p.gui->path.has_value()) {
result->_guiPath = *p.gui->path;
result->addProperty(result->_guiPath);
}
}
if (dictionary.hasKey(KeyGuiDescription)) {
result->setDescription(dictionary.value<std::string>(KeyGuiDescription));
result->_guiDescription = result->description();
result->addProperty(result->_guiDescription);
}
if (dictionary.hasKey(KeyGuiHidden)) {
result->_guiHidden = dictionary.value<bool>(KeyGuiHidden);
result->addProperty(result->_guiHidden);
}
if (dictionary.hasKey(BoundingSphereInfo.identifier)) {
result->_boundingSphere = dictionary.value<double>(BoundingSphereInfo.identifier);
if (p.boundingSphere.has_value()) {
result->_boundingSphere = *p.boundingSphere;
result->_boundingSphere.setVisibility(properties::Property::Visibility::All);
}
if (dictionary.hasKey(KeyTransformTranslation)) {
ghoul::Dictionary translationDictionary =
dictionary.value<ghoul::Dictionary>(KeyTransformTranslation);
result->_transform.translation = Translation::createFromDictionary(
translationDictionary
);
if (result->_transform.translation == nullptr) {
LERROR(fmt::format(
"Failed to create ephemeris for SceneGraphNode '{}'", result->identifier()
if (p.transform.has_value()) {
if (p.transform->translation.has_value()) {
result->_transform.translation = Translation::createFromDictionary(
*p.transform->translation
);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_transform.translation == nullptr) {
LERROR(fmt::format(
"Failed to create ephemeris for SceneGraphNode '{}'",
result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created ephemeris for '{}'", result->identifier()
));
return nullptr;
result->addPropertySubOwner(result->_transform.translation.get());
}
if (p.transform->rotation.has_value()) {
result->_transform.rotation = Rotation::createFromDictionary(
*p.transform->rotation
);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_transform.rotation == nullptr) {
LERROR(fmt::format(
"Failed to create rotation for SceneGraphNode '{}'",
result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created rotation for '{}'", result->identifier()
));
result->addPropertySubOwner(result->_transform.rotation.get());
}
if (p.transform->scale.has_value()) {
result->_transform.scale = Scale::createFromDictionary(*p.transform->scale);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_transform.scale == nullptr) {
LERROR(fmt::format(
"Failed to create scale for SceneGraphNode '{}'",
result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created scale for '{}'", result->identifier()
));
result->addPropertySubOwner(result->_transform.scale.get());
}
LDEBUG(fmt::format(
"Successfully created ephemeris for '{}'", result->identifier()
));
}
if (result->_transform.translation) {
result->addPropertySubOwner(result->_transform.translation.get());
}
if (dictionary.hasKey(KeyTransformRotation)) {
ghoul::Dictionary rotationDictionary =
dictionary.value<ghoul::Dictionary>(KeyTransformRotation);
result->_transform.rotation = Rotation::createFromDictionary(rotationDictionary);
if (result->_transform.rotation == nullptr) {
LERROR(fmt::format(
"Failed to create rotation for SceneGraphNode '{}'", result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format(
"Successfully created rotation for '{}'", result->identifier()
));
}
if (result->_transform.rotation) {
result->addPropertySubOwner(result->_transform.rotation.get());
}
if (p.timeFrame.has_value()) {
result->_timeFrame = TimeFrame::createFromDictionary(*p.timeFrame);
if (dictionary.hasKey(KeyTransformScale)) {
ghoul::Dictionary scaleDictionary =
dictionary.value<ghoul::Dictionary>(KeyTransformScale);
result->_transform.scale = Scale::createFromDictionary(scaleDictionary);
if (result->_transform.scale == nullptr) {
LERROR(fmt::format(
"Failed to create scale for SceneGraphNode '{}'", result->identifier()
));
return nullptr;
}
LDEBUG(fmt::format("Successfully created scale for '{}'", result->identifier()));
}
if (result->_transform.scale) {
result->addPropertySubOwner(result->_transform.scale.get());
}
if (dictionary.hasKey(KeyTimeFrame)) {
ghoul::Dictionary timeFrameDictionary =
dictionary.value<ghoul::Dictionary>(KeyTimeFrame);
result->_timeFrame = TimeFrame::createFromDictionary(timeFrameDictionary);
// @TODO(abock, 2021-03-05) I don't think this is necessary anymore as we
// transitioned to throwing exceptions when the construction fails
if (result->_timeFrame == nullptr) {
LERROR(fmt::format(
"Failed to create time frame for SceneGraphNode '{}'",
@@ -252,30 +335,26 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
));
return nullptr;
}
result->addPropertySubOwner(result->_timeFrame.get());
LDEBUG(fmt::format(
"Successfully created time frame for '{}'",
result->identifier()
"Successfully created time frame for '{}'", result->identifier()
));
result->addPropertySubOwner(result->_timeFrame.get());
}
// We initialize the renderable last as it probably has the most dependencies
if (dictionary.hasValue<ghoul::Dictionary>(KeyRenderable)) {
ghoul::Dictionary renderableDictionary =
dictionary.value<ghoul::Dictionary>(KeyRenderable);
result->_renderable = Renderable::createFromDictionary(renderableDictionary);
if (p.renderable.has_value()) {
result->_renderable = Renderable::createFromDictionary(*p.renderable);
ghoul_assert(result->_renderable, "Failed to create Renderable");
result->addPropertySubOwner(result->_renderable.get());
LDEBUG(fmt::format(
"Successfully created renderable for '{}'", result->identifier()
));
// If the renderable child has a larger bounding sphere, we allow it tooverride
// If the renderable child has a larger bounding sphere, we allow it to override
if (result->_renderable->boundingSphere() > result->_boundingSphere) {
result->_boundingSphere = result->_renderable->boundingSphere();
if (dictionary.hasKey(BoundingSphereInfo.identifier)) {
if (p.boundingSphere.has_value()) {
LWARNING(fmt::format(
"The specified property 'BoundingSphere' for '{}' was overwritten "
"by a child renderable",
@@ -285,37 +364,34 @@ ghoul::mm_unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(
}
}
if (dictionary.hasKey(KeyTag)) {
if (dictionary.hasValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty()) {
result->addTag(std::move(tagName));
}
if (p.tag.has_value()) {
if (std::holds_alternative<std::string>(*p.tag)) {
result->addTag(std::get<std::string>(*p.tag));
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyTag)) {
ghoul::Dictionary tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
std::string tagName;
for (std::string_view key : tagNames.keys()) {
tagName = tagNames.value<std::string>(key);
if (!tagName.empty()) {
result->addTag(std::move(tagName));
else if (std::holds_alternative<std::vector<std::string>>(*p.tag)) {
for (const std::string& tag : std::get<std::vector<std::string>>(*p.tag)) {
if (!tag.empty()) {
result->addTag(tag);
}
}
}
else {
throw ghoul::MissingCaseException();
}
}
if (dictionary.hasKey(KeyGuiPath)) {
result->_guiPath = dictionary.value<std::string>(KeyGuiPath);
result->addProperty(result->_guiPath);
}
LDEBUG(fmt::format("Successfully created SceneGraphNode '{}'", result->identifier()));
result->_lastScreenSpaceUpdateTime = std::chrono::high_resolution_clock::now();
return result;
}
documentation::Documentation SceneGraphNode::Documentation() {
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_scene_node";
return doc;
}
SceneGraphNode::SceneGraphNode()
: properties::PropertyOwner({ "" })
, _guiHidden(GuiHiddenInfo)

View File

@@ -1,145 +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 <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
namespace openspace {
documentation::Documentation SceneGraphNode::Documentation() {
using namespace documentation;
return {
"Scenegraph Node",
"core_scene_node",
{
{
"Identifier",
new StringVerifier,
Optional::No,
"The identifier of this scenegraph node. This name must be unique among all "
"scene graph nodes that are loaded in a specific scene. If a duplicate is "
"detected the loading of the node will fail, as will all childing that "
"depend on the node. The identifier must not contain any whitespaces or '.'."
},
{
"Parent",
new StringAnnotationVerifier(
"If specified, this must be a name for another scenegraph node"
),
Optional::Yes,
"This names the parent of the currently specified scenegraph node. The "
"parent must already exist in the scene graph. If not specified, the node "
"will be attached to the root of the scenegraph.",
},
{
"Renderable",
new ReferencingVerifier("renderable"),
Optional::Yes,
"The renderable that is to be created for this scenegraph node. A renderable "
"is a component of a scenegraph node that will lead to some visual result on "
"the screen. The specifics heavily depend on the 'Type' of the renderable. "
"If no Renderable is specified, this scenegraph node is an internal node and "
"can be used for either group children, or apply common transformations to a "
"group of children."
},
{
"Transform",
new TableVerifier({
{
"Translation",
new ReferencingVerifier("core_transform_translation"),
Optional::Yes,
"This node describes a translation that is applied to the scenegraph "
"node and all its children. Depending on the 'Type' of the "
"translation, this can either be a static translation or a "
"time-varying one."
},
{
"Rotation",
new ReferencingVerifier("core_transform_rotation"),
Optional::Yes,
"This nodes describes a rotation that is applied to the scenegraph "
"node and all its children. Depending on the 'Type' of the rotation, "
"this can either be a static rotation or a time-varying one."
},
{
"Scale",
new ReferencingVerifier("core_transform_scaling"),
Optional::Yes,
"This node describes a scaling that is applied to the scenegraph "
"node and all its children. Depending on the 'Type' of the scaling, "
"this can either be a static scaling or a time-varying one."
}
}),
Optional::Yes,
"This describes a set of transformations that are applied to this scenegraph "
"node and all of its children. There are only three possible values "
"corresponding to a 'Translation', a 'Rotation', and a 'Scale'."
},
{
"TimeFrame",
new ReferencingVerifier("core_time_frame"),
Optional::Yes,
"Specifies the time frame for when this node should be active."
},
{
"GUI",
new TableVerifier({
{
"Name",
new StringVerifier,
Optional::Yes,
"An optional user-facing name for this SceneGraphNode, which does "
"not have to be unique, though it is recommended, and can contain "
"any characters."
},
{
"Path",
new StringVerifier,
Optional::Yes,
"If this value is specified, this '/' separated URI specifies the "
"location of this scenegraph node in a GUI representation, for "
"instance '/SolarSystem/Earth/Moon'."
},
{
"Hidden",
new BoolVerifier,
Optional::Yes,
"If this value is specified, GUI applications are incouraged to "
"ignore this scenegraph node. This is most useful to trim collective "
"lists of nodes and not display, for example, barycenters."
}
}),
Optional::Yes,
"Additional information that is passed to GUI applications. These are all "
"hints and do not have any impact on the actual function of the scenegraph "
"node."
},
}
};
}
} // namespace openspace

View File

@@ -34,45 +34,33 @@
#include <ghoul/misc/templatefactory.h>
namespace {
const char* KeyType = "Type";
struct [[codegen::Dictionary(Translation)]] Parameters {
// The type of translation that is described in this element. The available types
// of translations depend on the configuration of the application and can be
// written to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid Translation type")]];
};
#include "translation_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation Translation::Documentation() {
using namespace documentation;
return {
"Transformation Translation",
"core_transform_translation",
{
{
KeyType,
new StringAnnotationVerifier("Must name a valid Translation type"),
Optional::No,
"The type of translation that is described in this element. "
"The available types of translations depend on the "
"configuration of the application and can be written to disk "
"on application startup into the FactoryDocumentation."
}
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_transform_translation";
return doc;
}
ghoul::mm_unique_ptr<Translation> Translation::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(Documentation(), dictionary, "Translation");
const Parameters p = codegen::bake<Parameters>(dictionary);
const std::string& translationType = dictionary.value<std::string>(KeyType);
ghoul::TemplateFactory<Translation>* factory
= FactoryManager::ref().factory<Translation>();
Translation* result = factory->create(
translationType,
Translation* result = FactoryManager::ref().factory<Translation>()->create(
p.type,
dictionary,
&global::memoryManager->PersistentMemory
);
result->setIdentifier("Translation");
return ghoul::mm_unique_ptr<Translation>(result);
}

View File

@@ -33,68 +33,51 @@
#include "scriptscheduler_lua.inl"
namespace {
constexpr const char* KeyTime = "Time";
constexpr const char* KeyForwardScript = "ForwardScript";
constexpr const char* KeyBackwardScript = "BackwardScript";
constexpr const char* KeyUniversalScript = "Script";
struct [[codegen::Dictionary(ScheduledScript)]] Parameters {
// The time at which, when the in game time passes it, the two scripts will
// be executed. If the traversal is forwards (towards + infinity), the
// ForwardScript will be executed, otherwise the BackwardScript will be
// executed instead
std::string time;
// The Lua script that will be executed when the specified time is passed
// independent of its direction. This script will be executed before the
// specific scripts if both versions are specified
std::optional<std::string> script;
// The Lua script that is executed when OpenSpace passes the time in a
// forward direction
std::optional<std::string> forwardScript;
// The Lua script that is executed when OpenSpace passes the time in a
// backward direction
std::optional<std::string> backwardScript;
};
#include "scriptscheduler_codegen.cpp"
} // namespace
namespace openspace::scripting {
documentation::Documentation ScriptScheduler::Documentation() {
using namespace openspace::documentation;
using TimeVerifier = StringVerifier;
using LuaScriptVerifier = StringVerifier;
return {
"Scheduled Scripts",
"core_scheduledscript",
{
{
"*",
new TableVerifier({
{
KeyTime,
new TimeVerifier,
Optional::No,
"The time at which, when the in game time passes it, the two "
"scripts will be executed. If the traversal is forwards (towards "
"+ infinity), the ForwardScript will be executed, otherwise the "
"BackwardScript will be executed instead."
},
{
KeyUniversalScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that will be executed when the specified time is "
"passed independent of its direction. This script will be "
"executed before the specific scripts if both versions are "
"specified"
},
{
KeyForwardScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that is executed when OpenSpace passes the time "
"in a forward direction."
},
{
KeyBackwardScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that is executed when OpenSpace passes the time "
"in a backward direction."
}
}),
Optional::No
}
}
};
// @TODO (abock, 2021-03-25) This is not really correct. This function currently
// returns the documentation for the ScheduledScript, not for the ScriptScheduler
// itself. This should be cleaned up a bit
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "core_scheduledscript";
return doc;
}
using namespace openspace::interaction;
ScriptScheduler::ScheduledScript::ScheduledScript(const ghoul::Dictionary& dict) {
const Parameters p = codegen::bake<Parameters>(dict);
time = Time::convertTime(p.time);
forwardScript = p.forwardScript.value_or(forwardScript);
backwardScript = p.backwardScript.value_or(backwardScript);
universalScript = p.script.value_or(universalScript);
}
void ScriptScheduler::loadScripts(std::vector<ScheduledScript> scheduledScripts) {
// Sort scripts by time; use a stable_sort as the user might have had an intention
// specifying multiple scripts for the same time in a specific order

View File

@@ -38,35 +38,20 @@ int loadFile(lua_State* L) {
return ghoul::lua::luaError(L, "filepath string is empty");
}
ghoul::Dictionary scriptsDict = ghoul::lua::loadDictionaryFromFile(fileName, L);
ghoul::Dictionary scriptsDict;
scriptsDict.setValue("Scripts", ghoul::lua::loadDictionaryFromFile(fileName, L));
documentation::testSpecificationAndThrow(
scripting::ScriptScheduler::Documentation(),
scriptsDict,
"ScriptScheduler"
);
std::vector<scripting::ScriptScheduler::ScheduledScript> scripts;
for (int i = 1; i <= scriptsDict.size(); ++i) {
for (size_t i = 1; i <= scriptsDict.size(); ++i) {
ghoul::Dictionary d = scriptsDict.value<ghoul::Dictionary>(std::to_string(i));
scripting::ScriptScheduler::ScheduledScript script;
constexpr const char* KeyTime = "Time";
if (d.hasValue<std::string>(KeyTime)) {
script.time = Time::convertTime(d.value<std::string>(KeyTime));
}
constexpr const char* KeyForwardScript = "ForwardScript";
if (d.hasValue<std::string>(KeyForwardScript)) {
script.forwardScript = d.value<std::string>(KeyForwardScript);
}
constexpr const char* KeyBackwardScript = "BackwardScript";
if (d.hasValue<std::string>(KeyBackwardScript)) {
script.backwardScript = d.value<std::string>(KeyBackwardScript);
}
constexpr const char* KeyUniversalScript = "Script";
if (d.hasValue<std::string>(KeyUniversalScript)) {
script.universalScript = d.value<std::string>(KeyUniversalScript);
}
scripting::ScriptScheduler::ScheduledScript script =
scripting::ScriptScheduler::ScheduledScript(d);
scripts.push_back(script);
}

View File

@@ -30,67 +30,41 @@
#include <ghoul/misc/templatefactory.h>
namespace {
constexpr const char* KeyType = "Type";
constexpr const char* KeyName = "Name";
struct [[codegen::Dictionary(ResourceSynchronization)]] Parameters {
// This key specifies the type of ResourceSyncrhonization that gets created. It
// has to be one of the valid ResourceSyncrhonizations that are available for
// creation (see the FactoryDocumentation for a list of possible
// ResourceSyncrhonizations), which depends on the configration of the application
std::string type
[[codegen::annotation("A ResourceSynchronization created by a factory")]];
// A user readable name of this synchronization
std::string name;
};
#include "resourcesynchronization_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ResourceSynchronization::Documentation() {
using namespace openspace::documentation;
return {
"ResourceSynchronization",
"resourceSynchronization",
{
{
KeyType,
new StringAnnotationVerifier(
"A valid ResourceSyncrhonization created by a factory"
),
Optional::No,
"This key specifies the type of ResourceSyncrhonization that gets "
"created. It has to be one of the valid ResourceSyncrhonizations that "
"are available for creation (see the FactoryDocumentation for a list of "
"possible ResourceSyncrhonizations), which depends on the configration "
"of the application"
},
{
KeyName,
new StringVerifier,
Optional::No,
"A user readable name of this synchronization"
},
}
};
documentation::Documentation doc = codegen::doc<Parameters>();
doc.id = "resourceSynchronization";
return doc;
}
std::unique_ptr<ResourceSynchronization> ResourceSynchronization::createFromDictionary(
const ghoul::Dictionary& dictionary)
{
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ResourceSynchronization"
);
const std::string& synchronizationType = dictionary.value<std::string>(KeyType);
const Parameters p = codegen::bake<Parameters>(dictionary);
auto factory = FactoryManager::ref().factory<ResourceSynchronization>();
ghoul_assert(factory, "ResourceSynchronization factory did not exist");
ResourceSynchronization* sync = factory->create(synchronizationType, dictionary);
ResourceSynchronization* sync = factory->create(p.type, dictionary);
sync->_name = p.name;
return std::unique_ptr<ResourceSynchronization>(sync);
}
ResourceSynchronization::ResourceSynchronization(const ghoul::Dictionary& dictionary) {
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ResourceSynchronization"
);
_name = dictionary.value<std::string>(KeyName);
}
ResourceSynchronization::ResourceSynchronization(const ghoul::Dictionary&) {}
ResourceSynchronization::State ResourceSynchronization::state() const {
return _state;
@@ -111,14 +85,14 @@ bool ResourceSynchronization::isSyncing() const {
ResourceSynchronization::CallbackHandle
ResourceSynchronization::addStateChangeCallback(StateChangeCallback cb)
{
std::lock_guard<std::mutex> guard(_callbackMutex);
std::lock_guard guard(_callbackMutex);
CallbackHandle callbackId = _nextCallbackId++;
_stateChangeCallbacks[callbackId] = std::move(cb);
return callbackId;
}
void ResourceSynchronization::removeStateChangeCallback(CallbackHandle id) {
std::lock_guard<std::mutex> guard(_callbackMutex);
std::lock_guard guard(_callbackMutex);
_stateChangeCallbacks.erase(id);
}

View File

@@ -586,7 +586,7 @@ std::string SpiceManager::dateFromEphemerisTime(double ephemerisTime, const char
ephemerisTime, format
));
}
return std::string(Buffer);
}