mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-21 04:19:19 -06:00
Merge branch 'master' into thesis/2021/skybrowser
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
88
src/rendering/dashboardtextitem.cpp
Normal file
88
src/rendering/dashboardtextitem.cpp
Normal 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
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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](){
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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" })
|
||||
{}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -586,7 +586,7 @@ std::string SpiceManager::dateFromEphemerisTime(double ephemerisTime, const char
|
||||
ephemerisTime, format
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
return std::string(Buffer);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user