mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-03-09 14:58:37 -05:00
* Add implementation of the EventEngine to handle global event chains * Add properties to SceneGraphNodes to determine two distance radii for camera-based events
738 lines
25 KiB
C++
738 lines
25 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* 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/scene/profile.h>
|
|
#include <openspace/scene/scene.h>
|
|
|
|
#include <openspace/navigation/navigationstate.h>
|
|
#include <openspace/scripting/lualibrary.h>
|
|
#include <openspace/properties/property.h>
|
|
#include <openspace/properties/propertyowner.h>
|
|
#include <ghoul/misc/assert.h>
|
|
#include <ghoul/fmt.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/misc.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
#include <set>
|
|
#include <json/json.hpp>
|
|
|
|
#include "profile_lua.inl"
|
|
|
|
namespace openspace {
|
|
|
|
namespace {
|
|
// Helper structs for the visitor pattern of the std::variant
|
|
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
|
|
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
|
|
|
std::vector<properties::Property*> changedProperties(
|
|
const properties::PropertyOwner& po)
|
|
{
|
|
std::vector<properties::Property*> res;
|
|
for (properties::PropertyOwner* subOwner : po.propertySubOwners()) {
|
|
std::vector<properties::Property*> ps = changedProperties(*subOwner);
|
|
res.insert(res.end(), ps.begin(), ps.end());
|
|
}
|
|
for (properties::Property* p : po.properties()) {
|
|
if (p->hasChanged()) {
|
|
res.push_back(p);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void checkValue(const nlohmann::json& j, const std::string& key,
|
|
bool (nlohmann::json::*checkFunc)() const,
|
|
std::string_view keyPrefix, bool isOptional)
|
|
{
|
|
if (j.find(key) == j.end()) {
|
|
if (!isOptional) {
|
|
throw Profile::ParsingError(
|
|
Profile::ParsingError::Severity::Error,
|
|
fmt::format("'{}.{}' field is missing", keyPrefix, key)
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
const nlohmann::json value = j[key];
|
|
if (!(value.*checkFunc)()) {
|
|
std::string type = [](auto c) {
|
|
if (c == &nlohmann::json::is_string) { return "a string"; }
|
|
else if (c == &nlohmann::json::is_number) { return "a number"; }
|
|
else if (c == &nlohmann::json::is_object) { return "an object"; }
|
|
else if (c == &nlohmann::json::is_array) { return "an array"; }
|
|
else if (c == &nlohmann::json::is_boolean) { return "a boolean"; }
|
|
else {
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}(checkFunc);
|
|
|
|
throw Profile::ParsingError(
|
|
Profile::ParsingError::Severity::Error,
|
|
fmt::format("'{}.{}' must be {}", keyPrefix, key, type)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void checkExtraKeys(const nlohmann::json& j, std::string_view prefix,
|
|
const std::set<std::string>& allowedKeys)
|
|
{
|
|
for (auto& [key, _] : j.items()) {
|
|
if (allowedKeys.find(key) == allowedKeys.end()) {
|
|
LINFOC(
|
|
"Profile",
|
|
fmt::format("Key '{}' not supported in '{}'", key, prefix)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
|
|
//
|
|
// Current version:
|
|
//
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Version& v) {
|
|
j["major"] = v.major;
|
|
j["minor"] = v.minor;
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Version& v) {
|
|
checkValue(j, "major", &nlohmann::json::is_number, "version", false);
|
|
checkValue(j, "minor", &nlohmann::json::is_number, "version", false);
|
|
checkExtraKeys(j, "version", { "major", "minor" });
|
|
|
|
j["major"].get_to(v.major);
|
|
j["minor"].get_to(v.minor);
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Module& v) {
|
|
j["name"] = v.name;
|
|
if (v.loadedInstruction.has_value()) {
|
|
j["loadedInstruction"] = *v.loadedInstruction;
|
|
}
|
|
if (v.notLoadedInstruction.has_value()) {
|
|
j["notLoadedInstruction"] = *v.notLoadedInstruction;
|
|
}
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Module& v) {
|
|
checkValue(j, "name", &nlohmann::json::is_string, "module", false);
|
|
checkValue(j, "loadedInstruction", &nlohmann::json::is_string, "module", true);
|
|
checkValue(j, "notLoadedInstruction", &nlohmann::json::is_string, "module", true);
|
|
checkExtraKeys(j, "module", { "name", "loadedInstruction", "notLoadedInstruction" });
|
|
|
|
j["name"].get_to(v.name);
|
|
if (j.find("loadedInstruction") != j.end()) {
|
|
v.loadedInstruction = j["loadedInstruction"].get<std::string>();
|
|
}
|
|
if (j.find("notLoadedInstruction") != j.end()) {
|
|
v.notLoadedInstruction = j["notLoadedInstruction"].get<std::string>();
|
|
}
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Meta& v) {
|
|
if (v.name.has_value()) {
|
|
j["name"] = *v.name;
|
|
}
|
|
if (v.version.has_value()) {
|
|
j["version"] = *v.version;
|
|
}
|
|
if (v.description.has_value()) {
|
|
j["description"] = *v.description;
|
|
}
|
|
if (v.author.has_value()) {
|
|
j["author"] = *v.author;
|
|
}
|
|
if (v.url.has_value()) {
|
|
j["url"] = *v.url;
|
|
}
|
|
if (v.license.has_value()) {
|
|
j["license"] = *v.license;
|
|
}
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Meta& v) {
|
|
checkValue(j, "name", &nlohmann::json::is_string, "meta", true);
|
|
checkValue(j, "version", &nlohmann::json::is_string, "meta", true);
|
|
checkValue(j, "description", &nlohmann::json::is_string, "meta", true);
|
|
checkValue(j, "author", &nlohmann::json::is_string, "meta", true);
|
|
checkValue(j, "url", &nlohmann::json::is_string, "meta", true);
|
|
checkValue(j, "license", &nlohmann::json::is_string, "meta", true);
|
|
checkExtraKeys(
|
|
j,
|
|
"meta",
|
|
{ "name", "version", "description", "author", "url", "license" }
|
|
);
|
|
|
|
if (j.find("name") != j.end()) {
|
|
v.name = j["name"].get<std::string>();
|
|
}
|
|
if (j.find("version") != j.end()) {
|
|
v.version = j["version"].get<std::string>();
|
|
}
|
|
if (j.find("description") != j.end()) {
|
|
v.description = j["description"].get<std::string>();
|
|
}
|
|
if (j.find("author") != j.end()) {
|
|
v.author = j["author"].get<std::string>();
|
|
}
|
|
if (j.find("url") != j.end()) {
|
|
v.url = j["url"].get<std::string>();
|
|
}
|
|
if (j.find("license") != j.end()) {
|
|
v.license = j["license"].get<std::string>();
|
|
}
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Property::SetType& v) {
|
|
j = [](Profile::Property::SetType t) {
|
|
switch (t) {
|
|
case Profile::Property::SetType::SetPropertyValue:
|
|
return "setPropertyValue";
|
|
case Profile::Property::SetType::SetPropertyValueSingle:
|
|
return "setPropertyValueSingle";
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}(v);
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Property::SetType& v) {
|
|
std::string value = j.get<std::string>();
|
|
if (value == "setPropertyValue") {
|
|
v = Profile::Property::SetType::SetPropertyValue;
|
|
}
|
|
else if (value == "setPropertyValueSingle") {
|
|
v = Profile::Property::SetType::SetPropertyValueSingle;
|
|
}
|
|
else {
|
|
throw Profile::ParsingError(
|
|
Profile::ParsingError::Severity::Error, "Unknown property set type"
|
|
);
|
|
}
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Property& v) {
|
|
j["type"] = v.setType;
|
|
j["name"] = v.name;
|
|
j["value"] = v.value;
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Property& v) {
|
|
checkValue(j, "type", &nlohmann::json::is_string, "property", false);
|
|
checkValue(j, "name", &nlohmann::json::is_string, "property", false);
|
|
checkValue(j, "value", &nlohmann::json::is_string, "property", false);
|
|
checkExtraKeys(j, "property", { "type", "name", "value" });
|
|
|
|
j["type"].get_to(v.setType);
|
|
j["name"].get_to(v.name);
|
|
j["value"].get_to(v.value);
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Action& v) {
|
|
j["identifier"] = v.identifier;
|
|
j["documentation"] = v.documentation;
|
|
j["name"] = v.name;
|
|
j["gui_path"] = v.guiPath;
|
|
j["is_local"] = v.isLocal;
|
|
j["script"] = v.script;
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Action& v) {
|
|
checkValue(j, "identifier", &nlohmann::json::is_string, "action", false);
|
|
checkValue(j, "documentation", &nlohmann::json::is_string, "action", false);
|
|
checkValue(j, "name", &nlohmann::json::is_string, "action", false);
|
|
checkValue(j, "gui_path", &nlohmann::json::is_string, "action", false);
|
|
checkValue(j, "is_local", &nlohmann::json::is_boolean, "action", false);
|
|
checkValue(j, "script", &nlohmann::json::is_string, "action", false);
|
|
checkExtraKeys(
|
|
j,
|
|
"action",
|
|
{ "identifier", "documentation", "name", "gui_path", "is_local", "script" }
|
|
);
|
|
|
|
j["identifier"].get_to(v.identifier);
|
|
j["documentation"].get_to(v.documentation);
|
|
j["name"].get_to(v.name);
|
|
j["gui_path"].get_to(v.guiPath);
|
|
j["is_local"].get_to(v.isLocal);
|
|
j["script"].get_to(v.script);
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Keybinding& v) {
|
|
j["key"] = keyToString(v.key);
|
|
j["action"] = v.action;
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Keybinding& v) {
|
|
checkValue(j, "key", &nlohmann::json::is_string, "keybinding", false);
|
|
checkValue(j, "action", &nlohmann::json::is_string, "keybinding", false);
|
|
checkExtraKeys(j, "keybinding", { "key", "action" });
|
|
|
|
v.key = stringToKey(j.at("key").get<std::string>());
|
|
j["action"].get_to(v.action);
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Time::Type& v) {
|
|
j = [](Profile::Time::Type t) {
|
|
switch (t) {
|
|
case Profile::Time::Type::Absolute: return "absolute";
|
|
case Profile::Time::Type::Relative: return "relative";
|
|
default: throw ghoul::MissingCaseException();
|
|
}
|
|
}(v);
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Time::Type& v) {
|
|
std::string value = j.get<std::string>();
|
|
if (value == "absolute") {
|
|
v = Profile::Time::Type::Absolute;
|
|
}
|
|
else if (value == "relative") {
|
|
v = Profile::Time::Type::Relative;
|
|
}
|
|
else {
|
|
throw Profile::ParsingError(
|
|
Profile::ParsingError::Severity::Error, "Unknown time type"
|
|
);
|
|
}
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::Time& v) {
|
|
j["type"] = v.type;
|
|
j["value"] = v.value;
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::Time& v) {
|
|
checkValue(j, "type", &nlohmann::json::is_string, "time", false);
|
|
checkValue(j, "value", &nlohmann::json::is_string, "time", false);
|
|
checkExtraKeys(j, "time", { "type", "value" });
|
|
|
|
j["type"].get_to(v.type);
|
|
j["value"].get_to(v.value);
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::CameraNavState& v) {
|
|
j["type"] = Profile::CameraNavState::Type;
|
|
j["anchor"] = v.anchor;
|
|
if (v.aim.has_value()) {
|
|
j["aim"] = *v.aim;
|
|
}
|
|
j["frame"] = v.referenceFrame;
|
|
nlohmann::json p {
|
|
{ "x", v.position.x },
|
|
{ "y", v.position.y },
|
|
{ "z", v.position.z }
|
|
};
|
|
j["position"] = p;
|
|
if (v.up.has_value()) {
|
|
nlohmann::json u {
|
|
{ "x", v.up->x },
|
|
{ "y", v.up->y },
|
|
{ "z", v.up->z }
|
|
};
|
|
j["up"] = u;
|
|
}
|
|
if (v.yaw.has_value()) {
|
|
j["yaw"] = *v.yaw;
|
|
}
|
|
if (v.pitch.has_value()) {
|
|
j["pitch"] = *v.pitch;
|
|
}
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::CameraNavState& v) {
|
|
ghoul_assert(
|
|
j.at("type").get<std::string>() == Profile::CameraNavState::Type,
|
|
"Wrong type for Camera"
|
|
);
|
|
|
|
checkValue(j, "anchor", &nlohmann::json::is_string, "camera", false);
|
|
checkValue(j, "aim", &nlohmann::json::is_string, "camera", true);
|
|
checkValue(j, "frame", &nlohmann::json::is_string, "camera", false);
|
|
checkValue(j, "position", &nlohmann::json::is_object, "camera", false);
|
|
checkValue(j["position"], "x", &nlohmann::json::is_number, "camera.position", false);
|
|
checkValue(j["position"], "y", &nlohmann::json::is_number, "camera.position", false);
|
|
checkValue(j["position"], "z", &nlohmann::json::is_number, "camera.position", false);
|
|
checkExtraKeys(j["position"], "camera.position", { "x", "y", "z" });
|
|
checkValue(j, "up", &nlohmann::json::is_object, "camera", true);
|
|
if (j.find("up") != j.end()) {
|
|
checkValue(j["up"], "x", &nlohmann::json::is_number, "camera.up", false);
|
|
checkValue(j["up"], "y", &nlohmann::json::is_number, "camera.up", false);
|
|
checkValue(j["up"], "z", &nlohmann::json::is_number, "camera.up", false);
|
|
checkExtraKeys(j["up"], "camera.up", { "x", "y", "z" });
|
|
}
|
|
checkValue(j, "yaw", &nlohmann::json::is_number, "camera", true);
|
|
checkValue(j, "pitch", &nlohmann::json::is_number, "camera", true);
|
|
checkExtraKeys(
|
|
j,
|
|
"camera",
|
|
{ "type", "anchor", "aim", "frame", "position", "up", "yaw", "pitch" }
|
|
);
|
|
|
|
j["anchor"].get_to(v.anchor);
|
|
if (j.find("aim") != j.end()) {
|
|
v.aim = j["aim"].get<std::string>();
|
|
}
|
|
j["frame"].get_to(v.referenceFrame);
|
|
nlohmann::json p = j["position"];
|
|
p["x"].get_to(v.position.x);
|
|
p["y"].get_to(v.position.y);
|
|
p["z"].get_to(v.position.z);
|
|
|
|
if (j.find("up") != j.end()) {
|
|
nlohmann::json u = j["up"];
|
|
glm::dvec3 up;
|
|
u["x"].get_to(up.x);
|
|
u["y"].get_to(up.y);
|
|
u["z"].get_to(up.z);
|
|
v.up = up;
|
|
}
|
|
|
|
if (j.find("yaw") != j.end()) {
|
|
v.yaw = j["yaw"].get<double>();
|
|
}
|
|
|
|
if (j.find("pitch") != j.end()) {
|
|
v.pitch = j["pitch"].get<double>();
|
|
}
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const Profile::CameraGoToGeo& v) {
|
|
j["type"] = Profile::CameraGoToGeo::Type;
|
|
j["anchor"] = v.anchor;
|
|
j["latitude"] = v.latitude;
|
|
j["longitude"] = v.longitude;
|
|
if (v.altitude.has_value()) {
|
|
j["altitude"] = *v.altitude;
|
|
}
|
|
}
|
|
|
|
void from_json(const nlohmann::json& j, Profile::CameraGoToGeo& v) {
|
|
ghoul_assert(
|
|
j.at("type").get<std::string>() == Profile::CameraGoToGeo::Type,
|
|
"Wrong type for Camera"
|
|
);
|
|
|
|
checkValue(j, "anchor", &nlohmann::json::is_string, "camera", false);
|
|
checkValue(j, "latitude", &nlohmann::json::is_number, "camera", false);
|
|
checkValue(j, "longitude", &nlohmann::json::is_number, "camera", false);
|
|
checkValue(j, "altitude", &nlohmann::json::is_number, "camera", true);
|
|
checkExtraKeys(
|
|
j,
|
|
"camera",
|
|
{ "type", "anchor", "latitude", "longitude", "altitude" }
|
|
);
|
|
|
|
j["anchor"].get_to(v.anchor);
|
|
j["latitude"].get_to(v.latitude);
|
|
j["longitude"].get_to(v.longitude);
|
|
|
|
if (j.find("altitude") != j.end()) {
|
|
v.altitude = j["altitude"].get<double>();
|
|
}
|
|
}
|
|
|
|
// In these namespaces we defined the structs as they used to be defined in the older
|
|
// versions. That way, we can keep the from_json files as they were originally written too
|
|
namespace version10 {
|
|
|
|
struct Keybinding {
|
|
KeyWithModifier key;
|
|
std::string documentation;
|
|
std::string name;
|
|
std::string guiPath;
|
|
bool isLocal = true;
|
|
std::string script;
|
|
};
|
|
|
|
void from_json(const nlohmann::json& j, version10::Keybinding& v) {
|
|
checkValue(j, "key", &nlohmann::json::is_string, "keybinding", false);
|
|
checkValue(j, "documentation", &nlohmann::json::is_string, "keybinding", false);
|
|
checkValue(j, "name", &nlohmann::json::is_string, "keybinding", false);
|
|
checkValue(j, "gui_path", &nlohmann::json::is_string, "keybinding", false);
|
|
checkValue(j, "is_local", &nlohmann::json::is_boolean, "keybinding", false);
|
|
checkValue(j, "script", &nlohmann::json::is_string, "keybinding", false);
|
|
checkExtraKeys(
|
|
j,
|
|
"keybinding",
|
|
{ "key", "documentation", "name", "gui_path", "is_local", "script" }
|
|
);
|
|
|
|
v.key = stringToKey(j.at("key").get<std::string>());
|
|
j["documentation"].get_to(v.documentation);
|
|
j["name"].get_to(v.name);
|
|
j["gui_path"].get_to(v.guiPath);
|
|
j["is_local"].get_to(v.isLocal);
|
|
j["script"].get_to(v.script);
|
|
}
|
|
|
|
void convertVersion10to11(nlohmann::json& profile) {
|
|
// Version 1.1 introduced actions and remove Lua function calling from keybindings
|
|
|
|
if (profile.find("keybindings") == profile.end()) {
|
|
// We didn't find any keybindings, so there is nothing to do
|
|
return;
|
|
}
|
|
|
|
std::vector<Profile::Action> actions;
|
|
std::vector<Profile::Keybinding> keybindings;
|
|
|
|
std::vector<version10::Keybinding> kbs =
|
|
profile.at("keybindings").get<std::vector<version10::Keybinding>>();
|
|
for (size_t i = 0; i < kbs.size(); ++i) {
|
|
version10::Keybinding& kb = kbs[i];
|
|
std::string identifier = fmt::format("profile.keybind.{}", i);
|
|
|
|
Profile::Action action;
|
|
action.identifier = identifier;
|
|
action.documentation = std::move(kb.documentation);
|
|
action.name = std::move(kb.name);
|
|
action.guiPath = std::move(kb.guiPath);
|
|
action.isLocal = std::move(kb.isLocal);
|
|
action.script = std::move(kb.script);
|
|
actions.push_back(std::move(action));
|
|
|
|
Profile::Keybinding keybinding;
|
|
keybinding.key = kb.key;
|
|
keybinding.action = identifier;
|
|
keybindings.push_back(keybinding);
|
|
}
|
|
|
|
profile["actions"] = actions;
|
|
profile["keybindings"] = keybindings;
|
|
|
|
profile["version"] = Profile::Version{ 1, 1 };
|
|
}
|
|
|
|
} // namespace version10
|
|
|
|
|
|
|
|
Profile::ParsingError::ParsingError(Severity severity_, std::string msg)
|
|
: ghoul::RuntimeError(std::move(msg), "profile")
|
|
, severity(severity_)
|
|
{}
|
|
|
|
void Profile::saveCurrentSettingsToProfile(const properties::PropertyOwner& rootOwner,
|
|
std::string currentTime,
|
|
interaction::NavigationState navState)
|
|
{
|
|
version = Profile::CurrentVersion;
|
|
|
|
// Update properties
|
|
std::vector<properties::Property*> ps = changedProperties(rootOwner);
|
|
|
|
for (properties::Property* prop : ps) {
|
|
Property p;
|
|
p.setType = Property::SetType::SetPropertyValueSingle;
|
|
p.name = prop->fullyQualifiedIdentifier();
|
|
p.value = prop->getStringValue();
|
|
properties.push_back(std::move(p));
|
|
}
|
|
|
|
// Add current time to profile file
|
|
Time t;
|
|
t.value = std::move(currentTime);
|
|
t.type = Time::Type::Absolute;
|
|
time = t;
|
|
|
|
// Delta times
|
|
std::vector<double> dts = global::timeManager->deltaTimeSteps();
|
|
deltaTimes = std::move(dts);
|
|
|
|
// Camera
|
|
CameraNavState c;
|
|
c.anchor = navState.anchor;
|
|
c.aim = navState.aim;
|
|
c.referenceFrame = navState.referenceFrame;
|
|
c.position = navState.position;
|
|
c.up = navState.up;
|
|
c.yaw = navState.yaw;
|
|
c.pitch = navState.pitch;
|
|
camera = std::move(c);
|
|
}
|
|
|
|
void Profile::addAsset(const std::string& path) {
|
|
ZoneScoped
|
|
|
|
if (ignoreUpdates) {
|
|
return;
|
|
}
|
|
|
|
const auto it = std::find(assets.cbegin(), assets.cend(), path);
|
|
if (it == assets.end()) {
|
|
assets.push_back(path);
|
|
}
|
|
}
|
|
|
|
void Profile::removeAsset(const std::string& path) {
|
|
ZoneScoped
|
|
|
|
if (ignoreUpdates) {
|
|
return;
|
|
}
|
|
|
|
const auto it = std::find(assets.cbegin(), assets.cend(), path);
|
|
if (it != assets.end()) {
|
|
assets.erase(it);
|
|
}
|
|
}
|
|
|
|
std::string Profile::serialize() const {
|
|
nlohmann::json r;
|
|
r["version"] = version;
|
|
if (!modules.empty()) {
|
|
r["modules"] = modules;
|
|
}
|
|
if (meta.has_value()) {
|
|
r["meta"] = *meta;
|
|
}
|
|
if (!assets.empty()) {
|
|
r["assets"] = assets;
|
|
}
|
|
if (!properties.empty()) {
|
|
r["properties"] = properties;
|
|
}
|
|
if (!actions.empty()) {
|
|
r["actions"] = actions;
|
|
}
|
|
if (!keybindings.empty()) {
|
|
r["keybindings"] = keybindings;
|
|
}
|
|
if (time.has_value()) {
|
|
r["time"] = *time;
|
|
}
|
|
if (!deltaTimes.empty()) {
|
|
r["delta_times"] = deltaTimes;
|
|
}
|
|
if (camera.has_value()) {
|
|
r["camera"] = std::visit(
|
|
overloaded {
|
|
[](const CameraNavState& c) { return nlohmann::json(c); },
|
|
[](const Profile::CameraGoToGeo& c) { return nlohmann::json(c); }
|
|
},
|
|
*camera
|
|
);
|
|
}
|
|
|
|
if (!markNodes.empty()) {
|
|
r["mark_nodes"] = markNodes;
|
|
}
|
|
if (!additionalScripts.empty()) {
|
|
r["additional_scripts"] = additionalScripts;
|
|
}
|
|
|
|
return r.dump(2);
|
|
}
|
|
|
|
Profile::Profile(const std::string& content) {
|
|
try {
|
|
nlohmann::json profile = nlohmann::json::parse(content);
|
|
profile.at("version").get_to(version);
|
|
|
|
// Update the file format in steps
|
|
if (version.major == 1 && version.minor == 0) {
|
|
version10::convertVersion10to11(profile);
|
|
profile.at("version").get_to(version);
|
|
}
|
|
|
|
|
|
if (profile.find("modules") != profile.end()) {
|
|
profile["modules"].get_to(modules);
|
|
}
|
|
if (profile.find("meta") != profile.end()) {
|
|
meta = profile["meta"].get<Meta>();
|
|
}
|
|
if (profile.find("assets") != profile.end()) {
|
|
profile["assets"].get_to(assets);
|
|
}
|
|
if (profile.find("properties") != profile.end()) {
|
|
profile["properties"].get_to(properties);
|
|
}
|
|
if (profile.find("actions") != profile.end()) {
|
|
profile["actions"].get_to(actions);
|
|
}
|
|
if (profile.find("keybindings") != profile.end()) {
|
|
profile["keybindings"].get_to(keybindings);
|
|
}
|
|
if (profile.find("time") != profile.end()) {
|
|
time = profile["time"].get<Time>();
|
|
}
|
|
if (profile.find("delta_times") != profile.end()) {
|
|
profile["delta_times"].get_to(deltaTimes);
|
|
}
|
|
if (profile.find("camera") != profile.end()) {
|
|
nlohmann::json c = profile.at("camera");
|
|
if (c["type"] == CameraNavState::Type) {
|
|
camera = c.get<CameraNavState>();
|
|
}
|
|
else if (c["type"] == CameraGoToGeo::Type) {
|
|
camera = c.get<CameraGoToGeo>();
|
|
}
|
|
else {
|
|
throw ParsingError(ParsingError::Severity::Error, "Unknown camera type");
|
|
}
|
|
}
|
|
if (profile.find("mark_nodes") != profile.end()) {
|
|
profile["mark_nodes"].get_to(markNodes);
|
|
}
|
|
if (profile.find("additional_scripts") != profile.end()) {
|
|
profile["additional_scripts"].get_to(additionalScripts);
|
|
}
|
|
}
|
|
catch (const nlohmann::json::exception& e) {
|
|
std::string err = e.what();
|
|
throw ParsingError(ParsingError::Severity::Error, err);
|
|
}
|
|
}
|
|
|
|
scripting::LuaLibrary Profile::luaLibrary() {
|
|
return {
|
|
"",
|
|
{
|
|
{
|
|
"saveSettingsToProfile",
|
|
&luascriptfunctions::saveSettingsToProfile,
|
|
{},
|
|
"[string, bool]",
|
|
"Collects all changes that have been made since startup, including all "
|
|
"property changes and assets required, requested, or removed. All "
|
|
"changes will be added to the profile that OpenSpace was started with, "
|
|
"and the new saved file will contain all of this information. If the "
|
|
"arugment is provided, the settings will be saved into new profile with "
|
|
"that name. If the argument is blank, the current profile will be saved "
|
|
"to a backup file and the original profile will be overwritten. The "
|
|
"second argument determines if a file that already exists should be "
|
|
"overwritten, which is 'false' by default"
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
} // namespace openspace
|