mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-26 14:08:53 -05:00
Merge branch 'master' into feature/horizons-framework
This commit is contained in:
+9
-6
@@ -1,15 +1,15 @@
|
||||
# Core Team
|
||||
Alexander Bock
|
||||
Emma Broman
|
||||
Emil Axelsson
|
||||
Gene Payne
|
||||
Kalle Bladin
|
||||
Jonathas Costa
|
||||
Gene Payne
|
||||
Emma Broman
|
||||
Jonas Strandstedt
|
||||
Micah Acinapura
|
||||
Michal Marcinkowski
|
||||
Malin Ejdbo
|
||||
Elon Olsson
|
||||
Micah Acinapura
|
||||
Jonas Strandstedt
|
||||
Michal Marcinkowski
|
||||
Joakim Kilby
|
||||
Lovisa Hassler
|
||||
Mikael Petterson
|
||||
@@ -23,6 +23,8 @@ Erik Broberg
|
||||
Jonathan Bosson
|
||||
Michael Nilsson
|
||||
Jonathan Franzen
|
||||
ChristianAdamsson
|
||||
Emilie Ho
|
||||
Karin Reidarman
|
||||
Hans-Christian Helltegen
|
||||
Anton Arbring
|
||||
@@ -39,15 +41,16 @@ Michael Sjöström
|
||||
Michael Novén
|
||||
Christoffer Särevall
|
||||
|
||||
|
||||
# Community Support
|
||||
Anteige
|
||||
arfon
|
||||
DavidLaidlaw
|
||||
ethanejohnsons
|
||||
johnriedel
|
||||
mik3caprio
|
||||
mingenuity
|
||||
nbartzokas
|
||||
nealmcb
|
||||
noahdasanaike
|
||||
PTrottier
|
||||
sa5bke
|
||||
|
||||
Vendored
+6
@@ -6,6 +6,12 @@ library('sharedSpace'); // jenkins-pipeline-lib
|
||||
def url = 'https://github.com/OpenSpace/OpenSpace';
|
||||
def branch = env.BRANCH_NAME;
|
||||
|
||||
// The CHANGE_BRANCH only exists if we are building a PR branch in which case it returns
|
||||
// the original branch
|
||||
if (env.CHANGE_BRANCH) {
|
||||
branch = env.CHANGE_BRANCH;
|
||||
}
|
||||
|
||||
@NonCPS
|
||||
def readDir() {
|
||||
def dirsl = [];
|
||||
|
||||
@@ -34,7 +34,7 @@ OpenSpace requires at least support for [OpenGL](https://www.opengl.org/) versio
|
||||

|
||||
|
||||
# Getting Started
|
||||
This repository contains the source code and example profiles for OpenSpace, but does not contain any data. To build and install the application, please check out the [OpenSpace Wiki](http://wiki.openspaceproject.com/). Here, you will find two pages, a [build instruction](http://wiki.openspaceproject.com/docs/developers/compiling/general) for all operating systems and then additional instructions for [Windows](http://wiki.openspaceproject.com/docs/developers/compiling/windows), [Linux (Ubuntu)](http://wiki.openspaceproject.com/docs/developers/compiling/ubuntu), and [MacOS](http://wiki.openspaceproject.com/docs/developers/compiling/macos).
|
||||
This repository contains the source code and example profiles for OpenSpace, but does not contain any data. To build and install the application, please check out the [GitHub Wiki](https://github.com/OpenSpace/OpenSpace/wiki). Here, you will find two pages, a [build instruction](https://github.com/OpenSpace/OpenSpace/wiki/Compiling) for all operating systems and then additional instructions for [Windows](https://github.com/OpenSpace/OpenSpace/wiki/Compiling-Windows), [Linux (Ubuntu)](https://github.com/OpenSpace/OpenSpace/wiki/Compiling-Ubuntu), and [MacOS](https://github.com/OpenSpace/OpenSpace/wiki/Compiling-MacOS). Please note that the Apple Silicon series of chips do not support OpenGL natively and Metal 2 does not support `double` precision accuracy (see [here](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf) Section 2.1), therefore only the Intel processors for MacOS are supported and maintained.
|
||||
|
||||
Requirements for compiling are:
|
||||
- CMake version 3.10 or above
|
||||
|
||||
@@ -58,6 +58,8 @@ private:
|
||||
void actionSaved();
|
||||
void clearActionFields();
|
||||
void actionRejected();
|
||||
void chooseScripts();
|
||||
void appendScriptsToTextfield(std::string scripts);
|
||||
|
||||
openspace::Profile::Keybinding* selectedKeybinding();
|
||||
void keybindingAdd();
|
||||
@@ -80,6 +82,7 @@ private:
|
||||
QLineEdit* guiPath = nullptr;
|
||||
QLineEdit* documentation = nullptr;
|
||||
QCheckBox* isLocal = nullptr;
|
||||
QPushButton* chooseScripts = nullptr;
|
||||
QTextEdit* script = nullptr;
|
||||
QPushButton* addButton = nullptr;
|
||||
QPushButton* removeButton = nullptr;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QTextBrowser>
|
||||
#include <QVector>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class DisplayWindowUnion : public QWidget {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "profile/actiondialog.h"
|
||||
|
||||
#include "profile/line.h"
|
||||
#include "profile/scriptlogdialog.h"
|
||||
#include <openspace/util/keys.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
@@ -81,7 +82,7 @@ void ActionDialog::createWidgets() {
|
||||
// | | Name | [oooooooooooo] | Row 2
|
||||
// | | GUI Path | [oooooooooooo] | Row 3
|
||||
// | | Documentation | [oooooooooooo] | Row 4
|
||||
// | | Is Local | [] | Row 5
|
||||
// | | Is Local | [] [choosescr] | Row 5
|
||||
// | | Script | [oooooooooooo] | Row 6
|
||||
// *----------------------*---------------*----------------*
|
||||
// | [+] [-] | | [Save] [Cancel]| Row 7
|
||||
@@ -205,6 +206,17 @@ void ActionDialog::createActionWidgets(QGridLayout* layout) {
|
||||
_actionWidgets.isLocal->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.isLocal, 5, 2);
|
||||
|
||||
_actionWidgets.chooseScripts = new QPushButton("Choose Scripts");
|
||||
_actionWidgets.chooseScripts->setToolTip(
|
||||
"Press this button to choose scripts for your action from the logs/scriptlog.txt"
|
||||
);
|
||||
connect(
|
||||
_actionWidgets.chooseScripts, &QPushButton::clicked,
|
||||
this, &ActionDialog::chooseScripts
|
||||
);
|
||||
_actionWidgets.chooseScripts->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.chooseScripts, 5, 2, Qt::AlignRight);
|
||||
|
||||
layout->addWidget(new QLabel("Script"), 6, 1);
|
||||
_actionWidgets.script = new QTextEdit;
|
||||
_actionWidgets.script->setToolTip(
|
||||
@@ -499,6 +511,7 @@ void ActionDialog::actionSelected() {
|
||||
_actionWidgets.documentation->setEnabled(true);
|
||||
_actionWidgets.isLocal->setChecked(action->isLocal);
|
||||
_actionWidgets.isLocal->setEnabled(true);
|
||||
_actionWidgets.chooseScripts->setEnabled(true);
|
||||
_actionWidgets.script->setText(QString::fromStdString(action->script));
|
||||
_actionWidgets.script->setEnabled(true);
|
||||
_actionWidgets.addButton->setEnabled(false);
|
||||
@@ -589,6 +602,7 @@ void ActionDialog::clearActionFields() {
|
||||
_actionWidgets.documentation->setEnabled(false);
|
||||
_actionWidgets.isLocal->setChecked(false);
|
||||
_actionWidgets.isLocal->setEnabled(false);
|
||||
_actionWidgets.chooseScripts->setEnabled(false);
|
||||
_actionWidgets.script->clear();
|
||||
_actionWidgets.script->setEnabled(false);
|
||||
_actionWidgets.saveButtons->setEnabled(false);
|
||||
@@ -604,6 +618,16 @@ void ActionDialog::actionRejected() {
|
||||
clearActionFields();
|
||||
}
|
||||
|
||||
void ActionDialog::chooseScripts() {
|
||||
ScriptlogDialog d(this);
|
||||
connect(&d, &ScriptlogDialog::scriptsSelected, this, &ActionDialog::appendScriptsToTextfield);
|
||||
d.exec();
|
||||
}
|
||||
|
||||
void ActionDialog::appendScriptsToTextfield(std::string scripts) {
|
||||
_actionWidgets.script->append(QString::fromStdString(std::move(scripts)));
|
||||
}
|
||||
|
||||
Profile::Keybinding* ActionDialog::selectedKeybinding() {
|
||||
QListWidgetItem* item = _keybindingWidgets.list->currentItem();
|
||||
const int idx = _keybindingWidgets.list->row(item);
|
||||
|
||||
@@ -71,13 +71,13 @@ CameraDialog::CameraDialog(QWidget* parent,
|
||||
_navState.anchor->setText(QString::fromStdString(nav.anchor));
|
||||
_navState.aim->setText(QString::fromStdString(*nav.aim));
|
||||
_navState.refFrame->setText(QString::fromStdString(nav.referenceFrame));
|
||||
_navState.positionX->setText(QString::number(nav.position.x));
|
||||
_navState.positionY->setText(QString::number(nav.position.y));
|
||||
_navState.positionZ->setText(QString::number(nav.position.z));
|
||||
_navState.positionX->setText(QString::number(nav.position.x, 'g', 17));
|
||||
_navState.positionY->setText(QString::number(nav.position.y, 'g', 17));
|
||||
_navState.positionZ->setText(QString::number(nav.position.z, 'g', 17));
|
||||
if (nav.up.has_value()) {
|
||||
_navState.upX->setText(QString::number(nav.up.value().x));
|
||||
_navState.upY->setText(QString::number(nav.up.value().y));
|
||||
_navState.upZ->setText(QString::number(nav.up.value().z));
|
||||
_navState.upX->setText(QString::number(nav.up.value().x, 'g', 17));
|
||||
_navState.upY->setText(QString::number(nav.up.value().y, 'g', 17));
|
||||
_navState.upZ->setText(QString::number(nav.up.value().z, 'g', 17));
|
||||
}
|
||||
else {
|
||||
_navState.upX->clear();
|
||||
@@ -85,13 +85,13 @@ CameraDialog::CameraDialog(QWidget* parent,
|
||||
_navState.upZ->clear();
|
||||
}
|
||||
if (nav.yaw.has_value()) {
|
||||
_navState.yaw->setText(QString::number(*nav.yaw));
|
||||
_navState.yaw->setText(QString::number(*nav.yaw, 'g', 17));
|
||||
}
|
||||
else {
|
||||
_navState.yaw->clear();
|
||||
}
|
||||
if (nav.pitch.has_value()) {
|
||||
_navState.pitch->setText(QString::number(*nav.pitch));
|
||||
_navState.pitch->setText(QString::number(*nav.pitch, 'g', 17));
|
||||
}
|
||||
else {
|
||||
_navState.pitch->clear();
|
||||
@@ -101,10 +101,10 @@ CameraDialog::CameraDialog(QWidget* parent,
|
||||
[this](const openspace::Profile::CameraGoToGeo& geo) {
|
||||
_tabWidget->setCurrentIndex(CameraTypeGeo);
|
||||
_geoState.anchor->setText(QString::fromStdString(geo.anchor));
|
||||
_geoState.latitude->setText(QString::number(geo.latitude));
|
||||
_geoState.longitude->setText(QString::number(geo.longitude));
|
||||
_geoState.latitude->setText(QString::number(geo.latitude, 'g', 17));
|
||||
_geoState.longitude->setText(QString::number(geo.longitude, 'g', 17));
|
||||
if (geo.altitude.has_value()) {
|
||||
_geoState.altitude->setText(QString::number(*geo.altitude));
|
||||
_geoState.altitude->setText(QString::number(*geo.altitude, 'g', 17));
|
||||
}
|
||||
else {
|
||||
_geoState.altitude->clear();
|
||||
|
||||
+1
-1
Submodule apps/OpenSpace/ext/sgct updated: 4964fd4109...2b3445eeb8
+32
-1
@@ -28,6 +28,7 @@
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/interaction/joystickinputstate.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <openspace/util/keys.h>
|
||||
#include <ghoul/ghoul.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
@@ -810,6 +811,36 @@ void setSgctDelegateFunctions() {
|
||||
vec2 scale = currentWindow->scale();
|
||||
return glm::vec2(scale.x, scale.y);
|
||||
};
|
||||
sgctDelegate.osDpiScaling = []() {
|
||||
ZoneScoped
|
||||
|
||||
// Detect which DPI scaling to use
|
||||
// 1. If there is a GUI window, use the GUI window's content scale value
|
||||
const Window* dpiWindow = nullptr;
|
||||
for (const std::unique_ptr<Window>& window : Engine::instance().windows()) {
|
||||
if (window->hasTag("GUI")) {
|
||||
dpiWindow = window.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If there isn't a GUI window, use the first window's value
|
||||
if (!dpiWindow) {
|
||||
dpiWindow = Engine::instance().windows().front().get();
|
||||
}
|
||||
|
||||
glm::vec2 scale = glm::vec2(1.f, 1.f);
|
||||
glfwGetWindowContentScale(dpiWindow->windowHandle(), &scale.x, &scale.y);
|
||||
|
||||
if (scale.x != scale.y) {
|
||||
LWARNING(fmt::format(
|
||||
"Non-square window scaling detected ({0}x{1}), using {0}x{0} instead",
|
||||
scale.x, scale.y
|
||||
));
|
||||
}
|
||||
|
||||
return scale.x;
|
||||
};
|
||||
sgctDelegate.hasGuiWindow = []() {
|
||||
ZoneScoped
|
||||
|
||||
@@ -1319,7 +1350,7 @@ int main(int argc, char* argv[]) {
|
||||
#endif // __APPLE__
|
||||
|
||||
|
||||
// Do not print message if slaves are waiting for the master
|
||||
// Do not print message if clients are waiting for the master
|
||||
// Only timeout after 15 minutes
|
||||
Engine::instance().setSyncParameters(false, 15.f * 60.f);
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
##########################################################################################
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2022 #
|
||||
# #
|
||||
# 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_CMAKE_EXT_DIR}/application_definition.cmake)
|
||||
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/openspace.icns
|
||||
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources"
|
||||
)
|
||||
|
||||
set(MACOSX_BUNDLE_ICON_FILE openspace.icns)
|
||||
|
||||
create_new_application(
|
||||
Wormhole MACOSX_BUNDLE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/openspace.rc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/openspace.icns
|
||||
)
|
||||
|
||||
target_link_libraries(Wormhole PRIVATE openspace-core openspace-module-collection)
|
||||
|
||||
# Web Browser and Web gui
|
||||
# Why not put these in the module's path? Because they do not have access to the
|
||||
# target as of July 2017, which is needed.
|
||||
if (OPENSPACE_MODULE_WEBBROWSER AND CEF_ROOT)
|
||||
# wanted by CEF
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "CMAKE_BUILD_TYPE")
|
||||
set(PROJECT_ARCH "x86_64")
|
||||
|
||||
if (WIN32)
|
||||
set(RESOURCE_FILE ${OPENSPACE_APPS_DIR}/OpenSpace/openspace.rc)
|
||||
endif ()
|
||||
|
||||
# Add the CEF binary distribution's cmake/ directory to the module path and
|
||||
# find CEF to initialize it properly.
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${WEBBROWSER_MODULE_PATH}/cmake")
|
||||
include(webbrowser_helpers)
|
||||
|
||||
set_cef_targets("${CEF_ROOT}" Wormhole)
|
||||
run_cef_platform_config("${CEF_ROOT}" "${CEF_TARGET}" "${WEBBROWSER_MODULE_PATH}")
|
||||
elseif (OPENSPACE_MODULE_WEBBROWSER)
|
||||
message(WARNING "Web configured to be included, but no CEF_ROOT was found, please try configuring CMake again.")
|
||||
endif ()
|
||||
@@ -1 +0,0 @@
|
||||
set(DEFAULT_APPLICATION ON)
|
||||
@@ -1,133 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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/network/parallelserver.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/cmdparser/commandlineparser.h>
|
||||
#include <ghoul/cmdparser/singlecommand.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <iomanip>
|
||||
|
||||
namespace {
|
||||
constexpr const char*_loggerCat = "Wormhole";
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
using namespace openspace;
|
||||
using namespace ghoul::cmdparser;
|
||||
|
||||
std::vector<std::string> arguments(argv, argv + argc);
|
||||
|
||||
CommandlineParser commandlineParser(
|
||||
"Wormhole",
|
||||
CommandlineParser::AllowUnknownCommands::Yes
|
||||
);
|
||||
|
||||
struct {
|
||||
std::string port;
|
||||
std::string password;
|
||||
std::string changeHostPassword;
|
||||
} settings;
|
||||
|
||||
commandlineParser.addCommand(
|
||||
std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
|
||||
settings.port,
|
||||
"--port",
|
||||
"-p",
|
||||
"Sets the port to listen on"
|
||||
)
|
||||
);
|
||||
|
||||
commandlineParser.addCommand(
|
||||
std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
|
||||
settings.password,
|
||||
"--password",
|
||||
"-l",
|
||||
"Sets the password to use"
|
||||
)
|
||||
);
|
||||
|
||||
commandlineParser.addCommand(
|
||||
std::make_unique<ghoul::cmdparser::SingleCommand<std::string>>(
|
||||
settings.changeHostPassword,
|
||||
"--hostpassword",
|
||||
"-h",
|
||||
"Sets the host password to use"
|
||||
)
|
||||
);
|
||||
|
||||
ghoul::logging::LogManager::initialize(
|
||||
ghoul::logging::LogLevel::Debug,
|
||||
ghoul::logging::LogManager::ImmediateFlush::Yes
|
||||
);
|
||||
|
||||
commandlineParser.setCommandLine(arguments);
|
||||
commandlineParser.execute();
|
||||
|
||||
if (settings.password.empty()) {
|
||||
std::stringstream defaultPassword;
|
||||
defaultPassword << std::hex << std::setfill('0') << std::setw(6) <<
|
||||
(std::hash<size_t>{}(
|
||||
std::chrono::system_clock::now().time_since_epoch().count()
|
||||
) % 0xffffff);
|
||||
|
||||
settings.password = defaultPassword.str();
|
||||
}
|
||||
if (settings.changeHostPassword.empty()) {
|
||||
std::stringstream defaultChangeHostPassword;
|
||||
defaultChangeHostPassword << std::hex << std::setfill('0') << std::setw(6) <<
|
||||
(std::hash<size_t>{}(
|
||||
std::chrono::system_clock::now().time_since_epoch().count() + 1
|
||||
) % 0xffffff);
|
||||
|
||||
settings.changeHostPassword = defaultChangeHostPassword.str();
|
||||
}
|
||||
LINFO(fmt::format("Connection password: {}", settings.password));
|
||||
LINFO(fmt::format("Host password: {}", settings.changeHostPassword));
|
||||
|
||||
int port = 25001;
|
||||
|
||||
if (!settings.port.empty()) {
|
||||
try {
|
||||
port = std::stoi(settings.port);
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
LERROR(fmt::format("Invalid port: {}", settings.port));
|
||||
}
|
||||
}
|
||||
|
||||
ParallelServer server;
|
||||
server.start(port, settings.password, settings.changeHostPassword);
|
||||
server.setDefaultHostAddress("127.0.0.1");
|
||||
LINFO(fmt::format("Server listening to port {}", port));
|
||||
|
||||
while (std::cin.get() != 'q') {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
|
||||
server.stop();
|
||||
LINFO("Server stopped");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
@@ -1 +0,0 @@
|
||||
IDI_ICON1 ICON DISCARDABLE "openspace.ico"
|
||||
@@ -32,7 +32,11 @@ local toggle_trail = {
|
||||
visibility = not openspace.getPropertyValue("Scene." .. trail .. ".Renderable.Enabled")
|
||||
end
|
||||
|
||||
openspace.setPropertyValueSingle("Scene." .. trail .. ".Renderable.Enabled", visibility)
|
||||
if visibility then
|
||||
openspace.setPropertyValueSingle("Scene." .. trail .. ".Renderable.Fade", 1.0, 1.0)
|
||||
else
|
||||
openspace.setPropertyValueSingle("Scene." .. trail .. ".Renderable.Fade", 0.0, 1.0)
|
||||
end
|
||||
]],
|
||||
Documentation = [[Toggles the visibility of the associated trail of a scene graph node.
|
||||
This action takes optional arguments to 1) determine which trail to hide (as the
|
||||
@@ -54,9 +58,9 @@ local hide_trail = {
|
||||
end
|
||||
|
||||
if openspace.hasSceneGraphNode(node .. "Trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "Trail.Renderable.Enabled", false)
|
||||
openspace.setPropertyValueSingle("Scene." .. node .. "Trail.Renderable.Fade", 0.0, 1.0)
|
||||
elseif openspace.hasSceneGraphNode(node .. "_trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "_trail.Renderable.Enabled", false)
|
||||
openspace.setPropertyValueSingle("Scene." .. node .. "_trail.Renderable.Fade", 0.0, 1.0)
|
||||
end
|
||||
]],
|
||||
Documentation = [[Hides the associated trail of a scene graph node. This action takes an
|
||||
@@ -79,9 +83,9 @@ local show_trail = {
|
||||
end
|
||||
|
||||
if openspace.hasSceneGraphNode(node .. "Trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "Trail.Renderable.Enabled", true)
|
||||
openspace.setPropertyValueSingle("Scene." .. node .. "Trail.Renderable.Fade", 1.0, 1.0)
|
||||
elseif openspace.hasSceneGraphNode(node .. "_trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "_trail.Renderable.Enabled", true)
|
||||
openspace.setPropertyValueSingle("Scene." .. node .. "_trail.Renderable.Fade", 1.0, 1.0)
|
||||
end
|
||||
]],
|
||||
Documentation = [[Shows the associated trail of a scene graph node. This action takes an
|
||||
|
||||
@@ -5,13 +5,16 @@ local propertyHelper = asset.require("util/property_helper")
|
||||
-- Specifying which other assets should be loaded in this scene
|
||||
asset.require("spice/base")
|
||||
|
||||
-- Load default key bindings applicable to most scenes
|
||||
asset.require("dashboard/default_dashboard")
|
||||
-- Load default key bindings applicable to most scenes
|
||||
asset.require("util/default_keybindings")
|
||||
|
||||
-- Load web gui
|
||||
local webGui = asset.require("util/webgui")
|
||||
|
||||
-- Scale the different UI components based on the operating system's DPI scaling value
|
||||
asset.require("util/dpiscaling")
|
||||
|
||||
local toggle_trails = {
|
||||
Identifier = "os_default.toggle_trails",
|
||||
Name = "Toggle Trails",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
local toggle_sun = {
|
||||
Identifier = "os.toggle_sun",
|
||||
Name = "Toggle Sun",
|
||||
Command = [[
|
||||
if not is_declared("args") then
|
||||
openspace.printError("Cannot execute 'os.toggle_sun' manually")
|
||||
return
|
||||
end
|
||||
|
||||
if not openspace.getPropertyValue("Scene.Sun.Renderable.Enabled") then
|
||||
openspace.setPropertyValueSingle("Scene.Sun.Renderable.Enabled", true)
|
||||
end
|
||||
|
||||
if args.Transition == "Approaching" then
|
||||
openspace.setPropertyValueSingle("Scene.SunGlare.Renderable.Fade", 0.0, 1.0)
|
||||
openspace.setPropertyValueSingle("Scene.Sun.Renderable.Fade", 1.0, 1.0)
|
||||
else -- "Exiting"
|
||||
openspace.setPropertyValueSingle("Scene.SunGlare.Renderable.Fade", 1.0, 1.0)
|
||||
openspace.setPropertyValueSingle("Scene.Sun.Renderable.Fade", 0.0, 1.0)
|
||||
end
|
||||
]],
|
||||
Documentation = [[Toggles the visibility of the Sun glare and the Sun globe when the
|
||||
camera is approaching either so that from far away the Sun Glare is rendered and when
|
||||
close up, the globe is rendered instead.]],
|
||||
GuiPath = "/Sun",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.action.registerAction(toggle_sun)
|
||||
openspace.event.registerEventAction(
|
||||
"CameraFocusTransition",
|
||||
toggle_sun.Identifier,
|
||||
{ Node = "Sun", Transition = "Approaching" }
|
||||
)
|
||||
openspace.event.registerEventAction(
|
||||
"CameraFocusTransition",
|
||||
toggle_sun.Identifier,
|
||||
{ Node = "Sun", Transition = "Exiting" }
|
||||
)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.event.unregisterEventAction(
|
||||
"CameraFocusTransition",
|
||||
toggle_sun.Identifier,
|
||||
{ Node = "Sun", Transition = "Exiting" }
|
||||
)
|
||||
openspace.event.unregisterEventAction(
|
||||
"CameraFocusTransition",
|
||||
toggle_sun.Identifier,
|
||||
{ Node = "Sun", Transition = "Approaching" }
|
||||
)
|
||||
openspace.action.removeAction(toggle_sun)
|
||||
end)
|
||||
|
||||
|
||||
asset.meta = {
|
||||
Name = "Actions - Toggle current Trails",
|
||||
Version = "1.0",
|
||||
Description = [[ Asset providing actions to toggle trails]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "http://openspaceproject.com",
|
||||
License = "MIT license"
|
||||
}
|
||||
@@ -5,12 +5,12 @@ asset.onInitialize(function()
|
||||
"CameraFocusTransition",
|
||||
action.show_trail,
|
||||
{ Transition = "Exiting" }
|
||||
);
|
||||
)
|
||||
openspace.event.registerEventAction(
|
||||
"CameraFocusTransition",
|
||||
action.hide_trail,
|
||||
{ Transition = "Approaching" }
|
||||
);
|
||||
)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
@@ -18,10 +18,10 @@ asset.onDeinitialize(function()
|
||||
"CameraFocusTransition",
|
||||
action.show_trail,
|
||||
{ Transition = "Exiting" }
|
||||
);
|
||||
)
|
||||
openspace.event.unregisterEventAction(
|
||||
"CameraFocusTransition",
|
||||
action.hide_trail,
|
||||
{ Transition = "Approaching" }
|
||||
);
|
||||
)
|
||||
end)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
local shared = asset.require("util/tle_helper")
|
||||
|
||||
local group = {
|
||||
Title = "Starlink",
|
||||
Url = "http://www.celestrak.com/NORAD/elements/starlink.txt",
|
||||
TrailColor = { 0.65, 0.55, 0.55 },
|
||||
Description = [[LEO satellite constellation launched by SpaceX to provide
|
||||
broadband internet access.]]
|
||||
}
|
||||
|
||||
local tle = shared.downloadTLEFile(asset, group.Url, group.Title)
|
||||
shared.registerSatelliteGroupObjects(asset, group, tle, true)
|
||||
|
||||
|
||||
asset.meta = {
|
||||
Name = "Satellites Communications - Starlink",
|
||||
Version = "1.0",
|
||||
Description = [[ Satellites asset for Communications - Starlink. Data from
|
||||
Celestrak]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "https://celestrak.com/NORAD/elements/",
|
||||
License = "Celestrak"
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
local shared = asset.require("util/tle_helper")
|
||||
|
||||
local group = {
|
||||
Title = "Active",
|
||||
Url = "http://www.celestrak.com/NORAD/elements/active.txt",
|
||||
TrailColor = { 0.45, 0.25, 0.45 },
|
||||
Description = [[Satellites that employ active communication.]]
|
||||
}
|
||||
|
||||
local tle = shared.downloadTLEFile(asset, group.Url, group.Title)
|
||||
shared.registerSatelliteGroupObjects(asset, group, tle, true)
|
||||
|
||||
|
||||
asset.meta = {
|
||||
Name = "Satellites Active",
|
||||
Version = "1.0",
|
||||
Description = [[ Satellites that employ active communication. Data from Celestrak]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "https://celestrak.com/NORAD/elements/",
|
||||
License = "Celestrak"
|
||||
}
|
||||
@@ -11,6 +11,7 @@ asset.require("./communications/other_comm")
|
||||
asset.require("./communications/gorizont")
|
||||
asset.require("./communications/raduga")
|
||||
asset.require("./communications/molniya")
|
||||
asset.require("./communications/starlink")
|
||||
|
||||
|
||||
asset.meta = {
|
||||
|
||||
@@ -2,6 +2,7 @@ asset.require("./misc/military")
|
||||
asset.require("./misc/radar")
|
||||
asset.require("./misc/cubesats")
|
||||
asset.require("./misc/other")
|
||||
asset.require("./misc/active")
|
||||
|
||||
|
||||
asset.meta = {
|
||||
|
||||
@@ -12,6 +12,7 @@ local Sun = {
|
||||
Layers = {},
|
||||
PerformShading = false
|
||||
},
|
||||
ApproachFactor = 15.0,
|
||||
GUI = {
|
||||
Name = "Sun",
|
||||
Path = "/Solar System/Sun",
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
openspace.printWarning("Warning asset_helper has been deprecated and will be removed in future verions");
|
||||
|
||||
local tableLength = function(table)
|
||||
local count = 0
|
||||
for _ in pairs(table) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
|
||||
|
||||
local registerSpiceKernels = function (spiceAsset, kernels)
|
||||
spiceAsset.onInitialize(function ()
|
||||
for i, kernel in ipairs(kernels) do
|
||||
openspace.spice.loadKernel(kernel)
|
||||
end
|
||||
end)
|
||||
spiceAsset.onDeinitialize(function ()
|
||||
for i = #kernels, 1, -1 do
|
||||
local kernel = kernels[i]
|
||||
openspace.spice.unloadKernel(kernel)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local registerSceneGraphNodes = function (sceneAsset, nodes, override)
|
||||
override = override or false
|
||||
if not override then
|
||||
if tableLength(nodes) == 0 then
|
||||
openspace.printWarning(sceneAsset.filePath .. ": Register function was called with an empty node list. Pass 'true' as third argument to silence this warning.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sceneAsset.onInitialize(function ()
|
||||
for i, node in ipairs(nodes) do
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
end)
|
||||
sceneAsset.onDeinitialize(function ()
|
||||
for i = #nodes, 1, -1 do
|
||||
node = nodes[i]
|
||||
openspace.removeSceneGraphNode(node.Identifier)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
local registerScreenSpaceRenderables = function (sceneAsset, renderables, override)
|
||||
override = override or false
|
||||
if not override then
|
||||
if tableLength(renderables) == 0 then
|
||||
openspace.printWarning(sceneAsset.filePath .. ": Register function was called with an empty node list. Pass 'true' as third argument to silence this warning.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
sceneAsset.onInitialize(function ()
|
||||
for i, node in ipairs(renderables) do
|
||||
openspace.addScreenSpaceRenderable(node)
|
||||
end
|
||||
end)
|
||||
sceneAsset.onDeinitialize(function ()
|
||||
for i = #renderables, 1, -1 do
|
||||
renderable = renderables[i]
|
||||
openspace.removeScreenSpaceRenderable(renderable.Identifier)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local registerDashboardItems = function (dashboardAsset, items)
|
||||
dashboardAsset.onInitialize(
|
||||
function ()
|
||||
for i, item in ipairs(items) do
|
||||
openspace.dashboard.addDashboardItem(item)
|
||||
end
|
||||
end
|
||||
)
|
||||
dashboardAsset.onDeinitialize(function ()
|
||||
for i = #items, 1, -1 do
|
||||
local item = items[i]
|
||||
openspace.dashboard.removeDashboardItem(item.Identifier)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local registerSceneGraphNodesAndExport = function (sceneAsset, nodes, override)
|
||||
override = override or false
|
||||
if not override then
|
||||
if tableLength(nodes) == 0 then
|
||||
openspace.printWarning(sceneAsset.filePath .. ": Register function was called with an empty node list. Pass 'true' as third argument to silence this warning.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
for i, node in ipairs(nodes) do
|
||||
if not node.Identifier then
|
||||
openspace.printError("Could not load asset as Identifier was missing")
|
||||
end
|
||||
end
|
||||
|
||||
sceneAsset.onInitialize(function ()
|
||||
for i, node in ipairs(nodes) do
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
end)
|
||||
sceneAsset.onDeinitialize(function ()
|
||||
for i = #nodes, 1, -1 do
|
||||
local node = nodes[i]
|
||||
openspace.removeSceneGraphNode(node.Identifier)
|
||||
end
|
||||
end)
|
||||
|
||||
for i, node in ipairs(nodes) do
|
||||
sceneAsset.export(node.Identifier, node)
|
||||
end
|
||||
end
|
||||
|
||||
local requireAll = function (sceneAsset, directory)
|
||||
function string.ends(String,End)
|
||||
return End=='' or string.sub(String,-string.len(End))==End
|
||||
end
|
||||
|
||||
local result = {}
|
||||
|
||||
local files = openspace.walkDirectoryFiles(sceneAsset.localResource('') .. directory, true)
|
||||
for _, file in pairs(files) do
|
||||
if file:ends('.asset') then
|
||||
openspace.printDebug("Requiring: " .. file:sub(file:find(directory), -7))
|
||||
local exports = sceneAsset.require(file:sub(1, -7))
|
||||
table.insert(result, exports)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local getDefaultLightSources = function (solarSystemBarycenterIdentifier)
|
||||
local sourceList = {
|
||||
{
|
||||
Type = "SceneGraphLightSource",
|
||||
Identifier = "Sun",
|
||||
Node = solarSystemBarycenterIdentifier,
|
||||
Intensity = 1.0
|
||||
},
|
||||
{
|
||||
Identifier = "Camera",
|
||||
Type = "CameraLightSource",
|
||||
Intensity = 0.5
|
||||
}
|
||||
}
|
||||
return sourceList
|
||||
end
|
||||
|
||||
|
||||
local createModelPart = function (parent, sunLightSourceNode, models, geometry, texture, performShading)
|
||||
local lightSources = {}
|
||||
if performShading then
|
||||
lightSources[1] = {
|
||||
Type = "SceneGraphLightSource",
|
||||
Identifier = "Sun",
|
||||
Node = sunLightSourceNode,
|
||||
Intensity = 1.0
|
||||
}
|
||||
end
|
||||
return {
|
||||
Identifier = parent .. "-" .. geometry,
|
||||
Parent = parent,
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = models .. "/" .. geometry .. ".obj",
|
||||
LightSources = lightSources,
|
||||
PerformShading = performShading,
|
||||
DisableFaceCulling = true
|
||||
},
|
||||
GUI = {
|
||||
Hidden = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
asset.export("registerSceneGraphNodes", registerSceneGraphNodes)
|
||||
asset.export("registerSceneGraphNodesAndExport", registerSceneGraphNodesAndExport)
|
||||
asset.export("registerScreenSpaceRenderables", registerScreenSpaceRenderables)
|
||||
asset.export("registerSpiceKernels", registerSpiceKernels)
|
||||
asset.export("registerDashboardItems", registerDashboardItems)
|
||||
asset.export("requireAll", requireAll)
|
||||
asset.export("getDefaultLightSources", getDefaultLightSources)
|
||||
asset.export("createModelPart", createModelPart)
|
||||
@@ -0,0 +1,37 @@
|
||||
asset.onInitialize(function ()
|
||||
local scale = openspace.dpiScaling()
|
||||
openspace.printInfo("Setting the DPI scaling factor to " .. tostring(scale))
|
||||
|
||||
|
||||
if openspace.modules.isLoaded("CefWebGui") then
|
||||
openspace.setPropertyValueSingle(
|
||||
"Modules.CefWebGui.GuiScale",
|
||||
openspace.getPropertyValue("Modules.CefWebGui.GuiScale") * scale
|
||||
)
|
||||
end
|
||||
|
||||
local dashboards = openspace.getProperty("Dashboard.*.FontSize");
|
||||
for _, v in ipairs(dashboards) do
|
||||
openspace.setPropertyValueSingle(
|
||||
v,
|
||||
openspace.getPropertyValue(v) * scale
|
||||
)
|
||||
end
|
||||
|
||||
local offset = openspace.getPropertyValue("Dashboard.StartPositionOffset")
|
||||
openspace.setPropertyValueSingle(
|
||||
"Dashboard.StartPositionOffset",
|
||||
{ offset[1] * scale, offset[2] * scale }
|
||||
)
|
||||
end)
|
||||
|
||||
asset.meta = {
|
||||
Name = "DPI Scaling",
|
||||
Version = "1.0",
|
||||
Description = [[ Retrieves the DPI scaling from the operating system and applies it to
|
||||
a few selected places to make them scale better on "large resolution but small size"
|
||||
monitors ]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "http://openspaceproject.com",
|
||||
License = "MIT license"
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
],
|
||||
"assets": [
|
||||
"base",
|
||||
"events/toggle_sun",
|
||||
"scene/solarsystem/planets/earth/earth",
|
||||
"scene/solarsystem/planets/earth/satellites/satellites"
|
||||
],
|
||||
@@ -104,4 +105,4 @@
|
||||
"major": 1,
|
||||
"minor": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
],
|
||||
"assets": [
|
||||
"base",
|
||||
"events/toggle_sun",
|
||||
"scene/solarsystem/planets/earth/earth",
|
||||
"scene/solarsystem/planets/earth/satellites/satellites",
|
||||
"scene/solarsystem/planets/jupiter/major_moons",
|
||||
@@ -130,4 +131,4 @@
|
||||
"major": 1,
|
||||
"minor": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
Submodule ext/ghoul updated: 8d85f42fc3...5e0f6278c4
@@ -57,7 +57,7 @@ public:
|
||||
|
||||
/**
|
||||
* Decodes the <code>SyncBuffer</code> into the added Syncables.
|
||||
* This method is only called on the SGCT slave nodes
|
||||
* This method is only called on the SGCT client nodes
|
||||
*/
|
||||
void decodeSyncables(std::vector<std::byte> data);
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ struct WindowDelegate {
|
||||
|
||||
glm::vec2 (*dpiScaling)() = []() { return glm::vec2(1.f); };
|
||||
|
||||
float (*osDpiScaling)() = []() { return 1.f; };
|
||||
|
||||
bool (*hasGuiWindow)() = []() { return false; };
|
||||
|
||||
bool (*isGuiWindow)() = []() { return false; };
|
||||
|
||||
@@ -87,6 +87,13 @@ public:
|
||||
*/
|
||||
bool hasReachedEnd() const;
|
||||
|
||||
/**
|
||||
* Compute the interpolated camera pose at a certain distance along a *linear*
|
||||
* path. Note that the linear path is a special case, to avoid risks of precision
|
||||
* problems for long paths
|
||||
*/
|
||||
CameraPose linearInterpolatedPose(double distance, double displacement);
|
||||
|
||||
/**
|
||||
* Compute the interpolated camera pose at a certain distance along the path
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
const Path* currentPath() const;
|
||||
double speedScale() const;
|
||||
double arrivalDistanceFactor() const;
|
||||
float linearRotationSpeedFactor() const;
|
||||
|
||||
bool hasCurrentPath() const;
|
||||
bool hasFinished() const;
|
||||
@@ -103,6 +104,7 @@ private:
|
||||
properties::FloatProperty _speedScale;
|
||||
properties::BoolProperty _applyIdleBehaviorOnFinish;
|
||||
properties::DoubleProperty _arrivalDistanceFactor;
|
||||
properties::FloatProperty _linearRotationSpeedFactor;
|
||||
properties::DoubleProperty _minValidBoundingSphere;
|
||||
properties::StringListProperty _relevantNodeTags;
|
||||
|
||||
|
||||
@@ -89,13 +89,13 @@ struct CameraKeyframe {
|
||||
sizeof(_followNodeRotation)
|
||||
);
|
||||
|
||||
int nodeNameLength = static_cast<int>(_focusNode.size());
|
||||
uint32_t nodeNameLength = static_cast<uint32_t>(_focusNode.size());
|
||||
|
||||
// Add focus node
|
||||
buffer.insert(
|
||||
buffer.end(),
|
||||
reinterpret_cast<const char*>(&nodeNameLength),
|
||||
reinterpret_cast<const char*>(&nodeNameLength) + sizeof(nodeNameLength)
|
||||
reinterpret_cast<const char*>(&nodeNameLength) + sizeof(uint32_t)
|
||||
);
|
||||
buffer.insert(
|
||||
buffer.end(),
|
||||
|
||||
@@ -42,14 +42,13 @@ public:
|
||||
Host
|
||||
};
|
||||
|
||||
enum class MessageType : uint32_t {
|
||||
enum class MessageType : uint8_t {
|
||||
Authentication = 0,
|
||||
Data,
|
||||
ConnectionStatus,
|
||||
HostshipRequest,
|
||||
HostshipResignation,
|
||||
NConnections,
|
||||
Disconnection
|
||||
NConnections
|
||||
};
|
||||
|
||||
struct Message {
|
||||
@@ -71,7 +70,9 @@ public:
|
||||
|
||||
class ConnectionLostError : public ghoul::RuntimeError {
|
||||
public:
|
||||
explicit ConnectionLostError();
|
||||
explicit ConnectionLostError(bool shouldLogError = true);
|
||||
|
||||
bool shouldLogError;
|
||||
};
|
||||
|
||||
ParallelConnection(std::unique_ptr<ghoul::io::TcpSocket> socket);
|
||||
@@ -84,9 +85,11 @@ public:
|
||||
|
||||
ParallelConnection::Message receiveMessage();
|
||||
|
||||
static const unsigned int ProtocolVersion;
|
||||
static const uint8_t ProtocolVersion;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ghoul::io::TcpSocket> _socket;
|
||||
bool _shouldDisconnect = false;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_CORE___PARALLELSERVER___H__
|
||||
#define __OPENSPACE_CORE___PARALLELSERVER___H__
|
||||
|
||||
#include <openspace/network/parallelconnection.h>
|
||||
|
||||
#include <openspace/util/concurrentqueue.h>
|
||||
#include <ghoul/io/socket/tcpsocketserver.h>
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class ParallelServer {
|
||||
public:
|
||||
void start(int port, const std::string& password,
|
||||
const std::string& changeHostPassword);
|
||||
|
||||
void setDefaultHostAddress(std::string defaultHostAddress);
|
||||
|
||||
std::string defaultHostAddress() const;
|
||||
|
||||
void stop();
|
||||
|
||||
size_t nConnections() const;
|
||||
|
||||
private:
|
||||
struct Peer {
|
||||
size_t id;
|
||||
std::string name;
|
||||
ParallelConnection parallelConnection;
|
||||
ParallelConnection::Status status;
|
||||
std::thread thread;
|
||||
};
|
||||
|
||||
struct PeerMessage {
|
||||
size_t peerId;
|
||||
ParallelConnection::Message message;
|
||||
};
|
||||
|
||||
bool isConnected(const Peer& peer) const;
|
||||
|
||||
void sendMessage(Peer& peer, ParallelConnection::MessageType messageType,
|
||||
const std::vector<char>& message);
|
||||
|
||||
void sendMessageToAll(ParallelConnection::MessageType messageType,
|
||||
const std::vector<char>& message);
|
||||
|
||||
void sendMessageToClients(ParallelConnection::MessageType messageType,
|
||||
const std::vector<char>& message);
|
||||
|
||||
void disconnect(Peer& peer);
|
||||
void setName(Peer& peer, std::string name);
|
||||
void assignHost(std::shared_ptr<Peer> newHost);
|
||||
void setToClient(Peer& peer);
|
||||
void setNConnections(size_t nConnections);
|
||||
void sendConnectionStatus(Peer& peer);
|
||||
|
||||
void handleAuthentication(std::shared_ptr<Peer> peer, std::vector<char> message);
|
||||
void handleData(const Peer& peer, std::vector<char> data);
|
||||
void handleHostshipRequest(std::shared_ptr<Peer> peer, std::vector<char> message);
|
||||
void handleHostshipResignation(Peer& peer);
|
||||
|
||||
void handleNewPeers();
|
||||
void eventLoop();
|
||||
std::shared_ptr<Peer> peer(size_t id);
|
||||
void handlePeer(size_t id);
|
||||
void handlePeerMessage(PeerMessage peerMessage);
|
||||
|
||||
std::unordered_map<size_t, std::shared_ptr<Peer>> _peers;
|
||||
mutable std::mutex _peerListMutex;
|
||||
|
||||
std::thread _serverThread;
|
||||
std::thread _eventLoopThread;
|
||||
ghoul::io::TcpSocketServer _socketServer;
|
||||
size_t _passwordHash;
|
||||
size_t _changeHostPasswordHash;
|
||||
size_t _nextConnectionId = 1;
|
||||
std::atomic_bool _shouldStop = false;
|
||||
|
||||
std::atomic_size_t _nConnections = 0;
|
||||
std::atomic_size_t _hostPeerId = 0;
|
||||
|
||||
mutable std::mutex _hostInfoMutex;
|
||||
std::string _hostName;
|
||||
std::string _defaultHostAddress;
|
||||
|
||||
ConcurrentQueue<PeerMessage> _incomingMessages;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_CORE___PARALLELSERVER___H__
|
||||
@@ -105,6 +105,7 @@ public:
|
||||
protected:
|
||||
properties::BoolProperty _enabled;
|
||||
properties::FloatProperty _opacity;
|
||||
properties::FloatProperty _fade;
|
||||
properties::StringProperty _renderableType;
|
||||
|
||||
void setBoundingSphere(double boundingSphere);
|
||||
@@ -113,6 +114,9 @@ protected:
|
||||
void setRenderBinFromOpacity();
|
||||
void registerUpdateRenderBinFromOpacity();
|
||||
|
||||
/// Returns the full opacity constructed from the _opactiy and _fade property values
|
||||
float opacity() const;
|
||||
|
||||
double _boundingSphere = 0.0;
|
||||
double _interactionSphere = 0.0;
|
||||
SceneGraphNode* _parent = nullptr;
|
||||
|
||||
@@ -172,7 +172,7 @@ private:
|
||||
|
||||
ghoul::opengl::OpenGLStateCache* _openglStateCache = nullptr;
|
||||
|
||||
properties::BoolProperty _showOverlayOnSlaves;
|
||||
properties::BoolProperty _showOverlayOnClients;
|
||||
properties::BoolProperty _showLog;
|
||||
properties::FloatProperty _verticalLogOffset;
|
||||
properties::BoolProperty _showVersionInfo;
|
||||
|
||||
@@ -88,6 +88,8 @@ protected:
|
||||
virtual void bindTexture() = 0;
|
||||
virtual void unbindTexture();
|
||||
|
||||
float opacity() const;
|
||||
|
||||
properties::BoolProperty _enabled;
|
||||
properties::BoolProperty _usePerspectiveProjection;
|
||||
properties::BoolProperty _useRadiusAzimuthElevation;
|
||||
@@ -108,6 +110,7 @@ protected:
|
||||
properties::Vec3Property _multiplyColor;
|
||||
properties::Vec4Property _backgroundColor;
|
||||
properties::FloatProperty _opacity;
|
||||
properties::FloatProperty _fade;
|
||||
properties::TriggerProperty _delete;
|
||||
|
||||
glm::ivec2 _objectSize = glm::ivec2(0);
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
static ghoul::mm_unique_ptr<Rotation> createFromDictionary(
|
||||
const ghoul::Dictionary& dictionary);
|
||||
|
||||
Rotation(const ghoul::Dictionary& dictionary);
|
||||
Rotation();
|
||||
virtual ~Rotation() = default;
|
||||
|
||||
virtual bool initialize();
|
||||
@@ -56,7 +56,6 @@ public:
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
protected:
|
||||
Rotation();
|
||||
void requireUpdate();
|
||||
|
||||
private:
|
||||
|
||||
@@ -115,10 +115,10 @@ private:
|
||||
|
||||
std::queue<QueueItem> _incomingScripts;
|
||||
|
||||
// Slave scripts are mutex protected since decode and rendering may
|
||||
// happen asynchronously.
|
||||
std::mutex _slaveScriptsMutex;
|
||||
std::queue<std::string> _slaveScriptQueue;
|
||||
// Client scripts are mutex protected since decode and rendering may happen
|
||||
// asynchronously
|
||||
std::mutex _clientScriptsMutex;
|
||||
std::queue<std::string> _clientScriptQueue;
|
||||
std::queue<QueueItem> _masterScriptQueue;
|
||||
|
||||
std::vector<std::string> _scriptsToSync;
|
||||
|
||||
@@ -144,14 +144,14 @@ public:
|
||||
/**
|
||||
* Sets a relative time from profile.
|
||||
*/
|
||||
void setTimeRelativeFromProfile(const std::string& setTime);
|
||||
static void setTimeRelativeFromProfile(const std::string& setTime);
|
||||
|
||||
/**
|
||||
* Sets an absolute time from profile.
|
||||
* \param setTime a string containing time to set, which must be a valid
|
||||
* ISO 8601-like date string of the format YYYY-MM-DDTHH:MN:SS
|
||||
*/
|
||||
void setTimeAbsoluteFromProfile(const std::string& setTime);
|
||||
static void setTimeAbsoluteFromProfile(const std::string& setTime);
|
||||
|
||||
/**
|
||||
* Returns the Lua library that contains all Lua functions available to change the
|
||||
|
||||
@@ -154,7 +154,7 @@ void RenderableBoxGrid::render(const RenderData& data, RendererTasks&){
|
||||
"MVPTransform",
|
||||
glm::dmat4(data.camera.projectionMatrix()) * modelViewTransform
|
||||
);
|
||||
_gridProgram->setUniform("opacity", _opacity);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
// Change GL state:
|
||||
|
||||
@@ -170,7 +170,7 @@ void RenderableGrid::render(const RenderData& data, RendererTasks&){
|
||||
"MVPTransform",
|
||||
glm::dmat4(data.camera.projectionMatrix()) * modelViewTransform
|
||||
);
|
||||
_gridProgram->setUniform("opacity", _opacity);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
// Change GL state:
|
||||
|
||||
@@ -181,7 +181,7 @@ void RenderableRadialGrid::render(const RenderData& data, RendererTasks&) {
|
||||
"MVPTransform",
|
||||
glm::dmat4(data.camera.projectionMatrix()) * modelViewTransform
|
||||
);
|
||||
_gridProgram->setUniform("opacity", _opacity);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
// Change GL state:
|
||||
|
||||
@@ -172,7 +172,7 @@ void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&){
|
||||
"MVPTransform",
|
||||
glm::dmat4(data.camera.projectionMatrix()) * modelViewTransform
|
||||
);
|
||||
_gridProgram->setUniform("opacity", _opacity);
|
||||
_gridProgram->setUniform("opacity", opacity());
|
||||
_gridProgram->setUniform("gridColor", _color);
|
||||
|
||||
// Change GL state:
|
||||
|
||||
@@ -155,7 +155,7 @@ void RenderableDisc::render(const RenderData& data, RendererTasks&) {
|
||||
data.camera.projectionMatrix() * glm::mat4(modelViewTransform)
|
||||
);
|
||||
_shader->setUniform(_uniformCache.width, _width);
|
||||
_shader->setUniform(_uniformCache.opacity, _opacity);
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
|
||||
ghoul::opengl::TextureUnit unit;
|
||||
unit.activate();
|
||||
|
||||
@@ -439,7 +439,7 @@ void RenderableLabels::renderLabels(const RenderData& data,
|
||||
glm::vec4 textColor = glm::vec4(glm::vec3(_color), 1.f);
|
||||
|
||||
textColor.a *= fadeInVariable;
|
||||
textColor.a *= _opacity;
|
||||
textColor.a *= opacity();
|
||||
|
||||
ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo;
|
||||
|
||||
|
||||
@@ -579,7 +579,7 @@ void RenderableModel::render(const RenderData& data, RendererTasks&) {
|
||||
if (distanceToCamera < maxDistance) {
|
||||
_program->activate();
|
||||
|
||||
_program->setUniform(_uniformCache.opacity, _opacity);
|
||||
_program->setUniform(_uniformCache.opacity, opacity());
|
||||
|
||||
// Model transform and view transform needs to be in double precision
|
||||
const glm::dmat4 modelTransform =
|
||||
|
||||
@@ -270,7 +270,7 @@ void RenderableNodeLine::render(const RenderData& data, RendererTasks&) {
|
||||
|
||||
_program->setUniform("modelViewTransform", glm::mat4(modelViewTransform));
|
||||
_program->setUniform("projectionTransform", data.camera.projectionMatrix());
|
||||
_program->setUniform("color", glm::vec4(_lineColor.value(), _opacity));
|
||||
_program->setUniform("color", glm::vec4(_lineColor.value(), opacity()));
|
||||
|
||||
// Change GL state:
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
@@ -217,7 +217,7 @@ void RenderablePlane::render(const RenderData& data, RendererTasks&) {
|
||||
ZoneScoped
|
||||
|
||||
_shader->activate();
|
||||
_shader->setUniform("opacity", _opacity);
|
||||
_shader->setUniform("opacity", opacity());
|
||||
|
||||
_shader->setUniform("mirrorBackside", _mirrorBackside);
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ void RenderablePrism::render(const RenderData& data, RendererTasks&) {
|
||||
|
||||
// Uniforms
|
||||
_shader->setUniform(_uniformCache.modelViewProjection, modelViewProjectionTransform);
|
||||
_shader->setUniform(_uniformCache.color, glm::vec4(_lineColor.value(), _opacity));
|
||||
_shader->setUniform(_uniformCache.color, glm::vec4(_lineColor.value(), opacity()));
|
||||
|
||||
// Render
|
||||
glEnable(GL_PRIMITIVE_RESTART);
|
||||
|
||||
@@ -280,7 +280,7 @@ void RenderableSphere::render(const RenderData& data, RendererTasks&) {
|
||||
);
|
||||
_shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation);
|
||||
|
||||
float adjustedOpacity = _opacity;
|
||||
float adjustedOpacity = opacity();
|
||||
|
||||
if (!_disableFadeInDistance) {
|
||||
if (_fadeInThreshold > -1.0) {
|
||||
|
||||
@@ -275,7 +275,7 @@ void RenderableTimeVaryingSphere::render(const RenderData& data, RendererTasks&)
|
||||
);
|
||||
_shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation);
|
||||
|
||||
float adjustedOpacity = _opacity;
|
||||
float adjustedOpacity = opacity();
|
||||
|
||||
if (!_disableFadeInDistance) {
|
||||
if (_fadeInThreshold > -1.0) {
|
||||
|
||||
@@ -384,7 +384,7 @@ void RenderableTrail::render(const RenderData& data, RendererTasks&) {
|
||||
ZoneScoped
|
||||
|
||||
_programObject->activate();
|
||||
_programObject->setUniform(_uniformCache.opacity, _opacity);
|
||||
_programObject->setUniform(_uniformCache.opacity, opacity());
|
||||
|
||||
glm::dmat4 modelTransform =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
||||
|
||||
@@ -683,7 +683,7 @@ void RenderableBillboardsCloud::renderBillboards(const RenderData& data,
|
||||
_program->setUniform(_uniformCache.minBillboardSize, minBillboardSize);
|
||||
_program->setUniform(_uniformCache.maxBillboardSize, maxBillboardSize);
|
||||
_program->setUniform(_uniformCache.color, _pointColor);
|
||||
_program->setUniform(_uniformCache.alphaValue, _opacity);
|
||||
_program->setUniform(_uniformCache.alphaValue, opacity());
|
||||
_program->setUniform(_uniformCache.scaleFactor, _scaleFactor);
|
||||
_program->setUniform(_uniformCache.up, glm::vec3(orthoUp));
|
||||
_program->setUniform(_uniformCache.right, glm::vec3(orthoRight));
|
||||
|
||||
@@ -320,7 +320,7 @@ void RenderableDUMeshes::renderMeshes(const RenderData&,
|
||||
|
||||
_program->setUniform(_uniformCache.modelViewTransform, modelViewMatrix);
|
||||
_program->setUniform(_uniformCache.projectionTransform, projectionMatrix);
|
||||
_program->setUniform(_uniformCache.alphaValue, _opacity);
|
||||
_program->setUniform(_uniformCache.alphaValue, opacity());
|
||||
|
||||
for (const std::pair<const int, RenderingMesh>& pair : _renderingMeshesMap) {
|
||||
_program->setUniform(_uniformCache.color, _meshColorMap[pair.second.colorIndex]);
|
||||
|
||||
@@ -455,7 +455,7 @@ void RenderablePlanesCloud::renderPlanes(const RenderData&,
|
||||
_uniformCache.modelViewProjectionTransform,
|
||||
modelViewProjectionMatrix
|
||||
);
|
||||
_program->setUniform(_uniformCache.alphaValue, _opacity);
|
||||
_program->setUniform(_uniformCache.alphaValue, opacity());
|
||||
_program->setUniform(_uniformCache.fadeInValue, fadeInVariable);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
@@ -242,7 +242,7 @@ void RenderablePoints::render(const RenderData& data, RendererTasks&) {
|
||||
|
||||
_program->setUniform(_uniformCache.color, _pointColor);
|
||||
_program->setUniform(_uniformCache.sides, 4);
|
||||
_program->setUniform(_uniformCache.alphaValue, _opacity);
|
||||
_program->setUniform(_uniformCache.alphaValue, opacity());
|
||||
_program->setUniform(_uniformCache.scaleFactor, _scaleFactor);
|
||||
|
||||
if (_hasSpriteTexture) {
|
||||
|
||||
@@ -176,7 +176,7 @@ void RenderableOrbitDisc::render(const RenderData& data, RendererTasks&) {
|
||||
data.camera.projectionMatrix() * glm::mat4(modelViewTransform)
|
||||
);
|
||||
_shader->setUniform(_uniformCache.offset, _offset);
|
||||
_shader->setUniform(_uniformCache.opacity, _opacity);
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
_shader->setUniform(_uniformCache.eccentricity, _eccentricity);
|
||||
_shader->setUniform(_uniformCache.semiMajorAxis, _size);
|
||||
|
||||
|
||||
@@ -967,8 +967,8 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&,
|
||||
_globalRenderer.program->setUniform("orenNayarRoughness", onr);
|
||||
}
|
||||
|
||||
_localRenderer.program->setUniform("opacity", _opacity);
|
||||
_globalRenderer.program->setUniform("opacity", _opacity);
|
||||
_localRenderer.program->setUniform("opacity", opacity());
|
||||
_globalRenderer.program->setUniform("opacity", opacity());
|
||||
|
||||
|
||||
if (_globalRenderer.updatedSinceLastCall) {
|
||||
|
||||
@@ -150,7 +150,7 @@ void RenderableHabitableZone::render(const RenderData& data, RendererTasks&) {
|
||||
data.camera.projectionMatrix() * glm::mat4(modelViewTransform)
|
||||
);
|
||||
_shader->setUniform(_uniformCache.width, _width);
|
||||
_shader->setUniform(_uniformCache.opacity, _opacity);
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
_shader->setUniform(_uniformCache.conservativeBounds, _conservativeBounds);
|
||||
_shader->setUniform(_uniformCache.showOptimistic, _showOptimistic);
|
||||
|
||||
|
||||
@@ -451,7 +451,7 @@ void RenderableOrbitalKepler::render(const RenderData& data, RendererTasks&) {
|
||||
}
|
||||
|
||||
_programObject->activate();
|
||||
_programObject->setUniform(_uniformCache.opacity, _opacity);
|
||||
_programObject->setUniform(_uniformCache.opacity, opacity());
|
||||
_programObject->setUniform(_uniformCache.inGameTime, data.time.j2000Seconds());
|
||||
|
||||
glm::dmat4 modelTransform =
|
||||
|
||||
@@ -1034,10 +1034,10 @@ void RenderableStars::render(const RenderData& data, RendererTasks&) {
|
||||
const double funcValue = a * distCamera + b;
|
||||
fadeInVariable *= static_cast<float>(funcValue > 1.f ? 1.f : funcValue);
|
||||
|
||||
_program->setUniform(_uniformCache.alphaValue, _opacity * fadeInVariable);
|
||||
_program->setUniform(_uniformCache.alphaValue, opacity() * fadeInVariable);
|
||||
}
|
||||
else {
|
||||
_program->setUniform(_uniformCache.alphaValue, _opacity);
|
||||
_program->setUniform(_uniformCache.alphaValue, opacity());
|
||||
}
|
||||
|
||||
ghoul::opengl::TextureUnit psfUnit;
|
||||
|
||||
@@ -290,7 +290,7 @@ void RenderableTravelSpeed::update(const UpdateData& data) {
|
||||
}
|
||||
|
||||
_shaderProgram->setUniform("lineColor", _lineColor);
|
||||
_shaderProgram->setUniform("opacity", _opacity);
|
||||
_shaderProgram->setUniform("opacity", opacity());
|
||||
}
|
||||
|
||||
void RenderableTravelSpeed::render(const RenderData& data, RendererTasks& ) {
|
||||
|
||||
@@ -45,13 +45,19 @@ namespace {
|
||||
constexpr const char* _loggerCat = "RenderablePlaneProjection";
|
||||
constexpr const char* GalacticFrame = "GALACTIC";
|
||||
|
||||
// @TODO (emmbr 2022-01-20) Add documentation
|
||||
struct [[codegen::Dictionary(RenderablePlaneProjection)]] Parameters {
|
||||
std::optional<std::string> spacecraft;
|
||||
std::optional<std::string> instrument;
|
||||
std::optional<bool> moving;
|
||||
std::optional<std::string> name;
|
||||
// The SPICE name of the spacecraft from which the projection is performed
|
||||
std::string spacecraft;
|
||||
|
||||
// The SPICE name of the instrument that is used to project the image onto this
|
||||
// RenderablePlaneProjection
|
||||
std::string instrument;
|
||||
|
||||
// The SPICE name of the default target that is imaged by this planet
|
||||
std::optional<std::string> defaultTarget;
|
||||
|
||||
// The image that is used on this plane before any image is loaded from the
|
||||
// ImageSequencerr
|
||||
std::optional<std::string> texture;
|
||||
};
|
||||
#include "renderableplaneprojection_codegen.cpp"
|
||||
@@ -60,17 +66,15 @@ namespace {
|
||||
namespace openspace {
|
||||
|
||||
documentation::Documentation RenderablePlaneProjection::Documentation() {
|
||||
return codegen::doc<Parameters>("spacecraftinstruments_renderableorbitdisc");
|
||||
return codegen::doc<Parameters>("spacecraftinstruments_renderableplaneprojection");
|
||||
}
|
||||
|
||||
RenderablePlaneProjection::RenderablePlaneProjection(const ghoul::Dictionary& dict)
|
||||
: Renderable(dict)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dict);
|
||||
_spacecraft = p.spacecraft.value_or(_spacecraft);
|
||||
_instrument = p.instrument.value_or(_instrument);
|
||||
_moving = p.moving.value_or(_moving);
|
||||
_name = p.name.value_or(_name);
|
||||
_spacecraft = p.spacecraft;
|
||||
_instrument = p.instrument;
|
||||
_defaultTarget = p.defaultTarget.value_or(_defaultTarget);
|
||||
|
||||
if (p.texture.has_value()) {
|
||||
@@ -118,7 +122,7 @@ void RenderablePlaneProjection::render(const RenderData& data, RendererTasks&) {
|
||||
_instrument
|
||||
);
|
||||
|
||||
if (!_hasImage || (_moving && !active)) {
|
||||
if (!_hasImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,7 +165,7 @@ void RenderablePlaneProjection::update(const UpdateData& data) {
|
||||
|
||||
const double timePast = std::abs(img.timeRange.start - _previousTime);
|
||||
|
||||
if (_moving || _planeIsDirty) {
|
||||
if (_planeIsDirty) {
|
||||
updatePlane(img, time);
|
||||
}
|
||||
else if (timePast > std::numeric_limits<double>::epsilon()) {
|
||||
@@ -245,9 +249,7 @@ void RenderablePlaneProjection::updatePlane(const Image& img, double currentTime
|
||||
) * bounds[j];
|
||||
glm::dvec3 cornerPosition = glm::proj(vecToTarget, bounds[j]);
|
||||
|
||||
if (!_moving) {
|
||||
cornerPosition -= vecToTarget;
|
||||
}
|
||||
cornerPosition -= vecToTarget;
|
||||
cornerPosition = SpiceManager::ref().frameTransformationMatrix(
|
||||
GalacticFrame,
|
||||
_target.frame,
|
||||
@@ -258,15 +260,6 @@ void RenderablePlaneProjection::updatePlane(const Image& img, double currentTime
|
||||
projection[j] = glm::vec3(cornerPosition * 1000.0);
|
||||
}
|
||||
|
||||
if (!_moving) {
|
||||
Scene* scene = global::renderEngine->scene();
|
||||
SceneGraphNode* thisNode = scene->sceneGraphNode(_name);
|
||||
SceneGraphNode* newParent = scene->sceneGraphNode(_target.node);
|
||||
if (thisNode && newParent) {
|
||||
thisNode->setParent(*newParent);
|
||||
}
|
||||
}
|
||||
|
||||
const GLfloat vertex_data[] = {
|
||||
// square of two triangles drawn within fov in target coordinates
|
||||
// x y z w s t
|
||||
@@ -299,7 +292,7 @@ void RenderablePlaneProjection::updatePlane(const Image& img, double currentTime
|
||||
reinterpret_cast<void*>(sizeof(GLfloat) * 4)
|
||||
);
|
||||
|
||||
if (!_moving && !img.path.empty()) {
|
||||
if (!img.path.empty()) {
|
||||
_texturePath = img.path;
|
||||
loadTexture();
|
||||
}
|
||||
|
||||
@@ -86,8 +86,6 @@ private:
|
||||
std::string frame;
|
||||
std::string node;
|
||||
} _target;
|
||||
std::string _name = "ImagePlane";
|
||||
bool _moving = false;
|
||||
bool _hasImage = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ void RenderableShadowCylinder::render(const RenderData& data, RendererTasks&) {
|
||||
);
|
||||
|
||||
_shader->setUniform(_uniformCache.shadowColor, _shadowColor);
|
||||
_shader->setUniform(_uniformCache.opacity, _opacity);
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
|
||||
glBindVertexArray(_vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, static_cast<GLsizei>(_vertices.size()));
|
||||
|
||||
@@ -140,7 +140,7 @@ RenderableToyVolume::RenderableToyVolume(const ghoul::Dictionary& dictionary)
|
||||
RenderableToyVolume::~RenderableToyVolume() {}
|
||||
|
||||
void RenderableToyVolume::initializeGL() {
|
||||
glm::vec4 color(glm::vec3(_color), _opacity);
|
||||
glm::vec4 color(glm::vec3(_color), opacity());
|
||||
_raycaster = std::make_unique<ToyVolumeRaycaster>(color);
|
||||
_raycaster->initialize();
|
||||
|
||||
@@ -197,7 +197,7 @@ void RenderableToyVolume::update(const UpdateData& data) {
|
||||
std::pow(10.f, static_cast<float>(_scalingExponent))
|
||||
);
|
||||
|
||||
glm::vec4 color(glm::vec3(_color), _opacity);
|
||||
glm::vec4 color(glm::vec3(_color), opacity());
|
||||
|
||||
_raycaster->setColor(color);
|
||||
_raycaster->setStepSize(_stepSize);
|
||||
|
||||
@@ -137,7 +137,7 @@ void BasicVolumeRaycaster::preRaycast(const RaycastData& data,
|
||||
program.setUniform("nClips_" + id, nClips);
|
||||
program.setUniform("clipNormals_" + id, clipNormals);
|
||||
program.setUniform("clipOffsets_" + id, clipOffsets);
|
||||
program.setUniform("opacity_" + id, _opacity);
|
||||
program.setUniform("opacity_" + id, opacity());
|
||||
program.setUniform("rNormalization_" + id, _rNormalization);
|
||||
program.setUniform("rUpperBound_" + id, _rUpperBound);
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ void RenderableTimeVaryingVolume::update(const UpdateData&) {
|
||||
_raycaster->setVolumeTexture(nullptr);
|
||||
}
|
||||
_raycaster->setStepSize(_stepSize);
|
||||
_raycaster->setOpacity(_opacity);
|
||||
_raycaster->setOpacity(opacity());
|
||||
_raycaster->setRNormalization(_rNormalization);
|
||||
_raycaster->setRUpperBound(_rUpperBound);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ set(OPENSPACE_SOURCE
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelconnection.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelpeer.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelpeer_lua.inl
|
||||
${OPENSPACE_BASE_DIR}/src/network/parallelserver.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/properties/optionproperty.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/properties/property.cpp
|
||||
${OPENSPACE_BASE_DIR}/src/properties/propertyowner.cpp
|
||||
@@ -262,7 +261,6 @@ set(OPENSPACE_HEADER
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/navigation/waypoint.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelconnection.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelpeer.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/parallelserver.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/messagestructures.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/network/messagestructureshelper.h
|
||||
${OPENSPACE_BASE_DIR}/include/openspace/properties/listproperty.h
|
||||
|
||||
@@ -1659,8 +1659,14 @@ void OpenSpaceEngine::removeModeChangeCallback(CallbackHandle handle) {
|
||||
|
||||
void setCameraFromProfile(const Profile& p) {
|
||||
if (!p.camera.has_value()) {
|
||||
throw ghoul::RuntimeError("No 'camera' entry exists in the startup profile");
|
||||
// If the camera is not specified, we want to set it to a sensible default value
|
||||
interaction::NavigationState nav;
|
||||
nav.anchor = "Root";
|
||||
nav.referenceFrame = "Root";
|
||||
global::navigationHandler->setNavigationStateNextFrame(nav);
|
||||
return;
|
||||
}
|
||||
|
||||
std::visit(
|
||||
overloaded{
|
||||
[](const Profile::CameraNavState& navStateProfile) {
|
||||
|
||||
@@ -48,7 +48,7 @@ std::vector<std::byte> SyncEngine::encodeSyncables() {
|
||||
return data;
|
||||
}
|
||||
|
||||
// Should be called on sgct slaves
|
||||
// Should be called on sgct clients
|
||||
void SyncEngine::decodeSyncables(std::vector<std::byte> data) {
|
||||
_syncBuffer.setData(std::move(data));
|
||||
for (Syncable* syncable : _syncables) {
|
||||
|
||||
+106
-51
@@ -40,6 +40,7 @@
|
||||
#include <openspace/util/universalhelpers.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/interpolator.h>
|
||||
#include <glm/ext/quaternion_relational.hpp>
|
||||
|
||||
namespace {
|
||||
constexpr const char _loggerCat[] = "Path";
|
||||
@@ -131,7 +132,6 @@ Path::Path(Waypoint start, Waypoint end, Type type,
|
||||
|
||||
// We now know how long it took to traverse the path. Use that
|
||||
_speedFactorFromDuration = _progressedTime / *duration;
|
||||
|
||||
resetPlaybackVariables();
|
||||
}
|
||||
}
|
||||
@@ -161,23 +161,7 @@ CameraPose Path::traversePath(double dt, float speedScale) {
|
||||
if (_type == Type::Linear) {
|
||||
// Special handling of linear paths, so that it can be used when we are
|
||||
// traversing very large distances without introducing precision problems
|
||||
const glm::dvec3 prevPosToEnd = _prevPose.position - _end.position();
|
||||
const double remainingDistance = glm::length(prevPosToEnd);
|
||||
|
||||
// Actual displacement may not be bigger than remaining distance
|
||||
if (displacement > remainingDistance) {
|
||||
displacement = remainingDistance;
|
||||
_traveledDistance = pathLength();
|
||||
_shouldQuit = true;
|
||||
return _end.pose();
|
||||
}
|
||||
|
||||
// Just move along the line from the current position to the target
|
||||
newPose.position = _prevPose.position -
|
||||
displacement * glm::normalize(prevPosToEnd);
|
||||
|
||||
const double relativeDistance = _traveledDistance / pathLength();
|
||||
newPose.rotation = interpolateRotation(relativeDistance);
|
||||
newPose = linearInterpolatedPose(_traveledDistance, displacement);
|
||||
}
|
||||
else {
|
||||
if (std::abs(prevDistance - _traveledDistance) < LengthEpsilon) {
|
||||
@@ -204,7 +188,14 @@ bool Path::hasReachedEnd() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (_traveledDistance / pathLength()) >= 1.0;
|
||||
bool isPositionFinished = (_traveledDistance / pathLength()) >= 1.0;
|
||||
bool isRotationFinished = glm::all(glm::equal(
|
||||
_prevPose.rotation,
|
||||
_end.rotation(),
|
||||
glm::epsilon<double>()
|
||||
));
|
||||
|
||||
return isPositionFinished && isRotationFinished;
|
||||
}
|
||||
|
||||
void Path::resetPlaybackVariables() {
|
||||
@@ -214,6 +205,28 @@ void Path::resetPlaybackVariables() {
|
||||
_shouldQuit = false;
|
||||
}
|
||||
|
||||
CameraPose Path::linearInterpolatedPose(double distance, double displacement) {
|
||||
ghoul_assert(_type == Type::Linear, "Path type must be linear");
|
||||
const double relativeDistance = distance / pathLength();
|
||||
const glm::dvec3 prevPosToEnd = _prevPose.position - _end.position();
|
||||
const double remainingDistance = glm::length(prevPosToEnd);
|
||||
CameraPose pose;
|
||||
|
||||
// Actual displacement may not be bigger than remaining distance
|
||||
if (displacement > remainingDistance) {
|
||||
_traveledDistance = pathLength();
|
||||
pose.position = _end.position();
|
||||
}
|
||||
else {
|
||||
// Just move along line from the current position to the target
|
||||
const glm::dvec3 lineDir = glm::normalize(prevPosToEnd);
|
||||
pose.position = _prevPose.position - displacement * lineDir;
|
||||
}
|
||||
|
||||
pose.rotation = linearPathRotation(relativeDistance);
|
||||
return pose;
|
||||
}
|
||||
|
||||
CameraPose Path::interpolatedPose(double distance) const {
|
||||
const double relativeDistance = distance / pathLength();
|
||||
CameraPose cs;
|
||||
@@ -227,6 +240,8 @@ glm::dquat Path::interpolateRotation(double t) const {
|
||||
case Type::AvoidCollision:
|
||||
return easedSlerpRotation(t);
|
||||
case Type::Linear:
|
||||
// @TODO (2022-03-29, emmbr) Fix so that rendering the rotation of linear
|
||||
// paths works again. I.e. openspace.debugging.renderCameraPath
|
||||
return linearPathRotation(t);
|
||||
case Type::ZoomOutOverview:
|
||||
case Type::AvoidCollisionWithLookAt:
|
||||
@@ -243,45 +258,59 @@ glm::dquat Path::easedSlerpRotation(double t) const {
|
||||
}
|
||||
|
||||
glm::dquat Path::linearPathRotation(double t) const {
|
||||
const double tHalf = 0.5;
|
||||
const glm::dvec3 a = ghoul::viewDirection(_start.rotation());
|
||||
const glm::dvec3 b = ghoul::viewDirection(_end.rotation());
|
||||
const double angle = std::acos(glm::dot(a, b)); // assumes length 1.0 for a & b
|
||||
|
||||
const glm::dvec3 endNodePos = _end.node()->worldPosition();
|
||||
const glm::dvec3 endUp = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0);
|
||||
// Seconds per pi angles. Per default, it takes 5 seconds to turn 90 degrees
|
||||
double factor = 5.0 / glm::half_pi<double>();
|
||||
factor *= global::navigationHandler->pathNavigator().linearRotationSpeedFactor();
|
||||
|
||||
if (t < tHalf) {
|
||||
// Interpolate to look at target
|
||||
const glm::dvec3 halfWayPosition = _curve->positionAt(tHalf);
|
||||
const glm::dquat q = ghoul::lookAtQuaternion(halfWayPosition, endNodePos, endUp);
|
||||
double turnDuration = std::max(angle * factor, 1.0); // Always at least 1 second
|
||||
const double time = glm::clamp(_progressedTime / turnDuration, 0.0, 1.0);
|
||||
return easedSlerpRotation(time);
|
||||
|
||||
const double tScaled = ghoul::sineEaseInOut(t / tHalf);
|
||||
return glm::slerp(_start.rotation(), q, tScaled);
|
||||
}
|
||||
// @TODO (2022-03-18, emmbr) Leaving this for now, as something similar might have to
|
||||
// be implemented for navigation states. But should be removed/reimplemented
|
||||
|
||||
// This distance is guaranteed to be strictly decreasing for linear paths
|
||||
const double distanceToEnd = glm::distance(_prevPose.position, _end.position());
|
||||
//const glm::dvec3 endNodePos = _end.node()->worldPosition();
|
||||
//const glm::dvec3 endUp = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0);
|
||||
|
||||
// Determine the distance at which to start interpolating to the target rotation.
|
||||
// The magic numbers here are just randomly picked constants, set to make the
|
||||
// resulting rotation look ok-ish
|
||||
double closingUpDistance = 10.0 * _end.validBoundingSphere();
|
||||
if (pathLength() < 2.0 * closingUpDistance) {
|
||||
closingUpDistance = 0.2 * pathLength();
|
||||
}
|
||||
//const double tHalf = 0.5;
|
||||
//if (t < tHalf) {
|
||||
// // Interpolate to look at target
|
||||
// const glm::dvec3 halfWayPosition = _curve->positionAt(tHalf);
|
||||
// const glm::dquat q = ghoul::lookAtQuaternion(halfWayPosition, endNodePos, endUp);
|
||||
|
||||
if (distanceToEnd < closingUpDistance) {
|
||||
// Interpolate to target rotation
|
||||
const double tScaled = ghoul::sineEaseInOut(1.0 - distanceToEnd / closingUpDistance);
|
||||
// const double tScaled = ghoul::sineEaseInOut(t / tHalf);
|
||||
// return glm::slerp(_start.rotation(), q, tScaled);
|
||||
//}
|
||||
|
||||
// Compute a position in front of the camera at the end orientation
|
||||
const double inFrontDistance = glm::distance(_end.position(), endNodePos);
|
||||
const glm::dvec3 viewDir = ghoul::viewDirection(_end.rotation());
|
||||
const glm::dvec3 inFrontOfEnd = _end.position() + inFrontDistance * viewDir;
|
||||
const glm::dvec3 lookAtPos = ghoul::interpolateLinear(tScaled, endNodePos, inFrontOfEnd);
|
||||
return ghoul::lookAtQuaternion(_prevPose.position, lookAtPos, endUp);
|
||||
}
|
||||
//// This distance is guaranteed to be strictly decreasing for linear paths
|
||||
//const double distanceToEnd = glm::distance(_prevPose.position, _end.position());
|
||||
|
||||
// Keep looking at the end node
|
||||
return ghoul::lookAtQuaternion(_prevPose.position, endNodePos, endUp);
|
||||
//// Determine the distance at which to start interpolating to the target rotation.
|
||||
//// The magic numbers here are just randomly picked constants, set to make the
|
||||
//// resulting rotation look ok-ish
|
||||
//double closingUpDistance = 10.0 * _end.validBoundingSphere();
|
||||
//if (pathLength() < 2.0 * closingUpDistance) {
|
||||
// closingUpDistance = 0.2 * pathLength();
|
||||
//}
|
||||
|
||||
//if (distanceToEnd < closingUpDistance) {
|
||||
// // Interpolate to target rotation
|
||||
// const double tScaled = ghoul::sineEaseInOut(1.0 - distanceToEnd / closingUpDistance);
|
||||
|
||||
// // Compute a position in front of the camera at the end orientation
|
||||
// const double inFrontDistance = glm::distance(_end.position(), endNodePos);
|
||||
// const glm::dvec3 viewDir = ghoul::viewDirection(_end.rotation());
|
||||
// const glm::dvec3 inFrontOfEnd = _end.position() + inFrontDistance * viewDir;
|
||||
// const glm::dvec3 lookAtPos = ghoul::interpolateLinear(tScaled, endNodePos, inFrontOfEnd);
|
||||
// return ghoul::lookAtQuaternion(_prevPose.position, lookAtPos, endUp);
|
||||
//}
|
||||
|
||||
//// Keep looking at the end node
|
||||
//return ghoul::lookAtQuaternion(_prevPose.position, endNodePos, endUp);
|
||||
}
|
||||
|
||||
glm::dquat Path::lookAtTargetsRotation(double t) const {
|
||||
@@ -350,8 +379,17 @@ double Path::speedAlongPath(double traveledDistance) const {
|
||||
double startUpDistance = DampenDistanceFactor * _start.validBoundingSphere();
|
||||
double closeUpDistance = DampenDistanceFactor * _end.validBoundingSphere();
|
||||
|
||||
// Kind of an ugly workaround to make damping behave over very long paths, and/or
|
||||
// where the target nodes might have large bounding spheres. The current max is set
|
||||
// based on the order of magnitude of the solar system, which ofc is very specific to
|
||||
// our space content...
|
||||
// @TODO (2022-03-22, emmbr) Come up with a better more general solution
|
||||
constexpr const double MaxDistance = 1E12;
|
||||
startUpDistance = glm::min(MaxDistance, startUpDistance);
|
||||
closeUpDistance = glm::min(MaxDistance, closeUpDistance);
|
||||
|
||||
if (pathLength() < startUpDistance + closeUpDistance) {
|
||||
startUpDistance = 0.49 * pathLength(); // a little less than half
|
||||
startUpDistance = 0.4 * pathLength(); // a little less than half
|
||||
closeUpDistance = startUpDistance;
|
||||
}
|
||||
|
||||
@@ -619,6 +657,23 @@ Path createPathFromDictionary(const ghoul::Dictionary& dictionary,
|
||||
p.useTargetUpDirection.value_or(false)
|
||||
};
|
||||
|
||||
double startToTargetCenterDistance = glm::distance(
|
||||
startPoint.position(),
|
||||
targetNode->worldPosition()
|
||||
);
|
||||
|
||||
// Use a linear path if camera start is within the bounding sphere
|
||||
const PathNavigator& navigator = global::navigationHandler->pathNavigator();
|
||||
const double bs = navigator.findValidBoundingSphere(targetNode);
|
||||
bool withinTargetBoundingSphere = startToTargetCenterDistance < bs;
|
||||
if (withinTargetBoundingSphere) {
|
||||
LINFO(
|
||||
"Camera is within the bounding sphere of the target node. "
|
||||
"Using linear path"
|
||||
);
|
||||
type = Path::Type::Linear;
|
||||
}
|
||||
|
||||
waypoints = { computeWaypointFromNodeInfo(info, startPoint, type) };
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace {
|
||||
constexpr openspace::properties::Property::PropertyInfo SpeedScaleInfo = {
|
||||
"SpeedScale",
|
||||
"Speed Scale",
|
||||
"Scale factor that the speed will be mulitplied with during path traversal. "
|
||||
"Scale factor that the speed will be multiplied with during path traversal. "
|
||||
"Can be used to speed up or slow down the camera motion, depending on if the "
|
||||
"value is larger than or smaller than one."
|
||||
};
|
||||
@@ -90,6 +90,14 @@ namespace {
|
||||
"object."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RotationSpeedFactorInfo = {
|
||||
"RotationSpeedFactor",
|
||||
"Rotation Speed Factor (Linear Path)",
|
||||
"Affects how fast the camera rotates to the target rotation during a linear "
|
||||
"path. A value of 1 means that the camera will rotate 90 degrees in about 5 "
|
||||
"seconds. A value of 2 means twice that fast, and so on."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo =
|
||||
{
|
||||
"MinimalValidBoundingSphere",
|
||||
@@ -119,6 +127,7 @@ PathNavigator::PathNavigator()
|
||||
, _speedScale(SpeedScaleInfo, 1.f, 0.01f, 2.f)
|
||||
, _applyIdleBehaviorOnFinish(IdleBehaviorOnFinishInfo, false)
|
||||
, _arrivalDistanceFactor(ArrivalDistanceFactorInfo, 2.0, 0.1, 20.0)
|
||||
, _linearRotationSpeedFactor(RotationSpeedFactorInfo, 1.f, 0.1f, 2.f)
|
||||
, _minValidBoundingSphere(MinBoundingSphereInfo, 10.0, 1.0, 3e10)
|
||||
, _relevantNodeTags(RelevantNodeTagsInfo)
|
||||
{
|
||||
@@ -134,6 +143,7 @@ PathNavigator::PathNavigator()
|
||||
addProperty(_speedScale);
|
||||
addProperty(_applyIdleBehaviorOnFinish);
|
||||
addProperty(_arrivalDistanceFactor);
|
||||
addProperty(_linearRotationSpeedFactor);
|
||||
addProperty(_minValidBoundingSphere);
|
||||
|
||||
_relevantNodeTags = std::vector<std::string>{
|
||||
@@ -166,6 +176,10 @@ double PathNavigator::arrivalDistanceFactor() const {
|
||||
return _arrivalDistanceFactor;
|
||||
}
|
||||
|
||||
float PathNavigator::linearRotationSpeedFactor() const {
|
||||
return _linearRotationSpeedFactor;
|
||||
}
|
||||
|
||||
bool PathNavigator::hasCurrentPath() const {
|
||||
return _currentPath != nullptr;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace {
|
||||
|
||||
namespace openspace {
|
||||
|
||||
const unsigned int ParallelConnection::ProtocolVersion = 5;
|
||||
// Gonna do some UTF-like magic once we reach 255 to introduce a second byte or something
|
||||
const uint8_t ParallelConnection::ProtocolVersion = 6;
|
||||
|
||||
ParallelConnection::Message::Message(MessageType t, std::vector<char> c)
|
||||
: type(t)
|
||||
@@ -52,8 +53,9 @@ ParallelConnection::DataMessage::DataMessage(datamessagestructures::Type t,
|
||||
, content(std::move(c))
|
||||
{}
|
||||
|
||||
ParallelConnection::ConnectionLostError::ConnectionLostError()
|
||||
ParallelConnection::ConnectionLostError::ConnectionLostError(bool shouldLogError_)
|
||||
: ghoul::RuntimeError("Parallel connection lost", "ParallelConnection")
|
||||
, shouldLogError(shouldLogError_)
|
||||
{}
|
||||
|
||||
ParallelConnection::ParallelConnection(std::unique_ptr<ghoul::io::TcpSocket> socket)
|
||||
@@ -65,14 +67,14 @@ bool ParallelConnection::isConnectedOrConnecting() const {
|
||||
}
|
||||
|
||||
void ParallelConnection::sendDataMessage(const DataMessage& dataMessage) {
|
||||
const uint32_t dataMessageTypeOut = static_cast<uint32_t>(dataMessage.type);
|
||||
const uint8_t dataMessageTypeOut = static_cast<uint8_t>(dataMessage.type);
|
||||
const double dataMessageTimestamp = dataMessage.timestamp;
|
||||
|
||||
std::vector<char> messageContent;
|
||||
messageContent.insert(
|
||||
messageContent.end(),
|
||||
reinterpret_cast<const char*>(&dataMessageTypeOut),
|
||||
reinterpret_cast<const char*>(&dataMessageTypeOut) + sizeof(uint32_t)
|
||||
reinterpret_cast<const char*>(&dataMessageTypeOut) + sizeof(uint8_t)
|
||||
);
|
||||
|
||||
messageContent.insert(
|
||||
@@ -90,7 +92,7 @@ void ParallelConnection::sendDataMessage(const DataMessage& dataMessage) {
|
||||
}
|
||||
|
||||
bool ParallelConnection::sendMessage(const Message& message) {
|
||||
const uint32_t messageTypeOut = static_cast<uint32_t>(message.type);
|
||||
const uint8_t messageTypeOut = static_cast<uint8_t>(message.type);
|
||||
const uint32_t messageSizeOut = static_cast<uint32_t>(message.content.size());
|
||||
std::vector<char> header;
|
||||
|
||||
@@ -100,12 +102,12 @@ bool ParallelConnection::sendMessage(const Message& message) {
|
||||
|
||||
header.insert(header.end(),
|
||||
reinterpret_cast<const char*>(&ProtocolVersion),
|
||||
reinterpret_cast<const char*>(&ProtocolVersion) + sizeof(uint32_t)
|
||||
reinterpret_cast<const char*>(&ProtocolVersion) + sizeof(uint8_t)
|
||||
);
|
||||
|
||||
header.insert(header.end(),
|
||||
reinterpret_cast<const char*>(&messageTypeOut),
|
||||
reinterpret_cast<const char*>(&messageTypeOut) + sizeof(uint32_t)
|
||||
reinterpret_cast<const char*>(&messageTypeOut) + sizeof(uint8_t)
|
||||
);
|
||||
|
||||
header.insert(header.end(),
|
||||
@@ -123,6 +125,7 @@ bool ParallelConnection::sendMessage(const Message& message) {
|
||||
}
|
||||
|
||||
void ParallelConnection::disconnect() {
|
||||
_shouldDisconnect = true;
|
||||
if (_socket) {
|
||||
_socket->disconnect();
|
||||
}
|
||||
@@ -136,7 +139,9 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
|
||||
// Header consists of...
|
||||
constexpr size_t HeaderSize =
|
||||
2 * sizeof(char) + // OS
|
||||
3 * sizeof(uint32_t); // Protocol version, message type and message size
|
||||
sizeof(uint8_t) + // Protocol version
|
||||
sizeof(uint8_t) + // Message type
|
||||
sizeof(uint32_t); // message size
|
||||
|
||||
// Create basic buffer for receiving first part of messages
|
||||
std::vector<char> headerBuffer(HeaderSize);
|
||||
@@ -144,20 +149,27 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
|
||||
|
||||
// Receive the header data
|
||||
if (!_socket->get(headerBuffer.data(), HeaderSize)) {
|
||||
LERROR("Failed to read header from socket. Disconencting.");
|
||||
throw ConnectionLostError();
|
||||
// The `get` call is blocking until something happens, so we might end up here if
|
||||
// the socket properly closed or if the loading legitimately failed
|
||||
if (_shouldDisconnect) {
|
||||
throw ConnectionLostError(false);
|
||||
}
|
||||
else {
|
||||
LERROR("Failed to read header from socket. Disconnecting");
|
||||
throw ConnectionLostError();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that header matches this version of OpenSpace
|
||||
if (!(headerBuffer[0] == 'O' && headerBuffer[1] == 'S')) {
|
||||
LERROR("Expected to read message header 'OS' from socket.");
|
||||
LERROR("Expected to read message header 'OS' from socket");
|
||||
throw ConnectionLostError();
|
||||
}
|
||||
|
||||
size_t offset = 2;
|
||||
const uint32_t protocolVersionIn =
|
||||
*reinterpret_cast<uint32_t*>(headerBuffer.data() + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
const uint8_t protocolVersionIn =
|
||||
*reinterpret_cast<uint8_t*>(headerBuffer.data() + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
if (protocolVersionIn != ProtocolVersion) {
|
||||
LERROR(fmt::format(
|
||||
@@ -168,9 +180,9 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
|
||||
throw ConnectionLostError();
|
||||
}
|
||||
|
||||
const uint32_t messageTypeIn =
|
||||
*reinterpret_cast<uint32_t*>(headerBuffer.data() + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
const uint8_t messageTypeIn =
|
||||
*reinterpret_cast<uint8_t*>(headerBuffer.data() + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
const uint32_t messageSizeIn =
|
||||
*reinterpret_cast<uint32_t*>(headerBuffer.data() + offset);
|
||||
@@ -181,7 +193,7 @@ ParallelConnection::Message ParallelConnection::receiveMessage() {
|
||||
// Receive the payload
|
||||
messageBuffer.resize(messageSize);
|
||||
if (!_socket->get(messageBuffer.data(), messageSize)) {
|
||||
LERROR("Failed to read message from socket. Disconencting.");
|
||||
LERROR("Failed to read message from socket. Disconnecting");
|
||||
throw ConnectionLostError();
|
||||
}
|
||||
|
||||
|
||||
@@ -163,31 +163,59 @@ void ParallelPeer::disconnect() {
|
||||
}
|
||||
|
||||
void ParallelPeer::sendAuthentication() {
|
||||
std::string name = _name;
|
||||
// Length of this nodes name
|
||||
const uint32_t nameLength = static_cast<uint32_t>(name.length());
|
||||
std::string password = _password;
|
||||
if (password.size() > std::numeric_limits<uint16_t>::max()) {
|
||||
password.resize(std::numeric_limits<uint16_t>::max());
|
||||
}
|
||||
const uint16_t passwordSize = static_cast<uint16_t>(password.size());
|
||||
|
||||
// Total size of the buffer: (passcode + namelength + name)
|
||||
const size_t size = sizeof(uint64_t) + sizeof(uint32_t) + nameLength;
|
||||
std::string hostPassword = _hostPassword;
|
||||
if (hostPassword.size() > std::numeric_limits<uint16_t>::max()) {
|
||||
hostPassword.resize(std::numeric_limits<uint16_t>::max());
|
||||
}
|
||||
const uint16_t hostPasswordSize = static_cast<uint16_t>(hostPassword.size());
|
||||
|
||||
std::string name = _name;
|
||||
if (name.size() > std::numeric_limits<uint8_t>::max()) {
|
||||
name.resize(std::numeric_limits<uint8_t>::max());
|
||||
}
|
||||
const uint8_t nameLength = static_cast<uint8_t>(name.length());
|
||||
|
||||
|
||||
// Total size of the buffer
|
||||
const size_t size =
|
||||
sizeof(uint16_t) + // password length
|
||||
passwordSize + // password
|
||||
sizeof(uint16_t) + // host password length
|
||||
hostPasswordSize + // host password
|
||||
sizeof(uint8_t) + // name length
|
||||
nameLength; // name
|
||||
|
||||
// Create and reserve buffer
|
||||
std::vector<char> buffer;
|
||||
buffer.reserve(size);
|
||||
|
||||
const uint64_t passCode = std::hash<std::string>{}(_password.value());
|
||||
|
||||
// Write the hashed password to buffer
|
||||
// Write the password to buffer
|
||||
buffer.insert(
|
||||
buffer.end(),
|
||||
reinterpret_cast<const char*>(&passCode),
|
||||
reinterpret_cast<const char*>(&passCode) + sizeof(uint64_t)
|
||||
reinterpret_cast<const char*>(&passwordSize),
|
||||
reinterpret_cast<const char*>(&passwordSize) + sizeof(uint16_t)
|
||||
);
|
||||
buffer.insert(buffer.end(), password.begin(), password.end());
|
||||
|
||||
// Write the host password to buffer
|
||||
buffer.insert(
|
||||
buffer.end(),
|
||||
reinterpret_cast<const char*>(&hostPasswordSize),
|
||||
reinterpret_cast<const char*>(&hostPasswordSize) + sizeof(uint16_t)
|
||||
);
|
||||
buffer.insert(buffer.end(), hostPassword.begin(), hostPassword.end());
|
||||
|
||||
// Write the length of the nodes name to buffer
|
||||
buffer.insert(
|
||||
buffer.end(),
|
||||
reinterpret_cast<const char*>(&nameLength),
|
||||
reinterpret_cast<const char*>(&nameLength) + sizeof(uint32_t)
|
||||
reinterpret_cast<const char*>(&nameLength) + sizeof(uint8_t)
|
||||
);
|
||||
|
||||
// Write this node's name to buffer
|
||||
@@ -265,8 +293,8 @@ void ParallelPeer::dataMessageReceived(const std::vector<char>& message) {
|
||||
size_t offset = 0;
|
||||
|
||||
// The type of data message received
|
||||
const uint32_t type = *(reinterpret_cast<const uint32_t*>(message.data() + offset));
|
||||
offset += sizeof(uint32_t);
|
||||
const uint8_t type = *(reinterpret_cast<const uint8_t*>(message.data() + offset));
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
const double timestamp = *(reinterpret_cast<const double*>(message.data() + offset));
|
||||
offset += sizeof(double);
|
||||
@@ -363,19 +391,19 @@ void ParallelPeer::dataMessageReceived(const std::vector<char>& message) {
|
||||
}
|
||||
|
||||
void ParallelPeer::connectionStatusMessageReceived(const std::vector<char>& message) {
|
||||
if (message.size() < 2 * sizeof(uint32_t)) {
|
||||
if (message.size() < 2 * sizeof(uint8_t)) {
|
||||
LERROR("Malformed connection status message");
|
||||
return;
|
||||
}
|
||||
size_t pointer = 0;
|
||||
uint32_t statusIn = *(reinterpret_cast<const uint32_t*>(&message[pointer]));
|
||||
const uint8_t statusIn = *(reinterpret_cast<const uint8_t*>(&message[pointer]));
|
||||
const ParallelConnection::Status status = static_cast<ParallelConnection::Status>(
|
||||
statusIn
|
||||
);
|
||||
pointer += sizeof(uint32_t);
|
||||
pointer += sizeof(uint8_t);
|
||||
|
||||
const size_t hostNameSize = *(reinterpret_cast<const uint32_t*>(&message[pointer]));
|
||||
pointer += sizeof(uint32_t);
|
||||
const uint8_t hostNameSize = *(reinterpret_cast<const uint8_t*>(&message[pointer]));
|
||||
pointer += sizeof(uint8_t);
|
||||
|
||||
if (hostNameSize > message.size() - pointer) {
|
||||
LERROR("Malformed connection status message");
|
||||
@@ -409,8 +437,7 @@ void ParallelPeer::connectionStatusMessageReceived(const std::vector<char>& mess
|
||||
global::timeManager->clearKeyframes();
|
||||
}
|
||||
|
||||
void ParallelPeer::nConnectionsMessageReceived(const std::vector<char>& message)
|
||||
{
|
||||
void ParallelPeer::nConnectionsMessageReceived(const std::vector<char>& message) {
|
||||
if (message.size() < sizeof(uint32_t)) {
|
||||
LERROR("Malformed host info message");
|
||||
return;
|
||||
@@ -424,8 +451,10 @@ void ParallelPeer::handleCommunication() {
|
||||
try {
|
||||
ParallelConnection::Message m = _connection.receiveMessage();
|
||||
queueInMessage(m);
|
||||
} catch (const ParallelConnection::ConnectionLostError&) {
|
||||
LERROR("Parallel connection lost");
|
||||
} catch (const ParallelConnection::ConnectionLostError& e) {
|
||||
if (e.shouldLogError) {
|
||||
LERROR("Parallel connection lost");
|
||||
}
|
||||
}
|
||||
}
|
||||
setStatus(ParallelConnection::Status::Disconnected);
|
||||
@@ -445,12 +474,14 @@ void ParallelPeer::setName(std::string name) {
|
||||
|
||||
void ParallelPeer::requestHostship() {
|
||||
std::vector<char> buffer;
|
||||
uint64_t passwordHash = std::hash<std::string>{}(_hostPassword);
|
||||
std::string hostPw = _hostPassword;
|
||||
uint16_t hostPwSize = static_cast<uint16_t>(hostPw.size());
|
||||
buffer.insert(
|
||||
buffer.end(),
|
||||
reinterpret_cast<char*>(&passwordHash),
|
||||
reinterpret_cast<char*>(&passwordHash) + sizeof(uint64_t)
|
||||
reinterpret_cast<const char*>(&hostPwSize),
|
||||
reinterpret_cast<const char*>(&hostPwSize) + sizeof(uint16_t)
|
||||
);
|
||||
buffer.insert(buffer.end(), hostPw.begin(), hostPw.end());
|
||||
|
||||
_connection.sendMessage(ParallelConnection::Message(
|
||||
ParallelConnection::MessageType::HostshipRequest,
|
||||
@@ -504,7 +535,7 @@ void ParallelPeer::resetTimeOffset() {
|
||||
void ParallelPeer::preSynchronization() {
|
||||
ZoneScoped
|
||||
|
||||
std::unique_lock<std::mutex> unqlock(_receiveBufferMutex);
|
||||
std::unique_lock<std::mutex> unlock(_receiveBufferMutex);
|
||||
while (!_receiveBuffer.empty()) {
|
||||
ParallelConnection::Message& message = _receiveBuffer.front();
|
||||
handleMessage(message);
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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/network/parallelserver.h>
|
||||
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/io/socket/tcpsocket.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <functional>
|
||||
|
||||
// @TODO(abock): In the entire class remove std::shared_ptr<Peer> by const Peer& where
|
||||
// possible to simplify the interface
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "ParallelServer";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
void ParallelServer::start(int port, const std::string& password,
|
||||
const std::string& changeHostPassword)
|
||||
{
|
||||
_socketServer.listen(port);
|
||||
_passwordHash = std::hash<std::string>{}(password);
|
||||
_changeHostPasswordHash = std::hash<std::string>{}(changeHostPassword);
|
||||
|
||||
_serverThread = std::thread([this](){ handleNewPeers(); });
|
||||
_eventLoopThread = std::thread([this]() { eventLoop(); });
|
||||
}
|
||||
|
||||
void ParallelServer::setDefaultHostAddress(std::string defaultHostAddress) {
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
_defaultHostAddress = std::move(defaultHostAddress);
|
||||
}
|
||||
|
||||
std::string ParallelServer::defaultHostAddress() const {
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
return _defaultHostAddress;
|
||||
}
|
||||
|
||||
void ParallelServer::stop() {
|
||||
_shouldStop = true;
|
||||
_socketServer.close();
|
||||
}
|
||||
|
||||
void ParallelServer::handleNewPeers() {
|
||||
while (!_shouldStop) {
|
||||
std::unique_ptr<ghoul::io::TcpSocket> s = _socketServer.awaitPendingTcpSocket();
|
||||
|
||||
s->startStreams();
|
||||
|
||||
const size_t id = _nextConnectionId++;
|
||||
auto p = std::make_shared<Peer>(Peer{
|
||||
id,
|
||||
"",
|
||||
ParallelConnection(std::move(s)),
|
||||
ParallelConnection::Status::Connecting,
|
||||
std::thread()
|
||||
});
|
||||
auto it = _peers.emplace(p->id, p);
|
||||
it.first->second->thread = std::thread([this, id]() { handlePeer(id); });
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ParallelServer::Peer> ParallelServer::peer(size_t id) {
|
||||
std::lock_guard lock(_peerListMutex);
|
||||
const auto it = _peers.find(id);
|
||||
if (it == _peers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ParallelServer::handlePeer(size_t id) {
|
||||
while (!_shouldStop) {
|
||||
std::shared_ptr<Peer> p = peer(id);
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!p->parallelConnection.isConnectedOrConnecting()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ParallelConnection::Message m = p->parallelConnection.receiveMessage();
|
||||
PeerMessage msg;
|
||||
msg.peerId = id;
|
||||
msg.message = m;
|
||||
_incomingMessages.push(msg);
|
||||
}
|
||||
catch (const ParallelConnection::ConnectionLostError&) {
|
||||
LERROR(fmt::format("Connection lost to {}", p->id));
|
||||
PeerMessage msg;
|
||||
msg.peerId = id;
|
||||
msg.message = ParallelConnection::Message(
|
||||
ParallelConnection::MessageType::Disconnection, std::vector<char>()
|
||||
);
|
||||
_incomingMessages.push(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::eventLoop() {
|
||||
while (!_shouldStop) {
|
||||
PeerMessage pm = _incomingMessages.pop();
|
||||
handlePeerMessage(std::move(pm));
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::handlePeerMessage(PeerMessage peerMessage) {
|
||||
const size_t peerId = peerMessage.peerId;
|
||||
auto it = _peers.find(peerId);
|
||||
if (it == _peers.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Peer>& peer = it->second;
|
||||
|
||||
const ParallelConnection::MessageType type = peerMessage.message.type;
|
||||
std::vector<char>& data = peerMessage.message.content;
|
||||
switch (type) {
|
||||
case ParallelConnection::MessageType::Authentication:
|
||||
handleAuthentication(peer, std::move(data));
|
||||
break;
|
||||
case ParallelConnection::MessageType::Data:
|
||||
handleData(*peer, std::move(data));
|
||||
break;
|
||||
case ParallelConnection::MessageType::HostshipRequest:
|
||||
handleHostshipRequest(peer, std::move(data));
|
||||
break;
|
||||
case ParallelConnection::MessageType::HostshipResignation:
|
||||
handleHostshipResignation(*peer);
|
||||
break;
|
||||
case ParallelConnection::MessageType::Disconnection:
|
||||
disconnect(*peer);
|
||||
break;
|
||||
default:
|
||||
LERROR(fmt::format("Unsupported message type: {}", static_cast<int>(type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::handleAuthentication(std::shared_ptr<Peer> peer,
|
||||
std::vector<char> message)
|
||||
{
|
||||
std::stringstream input(std::string(message.begin(), message.end()));
|
||||
|
||||
// 8 bytes passcode
|
||||
uint64_t passwordHash = 0;
|
||||
input.read(reinterpret_cast<char*>(&passwordHash), sizeof(uint64_t));
|
||||
|
||||
if (passwordHash != _passwordHash) {
|
||||
LERROR(fmt::format("Connection {} provided incorrect passcode.", peer->id));
|
||||
disconnect(*peer);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4 bytes name size
|
||||
uint32_t nameSize = 0;
|
||||
input.read(reinterpret_cast<char*>(&nameSize), sizeof(uint32_t));
|
||||
|
||||
// <nameSize> bytes name
|
||||
std::string name(nameSize, static_cast<char>(0));
|
||||
input.read(&name[0], nameSize);
|
||||
|
||||
if (nameSize == 0) {
|
||||
name = "Anonymous";
|
||||
}
|
||||
|
||||
setName(*peer, name);
|
||||
|
||||
LINFO(fmt::format("Connection established with {} ('{}')", peer->id, name));
|
||||
|
||||
std::string defaultHostAddress;
|
||||
{
|
||||
std::lock_guard _hostMutex(_hostInfoMutex);
|
||||
defaultHostAddress = _defaultHostAddress;
|
||||
}
|
||||
if (_hostPeerId == 0 &&
|
||||
peer->parallelConnection.socket()->address() == defaultHostAddress)
|
||||
{
|
||||
// Directly promote the conenction to host (initialize) if there is no host, and
|
||||
// ip matches default host ip.
|
||||
LINFO(fmt::format("Connection {} directly promoted to host", peer->id));
|
||||
assignHost(peer);
|
||||
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
|
||||
// sendConnectionStatus(it->second) ?
|
||||
sendConnectionStatus(*peer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setToClient(*peer);
|
||||
}
|
||||
|
||||
setNConnections(nConnections() + 1);
|
||||
}
|
||||
|
||||
void ParallelServer::handleData(const Peer& peer, std::vector<char> data) {
|
||||
if (peer.id != _hostPeerId) {
|
||||
LINFO(fmt::format(
|
||||
"Ignoring connection {} trying to send data without being host", peer.id
|
||||
));
|
||||
}
|
||||
sendMessageToClients(ParallelConnection::MessageType::Data, data);
|
||||
}
|
||||
|
||||
void ParallelServer::handleHostshipRequest(std::shared_ptr<Peer> peer,
|
||||
std::vector<char> message)
|
||||
{
|
||||
std::stringstream input(std::string(message.begin(), message.end()));
|
||||
|
||||
LINFO(fmt::format("Connection {} requested hostship", peer->id));
|
||||
|
||||
uint64_t passwordHash = 0;
|
||||
input.read(reinterpret_cast<char*>(&passwordHash), sizeof(uint64_t));
|
||||
|
||||
if (passwordHash != _changeHostPasswordHash) {
|
||||
LERROR(fmt::format("Connection {} provided incorrect host password", peer->id));
|
||||
return;
|
||||
}
|
||||
|
||||
size_t oldHostPeerId = 0;
|
||||
{
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
oldHostPeerId = _hostPeerId;
|
||||
}
|
||||
|
||||
if (oldHostPeerId == peer->id) {
|
||||
LINFO(fmt::format("Connection {} is already the host", peer->id));
|
||||
return;
|
||||
}
|
||||
|
||||
assignHost(peer);
|
||||
LINFO(fmt::format("Switched host from {} to {}", oldHostPeerId, peer->id));
|
||||
}
|
||||
|
||||
void ParallelServer::handleHostshipResignation(Peer& peer) {
|
||||
LINFO(fmt::format("Connection {} wants to resign its hostship", peer.id));
|
||||
|
||||
setToClient(peer);
|
||||
|
||||
LINFO(fmt::format("Connection {} resigned as host", peer.id));
|
||||
}
|
||||
|
||||
bool ParallelServer::isConnected(const Peer& peer) const {
|
||||
return peer.status != ParallelConnection::Status::Connecting &&
|
||||
peer.status != ParallelConnection::Status::Disconnected;
|
||||
}
|
||||
|
||||
void ParallelServer::sendMessage(Peer& peer, ParallelConnection::MessageType messageType,
|
||||
const std::vector<char>& message)
|
||||
{
|
||||
peer.parallelConnection.sendMessage({ messageType, message });
|
||||
}
|
||||
|
||||
void ParallelServer::sendMessageToAll(ParallelConnection::MessageType messageType,
|
||||
const std::vector<char>& message)
|
||||
{
|
||||
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
|
||||
if (isConnected(*it.second)) {
|
||||
it.second->parallelConnection.sendMessage({ messageType, message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::sendMessageToClients(ParallelConnection::MessageType messageType,
|
||||
const std::vector<char>& message)
|
||||
{
|
||||
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
|
||||
if (it.second->status == ParallelConnection::Status::ClientWithHost) {
|
||||
it.second->parallelConnection.sendMessage({ messageType, message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::disconnect(Peer& peer) {
|
||||
if (isConnected(peer)) {
|
||||
setNConnections(nConnections() - 1);
|
||||
}
|
||||
|
||||
size_t hostPeerId = 0;
|
||||
{
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
hostPeerId = _hostPeerId;
|
||||
}
|
||||
|
||||
// Make sure any disconnecting host is first degraded to client, in order to notify
|
||||
// other clients about host disconnection.
|
||||
if (peer.id == hostPeerId) {
|
||||
setToClient(peer);
|
||||
}
|
||||
|
||||
peer.parallelConnection.disconnect();
|
||||
peer.thread.join();
|
||||
_peers.erase(peer.id);
|
||||
}
|
||||
|
||||
void ParallelServer::setName(Peer& peer, std::string name) {
|
||||
peer.name = std::move(name);
|
||||
size_t hostPeerId = 0;
|
||||
{
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
hostPeerId = _hostPeerId;
|
||||
}
|
||||
|
||||
// Make sure everyone gets the new host name.
|
||||
if (peer.id == hostPeerId) {
|
||||
{
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
_hostName = peer.name;
|
||||
}
|
||||
|
||||
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
|
||||
// sendConnectionStatus(it->second) ?
|
||||
sendConnectionStatus(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::assignHost(std::shared_ptr<Peer> newHost) {
|
||||
{
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
std::shared_ptr<ParallelServer::Peer> oldHost = peer(_hostPeerId);
|
||||
|
||||
if (oldHost) {
|
||||
oldHost->status = ParallelConnection::Status::ClientWithHost;
|
||||
}
|
||||
_hostPeerId = newHost->id;
|
||||
_hostName = newHost->name;
|
||||
}
|
||||
newHost->status = ParallelConnection::Status::Host;
|
||||
|
||||
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
|
||||
if (it.second != newHost) {
|
||||
it.second->status = ParallelConnection::Status::ClientWithHost;
|
||||
}
|
||||
sendConnectionStatus(*it.second);
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::setToClient(Peer& peer) {
|
||||
if (peer.status == ParallelConnection::Status::Host) {
|
||||
{
|
||||
std::lock_guard lock(_hostInfoMutex);
|
||||
_hostPeerId = 0;
|
||||
_hostName.clear();
|
||||
}
|
||||
|
||||
// If host becomes client, make all clients hostless.
|
||||
for (std::pair<const size_t, std::shared_ptr<Peer>>& it : _peers) {
|
||||
it.second->status = ParallelConnection::Status::ClientWithoutHost;
|
||||
sendConnectionStatus(*it.second);
|
||||
}
|
||||
}
|
||||
else {
|
||||
peer.status = (_hostPeerId > 0) ?
|
||||
ParallelConnection::Status::ClientWithHost :
|
||||
ParallelConnection::Status::ClientWithoutHost;
|
||||
sendConnectionStatus(peer);
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelServer::setNConnections(size_t nConnections) {
|
||||
_nConnections = nConnections;
|
||||
std::vector<char> data;
|
||||
const uint32_t n = static_cast<uint32_t>(_nConnections);
|
||||
data.insert(
|
||||
data.end(),
|
||||
reinterpret_cast<const char*>(&n),
|
||||
reinterpret_cast<const char*>(&n) + sizeof(uint32_t)
|
||||
);
|
||||
sendMessageToAll(ParallelConnection::MessageType::NConnections, data);
|
||||
}
|
||||
|
||||
void ParallelServer::sendConnectionStatus(Peer& peer) {
|
||||
std::vector<char> data;
|
||||
const uint32_t outStatus = static_cast<uint32_t>(peer.status);
|
||||
data.insert(
|
||||
data.end(),
|
||||
reinterpret_cast<const char*>(&outStatus),
|
||||
reinterpret_cast<const char*>(&outStatus) + sizeof(uint32_t)
|
||||
);
|
||||
|
||||
const uint32_t outHostNameSize = static_cast<uint32_t>(_hostName.size());
|
||||
data.insert(
|
||||
data.end(),
|
||||
reinterpret_cast<const char*>(&outHostNameSize),
|
||||
reinterpret_cast<const char*>(&outHostNameSize) + sizeof(uint32_t)
|
||||
);
|
||||
|
||||
data.insert(
|
||||
data.end(),
|
||||
_hostName.data(),
|
||||
_hostName.data() + outHostNameSize
|
||||
);
|
||||
|
||||
sendMessage(peer, ParallelConnection::MessageType::ConnectionStatus, data);
|
||||
}
|
||||
|
||||
size_t ParallelServer::nConnections() const {
|
||||
return _nConnections;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -51,6 +51,18 @@ namespace {
|
||||
"completely transparent."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FadeInfo = {
|
||||
"Fade",
|
||||
"Fade",
|
||||
"This value is used by the system to be able to fade out renderables "
|
||||
"independently from the Opacity value selected by the user. This value should "
|
||||
"not be directly manipulated through a user interface, but instead used by other "
|
||||
"components of the system programmatically",
|
||||
// The Developer mode should be used once the properties in the UI listen to this
|
||||
// openspace::properties::Property::Visibility::Developer
|
||||
openspace::properties::Property::Visibility::Hidden
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RenderableTypeInfo = {
|
||||
"Type",
|
||||
"Renderable Type",
|
||||
@@ -58,7 +70,8 @@ namespace {
|
||||
openspace::properties::Property::Visibility::Hidden
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RenderableRenderBinModeInfo = {
|
||||
constexpr openspace::properties::Property::PropertyInfo RenderableRenderBinModeInfo =
|
||||
{
|
||||
"RenderBinMode",
|
||||
"Render Bin Mode",
|
||||
"This value specifies if the renderable should be rendered in the Background,"
|
||||
@@ -128,6 +141,7 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
|
||||
: properties::PropertyOwner({ "Renderable" })
|
||||
, _enabled(EnabledInfo, true)
|
||||
, _opacity(OpacityInfo, 1.f, 0.f, 1.f)
|
||||
, _fade(FadeInfo, 1.f, 0.f, 1.f)
|
||||
, _renderableType(RenderableTypeInfo, "Renderable")
|
||||
{
|
||||
ZoneScoped
|
||||
@@ -158,6 +172,8 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
|
||||
// We don't add the property here as subclasses should decide on their own whether
|
||||
// they to expose the opacity or not
|
||||
|
||||
addProperty(_fade);
|
||||
|
||||
// set type for UI
|
||||
_renderableType = p.type.value_or(_renderableType);
|
||||
addProperty(_renderableType);
|
||||
@@ -224,7 +240,7 @@ bool Renderable::matchesRenderBinMask(int binMask) {
|
||||
}
|
||||
|
||||
bool Renderable::isVisible() const {
|
||||
return _enabled;
|
||||
return _enabled && _opacity > 0.f && _fade > 0.f;
|
||||
}
|
||||
|
||||
bool Renderable::isReady() const {
|
||||
@@ -249,7 +265,8 @@ void Renderable::setRenderBinFromOpacity() {
|
||||
if ((_renderBin != Renderable::RenderBin::PostDeferredTransparent) &&
|
||||
(_renderBin != Renderable::RenderBin::Overlay))
|
||||
{
|
||||
if (_opacity >= 0.f && _opacity < 1.f) {
|
||||
const float v = opacity();
|
||||
if (v >= 0.f && v < 1.f) {
|
||||
setRenderBin(Renderable::RenderBin::PreDeferredTransparent);
|
||||
}
|
||||
else {
|
||||
@@ -262,4 +279,8 @@ void Renderable::registerUpdateRenderBinFromOpacity() {
|
||||
_opacity.onChange([this]() { setRenderBinFromOpacity(); });
|
||||
}
|
||||
|
||||
float Renderable::opacity() const {
|
||||
return _opacity * _fade;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -94,11 +94,11 @@ namespace {
|
||||
constexpr const char* KeyFontMono = "Mono";
|
||||
constexpr const char* KeyFontLight = "Light";
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowOverlaySlavesInfo = {
|
||||
"ShowOverlayOnSlaves",
|
||||
"Show Overlay Information on Slaves",
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowOverlayClientsInfo = {
|
||||
"ShowOverlayOnClients",
|
||||
"Show Overlay Information on Clients",
|
||||
"If this value is enabled, the overlay information text is also automatically "
|
||||
"rendered on the slave nodes. This values is disabled by default."
|
||||
"rendered on client nodes. This values is disabled by default."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo ShowLogInfo = {
|
||||
@@ -280,7 +280,7 @@ namespace openspace {
|
||||
|
||||
RenderEngine::RenderEngine()
|
||||
: properties::PropertyOwner({ "RenderEngine" })
|
||||
, _showOverlayOnSlaves(ShowOverlaySlavesInfo, false)
|
||||
, _showOverlayOnClients(ShowOverlayClientsInfo, false)
|
||||
, _showLog(ShowLogInfo, true)
|
||||
, _verticalLogOffset(VerticalLogOffsetInfo, 0.f, 0.f, 1.f)
|
||||
, _showVersionInfo(ShowVersionInfo, true)
|
||||
@@ -321,7 +321,7 @@ RenderEngine::RenderEngine()
|
||||
, _enabledFontColor(EnabledFontColorInfo, glm::vec4(0.2f, 0.75f, 0.2f, 1.f))
|
||||
, _disabledFontColor(DisabledFontColorInfo, glm::vec4(0.55f, 0.2f, 0.2f, 1.f))
|
||||
{
|
||||
addProperty(_showOverlayOnSlaves);
|
||||
addProperty(_showOverlayOnClients);
|
||||
addProperty(_showLog);
|
||||
addProperty(_verticalLogOffset);
|
||||
addProperty(_showVersionInfo);
|
||||
@@ -804,7 +804,7 @@ void RenderEngine::renderOverlays(const ShutdownInformation& shutdownInfo) {
|
||||
ZoneScoped
|
||||
|
||||
const bool isMaster = global::windowDelegate->isMaster();
|
||||
if (isMaster || _showOverlayOnSlaves) {
|
||||
if (isMaster || _showOverlayOnClients) {
|
||||
renderScreenLog();
|
||||
renderVersionInformation();
|
||||
renderDashboard();
|
||||
@@ -1072,7 +1072,8 @@ scripting::LuaLibrary RenderEngine::luaLibrary() {
|
||||
{
|
||||
codegen::lua::AddScreenSpaceRenderable,
|
||||
codegen::lua::RemoveScreenSpaceRenderable,
|
||||
codegen::lua::TakeScreenshot
|
||||
codegen::lua::TakeScreenshot,
|
||||
codegen::lua::DpiScaling
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,6 +67,12 @@ namespace {
|
||||
return static_cast<int>(screenshotNumber);
|
||||
}
|
||||
|
||||
// Extracts the DPI scaling for either the GUI window or if there is no dedicated GUI
|
||||
// window, the first window.
|
||||
[[codegen::luawrap]] float dpiScaling() {
|
||||
return openspace::global::windowDelegate->osDpiScaling();
|
||||
}
|
||||
|
||||
#include "renderengine_lua_codegen.cpp"
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -124,6 +124,16 @@ namespace {
|
||||
"completely transparent."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo FadeInfo = {
|
||||
"Fade",
|
||||
"Fade",
|
||||
"This value is used by the system to be able to fade out renderables "
|
||||
"independently from the Opacity value selected by the user. This value should "
|
||||
"not be directly manipulated through a user interface, but instead used by other "
|
||||
"components of the system programmatically",
|
||||
openspace::properties::Property::Visibility::Developer
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DeleteInfo = {
|
||||
"Delete",
|
||||
"Delete",
|
||||
@@ -350,6 +360,7 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
|
||||
glm::vec4(1.f)
|
||||
)
|
||||
, _opacity(OpacityInfo, 1.f, 0.f, 1.f)
|
||||
, _fade(FadeInfo, 1.f, 0.f, 1.f)
|
||||
, _delete(DeleteInfo)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
@@ -383,6 +394,7 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
|
||||
addProperty(_multiplyColor);
|
||||
addProperty(_backgroundColor);
|
||||
addProperty(_opacity);
|
||||
addProperty(_fade);
|
||||
addProperty(_localRotation);
|
||||
|
||||
|
||||
@@ -589,7 +601,7 @@ void ScreenSpaceRenderable::draw(glm::mat4 modelTransform) {
|
||||
_shader->activate();
|
||||
|
||||
_shader->setUniform(_uniformCache.color, _multiplyColor);
|
||||
_shader->setUniform(_uniformCache.opacity, _opacity);
|
||||
_shader->setUniform(_uniformCache.opacity, opacity());
|
||||
_shader->setUniform(_uniformCache.backgroundColor, _backgroundColor);
|
||||
|
||||
_shader->setUniform(
|
||||
@@ -613,4 +625,8 @@ void ScreenSpaceRenderable::draw(glm::mat4 modelTransform) {
|
||||
|
||||
void ScreenSpaceRenderable::unbindTexture() {}
|
||||
|
||||
float ScreenSpaceRenderable::opacity() const {
|
||||
return _opacity * _fade;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -343,6 +343,10 @@ bool AssetManager::loadAsset(Asset* asset, Asset* parent) {
|
||||
LERROR(fmt::format("Could not load asset {}: {}", asset->path(), e.message));
|
||||
return false;
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract meta information from the asset file if it was provided
|
||||
lua_getglobal(*_luaState, AssetGlobalVariableName);
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace {
|
||||
* Returns the paths to all loaded assets, loaded directly or indirectly, as a table
|
||||
* containing the paths to all loaded assets.
|
||||
*/
|
||||
[[codegen::luawrap]] std::vector<std::string> allAssets(std::string assetName) {
|
||||
[[codegen::luawrap]] std::vector<std::string> allAssets() {
|
||||
using namespace openspace;
|
||||
std::vector<const Asset*> as = global::openSpaceEngine->assetManager().allAssets();
|
||||
std::vector<std::string> res;
|
||||
|
||||
@@ -66,12 +66,6 @@ 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" })
|
||||
{}
|
||||
|
||||
void Rotation::requireUpdate() {
|
||||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
@@ -715,8 +715,7 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) {
|
||||
}
|
||||
|
||||
const bool visible = _renderable && _renderable->isVisible() &&
|
||||
_renderable->isReady() && _renderable->isEnabled() &&
|
||||
_renderable->matchesRenderBinMask(data.renderBinMask);
|
||||
_renderable->isReady() && _renderable->matchesRenderBinMask(data.renderBinMask);
|
||||
|
||||
if (!visible) {
|
||||
return;
|
||||
|
||||
@@ -600,7 +600,7 @@ void ScriptEngine::preSync(bool isMaster) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard guard(_slaveScriptsMutex);
|
||||
std::lock_guard guard(_clientScriptsMutex);
|
||||
while (!_incomingScripts.empty()) {
|
||||
QueueItem item = std::move(_incomingScripts.front());
|
||||
_incomingScripts.pop();
|
||||
@@ -634,14 +634,14 @@ void ScriptEngine::encode(SyncBuffer* syncBuffer) {
|
||||
void ScriptEngine::decode(SyncBuffer* syncBuffer) {
|
||||
ZoneScoped
|
||||
|
||||
std::lock_guard guard(_slaveScriptsMutex);
|
||||
std::lock_guard guard(_clientScriptsMutex);
|
||||
size_t nScripts;
|
||||
syncBuffer->decode(nScripts);
|
||||
|
||||
for (size_t i = 0; i < nScripts; ++i) {
|
||||
std::string script;
|
||||
syncBuffer->decode(script);
|
||||
_slaveScriptQueue.push(std::move(script));
|
||||
_clientScriptQueue.push(std::move(script));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,11 +663,11 @@ void ScriptEngine::postSync(bool isMaster) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::lock_guard guard(_slaveScriptsMutex);
|
||||
while (!_slaveScriptQueue.empty()) {
|
||||
std::lock_guard guard(_clientScriptsMutex);
|
||||
while (!_clientScriptQueue.empty()) {
|
||||
try {
|
||||
runScript(_slaveScriptQueue.front());
|
||||
_slaveScriptQueue.pop();
|
||||
runScript(_clientScriptQueue.front());
|
||||
_clientScriptQueue.pop();
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERRORC(e.component, e.message);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace {
|
||||
OS os = CpuCap.operatingSystem();
|
||||
|
||||
switch (os) {
|
||||
case OS::Windows10:
|
||||
case OS::Windows10or11:
|
||||
case OS::WindowsServer2016:
|
||||
case OS::WindowsVista:
|
||||
case OS::WindowsServer2008:
|
||||
|
||||
+11
-11
@@ -896,24 +896,24 @@ bool TimeManager::isPlayingBackSessionRecording() const {
|
||||
}
|
||||
|
||||
void TimeManager::setTimeFromProfile(const Profile& p) {
|
||||
Time t;
|
||||
|
||||
if (p.time.has_value()) {
|
||||
switch (p.time.value().type) {
|
||||
case Profile::Time::Type::Relative:
|
||||
t.setTimeRelativeFromProfile(p.time.value().value);
|
||||
break;
|
||||
case Profile::Time::Type::Relative:
|
||||
Time::setTimeRelativeFromProfile(p.time.value().value);
|
||||
break;
|
||||
|
||||
case Profile::Time::Type::Absolute:
|
||||
t.setTimeAbsoluteFromProfile(p.time.value().value);
|
||||
break;
|
||||
case Profile::Time::Type::Absolute:
|
||||
Time::setTimeAbsoluteFromProfile(p.time.value().value);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw ghoul::RuntimeError("No 'time' entry exists in the startup profile");
|
||||
// No value was specified so we are using 'now' instead
|
||||
std::string now = std::string(Time::now().UTC());
|
||||
Time::setTimeAbsoluteFromProfile(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user