Merge branch 'master' into project/infravis-2025-visA

This commit is contained in:
Andreas Engberg
2025-04-09 09:51:06 +02:00
95 changed files with 2398 additions and 414 deletions

View File

@@ -27,6 +27,12 @@ cmake_policy(VERSION 3.25)
project(OpenSpace)
# CMake 4.0 will no longer allow minimum required version below 3.5 and some of our
# dependencies have not been updated to reflect this. To make things work, we blanked
# require to 3.10 here to remove those issues. Once CMake configures and generates
# successfully without this next line, it can be removed
set(CMAKE_POLICY_VERSION_MINIMUM 3.10)
set(OPENSPACE_RELEASE_BUILD OFF)
set(OPENSPACE_VERSION_MAJOR)
set(OPENSPACE_VERSION_MINOR)

View File

@@ -48,6 +48,7 @@ set(HEADER_FILES
include/profile/timedialog.h
include/profile/profileedit.h
include/profile/propertiesdialog.h
include/profile/uipanelsdialog.h
include/sgctedit/displaywindowunion.h
include/sgctedit/monitorbox.h
include/sgctedit/sgctedit.h
@@ -78,6 +79,7 @@ set(SOURCE_FILES
src/profile/timedialog.cpp
src/profile/profileedit.cpp
src/profile/propertiesdialog.cpp
src/profile/uipanelsdialog.cpp
src/sgctedit/sgctedit.cpp
src/sgctedit/displaywindowunion.cpp
src/sgctedit/monitorbox.cpp

View File

@@ -62,18 +62,16 @@ public:
bool wasLaunchSelected() const;
/**
* Returns the selected profile name when launcher window closed.
* Returns the selected profile name when the launcher window closed.
*
* \return The name of selected profile (this is only the name without file extension
* and without path)
* \return The path to the selected profile
*/
std::string selectedProfile() const;
/**
* Returns the selected sgct window configuration when launcher window closed.
* Returns the selected SGCT window configuration when the launcher window closed.
*
* \return The name of selected profile (this is only the name without file extension
* and without path)
* \return The path to the selected profile
*/
std::string selectedWindowConfig() const;

View File

@@ -86,16 +86,17 @@ signals:
void raiseExitWindow();
private slots:
void openMeta();
void openProperties();
void openModules();
void openKeybindings();
void openAssets();
void openTime();
void openAddedScripts();
void openKeybindings();
void openMeta();
void openMarkNodes();
void openDeltaTimes();
void openCamera();
void openMarkNodes();
void openTime();
void openModules();
void openUiPanels();
void openAddedScripts();
void approved();
private:
@@ -112,18 +113,19 @@ private:
std::string _profileFilename;
QLineEdit* _profileEdit = nullptr;
QLabel* _modulesLabel = nullptr;
QLabel* _assetsLabel = nullptr;
QTextEdit* _assetsEdit = nullptr;
QLabel* _propertiesLabel = nullptr;
QTextEdit* _propertiesEdit = nullptr;
QLabel* _assetsLabel = nullptr;
QTextEdit* _assetsEdit = nullptr;
QLabel* _keybindingsLabel = nullptr;
QTextEdit* _keybindingsEdit = nullptr;
QLabel* _deltaTimesLabel = nullptr;
QLabel* _metaLabel = nullptr;
QLabel* _interestingNodesLabel = nullptr;
QLabel* _deltaTimesLabel = nullptr;
QLabel* _cameraLabel = nullptr;
QLabel* _timeLabel = nullptr;
QLabel* _metaLabel = nullptr;
QLabel* _modulesLabel = nullptr;
QLabel* _uiPanelVisibilityLabel = nullptr;
QLabel* _additionalScriptsLabel = nullptr;
};

View File

@@ -0,0 +1,51 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_UI_LAUNCHER___UIPANELSDIALOG___H__
#define __OPENSPACE_UI_LAUNCHER___UIPANELSDIALOG___H__
#include <QDialog>
class QCheckBox;
class UiPanelsDialog final : public QDialog {
Q_OBJECT
public:
/**
* Constructor for UiPanelsDialog class.
*
* \param parent Pointer to parent Qt widget
* \param uiPanels The list of ui panels and their visibility
*/
UiPanelsDialog(QWidget* parent, std::map<std::string, bool>* uiPanels);
private slots:
void parseSelections();
private:
std::map<std::string, bool>* _uiPanels;
std::map<QCheckBox*, std::string> _checkboxToId;
};
#endif // __OPENSPACE_UI_LAUNCHER___UIPANELSDIALOG___H__

View File

@@ -127,9 +127,10 @@ public:
*
* \param quality The value for number of vertical lines of resolution. This will be
* compared against the QualityValues array in order to set the correct
* combobox index
* combobox index
* \tilt The tilt of the fisheye in degrees
*/
void setProjectionFisheye(int quality);
void setProjectionFisheye(int quality, float tilt);
/**
* Sets the window's projection type to spherical mirror, with the accompanying
@@ -162,6 +163,21 @@ public:
*/
void setProjectionEquirectangular(int quality);
/**
* Sets the window's projection type to blitting the contents of another window.
*
* \param windowBlitId The id of the window from which to blit
*/
void setProjectionBlit(int windowBlitId);
/**
* This function must be called by users of this class whenever the total number of
* windows has changed.
*
* \param newWindowCount the number of windows after the change
*/
void updateWindowCount(int newWindowCount);
signals:
void windowChanged(int monitorIndex, int windowIndex, const QRectF& newDimensions);
@@ -171,6 +187,7 @@ private:
QWidget* createSphericalMirrorWidget();
QWidget* createCylindricalWidget();
QWidget* createEquirectangularWidget();
QWidget* createBlitWidget();
void onSizeXChanged(int newValue);
void onSizeYChanged(int newValue);
@@ -209,44 +226,39 @@ private:
struct {
QWidget* widget = nullptr;
QLabel* labelInfo = nullptr;
QDoubleSpinBox* fovH = nullptr;
QDoubleSpinBox* fovV = nullptr;
QLabel* labelFovH = nullptr;
QLabel* labelFovV = nullptr;
QPushButton* buttonLockFov = nullptr;
} _planar;
struct {
QWidget* widget = nullptr;
QLabel* labelInfo = nullptr;
QComboBox* quality = nullptr;
QLabel* labelQuality = nullptr;
QDoubleSpinBox* tilt = nullptr;
} _fisheye;
struct {
QWidget* widget = nullptr;
QLabel* labelInfo = nullptr;
QComboBox* quality = nullptr;
QLabel* labelQuality = nullptr;
} _sphericalMirror;
struct {
QWidget* widget = nullptr;
QLabel* labelInfo = nullptr;
QComboBox* quality = nullptr;
QLabel* labelQuality = nullptr;
QDoubleSpinBox* heightOffset = nullptr;
QLabel* labelHeightOffset = nullptr;
} _cylindrical;
struct {
QWidget* widget = nullptr;
QLabel* labelInfo = nullptr;
QComboBox* quality = nullptr;
QLabel* labelQuality = nullptr;
} _equirectangular;
struct {
QWidget* widget = nullptr;
QComboBox* windowId = nullptr;
QLabel* unavailable = nullptr;
} _blit;
const QIcon _lockIcon;
const QIcon _unlockIcon;
};

View File

@@ -35,6 +35,7 @@
#include "profile/modulesdialog.h"
#include "profile/propertiesdialog.h"
#include "profile/timedialog.h"
#include "profile/uipanelsdialog.h"
#include <openspace/scene/profile.h>
#include <ghoul/format.h>
#include <QDialogButtonBox>
@@ -305,6 +306,21 @@ void ProfileEdit::createWidgets() {
rightLayout->addLayout(container);
}
rightLayout->addWidget(new Line);
{
QBoxLayout* container = new QVBoxLayout;
_uiPanelVisibilityLabel = new QLabel("User Interface Panels");
_uiPanelVisibilityLabel->setObjectName("heading");
_uiPanelVisibilityLabel->setWordWrap(true);
container->addWidget(_uiPanelVisibilityLabel);
QPushButton* uiPanelEdit = new QPushButton("Edit");
connect(uiPanelEdit, &QPushButton::clicked, this, &ProfileEdit::openUiPanels);
uiPanelEdit->setLayoutDirection(Qt::RightToLeft);
uiPanelEdit->setAccessibleName("Edit user interface panels");
container->addWidget(uiPanelEdit);
rightLayout->addLayout(container);
}
rightLayout->addWidget(new Line);
{
QBoxLayout* container = new QVBoxLayout;
_additionalScriptsLabel = new QLabel("Additional Scripts");
@@ -375,6 +391,10 @@ void ProfileEdit::openModules() {
_modulesLabel->setText(labelText(_profile.modules.size(), "Modules"));
}
void ProfileEdit::openUiPanels() {
UiPanelsDialog(this, &_profile.uiPanelVisibility).exec();
}
void ProfileEdit::openProperties() {
PropertiesDialog(this, &_profile.properties).exec();
_propertiesLabel->setText(labelText(_profile.properties.size(), "Properties"));

View File

@@ -0,0 +1,130 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include "profile/uipanelsdialog.h"
#include "profile/line.h"
#include <openspace/json.h>
#include <ghoul/filesystem/filesystem.h>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QVBoxLayout>
#include <fstream>
#include <string_view>
namespace {
constexpr std::string_view DefaultPanelPath = "${DATA}/web/default_ui_panels.json";
struct Panel {
std::string id;
std::string name;
bool isVisible;
};
void from_json(const nlohmann::json& j, Panel& layout) {
j["id"].get_to(layout.id);
j["name"].get_to(layout.name);
j["visible"].get_to(layout.isVisible);
}
std::vector<Panel> loadPanels() {
std::ifstream panelFile = std::ifstream(absPath(DefaultPanelPath));
const std::string panelContent = std::string(
std::istreambuf_iterator<char>(panelFile),
std::istreambuf_iterator<char>()
);
const nlohmann::json panel = nlohmann::json::parse(panelContent);
std::map<std::string, Panel> panels = panel.get<std::map<std::string, Panel>>();
std::vector<Panel> result;
for (const auto& [key, value] : panels) {
result.push_back(value);
}
std::sort(
result.begin(),
result.end(),
[](const Panel& lhs, const Panel& rhs) { return lhs.name < rhs.name; }
);
return result;
}
} // namespace
UiPanelsDialog::UiPanelsDialog(QWidget* parent, std::map<std::string, bool>* uiPanels)
: QDialog(parent)
, _uiPanels(uiPanels)
{
setWindowTitle("User Interface Panels");
std::vector<Panel> panels = loadPanels();
QBoxLayout* layout = new QVBoxLayout(this);
QLabel* info = new QLabel(
"Select the user interface panels that should be visible by default in the "
"current profile."
);
info->setWordWrap(true);
layout->addWidget(info);
for (const Panel& panel : panels) {
QCheckBox* box = new QCheckBox(QString::fromStdString(panel.name));
// If the profile already has a desired value for the checkbox, use it. Otherwise
// use the default values
auto it = _uiPanels->find(panel.id);
if (it != _uiPanels->end()) {
box->setChecked(it->second);
}
else {
box->setChecked(panel.isVisible);
}
layout->addWidget(box);
_checkboxToId[box] = panel.id;
}
layout->addWidget(new Line);
{
QDialogButtonBox* buttons = new QDialogButtonBox;
buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
connect(
buttons, &QDialogButtonBox::accepted,
this, &UiPanelsDialog::parseSelections
);
connect(buttons, &QDialogButtonBox::rejected, this, &UiPanelsDialog::reject);
layout->addWidget(buttons);
}
}
void UiPanelsDialog::parseSelections() {
_uiPanels->clear();
for (const auto& [key, value] : _checkboxToId) {
_uiPanels->emplace(value, key->isChecked());
}
accept();
}

View File

@@ -194,7 +194,7 @@ void DisplayWindowUnion::initialize(const std::vector<QRect>& monitorSizeList,
},
[&](const sgct::config::FisheyeProjection& p) {
if (p.quality.has_value()) {
wCtrl->setProjectionFisheye(*p.quality);
wCtrl->setProjectionFisheye(*p.quality, p.tilt.value_or(0.f));
}
},
[&](const sgct::config::PlanarProjection& p) {
@@ -208,7 +208,12 @@ void DisplayWindowUnion::initialize(const std::vector<QRect>& monitorSizeList,
wCtrl->setProjectionSphericalMirror(*p.quality);
}
},
[&](const sgct::config::NoProjection&) {},
[&](const sgct::config::NoProjection&) {
// We can only generate blitting when there is no projection selected.
if (w.blitWindowId.has_value()) {
wCtrl->setProjectionBlit(*w.blitWindowId);
}
},
[&](const sgct::config::ProjectionPlane&) {},
[&](const sgct::config::CubemapProjection&) {},
},
@@ -251,6 +256,7 @@ void DisplayWindowUnion::updateWindows() {
_addWindowButton->setEnabled(_nWindowsDisplayed != _windowControls.size());
for (WindowControl* w : _windowControls) {
w->showWindowLabel(_nWindowsDisplayed > 1);
w->updateWindowCount(_nWindowsDisplayed);
}
emit nWindowsChanged(_nWindowsDisplayed);

View File

@@ -64,7 +64,8 @@ namespace {
Fisheye,
SphericalMirror,
Cylindrical,
Equirectangular
Equirectangular,
Blit
};
constexpr int LineEditWidthFixedWindowSize = 95;
@@ -378,7 +379,7 @@ WindowControl::WindowControl(int monitorIndex, int windowIndex,
_projectionType = new QComboBox;
_projectionType->addItems({
"Planar Projection", "Fisheye", "Spherical Mirror Projection",
"Cylindrical Projection", "Equirectangular Projection"
"Cylindrical Projection", "Equirectangular Projection", "Copy Window Contents"
});
_projectionType->setToolTip("Select from the supported window projection types");
_projectionType->setCurrentIndex(0);
@@ -403,6 +404,9 @@ WindowControl::WindowControl(int monitorIndex, int windowIndex,
_equirectangular.widget = createEquirectangularWidget();
projectionLayout->addWidget(_equirectangular.widget);
_blit.widget = createBlitWidget();
projectionLayout->addWidget(_blit.widget);
// We need to trigger this once to ensure that all of the defaults are correct
onProjectionChanged(0);
@@ -424,21 +428,21 @@ QWidget* WindowControl::createPlanarWidget() {
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
_planar.labelInfo = new QLabel(
QLabel* labelInfo = new QLabel(
"This projection type is the 'regular' projection with a horizontal and a "
"vertical field of view, given in degrees. The wider the field of view, the "
"more content is shown at the same time, but everything becomes smaller. Very "
"large values will introduce distortions on the corners."
);
_planar.labelInfo->setObjectName("info");
_planar.labelInfo->setWordWrap(true);
layout->addWidget(_planar.labelInfo, 0, 0, 1, 3);
labelInfo->setObjectName("info");
labelInfo->setWordWrap(true);
layout->addWidget(labelInfo, 0, 0, 1, 3);
_planar.labelFovH = new QLabel("Horizontal FOV");
QLabel* labelFovH = new QLabel("Horizontal FOV");
const QString hfovTip =
"The total horizontal field of view of the viewport (degrees)";
_planar.labelFovH->setToolTip(hfovTip);
layout->addWidget(_planar.labelFovH, 1, 0);
labelFovH->setToolTip(hfovTip);
layout->addWidget(labelFovH, 1, 0);
_planar.fovH = new QDoubleSpinBox;
_planar.fovH->setMinimum(FovEpsilon);
@@ -452,11 +456,11 @@ QWidget* WindowControl::createPlanarWidget() {
);
layout->addWidget(_planar.fovH, 1, 1);
_planar.labelFovV = new QLabel("Vertical FOV");
QLabel* labelFovV = new QLabel("Vertical FOV");
const QString vfovTip = "The total vertical field of view of the viewport (degrees). "
"Internally,\nthe values for 'up' & 'down' will each be half this value";
_planar.labelFovV->setToolTip(vfovTip);
layout->addWidget(_planar.labelFovV, 2, 0);
labelFovV->setToolTip(vfovTip);
layout->addWidget(labelFovV, 2, 0);
_planar.fovV = new QDoubleSpinBox;
_planar.fovV->setMinimum(FovEpsilon);
@@ -493,29 +497,29 @@ QWidget* WindowControl::createFisheyeWidget() {
// *------------*-----------*
// | { Informational text } | Row 0
// | Quality * [DDDDD>] | Row 1
// | [] Spout Output | Row 2
// | Tilt * [oooooo] | Row 2
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
_fisheye.labelInfo = new QLabel(
QLabel* labelInfo = new QLabel(
"This projection provides a rendering in a format that is suitable for "
"planetariums and other immersive environments. A field-of-view of 180 degrees "
"is presented as a circular image in the center of the screen. For this "
"projection a square window is suggested, but not necessary."
);
_fisheye.labelInfo->setObjectName("info");
_fisheye.labelInfo->setWordWrap(true);
layout->addWidget(_fisheye.labelInfo, 0, 0, 1, 2);
labelInfo->setObjectName("info");
labelInfo->setWordWrap(true);
layout->addWidget(labelInfo, 0, 0, 1, 2);
_fisheye.labelQuality = new QLabel("Quality");
QLabel* labelQuality = new QLabel("Quality");
const QString qualityTip = "Determines the pixel resolution of the projection "
"rendering. The higher resolution,\nthe better the rendering quality, but at the "
"expense of increased rendering times";
_fisheye.labelQuality->setToolTip(qualityTip);
layout->addWidget(_fisheye.labelQuality, 1, 0);
"expense of increased rendering times.";
labelQuality->setToolTip(qualityTip);
layout->addWidget(labelQuality, 1, 0);
_fisheye.quality = new QComboBox;
_fisheye.quality->addItems(qualityList());
@@ -523,6 +527,19 @@ QWidget* WindowControl::createFisheyeWidget() {
_fisheye.quality->setCurrentIndex(2);
layout->addWidget(_fisheye.quality, 1, 1);
QLabel* labelTilt = new QLabel("Tilt");
const QString tiltTip = "Determines the tilt (in degrees) of the fisheye rendering. "
"Changing this value will cause the entire rendering to be tilted by the set "
"number of degrees.";
labelTilt->setToolTip(tiltTip);
layout->addWidget(labelTilt, 2, 0);
_fisheye.tilt = new QDoubleSpinBox;
_fisheye.tilt->setToolTip(tiltTip);
_fisheye.tilt->setMinimum(-180.0);
_fisheye.tilt->setMaximum(180.0);
layout->addWidget(_fisheye.tilt, 2, 1);
return widget;
}
@@ -536,22 +553,22 @@ QWidget* WindowControl::createSphericalMirrorWidget() {
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
_sphericalMirror.labelInfo = new QLabel(
QLabel* labelInfo = new QLabel(
"This projection is rendering a image suite for use with a spherical mirror "
"projection as described by Paul Bourke (http://paulbourke.net/dome/mirrordome/) "
"and which is a low-cost yet effective way to provide content for a sphericalal "
"display surface using a regular projector."
);
_sphericalMirror.labelInfo->setObjectName("info");
_sphericalMirror.labelInfo->setWordWrap(true);
layout->addWidget(_sphericalMirror.labelInfo, 0, 0, 1, 2);
labelInfo->setObjectName("info");
labelInfo->setWordWrap(true);
layout->addWidget(labelInfo, 0, 0, 1, 2);
_sphericalMirror.labelQuality = new QLabel("Quality");
QLabel* labelQuality = new QLabel("Quality");
const QString qualityTip = "Determines the pixel resolution of the projection "
"rendering. The higher resolution,\nthe better the rendering quality, but at the "
"expense of increased rendering times";
_sphericalMirror.labelQuality->setToolTip(qualityTip);
layout->addWidget(_sphericalMirror.labelQuality, 1, 0);
labelQuality->setToolTip(qualityTip);
layout->addWidget(labelQuality, 1, 0);
_sphericalMirror.quality = new QComboBox;
_sphericalMirror.quality->addItems(qualityList());
@@ -573,22 +590,22 @@ QWidget* WindowControl::createCylindricalWidget() {
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
_cylindrical.labelInfo = new QLabel(
QLabel* labelInfo = new QLabel(
"This projection type provides a cylindrical rendering that covers 360 degrees "
"around the camera, which can be useful in immersive environments that are not "
"spherical, but where, for example, all walls of a room are covered with "
"projectors."
);
_cylindrical.labelInfo->setObjectName("info");
_cylindrical.labelInfo->setWordWrap(true);
layout->addWidget(_cylindrical.labelInfo, 0, 0, 1, 2);
labelInfo->setObjectName("info");
labelInfo->setWordWrap(true);
layout->addWidget(labelInfo, 0, 0, 1, 2);
_cylindrical.labelQuality = new QLabel("Quality");
QLabel* labelQuality = new QLabel("Quality");
const QString qualityTip = "Determines the pixel resolution of the projection "
"rendering. The higher resolution,\nthe better the rendering quality, but at the "
"expense of increased rendering times";
_cylindrical.labelQuality->setToolTip(qualityTip);
layout->addWidget(_cylindrical.labelQuality, 1, 0);
labelQuality->setToolTip(qualityTip);
layout->addWidget(labelQuality, 1, 0);
_cylindrical.quality = new QComboBox;
_cylindrical.quality->addItems(qualityList());
@@ -596,13 +613,13 @@ QWidget* WindowControl::createCylindricalWidget() {
_cylindrical.quality->setCurrentIndex(2);
layout->addWidget(_cylindrical.quality, 1, 1);
_cylindrical.labelHeightOffset = new QLabel("Height Offset");
QLabel* labelHeightOffset = new QLabel("Height Offset");
const QString heightTip = "Offsets the height from which the cylindrical projection "
"is generated.\nThis is, in general, only necessary if the user position is "
"offset and\ncountering that offset is desired in order to continue producing\n"
"a 'standard' cylindrical projection";
_cylindrical.labelHeightOffset->setToolTip(heightTip);
layout->addWidget(_cylindrical.labelHeightOffset, 2, 0);
labelHeightOffset->setToolTip(heightTip);
layout->addWidget(labelHeightOffset, 2, 0);
_cylindrical.heightOffset = new QDoubleSpinBox;
_cylindrical.heightOffset->setMinimum(-1000000.0);
@@ -611,7 +628,6 @@ QWidget* WindowControl::createCylindricalWidget() {
_cylindrical.heightOffset->setToolTip(heightTip);
layout->addWidget(_cylindrical.heightOffset, 2, 1);
return widget;
}
@@ -620,28 +636,27 @@ QWidget* WindowControl::createEquirectangularWidget() {
// *------------*-----------*
// | { Informational text } | Row 0
// | Quality * [DDDDD>] | Row 1
// | [] Spout Output | Row 2
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
_equirectangular.labelInfo = new QLabel(
QLabel* labelInfo = new QLabel(
"This projection provides the rendering as an image in equirectangular "
"projection, which is a common display type for 360 surround video. When "
"uploading a video in equirectangular projection to YouTube, for example, it "
"will use it as a 360 video."
);
_equirectangular.labelInfo->setObjectName("info");
_equirectangular.labelInfo->setWordWrap(true);
layout->addWidget(_equirectangular.labelInfo, 0, 0, 1, 2);
labelInfo->setObjectName("info");
labelInfo->setWordWrap(true);
layout->addWidget(labelInfo, 0, 0, 1, 2);
_equirectangular.labelQuality = new QLabel("Quality");
QLabel* labelQuality = new QLabel("Quality");
const QString qualityTip = "Determines the pixel resolution of the projection "
"rendering. The higher resolution,\nthe better the rendering quality, but at the "
"expense of increased rendering times";
_equirectangular.labelQuality->setToolTip(qualityTip);
layout->addWidget(_equirectangular.labelQuality, 1, 0);
labelQuality->setToolTip(qualityTip);
layout->addWidget(labelQuality, 1, 0);
_equirectangular.quality = new QComboBox;
_equirectangular.quality->addItems(qualityList());
@@ -652,6 +667,49 @@ QWidget* WindowControl::createEquirectangularWidget() {
return widget;
}
QWidget* WindowControl::createBlitWidget() {
// Column 0 Column 1
// *------------*-----------*
// | { Informational text } | Row 0
// | Window ID * [DDDDD>] | Row 1
// | { Unavailability } | Row 2
// *------------*-----------*
QWidget* widget = new QWidget;
QGridLayout* layout = new QGridLayout(widget);
layout->setColumnStretch(1, 1);
QLabel* labelInfo = new QLabel(
"This projection type will reuse the contents of another window. This can be "
"useful for GUI windows that should show the 3D scene, but not incur the cost of "
"rendering the scene twice. Note that the contents of the rendering will be "
"copied in their entirety, which means that if the rendering windows have "
"different aspect ratios, the image in the receiving window will be stretched."
);
labelInfo->setObjectName("info");
labelInfo->setWordWrap(true);
layout->addWidget(labelInfo, 0, 0, 1, 2);
QLabel* labelBlitId = new QLabel("Window ID");
const QString blitTip = "Determines the window from which to copy the contents.";
labelBlitId->setToolTip(blitTip);
layout->addWidget(labelBlitId, 1, 0);
_blit.windowId = new QComboBox;
_blit.windowId->setToolTip(blitTip);
layout->addWidget(_blit.windowId, 1, 1);
_blit.unavailable = new QLabel(
"It is only possible to copy the contents of another window if at least two "
"windows have been created. Add a second window before selecting this projection "
"type."
);
_blit.unavailable->setWordWrap(true);
layout->addWidget(_blit.unavailable, 2, 0, 1, 2);
return widget;
}
void WindowControl::resetToDefaults() {
//
// Determine ideal window sizes
@@ -684,9 +742,11 @@ void WindowControl::resetToDefaults() {
_planar.fovV->setValue(DefaultFovShortEdge);
_cylindrical.heightOffset->setValue(DefaultHeightOffset);
_fisheye.quality->setCurrentIndex(2);
_fisheye.tilt->setValue(0.0);
_sphericalMirror.quality->setCurrentIndex(2);
_cylindrical.quality->setCurrentIndex(2);
_equirectangular.quality->setCurrentIndex(2);
_blit.windowId->setCurrentIndex(0);
emit windowChanged(_monitorIndexDefault, _windowIndex, _windowDimensions);
}
@@ -736,6 +796,7 @@ void WindowControl::generateWindowInformation(sgct::config::Window& window) cons
);
window.draw2D = _render2D->isChecked();
window.draw3D = _render3D->isChecked();
window.isDecorated = _windowDecoration->isChecked();
if (_spoutOutput->isChecked()) {
window.spout = sgct::config::Window::Spout{
@@ -746,6 +807,8 @@ void WindowControl::generateWindowInformation(sgct::config::Window& window) cons
window.name = _windowName->text().toStdString();
}
window.viewports.clear();
// The rest of this function is just specifying the rendering, which we can skip if we
// don't want to render 3D anyway
if (!window.draw3D) {
@@ -762,7 +825,7 @@ void WindowControl::generateWindowInformation(sgct::config::Window& window) cons
vp.projection = sgct::config::FisheyeProjection {
.fov = 180.f,
.quality = Quality[_fisheye.quality->currentIndex()].first,
.tilt = 0.f
.tilt = static_cast<float>(_fisheye.tilt->value())
};
break;
case ProjectionIndices::SphericalMirror:
@@ -781,6 +844,16 @@ void WindowControl::generateWindowInformation(sgct::config::Window& window) cons
.quality = Quality[_equirectangular.quality->currentIndex()].first
};
break;
case ProjectionIndices::Blit:
// We have to subtract here as SGCT uses 0-indexing, but we present it to the
// user as 1-indexing
window.blitWindowId = _blit.windowId->currentText().toInt() - 1;
window.draw3D = false;
// We are falling through the planar value on purpose as for a variety of
// reasons requires a projection to be defined even when we are blitting the
// contents of another window.
[[fallthrough]];
case ProjectionIndices::Planar:
{
double fovH = _planar.fovH->value();
@@ -799,8 +872,6 @@ void WindowControl::generateWindowInformation(sgct::config::Window& window) cons
break;
}
}
window.viewports.clear();
window.viewports.push_back(vp);
}
@@ -810,8 +881,9 @@ void WindowControl::setProjectionPlanar(float hfov, float vfov) {
_projectionType->setCurrentIndex(static_cast<int>(ProjectionIndices::Planar));
}
void WindowControl::setProjectionFisheye(int quality) {
void WindowControl::setProjectionFisheye(int quality, float tilt) {
_fisheye.quality->setCurrentIndex(indexForQuality(quality));
_fisheye.tilt->setValue(tilt);
_projectionType->setCurrentIndex(static_cast<int>(ProjectionIndices::Fisheye));
}
@@ -835,6 +907,32 @@ void WindowControl::setProjectionEquirectangular(int quality) {
);
}
void WindowControl::setProjectionBlit(int windowBlitId) {
// We add 1 here as SGCT uses a 0-indexing for the window idx, but we present it to
// the user as a 1-indexing
int idx = _blit.windowId->findText(QString::number(windowBlitId + 1));
ghoul_assert(idx != -1, "Could not find window blit id");
_blit.windowId->setCurrentIndex(idx);
_projectionType->setCurrentIndex(
static_cast<int>(ProjectionIndices::Blit)
);
}
void WindowControl::updateWindowCount(int newWindowCount) {
QString currentIdx = _blit.windowId->currentText();
_blit.windowId->clear();
for (int idx = 0; idx < newWindowCount; idx++) {
if (idx == _windowIndex) {
continue;
}
_blit.windowId->addItem(QString::number(idx + 1));
}
_blit.windowId->setCurrentText(currentIdx);
// Set the correct visibility
_blit.unavailable->setVisible(newWindowCount == 1);
}
void WindowControl::onSizeXChanged(int newValue) {
_windowDimensions.setWidth(newValue);
if (_aspectRatioLocked) {
@@ -896,6 +994,7 @@ void WindowControl::onProjectionChanged(int newSelection) const {
_sphericalMirror.widget->setVisible(selected == ProjectionIndices::SphericalMirror);
_cylindrical.widget->setVisible(selected == ProjectionIndices::Cylindrical);
_equirectangular.widget->setVisible(selected == ProjectionIndices::Equirectangular);
_blit.widget->setVisible(selected == ProjectionIndices::Blit);
}
void WindowControl::onAspectRatioLockClicked() {

View File

@@ -1415,9 +1415,39 @@ int main(int argc, char* argv[]) {
openspace::Settings settings = loadSettings();
settings.hasStartedBefore = true;
const std::filesystem::path p = global::configuration->profile;
const std::filesystem::path reducedName = p.filename().replace_extension();
settings.profile = reducedName.string();
const std::filesystem::path profile = global::configuration->profile;
const bool isDefaultProfile = ghoul::filesystem::isSubdirectory(
profile,
absPath("${PROFILES}")
);
const bool isUserProfile = ghoul::filesystem::isSubdirectory(
profile,
absPath("${USER_PROFILES}")
);
if (isDefaultProfile) {
std::filesystem::path p = std::filesystem::relative(
profile,
absPath("${PROFILES}")
);
p.replace_extension();
settings.profile = p.string();
}
else if (isUserProfile) {
std::filesystem::path p = std::filesystem::relative(
profile,
absPath("${USER_PROFILES}")
);
p.replace_extension();
settings.profile = p.string();
}
else {
LWARNING(
"Cannot save remembered profile when starting a profile that is not in "
"the data/profiles or user/data/profiles folder."
);
}
settings.configuration =
isGeneratedWindowConfig ? "" : global::configuration->windowConfiguration;

View File

@@ -0,0 +1,22 @@
-- Basic
-- Creates a screenspace image that shows a spherical grid as an example for any
-- [Renderable](#renderable) that can be displayed.
local Object = {
Type = "ScreenSpaceRenderableRenderable",
Identifier = "ScreenSpaceRenderableRenderable_Example",
Renderable = {
Type = "RenderableSphericalGrid",
Opacity = 1.0,
Color = { 0.3, 0.84, 1.0 },
LineWidth = 2.0
}
}
asset.onInitialize(function()
openspace.addScreenSpaceRenderable(Object)
end)
asset.onDeinitialize(function()
openspace.removeScreenSpaceRenderable(Object)
end)

View File

@@ -0,0 +1,24 @@
-- Axes
-- Creates a screenspace image that renders three Cartesian axes into the screen space
-- window. This example also modifies the original camera position to give an oblique view
-- onto the axes and increases the field of view of the camera to a wider degree. We also
-- set the background color to be fully opaque to make it easier to see the axes.
local Object = {
Type = "ScreenSpaceRenderableRenderable",
Identifier = "ScreenSpaceRenderableRenderable_Example_Axes",
Renderable = {
Type = "RenderableCartesianAxes"
},
BackgroundColor = { 0.0, 0.0, 0.0, 1.0 },
CameraPosition = { 1.0, 1.0, 1.0 },
CameraFov = 80.0
}
asset.onInitialize(function()
openspace.addScreenSpaceRenderable(Object)
end)
asset.onDeinitialize(function()
openspace.removeScreenSpaceRenderable(Object)
end)

View File

@@ -0,0 +1,41 @@
-- Model Distance
-- Creates a screen space window into which 3D model of the Eiffel tower is rendered. As
-- the objects are rendered in meter scale, and the Eiffel tower is about 300m tall, we
-- place the camera at a great distance to be able to see the entire Eiffel tower at the
-- same time.
-- Download the model file for the Eiffel tower
local modelFolder = asset.resource({
Name = "Scale Eiffel Tower",
Type = "HttpSynchronization",
Identifier = "scale_model_eiffel_tower",
Version = 1
})
local Object = {
Type = "ScreenSpaceRenderableRenderable",
Identifier = "ScreenSpaceRenderableRenderable_Example_ModelDistance",
Renderable = {
Type = "RenderableModel",
GeometryFile = modelFolder .. "eiffeltower.osmodel",
RotationVector = { 0.0, 45.0, 0.0 },
LightSources = {
{
Identifier = "Camera",
Type = "CameraLightSource",
Intensity = 5.0
}
}
},
Scale = 1.25,
CameraPosition = { 0.0, 3500.0, 9000.0 },
CameraCenter = { 0.0, 2750.0, 0.0 }
}
asset.onInitialize(function()
openspace.addScreenSpaceRenderable(Object)
end)
asset.onDeinitialize(function()
openspace.removeScreenSpaceRenderable(Object)
end)

View File

@@ -0,0 +1,47 @@
-- Model
-- Creates a screen space window into which 3D model of the Eiffel tower is rendered. As
-- the objects are rendered in meter scale, and the Eiffel tower is about 300m tall, we
-- both shrink the rendering to make the entire model fit into the view and also modify
-- the position of the camera.
-- Download the model file for the Eiffel tower
local modelFolder = asset.resource({
Name = "Scale Eiffel Tower",
Type = "HttpSynchronization",
Identifier = "scale_model_eiffel_tower",
Version = 1
})
local Object = {
Type = "ScreenSpaceRenderableRenderable",
Identifier = "ScreenSpaceRenderableRenderable_Example_Model",
Transform = {
Scale = {
Type = "StaticScale",
Scale = 0.0002
}
},
Renderable = {
Type = "RenderableModel",
GeometryFile = modelFolder .. "eiffeltower.osmodel",
RotationVector = { 0.0, 45.0, 0.0 },
LightSources = {
{
Identifier = "Camera",
Type = "CameraLightSource",
Intensity = 5.0
}
}
},
Scale = 1.25,
CameraPosition = { 0.0, 1.0, 2.0 },
CameraCenter = { 0.0, 0.5, 0.0 }
}
asset.onInitialize(function()
openspace.addScreenSpaceRenderable(Object)
end)
asset.onDeinitialize(function()
openspace.removeScreenSpaceRenderable(Object)
end)

View File

@@ -42,8 +42,7 @@ local LightPollutionSphere = {
Orientation = "Inside",
MirrorTexture = true,
FadeOutThreshold = 1.00,
RenderBinMode = "PostDeferredTransparent",
Enabled = asset.enabled
RenderBinMode = "PostDeferredTransparent"
},
GUI = {
Name = "Light Pollution Sphere",

View File

@@ -23,7 +23,9 @@ local Object = {
Texture = textures .. "mwHalpha-f.png",
Orientation = "Inside",
MirrorTexture = true,
FadeOutThreshold = 0.025
FadeOutThreshold = 0.025,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "Hydrogen Alpha",

View File

@@ -30,7 +30,9 @@ local COBE = {
Texture = textures .. "COBErect.png",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "1990 COBE CMB",
@@ -62,7 +64,9 @@ local WMAP = {
Texture = textures .. "wmap_ilc_7yr_v4_200uK_RGB_sos.png",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "2003 WMAP CMB",
@@ -93,7 +97,9 @@ local Planck = {
Texture = textures .. "cmb4k.jpg",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "2013 Planck CMB",

View File

@@ -27,7 +27,9 @@ local PlanckMultiverse1 = {
Texture = textures .. "cmb4k.jpg",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "Planck Multiverse 1",
@@ -56,7 +58,9 @@ local PlanckMultiverse2 = {
Texture = textures .. "cmb4k.jpg",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "Planck Multiverse 2",
@@ -85,7 +89,9 @@ local PlanckMultiverse3 = {
Texture = textures .. "cmb4k.jpg",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "Planck Multiverse 3",
@@ -114,7 +120,9 @@ local PlanckMultiverse4 = {
Texture = textures .. "cmb4k.jpg",
Orientation = "Both",
MirrorTexture = true,
FadeInThreshold = 0.4
FadeInThreshold = 0.4,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "Planck Multiverse 4",

View File

@@ -22,7 +22,9 @@ local Object = {
Texture = textures .. "eso0932a_blend.png",
Orientation = "Inside",
MirrorTexture = true,
FadeOutThreshold = 0.01
FadeOutThreshold = 0.01,
BlendingOption = "Additive",
DisableDepth = true
},
GUI = {
Name = "Milky Way (ESO)",

View File

@@ -0,0 +1,36 @@
local globe = asset.require("../../titan")
local Layer = {
Identifier = "Cassini_SAR_HiSAR_Global_Mosaic_351m",
Name = "Cassini SAR - HiSAR Global Mosaic (351m)",
Enabled = asset.enabled,
ZIndex = 20,
FilePath = asset.resource("cassini_sar_hisar_global_mosaic_351m_sweden.wms"),
Description = [[This global map of Titan is a preliminary product showing coverage from
Synthetic Aperture Radar (SAR) and High Altitude Synthetic Aperture Radar (HiSAR)
images at a pixel resolution of 351 meters per pixel (m). This mosaic merges Cassini
swaths through flyby T104 into a single mosaic. (Description from URL)]]
}
asset.onInitialize(function()
openspace.globebrowsing.addLayer(globe.Titan.Identifier, "ColorLayers", Layer)
end)
asset.onDeinitialize(function()
openspace.globebrowsing.deleteLayer(globe.Titan.Identifier, "ColorLayers", Layer)
end)
asset.export("layer", Layer)
asset.meta = {
Name = "Titan Cassini SAR - HiSAR Global Mosaic 351m",
Description = "Cassini global synthetic aperture radar image layer for Titan",
Author = "USGS",
URL = "https://astrogeology.usgs.gov/search/map/titan_cassini_sar_hisar_global_mosaic_351m",
License = "NASA/PDS"
}

View File

@@ -0,0 +1,20 @@
<GDAL_WMS>
<Service name="TMS">
<ServerUrl>http://wms.itn.liu.se/Titan/HiSAR_Global_Mosaic_351m/tile/${z}/${y}/${x}</ServerUrl>
</Service>
<DataWindow>
<UpperLeftX>-180.0</UpperLeftX>
<UpperLeftY>90.0</UpperLeftY>
<LowerRightX>180.0</LowerRightX>
<LowerRightY>-90.0</LowerRightY>
<SizeX>46080</SizeX>
<SizeY>23040</SizeY>
<TileLevel>7</TileLevel>
<YOrigin>top</YOrigin>
</DataWindow>
<Projection>EPSG:4326</Projection>
<BlockSizeX>512</BlockSizeX>
<BlockSizeY>512</BlockSizeY>
<BandsCount>1</BandsCount>
<MaxConnections>10</MaxConnections>
</GDAL_WMS>

View File

@@ -0,0 +1,221 @@
local textures = asset.resource({
Name = "Calibration Images",
Type = "HttpSynchronization",
Identifier = "calibration_images",
Version = 1
})
local Distance = 1000
local Size = 1000
local Center = {
Identifier = "Calibration",
GUI = {
Name = "Calibration",
Description = "The centerpoint of the calibration cube",
Path = "/Calibration"
}
}
local Front = {
Identifier = "Calibration_Front",
Parent = Center.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0, Distance, 0 }
},
Rotation = {
Type = "StaticRotation",
Rotation = { math.pi / 2.0, 0.0, 0.0 }
}
},
Renderable = {
Type = "RenderablePlaneImageLocal",
Enabled = asset.enabled,
Size = Size,
Origin = "Center",
Texture = textures .. "test-pattern-0.png"
},
GUI = {
Name = "Calibration (Front)",
Description = "The front face of the calibration cube",
Path = "/Calibration"
}
}
local Right = {
Identifier = "Calibration_Right",
Parent = Center.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { Distance, 0, 0 }
},
Rotation = {
Type = "StaticRotation",
Rotation = { math.pi / 2.0, 0.0, -math.pi / 2.0 }
}
},
Renderable = {
Type = "RenderablePlaneImageLocal",
Enabled = asset.enabled,
Size = Size,
Origin = "Center",
Texture = textures .. "test-pattern-1.png"
},
GUI = {
Name = "Calibration (Right)",
Description = "The right face of the calibration cube",
Path = "/Calibration"
}
}
local Back = {
Identifier = "Calibration_Back",
Parent = Center.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0, -Distance, 0 }
},
Rotation = {
Type = "StaticRotation",
Rotation = { -math.pi / 2.0, math.pi, 0.0 }
}
},
Renderable = {
Type = "RenderablePlaneImageLocal",
Enabled = asset.enabled,
Size = Size,
Origin = "Center",
Texture = textures .. "test-pattern-2.png"
},
GUI = {
Name = "Calibration (Back)",
Description = "The back face of the calibration cube",
Path = "/Calibration"
}
}
local Left = {
Identifier = "Calibration_Left",
Parent = Center.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { -Distance, 0, 0 }
},
Rotation = {
Type = "StaticRotation",
Rotation = { math.pi / 2.0, 0.0, math.pi / 2.0 }
}
},
Renderable = {
Type = "RenderablePlaneImageLocal",
Enabled = asset.enabled,
Size = Size,
Origin = "Center",
Texture = textures .. "test-pattern-3.png"
},
GUI = {
Name = "Calibration (Left)",
Description = "The left face of the calibration cube",
Path = "/Calibration"
}
}
local Top = {
Identifier = "Calibration_Top",
Parent = Center.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0, 0, Distance }
},
Rotation = {
Type = "StaticRotation",
Rotation = { 0.0, math.pi, -math.pi }
}
},
Renderable = {
Type = "RenderablePlaneImageLocal",
Enabled = asset.enabled,
Size = Size,
Origin = "Center",
Texture = textures .. "test-pattern-4.png"
},
GUI = {
Name = "Calibration (Top)",
Description = "The top face of the calibration cube",
Path = "/Calibration"
}
}
local Bottom = {
Identifier = "Calibration_Bottom",
Parent = Center.Identifier,
Transform = {
Translation = {
Type = "StaticTranslation",
Position = { 0, 0, -Distance }
},
Rotation = {
Type = "StaticRotation",
Rotation = { 0.0, 0.0, 0.0 }
}
},
Renderable = {
Type = "RenderablePlaneImageLocal",
Enabled = asset.enabled,
Size = Size,
Origin = "Center",
Texture = textures .. "test-pattern-5.png"
},
GUI = {
Name = "Calibration (Bottom)",
Description = "The bottom face of the calibration cube",
Path = "/Calibration"
}
}
asset.onInitialize(function()
openspace.addSceneGraphNode(Center)
openspace.addSceneGraphNode(Left)
openspace.addSceneGraphNode(Right)
openspace.addSceneGraphNode(Front)
openspace.addSceneGraphNode(Back)
openspace.addSceneGraphNode(Top)
openspace.addSceneGraphNode(Bottom)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(Bottom)
openspace.removeSceneGraphNode(Top)
openspace.removeSceneGraphNode(Back)
openspace.removeSceneGraphNode(Front)
openspace.removeSceneGraphNode(Right)
openspace.removeSceneGraphNode(Left)
openspace.removeSceneGraphNode(Center)
end)
asset.export(Center)
asset.export(Left)
asset.export(Right)
asset.export(Front)
asset.export(Back)
asset.export(Top)
asset.export(Bottom)
asset.meta = {
Name = "Calibrator",
Description = [[A cube that can be used to verify calibration in a complicated display
environment]],
Author = "OpenSpace Team",
URL = "http://openspaceproject.com",
License = "MIT license"
}

View File

@@ -0,0 +1,36 @@
{
"assets": [
"base_blank",
"util/calibration"
],
"camera": {
"aim": "Calibration_Front",
"anchor": "Calibration",
"frame": "Calibration",
"position": {
"x": 0.5,
"y": 0.5,
"z": 0.5
},
"type": "setNavigationState"
},
"meta": {
"author": "OpenSpace Team",
"description": "This profile places the camera in the inside of a calibration cube. This profile can be used to verify that a display environment is set up correctly. If a setup is correct, the different windows/viewports should show the correct parts of the surrounding cube accurately and withou any unwanted distortion.",
"license": "MIT license",
"name": "Calibration",
"url": "https://openspaceproject.com",
"version": "1.0"
},
"properties": [
{
"name": "NavigationHandler.OrbitalNavigator.LimitZoom.EnabledMinimumAllowedDistance",
"type": "setPropertyValueSingle",
"value": "false"
}
],
"version": {
"major": 1,
"minor": 4
}
}

View File

@@ -0,0 +1,17 @@
{
"0": { "id": "scene", "name": "Scene", "visible": true, "enabled": true },
"1": { "id": "settings", "name": "Settings", "visible": false, "enabled": true },
"2": { "id": "navigation", "name": "Navigation", "visible": true, "enabled": true },
"3": { "id": "timePanel", "name": "Date & Time", "visible": true, "enabled": true },
"4": { "id": "sessionRecording", "name": "Session Recording", "visible": true, "enabled": true },
"5": { "id": "geoLocation", "name": "Geo Location", "visible": true, "enabled": true },
"6": { "id": "screenSpaceRenderables", "name": "ScreenSpace Renderables", "visible": true, "enabled": true },
"7": { "id": "exoplanets", "name": "Exoplanets", "visible": true, "enabled": true },
"8": { "id": "userPanels", "name": "User Panels", "visible": true, "enabled": true },
"9": { "id": "actions", "name": "Actions", "visible": true, "enabled": true },
"10": { "id": "skyBrowser", "name": "SkyBrowser", "visible": true, "enabled": true },
"11": { "id": "mission", "name": "Mission", "visible": false, "enabled": false },
"12": { "id": "flightControl", "name": "Flight Control", "visible": false, "enabled": true },
"13": { "id": "keybindingsLayout", "name": "Keybindings", "visible": true, "enabled": true },
"14": { "id": "gettingStartedTour", "name": "Getting Started Tour", "visible": true, "enabled": true }
}

View File

@@ -48,11 +48,6 @@ public:
std::string description;
};
enum class DisplayType {
Radio,
Dropdown
};
/**
* The constructor delegating the `identifier` and the `guiName` to its super class.
*
@@ -64,18 +59,6 @@ public:
*/
OptionProperty(Property::PropertyInfo info);
/**
* The constructor delegating the `identifier` and the `guiName` to its super class.
*
* \param info The PropertyInfo structure that contains all the required static
* information for initializing this Property
* \param displayType Optional DisplayType for GUI (default RADIO)
*
* \pre \p info.identifier must not be empty
* \pre \p info.guiName must not be empty
*/
OptionProperty(PropertyInfo info, DisplayType displayType);
/**
* Returns the name of the class for reflection purposes.
*
@@ -86,21 +69,14 @@ public:
using TemplateProperty<int>::operator=;
/**
* Returns the type for GUI display.
*
* \return OptionType for display purposes
*/
DisplayType displayType() const;
/**
* Adds the passed option to the list of available options. The `value` of the
* `option` must not have been registered previously, or a warning will be logged.
*
* \param value The option that will be added to the list of available options
* \param desc The description of the value that will be added
* \param description The description of the value that will be added
*/
void addOption(int value, std::string desc);
void addOption(int value, std::string description);
/**
* Adds multiple options to the OptionProperty. Each value in the vector consists of
@@ -171,7 +147,6 @@ private:
/// The list of options which have been registered with this OptionProperty
std::vector<Option> _options;
DisplayType _displayType;
};
} // namespace openspace::properties

View File

@@ -178,7 +178,7 @@ public:
/// Removes an asset unless the `ignoreUpdates` member is set to `true`
void removeAsset(const std::string& path);
static constexpr Version CurrentVersion = Version{ 1, 3 };
static constexpr Version CurrentVersion = Version{ 1, 4 };
Version version = CurrentVersion;
std::vector<Module> modules;
@@ -192,6 +192,7 @@ public:
std::optional<CameraType> camera;
std::vector<std::string> markNodes;
std::vector<std::string> additionalScripts;
std::map<std::string, bool> uiPanelVisibility;
bool ignoreUpdates = false;

View File

@@ -72,6 +72,7 @@ set(HEADER_FILES
rendering/screenspaceframebuffer.h
rendering/screenspaceimagelocal.h
rendering/screenspaceimageonline.h
rendering/screenspacerenderablerenderable.h
rotation/timelinerotation.h
rotation/constantrotation.h
rotation/fixedrotation.h
@@ -142,6 +143,7 @@ set(SOURCE_FILES
rendering/screenspaceframebuffer.cpp
rendering/screenspaceimagelocal.cpp
rendering/screenspaceimageonline.cpp
rendering/screenspacerenderablerenderable.cpp
rotation/timelinerotation.cpp
rotation/constantrotation.cpp
rotation/fixedrotation.cpp

View File

@@ -68,6 +68,7 @@
#include <modules/base/rendering/screenspaceimagelocal.h>
#include <modules/base/rendering/screenspaceimageonline.h>
#include <modules/base/rendering/screenspaceframebuffer.h>
#include <modules/base/rendering/screenspacerenderablerenderable.h>
#include <modules/base/rotation/constantrotation.h>
#include <modules/base/rotation/fixedrotation.h>
#include <modules/base/rotation/luarotation.h>
@@ -110,6 +111,9 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) {
fSsRenderable->registerClass<ScreenSpaceImageLocal>("ScreenSpaceImageLocal");
fSsRenderable->registerClass<ScreenSpaceImageOnline>("ScreenSpaceImageOnline");
fSsRenderable->registerClass<ScreenSpaceFramebuffer>("ScreenSpaceFramebuffer");
fSsRenderable->registerClass<ScreenSpaceRenderableRenderable>(
"ScreenSpaceRenderableRenderable"
);
ghoul::TemplateFactory<DashboardItem>* fDashboard =
@@ -281,6 +285,7 @@ std::vector<documentation::Documentation> BaseModule::documentations() const {
ScreenSpaceFramebuffer::Documentation(),
ScreenSpaceImageLocal::Documentation(),
ScreenSpaceImageOnline::Documentation(),
ScreenSpaceRenderableRenderable::Documentation(),
ConstantRotation::Documentation(),
FixedRotation::Documentation(),

View File

@@ -143,26 +143,17 @@ documentation::Documentation DashboardItemAngle::Documentation() {
DashboardItemAngle::DashboardItemAngle(const ghoul::Dictionary& dictionary)
: DashboardTextItem(dictionary)
, _source{
properties::OptionProperty(
SourceTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(SourceTypeInfo),
properties::StringProperty(SourceNodeIdentifierInfo),
nullptr
}
, _reference{
properties::OptionProperty(
ReferenceTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(ReferenceTypeInfo),
properties::StringProperty(ReferenceNodeIdentifierInfo),
nullptr
}
, _destination{
properties::OptionProperty(
DestinationTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(DestinationTypeInfo),
properties::StringProperty(DestinationNodeIdentifierInfo),
nullptr
}

View File

@@ -159,21 +159,15 @@ documentation::Documentation DashboardItemDistance::Documentation() {
DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary)
: DashboardTextItem(dictionary)
, _doSimplification(SimplificationInfo, true)
, _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
, _requestedUnit(RequestedUnitInfo)
, _formatString(FormatStringInfo, "Distance from {} to {}: {:f} {}")
, _source{
properties::OptionProperty(
SourceTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(SourceTypeInfo),
properties::StringProperty(SourceNodeIdentifierInfo),
nullptr
}
, _destination{
properties::OptionProperty(
DestinationTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(DestinationTypeInfo),
properties::StringProperty(DestinationNodeIdentifierInfo),
nullptr
}

View File

@@ -197,7 +197,7 @@ documentation::Documentation DashboardItemFramerate::Documentation() {
DashboardItemFramerate::DashboardItemFramerate(const ghoul::Dictionary& dictionary)
: DashboardTextItem(dictionary)
, _frametimeType(FrametimeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _frametimeType(FrametimeInfo)
, _clearCache(ClearCacheInfo)
{
const Parameters p = codegen::bake<Parameters>(dictionary);

View File

@@ -125,7 +125,7 @@ DashboardItemSimulationIncrement::DashboardItemSimulationIncrement(
const ghoul::Dictionary& dictionary)
: DashboardTextItem(dictionary)
, _doSimplification(SimplificationInfo, true)
, _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
, _requestedUnit(RequestedUnitInfo)
, _transitionFormat(
TransitionFormatInfo,
"Simulation increment: {:.1f} {:s} / second{:s} (current: {:.1f} {:s})"

View File

@@ -84,7 +84,7 @@ documentation::Documentation DashboardItemVelocity::Documentation() {
DashboardItemVelocity::DashboardItemVelocity(const ghoul::Dictionary& dictionary)
: DashboardTextItem(dictionary)
, _doSimplification(SimplificationInfo, true)
, _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
, _requestedUnit(RequestedUnitInfo)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
_doSimplification.onChange([this]() {

View File

@@ -37,6 +37,9 @@ namespace {
openspace::properties::Property::Visibility::NoviceUser
};
// This `LightSource` type represents a light source placed at the position of the
// camera. An object with this light source will always be illuminated from the
// current view direction.
struct [[codegen::Dictionary(CameraLightSource)]] Parameters {
// [[codegen::verbatim(IntensityInfo.description)]]
std::optional<float> intensity;

View File

@@ -41,18 +41,26 @@ namespace {
openspace::properties::Property::Visibility::NoviceUser
};
constexpr openspace::properties::Property::PropertyInfo NodeCameraStateInfo = {
constexpr openspace::properties::Property::PropertyInfo NodeInfo = {
"Node",
"Node",
"The identifier of the scene graph node to follow.",
openspace::properties::Property::Visibility::AdvancedUser
};
// This `LightSource` type represents a light source placed at the position of a
// scene graph node. That is, the direction of the light will follow the position
// of an existing object in the scene. It will also update dynamically as the
// object moves.
//
// Note that the brightness of the light from the light source does not depend on
// the distance between the two scene graph nodes. Only the `Intensity` value has
// an impact on the brightness.
struct [[codegen::Dictionary(SceneGraphLightSource)]] Parameters {
// [[codegen::verbatim(IntensityInfo.description)]]
std::optional<float> intensity;
// [[codegen::verbatim(NodeCameraStateInfo.description)]]
// [[codegen::verbatim(NodeInfo.description)]]
std::string node [[codegen::identifier()]];
};
#include "scenegraphlightsource_codegen.cpp"
@@ -67,7 +75,7 @@ documentation::Documentation SceneGraphLightSource::Documentation() {
SceneGraphLightSource::SceneGraphLightSource(const ghoul::Dictionary& dictionary)
: LightSource(dictionary)
, _intensity(IntensityInfo, 1.f, 0.f, 1.f)
, _sceneGraphNodeReference(NodeCameraStateInfo, "")
, _sceneGraphNodeReference(NodeInfo, "")
{
const Parameters p = codegen::bake<Parameters>(dictionary);

View File

@@ -637,10 +637,7 @@ RenderablePointCloud::RenderablePointCloud(const ghoul::Dictionary& dictionary)
, _useAdditiveBlending(UseAdditiveBlendingInfo, true)
, _useRotation(UseOrientationDataInfo, false)
, _drawElements(DrawElementsInfo, true)
, _renderOption(
OrientationRenderOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _renderOption(OrientationRenderOptionInfo)
, _nDataPoints(NumShownDataPointsInfo, 0)
, _hasOrientationData(HasOrientationDataInfo, false)
{

View File

@@ -121,10 +121,7 @@ documentation::Documentation SizeMappingComponent::Documentation() {
SizeMappingComponent::SizeMappingComponent()
: properties::PropertyOwner({ "SizeMapping", "Size Mapping", "" })
, enabled(EnabledInfo, true)
, parameterOption(
OptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, parameterOption(OptionInfo)
, scaleFactor(ScaleFactorInfo, 1.f, 0.f, 1000.f)
, isRadius(IsRadiusInfo, false)
{

View File

@@ -106,7 +106,7 @@ documentation::Documentation RenderableDistanceLabel::Documentation() {
RenderableDistanceLabel::RenderableDistanceLabel(const ghoul::Dictionary& dictionary)
: RenderableLabel(dictionary)
, _nodelineId(NodeLineInfo)
, _distanceUnit(DistanceUnitInfo, properties::OptionProperty::DisplayType::Dropdown)
, _distanceUnit(DistanceUnitInfo)
, _customUnitDescriptor(CustomUnitDescriptorInfo)
, _precision(PrecisionInfo, 0, 0, 10)
{

View File

@@ -252,7 +252,7 @@ documentation::Documentation RenderableLabel::Documentation() {
RenderableLabel::RenderableLabel(const ghoul::Dictionary& dictionary)
: Renderable(dictionary, { .automaticallyUpdateRenderBin = false })
, _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _blendMode(BlendModeInfo)
, _text(TextInfo, "")
, _color(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
, _fontSize(FontSizeInfo, 50.f, 1.f, 100.f)
@@ -261,14 +261,8 @@ RenderableLabel::RenderableLabel(const ghoul::Dictionary& dictionary)
, _enableFadingEffect(EnableFadingEffectInfo, false)
, _fadeWidths(FadeWidthsInfo, glm::vec2(1.f), glm::vec2(0.f), glm::vec2(100.f))
, _fadeDistances(FadeDistancesInfo, glm::vec2(1.f), glm::vec2(0.f), glm::vec2(100.f))
, _fadeUnitOption(
FadeUnitOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _orientationOption(
OrientationOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _fadeUnitOption(FadeUnitOptionInfo)
, _orientationOption(OrientationOptionInfo)
{
const Parameters p = codegen::bake<Parameters>(dictionary);

View File

@@ -295,10 +295,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary)
, _modelScale(ModelScaleInfo, 1.0, std::numeric_limits<double>::epsilon(), 4e+27)
, _rotationVec(RotationVecInfo, glm::dvec3(0.0), glm::dvec3(0.0), glm::dvec3(360.0))
, _enableDepthTest(EnableDepthTestInfo, true)
, _blendingFuncOption(
BlendingOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _blendingFuncOption(BlendingOptionInfo)
, _lightSourcePropertyOwner({ "LightSources", "Light Sources" })
{
const Parameters p = codegen::bake<Parameters>(dictionary);

View File

@@ -131,7 +131,7 @@ documentation::Documentation RenderablePlane::Documentation() {
RenderablePlane::RenderablePlane(const ghoul::Dictionary& dictionary)
: Renderable(dictionary, { .automaticallyUpdateRenderBin = false })
, _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _blendMode(BlendModeInfo)
, _billboard(BillboardInfo, false)
, _mirrorBackside(MirrorBacksideInfo, false)
, _size(SizeInfo, glm::vec2(10.f), glm::vec2(0.f), glm::vec2(1e25f))

View File

@@ -180,15 +180,12 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _size(SizeInfo, 1.f, 0.f, 1e25f)
, _segments(SegmentsInfo, 16, 4, 1000)
, _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown)
, _orientation(OrientationInfo)
, _mirrorTexture(MirrorTextureInfo, false)
, _disableFadeInDistance(DisableFadeInOutInfo, false)
, _fadeInThreshold(FadeInThresholdInfo, 0.f, 0.f, 1.f, 0.001f)
, _fadeOutThreshold(FadeOutThresholdInfo, 0.f, 0.f, 1.f, 0.001f)
, _blendingFuncOption(
BlendingOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _blendingFuncOption(BlendingOptionInfo)
, _disableDepth(DisableDepthInfo, false)
{
const Parameters p = codegen::bake<Parameters>(dictionary);

View File

@@ -188,10 +188,7 @@ RenderableTrail::Appearance::Appearance()
, useLineFade(EnableFadeInfo, true)
, lineWidth(LineWidthInfo, 10.f, 1.f, 20.f)
, pointSize(PointSizeInfo, 1, 1, 64)
, renderingModes(
RenderingModeInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, renderingModes(RenderingModeInfo)
, lineLength(LineLengthInfo, 1.f, 0.f, 1.f)
, lineFadeAmount(LineFadeAmountInfo, 1.f, 0.f, 1.f)
{

View File

@@ -101,7 +101,7 @@ bool ScreenSpaceDashboard::initializeGL() {
ScreenSpaceFramebuffer::initializeGL();
addRenderFunction([this]() {
glm::vec2 penPosition = glm::vec2(0.f, _size.value().w);
glm::vec2 penPosition = glm::vec2(0.f, _size.value().x);
if (_useMainDashboard) {
global::dashboard->render(penPosition);

View File

@@ -40,7 +40,7 @@ namespace openspace {
namespace documentation { struct Documentation; }
namespace scripting { struct LuaLibrary; }
class ScreenSpaceDashboard: public ScreenSpaceFramebuffer {
class ScreenSpaceDashboard : public ScreenSpaceFramebuffer {
public:
explicit ScreenSpaceDashboard(const ghoul::Dictionary& dictionary);
virtual ~ScreenSpaceDashboard() override = default;

View File

@@ -55,7 +55,7 @@ documentation::Documentation ScreenSpaceFramebuffer::Documentation() {
ScreenSpaceFramebuffer::ScreenSpaceFramebuffer(const ghoul::Dictionary& dictionary)
: ScreenSpaceRenderable(dictionary)
, _size(SizeInfo, glm::vec4(0), glm::vec4(0), glm::vec4(16384))
, _size(SizeInfo, glm::vec2(16), glm::vec2(16), glm::vec2(16384))
{
documentation::testSpecificationAndThrow(
Documentation(),
@@ -75,14 +75,8 @@ ScreenSpaceFramebuffer::ScreenSpaceFramebuffer(const ghoul::Dictionary& dictiona
}
}
if (_guiName.empty()) {
// Adding an extra space to the user-facing name as it looks nicer
setGuiName("ScreenSpaceFramebuffer " + std::to_string(iIdentifier));
}
const glm::vec2 resolution = global::windowDelegate->currentDrawBufferResolution();
_size = global::windowDelegate->currentDrawBufferResolution();
addProperty(_size);
_size = glm::vec4(0.f, 0.f, resolution.x, resolution.y);
}
ScreenSpaceFramebuffer::~ScreenSpaceFramebuffer() {}
@@ -107,22 +101,13 @@ bool ScreenSpaceFramebuffer::deinitializeGL() {
void ScreenSpaceFramebuffer::render(const RenderData& renderData) {
const glm::vec2& resolution = global::windowDelegate->currentDrawBufferResolution();
const glm::vec4& size = _size.value();
const float xratio = resolution.x / (size.z - size.x);
const float yratio = resolution.y / (size.w - size.y);
const glm::vec2& size = _size.value();
const glm::vec2 ratio = resolution / size;
if (!_renderFunctions.empty()) {
std::array<GLint, 4> viewport;
//glGetIntegerv(GL_VIEWPORT, viewport);
global::renderEngine->openglStateCache().viewport(viewport.data());
glViewport(
static_cast<GLint>(-size.x * xratio),
static_cast<GLint>(-size.y * yratio),
static_cast<GLsizei>(resolution.x * xratio),
static_cast<GLsizei>(resolution.y * yratio)
);
global::renderEngine->openglStateCache().setViewportState(viewport.data());
glGetIntegerv(GL_VIEWPORT, viewport.data());
glViewport(0, 0, static_cast<GLint>(size.x), static_cast<GLint>(size.y));
const GLint defaultFBO = ghoul::opengl::FramebufferObject::getActiveObject();
_framebuffer->activate();
@@ -142,7 +127,7 @@ void ScreenSpaceFramebuffer::render(const RenderData& renderData) {
const glm::mat4 localRotation = localRotationMatrix();
const glm::mat4 scale = glm::scale(
scaleMatrix(),
glm::vec3((1.f / xratio), (1.f / yratio), 1.f)
glm::vec3((1.f / ratio.x), (1.f / ratio.y), 1.f)
);
const glm::mat4 modelTransform = globalRotation*translation*localRotation*scale;
draw(modelTransform, renderData);
@@ -153,7 +138,7 @@ bool ScreenSpaceFramebuffer::isReady() const {
return _shader && _texture;
}
void ScreenSpaceFramebuffer::setSize(glm::vec4 size) {
void ScreenSpaceFramebuffer::setSize(glm::vec2 size) {
_size = std::move(size);
}

View File

@@ -27,7 +27,7 @@
#include <openspace/rendering/screenspacerenderable.h>
#include <openspace/properties/vector/vec4property.h>
#include <openspace/properties/vector/vec2property.h>
namespace ghoul::opengl {
class FramebufferObject;
@@ -56,7 +56,7 @@ public:
void render(const RenderData& renderData) override;
bool isReady() const override;
void setSize(glm::vec4 size);
void setSize(glm::vec2 size);
void addRenderFunction(RenderFunction renderFunction);
void removeAllRenderFunctions();
@@ -64,7 +64,7 @@ public:
protected:
void createFramebuffer();
properties::Vec4Property _size;
properties::Vec2Property _size;
private:
void bindTexture() override;

View File

@@ -0,0 +1,307 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* 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 <modules/base/rendering/screenspacerenderablerenderable.h>
#include <openspace/camera/camera.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/rotation.h>
#include <openspace/scene/scale.h>
#include <openspace/scene/translation.h>
#include <openspace/util/updatestructures.h>
namespace {
constexpr openspace::properties::Property::PropertyInfo TimeInfo = {
"Time",
"Time",
"The time (in J2000 seconds) that is used to calculate transformations and the "
"renderable's data."
};
constexpr openspace::properties::Property::PropertyInfo CameraPositionInfo = {
"CameraPosition",
"Camera Position",
"Specifies the location of the virtual camera that is showing the renderable "
"class. This position is provided in meters."
};
constexpr openspace::properties::Property::PropertyInfo CameraCenterInfo = {
"CameraCenter",
"Camera Center",
"The location of the camera's focal point. The camera's view direction will "
"always be pointing at the provided center location. This position is provided "
"in meters."
};
constexpr openspace::properties::Property::PropertyInfo CameraUpInfo = {
"CameraUp",
"Camera Up",
"The direction that is 'up' for the provided camera. This value does not have "
"any units."
};
constexpr openspace::properties::Property::PropertyInfo CameraFovInfo = {
"CameraFov",
"Camera Field of view",
"The camera's field of view in degrees."
};
const openspace::properties::PropertyOwner::PropertyOwnerInfo TransformInfo = {
"Transform",
"Transform",
"The Translation, Rotation, and Scale that are applied to the rendered "
"Renderable."
};
// This [ScreenSpaceRenderable](#core_screenspacerenderable) object can render any
// [Renderable](#renderable) type into an image that is shown in screen space. This
// can be used to display a rendered object as an overlay in front of the regular 3D
// rendering of the scene.
//
// Note that to use this `ScreenSpaceRenderable`, it might be necessary to specify the
// `size` parameter, which determines the resolution of the inset window into which
// the Renderable is rendered. For many use cases, the default should suffice,
// however.
//
// A possible use-case for the ScreenSpaceRenderable would be to show a 3D model of a
// spacecraft without the need to place it at a position in the 3D scene with the need
// to fly to that object to talk about it.
struct [[codegen::Dictionary(ScreenSpaceRenderableRenderable)]] Parameters {
std::optional<std::string> identifier [[codegen::private()]];
// The [Renderable](#renderable) object that is shown in this ScreenSpace object.
// See the list of creatable renderable objects for options that can be used for
// this type.
ghoul::Dictionary renderable [[codegen::reference("renderable")]];
struct Transform {
// The [Translation](#core_transform_translation) object that is used for the
// provided [Renderable](#renderable). If no value is specified, a
// [StaticTranslation](#base_transform_translation_static) is created instead.
std::optional<ghoul::Dictionary> translation
[[codegen::reference("core_transform_translation")]];
// The [Rotation](#core_transform_rotation) object that is used for the
// provided [Renderable](#renderable). If no value is specified, a
// [StaticRotation](#base_transform_rotation_static) is created instead.
std::optional<ghoul::Dictionary> rotation
[[codegen::reference("core_transform_rotation")]];
// The [Scale](#core_transform_scale) object that is used for the provided
// [Renderable](#renderable). If no value is specified, a
// [StaticScale](#base_transform_scale_static) is created instead.
std::optional<ghoul::Dictionary> scale
[[codegen::reference("core_transform_scale")]];
};
// The collection of transformations that are applied to the
// [Renderable](#renderable) before it is shown on screen.
std::optional<Transform> transform;
// Specifies the start date that is used to control the renderable and the
// transforms. If no value is specified the date of 2000 JAN 01 12:00:00 is used
// instead.
std::optional<std::string> time [[codegen::datetime()]];
// [[codegen::verbatim(CameraPositionInfo.description)]]
std::optional<glm::vec3> cameraPosition;
// [[codegen::verbatim(CameraCenterInfo.description)]]
std::optional<glm::vec3> cameraCenter;
// [[codegen::verbatim(CameraUpInfo.description)]]
std::optional<glm::vec3> cameraUp;
// [[codegen::verbatim(CameraFovInfo.description)]]
std::optional<float> cameraFov;
};
#include "screenspacerenderablerenderable_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation ScreenSpaceRenderableRenderable::Documentation() {
return codegen::doc<Parameters>(
"base_screenspace_renderable",
ScreenSpaceFramebuffer::Documentation()
);
}
ScreenSpaceRenderableRenderable::ScreenSpaceRenderableRenderable(
const ghoul::Dictionary& dictionary)
: ScreenSpaceFramebuffer(dictionary)
, _time(
TimeInfo,
0.0,
-std::numeric_limits<double>::max(),
std::numeric_limits<double>::max()
)
, _cameraPosition(
CameraPositionInfo,
glm::vec3(0.f, 2.f, 2.f),
glm::vec3(-10.f, -10.f, -10.f),
glm::vec3(10.f, 10.f, 10.f)
)
, _cameraCenter(CameraCenterInfo, glm::vec3(0.f), glm::vec3(-1.f), glm::vec3(1.f))
, _cameraUp(CameraUpInfo, glm::vec3(0.f, 1.f, 0.f), glm::vec3(-1.f), glm::vec3(1.f))
, _cameraFov(CameraFovInfo, 45.f, 5.f, 90.f)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
std::string identifier = p.identifier.value_or("ScreenSpaceRenderableRenderable");
setIdentifier(makeUniqueIdentifier(std::move(identifier)));
if (p.time.has_value()) {
_time = Time(*p.time).j2000Seconds();
}
_time.onChange([this]() { _previousTime = _time; });
addProperty(_time);
_cameraPosition = p.cameraPosition.value_or(_cameraPosition);
addProperty(_cameraPosition);
_cameraCenter = p.cameraCenter.value_or(_cameraCenter);
addProperty(_cameraCenter);
_cameraUp = p.cameraUp.value_or(_cameraUp);
addProperty(_cameraUp);
_cameraFov = p.cameraFov.value_or(_cameraFov);
addProperty(_cameraFov);
_renderable = Renderable::createFromDictionary(p.renderable);
_renderable->initialize();
addPropertySubOwner(_renderable.get());
_transform = ghoul::mm_unique_ptr<properties::PropertyOwner>(
new properties::PropertyOwner(TransformInfo)
);
addPropertySubOwner(_transform.get());
if (p.transform.has_value() && p.transform->translation.has_value()) {
_translation = Translation::createFromDictionary(*p.transform->translation);
}
else {
ghoul::Dictionary translation;
translation.setValue("Type", std::string("StaticTranslation"));
translation.setValue("Position", glm::dvec3(0.0));
_translation = Translation::createFromDictionary(translation);
}
_translation->initialize();
_transform->addPropertySubOwner(_translation.get());
if (p.transform.has_value() && p.transform->rotation.has_value()) {
_rotation = Rotation::createFromDictionary(*p.transform->rotation);
}
else {
ghoul::Dictionary rotation;
rotation.setValue("Type", std::string("StaticRotation"));
rotation.setValue("Rotation", glm::dvec3(0.0));
_rotation = Rotation::createFromDictionary(rotation);
}
_rotation->initialize();
_transform->addPropertySubOwner(_rotation.get());
if (p.transform.has_value() && p.transform->scale.has_value()) {
_scale = Scale::createFromDictionary(*p.transform->scale);
}
else {
ghoul::Dictionary scale;
scale.setValue("Type", std::string("StaticScale"));
scale.setValue("Scale", 1.0);
_scale = Scale::createFromDictionary(scale);
}
_scale->initialize();
_transform->addPropertySubOwner(_scale.get());
}
ScreenSpaceRenderableRenderable::~ScreenSpaceRenderableRenderable() {}
bool ScreenSpaceRenderableRenderable::initializeGL() {
ScreenSpaceFramebuffer::initializeGL();
_renderable->initializeGL();
addRenderFunction([this]() {
glm::vec4 bg = _backgroundColor;
glClearColor(bg.r, bg.g, bg.b, bg.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Camera camera;
// @TODO (2025-03-24, abock): These two lines can be removed once #3573 is fixed
camera.setPositionVec3(glm::dvec3(0.0, 0.0, 0.0));
camera.setRotation(glm::dvec3(0.0, 0.0, 0.0));
glm::mat4 view = glm::lookAt(
_cameraPosition.value(),
_cameraCenter.value(),
glm::normalize(_cameraUp.value())
);
camera.sgctInternal.setViewMatrix(view);
glm::mat4 proj = glm::perspectiveFov(
glm::radians(_cameraFov.value()),
_size.value().x,
_size.value().y,
0.1f,
20.f
);
camera.sgctInternal.setProjectionMatrix(proj);
openspace::RenderData renderData = {
.camera = camera,
.time = Time(_time),
.modelTransform = {
.translation = _translation->position(),
.rotation = _rotation->matrix(),
.scale = _scale->scaleValue()
}
};
RendererTasks tasks;
_renderable->render(renderData, tasks);
});
return true;
}
bool ScreenSpaceRenderableRenderable::deinitializeGL() {
_renderable->deinitializeGL();
_renderable->deinitialize();
return ScreenSpaceFramebuffer::deinitializeGL();
}
void ScreenSpaceRenderableRenderable::update() {
UpdateData updateData = {
.time = Time(_time),
.previousFrameTime = Time(_previousTime)
};
_translation->update(updateData);
updateData.modelTransform.translation = _translation->position();
_rotation->update(updateData);
updateData.modelTransform.rotation = _rotation->matrix();
_scale->update(updateData);
updateData.modelTransform.scale = _scale->scaleValue();
_renderable->update(updateData);
}
} // namespace openspace

View File

@@ -0,0 +1,76 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* 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_MODULE_BASE___SCREENSPACERENDERABLERENDERABLE___H__
#define __OPENSPACE_MODULE_BASE___SCREENSPACERENDERABLERENDERABLE___H__
#include <modules/base//rendering/screenspaceframebuffer.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/doubleproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/vector/vec3property.h>
namespace openspace {
namespace properties { class PropertyOwner; }
class Renderable;
class Rotation;
class Scale;
class Translation;
namespace documentation { struct Documentation; }
class ScreenSpaceRenderableRenderable : public ScreenSpaceFramebuffer {
public:
using RenderFunction = std::function<void()>;
explicit ScreenSpaceRenderableRenderable(const ghoul::Dictionary& dictionary);
virtual ~ScreenSpaceRenderableRenderable() override;
bool initializeGL() override;
bool deinitializeGL() override;
void update() override;
static documentation::Documentation Documentation();
private:
ghoul::mm_unique_ptr<Translation> _translation = nullptr;
ghoul::mm_unique_ptr<properties::PropertyOwner> _transform = nullptr;
ghoul::mm_unique_ptr<Rotation> _rotation = nullptr;
ghoul::mm_unique_ptr<Scale> _scale = nullptr;
ghoul::mm_unique_ptr<Renderable> _renderable = nullptr;
double _previousTime = 0.0;
properties::DoubleProperty _time;
properties::Vec3Property _cameraPosition;
properties::Vec3Property _cameraCenter;
properties::Vec3Property _cameraUp;
properties::FloatProperty _cameraFov;
};
} //namespace openspace
#endif // __OPENSPACE_MODULE_BASE___SCREENSPACERENDERABLERENDERABLE___H__

View File

@@ -284,10 +284,7 @@ FixedRotation::FixedRotation(const ghoul::Dictionary& dictionary)
: Rotation(dictionary)
, _enabled(EnableInfo, true)
, _xAxis{
properties::OptionProperty(
XAxisTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(XAxisTypeInfo),
properties::StringProperty(XAxisObjectInfo, ""),
properties::BoolProperty(XAxisInvertObjectInfo, false),
properties::Vec3Property(
@@ -300,10 +297,7 @@ FixedRotation::FixedRotation(const ghoul::Dictionary& dictionary)
nullptr
}
, _yAxis{
properties::OptionProperty(
YAxisTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(YAxisTypeInfo),
properties::StringProperty(YAxisObjectInfo, ""),
properties::BoolProperty(YAxisInvertObjectInfo, false),
properties::Vec3Property(
@@ -316,10 +310,7 @@ FixedRotation::FixedRotation(const ghoul::Dictionary& dictionary)
nullptr
}
, _zAxis{
properties::OptionProperty(
ZAxisTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(ZAxisTypeInfo),
properties::StringProperty(ZAxisObjectInfo, ""),
properties::BoolProperty(ZAxisInvertObjectInfo, false),
properties::Vec3Property(

View File

@@ -192,7 +192,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary)
glm::ivec2(1000)
)
, _lineWidth(LineWidthInfo, 2.f, 1.f, 16.f)
, _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown)
, _renderOption(RenderOptionInfo)
{
const Parameters p = codegen::bake<Parameters>(dictionary);

View File

@@ -300,8 +300,8 @@ RenderableFieldlinesSequence::RenderableFieldlinesSequence(
const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _colorGroup({ "Color" })
, _colorMethod(ColorMethodInfo, properties::OptionProperty::DisplayType::Radio)
, _colorQuantity(ColorQuantityInfo, properties::OptionProperty::DisplayType::Dropdown)
, _colorMethod(ColorMethodInfo)
, _colorQuantity(ColorQuantityInfo)
, _colorQuantityMinMax(
ColorMinMaxInfo,
glm::vec2(-0.f, 100.f),
@@ -342,10 +342,7 @@ RenderableFieldlinesSequence::RenderableFieldlinesSequence(
glm::vec2(-5000.f),
glm::vec2(5000.f)
)
, _maskingQuantity(
MaskingQuantityInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _maskingQuantity(MaskingQuantityInfo)
, _lineWidth(LineWidthInfo, 1.f, 1.f, 20.f)
, _jumpToStartBtn(TimeJumpButtonInfo)
{

View File

@@ -472,12 +472,9 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary)
, _firstRow(FirstRowInfo, 0, 0, 2539913) // DR1-max: 2539913
, _lastRow(LastRowInfo, 0, 0, 2539913)
, _columnNamesList(ColumnNamesInfo)
, _fileReaderOption(
FileReaderOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _renderMode(RenderModeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _shaderOption(ShaderOptionInfo, properties::OptionProperty::DisplayType::Dropdown)
, _fileReaderOption(FileReaderOptionInfo)
, _renderMode(RenderModeInfo)
, _shaderOption(ShaderOptionInfo)
, _nRenderedStars(NumRenderedStarsInfo, 0, 0, 2000000000) // 2 Billion stars
, _cpuRamBudgetProperty(CpuRamBudgetInfo, 0.f, 0.f, 1.f)
, _gpuStreamBudgetProperty(GpuStreamBudgetInfo, 0.f, 0.f, 1.f)

View File

@@ -146,7 +146,7 @@ namespace {
"The number of integration steps used during the raycasting procedure.",
openspace::properties::Property::Visibility::AdvancedUser
};
void saveCachedFile(const std::filesystem::path& file,
const std::vector<glm::vec3>& positions,
const std::vector<glm::vec3>& colors, int64_t nPoints,
@@ -254,10 +254,7 @@ RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary)
, _stepSize(StepSizeInfo, 0.01f, 0.001f, 0.05f, 0.001f)
, _absorptionMultiply(AbsorptionMultiplyInfo, 40.f, 0.f, 200.f)
, _emissionMultiply(EmissionMultiplyInfo, 200.f, 0.f, 1000.f)
, _starRenderingMethod(
StarRenderingMethodInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _starRenderingMethod(StarRenderingMethodInfo)
, _enabledPointsRatio(EnabledPointsRatioInfo, 0.5f, 0.01f, 1.f)
, _rotation(
RotationInfo,

View File

@@ -300,10 +300,7 @@ GeoJsonComponent::GeoJsonComponent(const ghoul::Dictionary& dictionary,
)
, _pointSizeScale(PointSizeScaleInfo, 1.f, 0.01f, 100.f)
, _lineWidthScale(LineWidthScaleInfo, 1.f, 0.01f, 10.f)
, _pointRenderModeOption(
PointRenderModeInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _pointRenderModeOption(PointRenderModeInfo)
, _drawWireframe(DrawWireframeInfo, false)
, _preventUpdatesFromHeightMap(PreventHeightUpdateInfo, false)
, _forceUpdateHeightData(ForceUpdateHeightDataInfo)

View File

@@ -37,7 +37,7 @@ namespace {
namespace openspace::globebrowsing {
GeoJsonManager::GeoJsonManager()
: properties::PropertyOwner({ "GeoJson", "Geometry Overlays" })
: properties::PropertyOwner({ "GeographicOverlays", "Geographic Overlays" })
{}
void GeoJsonManager::initialize(RenderableGlobe* globe) {

View File

@@ -380,16 +380,10 @@ GeoJsonProperties::GeoJsonProperties()
, lineWidth(LineWidthInfo, 2.f, 0.01f, 10.f)
, pointSize(PointSizeInfo, 10.f, 0.01f, 100.f)
, pointTexture(PointTextureInfo)
, pointAnchorOption(
PointAnchorOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, pointAnchorOption(PointAnchorOptionInfo)
, extrude(ExtrudeInfo, false)
, performShading(PerformShadingInfo, false)
, altitudeModeOption(
AltitudeModeInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, altitudeModeOption(AltitudeModeInfo)
{
addProperty(opacity);
color.setViewOption(properties::Property::ViewOptions::Color);

View File

@@ -280,10 +280,7 @@ GlobeLabelsComponent::GlobeLabelsComponent()
, _fadeOutEnabled(FadeOutEnabledInfo, false)
, _disableCulling(DisableCullingInfo, false)
, _distanceEPS(DistanceEPSInfo, 100000.f, 1000.f, 10000000.f)
, _alignmentOption(
AlignmentOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _alignmentOption(AlignmentOptionInfo)
{
addProperty(_enabled);
addProperty(_color);

View File

@@ -186,8 +186,8 @@ Layer::Layer(layers::Group::ID id, const ghoul::Dictionary& layerDict, LayerGrou
layerDict.hasKey(KeyDesc) ? layerDict.value<std::string>(KeyDesc) : ""
})
, _parent(parent)
, _typeOption(TypeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _blendModeOption(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _typeOption(TypeInfo)
, _blendModeOption(BlendModeInfo)
, _enabled(EnabledInfo, false)
, _reset(ResetInfo)
, _remove(RemoveInfo)

View File

@@ -80,7 +80,7 @@ LayerAdjustment::LayerAdjustment()
: properties::PropertyOwner({ "Adjustment" })
, _chromaKeyColor(ChromaKeyColorInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(1.f))
, _chromaKeyTolerance(ChromaKeyToleranceInfo, 0.f, 0.f, 1.f)
, _typeOption(TypeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _typeOption(TypeInfo)
, _typeId(static_cast<layers::Adjustment::ID>(_typeOption.value()))
{
// Add options to option properties

View File

@@ -110,48 +110,33 @@ void renderOptionProperty(Property* prop, const std::string& ownerName,
int value = *p;
const std::vector<OptionProperty::Option>& options = p->options();
switch (p->displayType()) {
case OptionProperty::DisplayType::Radio:
ImGui::Text("%s", name.c_str());
ImGui::Separator();
for (const OptionProperty::Option& o : options) {
ImGui::RadioButton(o.description.c_str(), &value, o.value);
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
}
ImGui::Separator();
break;
case OptionProperty::DisplayType::Dropdown: {
// The order of the options does not have to correspond with the value of the
// option
std::string nodeNames;
for (const OptionProperty::Option& o : options) {
nodeNames += o.description + '\0';
}
nodeNames += '\0';
int idx = static_cast<int>(std::distance(
options.begin(),
std::find_if(
options.begin(),
options.end(),
[value](const OptionProperty::Option& o) { return o.value == value; }
)
));
const bool hasChanged = ImGui::Combo(name.c_str(), &idx, nodeNames.c_str());
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
if (hasChanged) {
value = options[idx].value;
}
break;
}
// The order of the options does not have to correspond with the value of the
// option
std::string nodeNames;
for (const OptionProperty::Option& o : options) {
nodeNames += o.description + '\0';
}
nodeNames += '\0';
int idx = static_cast<int>(std::distance(
options.begin(),
std::find_if(
options.begin(),
options.end(),
[value](const OptionProperty::Option& o) { return o.value == value; }
)
));
const bool hasChanged = ImGui::Combo(name.c_str(), &idx, nodeNames.c_str());
if (showTooltip) {
renderTooltip(prop, tooltipDelay);
}
if (hasChanged) {
value = options[idx].value;
}
if (value != p->value() && !isReadOnly) {
executeSetPropertyScript(p->uri(), std::to_string(value));
}

View File

@@ -191,7 +191,7 @@ RenderableKameleonVolume::RenderableKameleonVolume(const ghoul::Dictionary& dict
, _domainScale(DomainScaleInfo, glm::vec3(1.f))
, _lowerValueBound(LowerValueBoundInfo, 0.f, 0.f, 1.f)
, _upperValueBound(UpperValueBoundInfo, 1.f, 0.01f, 1.f)
, _gridType(GridTypeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _gridType(GridTypeInfo)
, _stepSize(StepSizeInfo, 0.02f, 0.01f, 1.f)
, _sourcePath(SourcePathInfo)
, _transferFunctionPath(TransferFunctionInfo)

View File

@@ -43,6 +43,7 @@ set(HEADER_FILES
include/topics/getpropertytopic.h
include/topics/luascripttopic.h
include/topics/missiontopic.h
include/topics/profiletopic.h
include/topics/sessionrecordingtopic.h
include/topics/setpropertytopic.h
include/topics/shortcuttopic.h
@@ -73,6 +74,7 @@ set(SOURCE_FILES
src/topics/getpropertytopic.cpp
src/topics/luascripttopic.cpp
src/topics/missiontopic.cpp
src/topics/profiletopic.cpp
src/topics/sessionrecordingtopic.cpp
src/topics/setpropertytopic.cpp
src/topics/shortcuttopic.cpp

View File

@@ -0,0 +1,45 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* 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_MODULE_SERVER___PROFILETOPIC___H__
#define __OPENSPACE_MODULE_SERVER___PROFILETOPIC___H__
#include <modules/server/include/topics/topic.h>
using nlohmann::json;
namespace openspace {
class ProfileTopic : public Topic {
public:
ProfileTopic() = default;
~ProfileTopic() override = default;
void handleJson(const nlohmann::json& json) override;
bool isDone() const override;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_SERVER___PROFILETOPIC___H__

View File

@@ -53,6 +53,7 @@
#include <ghoul/io/socket/tcpsocketserver.h>
#include <ghoul/io/socket/websocketserver.h>
#include <ghoul/misc/profiling.h>
#include <include/topics/profiletopic.h>
namespace {
constexpr std::string_view _loggerCat = "ServerModule: Connection";
@@ -97,6 +98,7 @@ Connection::Connection(std::unique_ptr<ghoul::io::Socket> s, std::string address
_topicFactory.registerClass<GetPropertyTopic>("get");
_topicFactory.registerClass<LuaScriptTopic>("luascript");
_topicFactory.registerClass<MissionTopic>("missions");
_topicFactory.registerClass<ProfileTopic>("profile");
_topicFactory.registerClass<SessionRecordingTopic>("sessionRecording");
_topicFactory.registerClass<SetPropertyTopic>("set");
_topicFactory.registerClass<ShortcutTopic>("shortcuts");

View File

@@ -0,0 +1,46 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* 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 <modules/server/include/topics/profiletopic.h>
#include <modules/server/include/connection.h>
#include <modules/server/include/jsonconverters.h>
#include <openspace/engine/globals.h>
#include <openspace/scene/profile.h>
namespace openspace {
bool ProfileTopic::isDone() const {
return true;
}
void ProfileTopic::handleJson(const nlohmann::json&) {
const nlohmann::json data = {
{ "uiPanelVisibility", global::profile->uiPanelVisibility },
{ "markNodes", global::profile->markNodes }
};
_connection->sendJson(wrappedPayload(data));
}
} // namespace openspace

View File

@@ -208,12 +208,25 @@ namespace {
if (e.find('.') == std::string::npos) {
e += ".0";
}
// @TODO(abock, 2024-05-20) 'd' suffix is needed until scnlib updates to v3
auto res = scn::scan<int, double>(e, "{:2d}{}");
if (!res) {
if (e.size() <= 2) {
throw ghoul::RuntimeError(
std::format("Error parsing epoch '{}'. Invalid date string", epoch)
);
}
std::string_view yearStr = std::string_view(e).substr(0, 2);
std::string_view daysInYearStr = std::string_view(e).substr(2);
auto resYear = scn::scan<int>(yearStr, "{}");
if (!resYear) {
throw ghoul::RuntimeError(std::format("Error parsing epoch '{}'", epoch));
}
auto [year, daysInYear] = res->values();
int year = resYear->value();
auto resDaysInYear = scn::scan<double>(daysInYearStr, "{}");
if (!resDaysInYear) {
throw ghoul::RuntimeError(std::format("Error parsing epoch '{}'", epoch));
}
double daysInYear = resDaysInYear->value();
year += year > 57 ? 1900 : 2000;
const int daysSince2000 = countDays(year);
@@ -336,9 +349,8 @@ namespace {
// We have the first form
int month = 0;
int days = 0;
// @TODO(abock, 2024-05-20) 'd' suffix is needed until scnlib updates to v3
auto res = scn::scan<int, int, int, int, int, double>(
epoch, "{:4d}-{:2d}-{:2d}T{:2d}:{:2d}:{}"
epoch, "{:4}-{:2}-{:2}T{:2}:{:2}:{}"
);
if (!res) {
throw ghoul::RuntimeError(std::format("Error parsing epoch '{}'", epoch));
@@ -350,9 +362,8 @@ namespace {
else if (pos == 8) {
// We have the second form
// @TODO(abock, 2024-05-20) 'd' suffix is needed until scnlib updates to v3
auto res = scn::scan<int, int, int, int, double>(
epoch, "{:4d}-{:3d}T{:2d}:{:2d}:{}"
epoch, "{:4}-{:3}T{:2}:{:2}:{}"
//date.year, date.nDays, date.hours, date.minutes, date.seconds
);
if (!res) {

View File

@@ -298,9 +298,9 @@ documentation::Documentation RenderableFluxNodes::Documentation() {
RenderableFluxNodes::RenderableFluxNodes(const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _earthdistGroup({ "Earthfocus" })
, _goesEnergyBins(GoesEnergyBinsInfo, properties::OptionProperty::DisplayType::Radio)
, _goesEnergyBins(GoesEnergyBinsInfo)
, _styleGroup({ "Style" })
, _colorMode(ColorModeInfo, properties::OptionProperty::DisplayType::Radio)
, _colorMode(ColorModeInfo)
, _streamColor(
StreamColorInfo,
glm::vec4(0.96f, 0.88f, 0.8f, 1.f),
@@ -311,7 +311,7 @@ RenderableFluxNodes::RenderableFluxNodes(const ghoul::Dictionary& dictionary)
, _colorTableRange(colorTableRangeInfo, { -2.f, 4.f }, { -8.f, -8.f }, { 8.f, 8.f })
, _fluxColorAlpha(FluxColorAlphaInfo, 1.f, 0.f, 1.f)
, _streamGroup({ "Streams" })
, _scalingMethod(ScalingmethodInfo, properties::OptionProperty::DisplayType::Radio)
, _scalingMethod(ScalingmethodInfo)
, _nodesAmountGroup({ "NodeGroup" })
, _nodeSize(NodeSizeInfo, 2.f, 1.f, 10.f)
, _distanceThreshold(DistanceThresholdInfo, 0.f, 0.f, 1.f)
@@ -323,7 +323,7 @@ RenderableFluxNodes::RenderableFluxNodes(const ghoul::Dictionary& dictionary)
, _filteringLower(FilteringInfo, 0.f, 0.f, 5.f)
, _filteringUpper(FilteringUpperInfo, 5.f, 0.f, 5.f)
, _amountofNodes(AmountofNodesInfo, 1, 1, 100)
, _nodeskipMethod(NodeskipMethodInfo, properties::OptionProperty::DisplayType::Radio)
, _nodeskipMethod(NodeskipMethodInfo)
, _defaultNodeSkip(DefaultNodeSkipInfo, 1, 1, 100)
, _fluxNodeskipThreshold(FluxNodeskipThresholdInfo, 0, -20, 10)
, _earthNodeSkip(EarthNodeSkipInfo, 1, 1, 100)

View File

@@ -258,10 +258,7 @@ RenderableOrbitalKepler::Appearance::Appearance()
, pointSizeExponent(PointSizeExponentInfo, 1.0f, 0.f, 11.f)
, enableMaxSize(EnableMaxSizeInfo, true)
, maxSize(MaxSizeInfo, 5.f, 0.f, 45.f)
, renderingModes(
RenderingModeInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, renderingModes(RenderingModeInfo)
, trailFade(TrailFadeInfo, 20.f, 0.f, 30.f)
, enableOutline(EnableOutlineInfo, true)
, outlineColor(OutlineColorInfo, glm::vec3(0.f), glm::vec3(0.f), glm::vec3(1.f))

View File

@@ -450,11 +450,8 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary)
properties::StringProperty(MappingVzInfo),
properties::StringProperty(MappingSpeedInfo)
}
, _colorOption(ColorOptionInfo, properties::OptionProperty::DisplayType::Dropdown)
, _otherDataOption(
OtherDataOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _colorOption(ColorOptionInfo)
, _otherDataOption(OtherDataOptionInfo)
, _otherDataColorMapPath(OtherDataColorMapInfo)
, _otherDataRange(
OtherDataValueRangeInfo,
@@ -484,10 +481,7 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary)
}
, _parameters {
properties::PropertyOwner(SizeCompositionInfo),
properties::OptionProperty(
SizeCompositionMethodInfo,
properties::OptionProperty::DisplayType::Dropdown
),
properties::OptionProperty(SizeCompositionMethodInfo),
properties::FloatProperty(LumPercentInfo, 0.5f, 0.f, 3.f),
properties::FloatProperty(RadiusPercentInfo, 0.5f, 0.f, 3.f)
}
@@ -614,7 +608,7 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary)
_useProperMotion = p.useProperMotion.value_or(_useProperMotion);
addProperty(_useProperMotion);
_properMotionEpoch = p.properMotionEpoch.value_or(_properMotionEpoch);
addProperty(_properMotionEpoch);

View File

@@ -157,10 +157,7 @@ RenderableShadowCylinder::RenderableShadowCylinder(const ghoul::Dictionary& dict
, _numberOfPoints(NumberPointsInfo, 190, 1, 300)
, _shadowLength(ShadowLengthInfo, 0.1f, 0.f, 0.5f)
, _shadowColor(ShadowColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f))
, _terminatorType(
TerminatorTypeInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _terminatorType(TerminatorTypeInfo)
, _lightSource(LightSourceInfo)
, _observer(ObserverInfo)
, _body(BodyInfo)

View File

@@ -173,7 +173,7 @@ documentation::Documentation RenderableTimeVaryingVolume::Documentation() {
RenderableTimeVaryingVolume::RenderableTimeVaryingVolume(
const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _gridType(GridTypeInfo, properties::OptionProperty::DisplayType::Dropdown)
, _gridType(GridTypeInfo)
, _stepSize(StepSizeInfo, 0.02f, 0.001f, 0.1f)
, _brightness(BrightnessInfo, 0.33f, 0.f, 1.f)
, _rNormalization(rNormalizationInfo, 0.f, 0.f, 2.f)

View File

@@ -38,7 +38,7 @@ set(PROJECT_ARCH "x86_64")
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
cmake_minimum_required(VERSION 2.8.12.1)
cmake_minimum_required(VERSION 3.10)
# Use folders in the resulting project files.
set_property(GLOBAL PROPERTY OS_FOLDERS ON)

View File

@@ -128,10 +128,7 @@ namespace openspace::interaction {
PathNavigator::PathNavigator()
: properties::PropertyOwner({ "PathNavigator", "Path Navigator" })
, _defaultPathType(
DefaultCurveOptionInfo,
properties::OptionProperty::DisplayType::Dropdown
)
, _defaultPathType(DefaultCurveOptionInfo)
, _includeRoll(IncludeRollInfo, false)
, _speedScale(SpeedScaleInfo, 1.f, 0.01f, 2.f)
, _applyIdleBehaviorOnFinish(IdleBehaviorOnFinishInfo, false)

View File

@@ -1,5 +1,4 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
@@ -32,6 +31,26 @@
namespace {
constexpr std::string_view _loggerCat = "OptionProperty";
using Option = openspace::properties::OptionProperty::Option;
bool addOptionInternal(int value, std::string desc, std::vector<Option>& options) {
Option option = { .value = value, .description = std::move(desc) };
for (const Option& o : options) {
if (o.value == option.value) {
LWARNING(std::format(
"The value of option {{ {} -> {} }} was already registered when "
"trying to add option {{ {} -> {} }}",
o.value, o.description, option.value, option.description
));
return false;
}
}
options.push_back(std::move(option));
return true;
}
} // namespace
namespace openspace::properties {
@@ -46,18 +65,6 @@ OptionProperty::OptionProperty(PropertyInfo info)
std::numeric_limits<int>::max(),
1
)
, _displayType(DisplayType::Radio)
{}
OptionProperty::OptionProperty(PropertyInfo info, DisplayType displayType)
: NumericalProperty<int>(
std::move(info),
0,
std::numeric_limits<int>::lowest(),
std::numeric_limits<int>::max(),
1
)
, _displayType(displayType)
{}
std::string_view OptionProperty::className() const {
@@ -70,48 +77,37 @@ ghoul::lua::LuaTypes OptionProperty::typeLua() const {
);
}
OptionProperty::DisplayType OptionProperty::displayType() const {
return _displayType;
}
const std::vector<OptionProperty::Option>& OptionProperty::options() const {
return _options;
}
void OptionProperty::addOption(int value, std::string desc) {
Option option = { .value = value, .description = std::move(desc) };
for (const Option& o : _options) {
if (o.value == option.value) {
LWARNING(std::format(
"The value of option {{ {} -> {} }} was already registered when trying "
"to add option {{ {} -> {} }}",
o.value, o.description, option.value, option.description
));
return;
}
void OptionProperty::addOption(int value, std::string description) {
bool success = addOptionInternal(value, description, _options);
if (success) {
// Set default value to option added first
NumericalProperty::setValue(_options[0].value);
}
_options.push_back(std::move(option));
// Set default value to option added first
NumericalProperty::setValue(_options[0].value);
}
void OptionProperty::addOptions(std::vector<std::pair<int, std::string>> options) {
for (std::pair<int, std::string>& p : options) {
addOption(p.first, std::move(p.second));
addOptionInternal(p.first, std::move(p.second), _options);
}
// Set default value to option added first
NumericalProperty::setValue(_options[0].value);
}
void OptionProperty::addOptions(std::vector<std::string> options) {
for (int i = 0; i < static_cast<int>(options.size()); i++) {
addOption(i, std::move(options[i]));
addOptionInternal(i, std::move(options[i]), _options);
}
// Set default value to option added first
NumericalProperty::setValue(_options[0].value);
}
void OptionProperty::clearOptions() {
NumericalProperty::setValue(0);
_options.clear();
_value = 0;
}
void OptionProperty::setValue(int value) {

View File

@@ -302,7 +302,8 @@ void PropertyOwner::addPropertySubOwner(openspace::properties::PropertyOwner* ow
const bool hasProp = hasProperty(owner->identifier());
if (hasProp) {
LERROR(std::format(
"PropertyOwner '{}'s name already names a Property", owner->identifier()
"PropertyOwner '{}'s identifier is already in use for a Property",
owner->identifier()
));
return;
}

View File

@@ -35,34 +35,30 @@ namespace {
constexpr openspace::properties::Property::PropertyInfo EnabledInfo = {
"Enabled",
"Color Map Enabled",
"If this value is set to 'true', the provided color map is used (if one was "
"provided in the configuration). If no color map was provided, changing this "
"setting does not do anything.",
"Decides if the color mapping should be used or not.",
openspace::properties::Property::Visibility::NoviceUser
};
constexpr openspace::properties::Property::PropertyInfo FileInfo = {
"File",
"Color Map File",
"The path to the color map file to use for coloring the points.",
"The path to the color map file to use. Should be a .cmap file",
openspace::properties::Property::Visibility::AdvancedUser
};
constexpr openspace::properties::Property::PropertyInfo ParameterInfo = {
"Parameter",
"Parameter",
"This value determines which paramenter is used for coloring the points based "
"on the color map. The property is set based on predefined options specified in "
"the asset file. When changing the parameter, the value range to used for the"
"mapping will also be changed. Per default, it is set to the last parameter in "
"the list of options.",
"The paramenter in the dataset to use for the color mapping. On change, the "
"value range to used for the mapping will also be changed.",
openspace::properties::Property::Visibility::User
};
constexpr openspace::properties::Property::PropertyInfo RangeInfo = {
"ValueRange",
"Value Range",
"This value changes the range of values to be mapped with the current color map.",
"The range of values to use in the color mapping. The lowest value will be "
"mapped to the first color in the color map.",
openspace::properties::Property::Visibility::User
};
@@ -150,30 +146,55 @@ namespace {
openspace::properties::Property::Visibility::AdvancedUser
};
// This component handles settings for dynamic color mapping of data points based on
// the columns in a dataset. It is for example utilized by the
// [RenderablePointCloud](#base_renderablepointcloud) renderable type.
//
// The option for the parameters will be initialized based on the dataset used for
// the specific renderable. The value range will be set based on the min and max
// values for the respective column in the dataset, but can be manually adjusted.
// Per default, all columns will be loaded, but this can also be adjusted.
//
// In addition to specifying a color map (.cmap) file and changing the parameter used
// for determining the color, this component includes features such as:
//
// - Inverting the color map
//
// - Handling colors for missing data values (or hiding these data points)
//
// - Handling colors for values outside the provided range
//
// - Predefining which parameters to load from the dataset, and the range of values
// to use for color mapping
struct [[codegen::Dictionary(ColorMappingComponent)]] Parameters {
// [[codegen::verbatim(EnabledInfo.description)]]
std::optional<bool> enabled;
// [[codegen::verbatim(FileInfo.description)]]
std::optional<std::filesystem::path> file;
std::filesystem::path file;
struct ColorMapParameter {
// The key for the data variable to use for color
// The key for the data variable to use for color.
std::string key;
// An optional value range to use for coloring when this option is selected.
// If not included, the range will be set from the min and max value in the
// dataset
// dataset.
std::optional<glm::vec2> range;
};
// A list of options for color parameters to use for color mapping, that will
// appear as options in the drop-down menu in the user interface. Per default,
// the first option in the list is selected. Each option is a table in the form
// { Key = \"theKey\", Range = {min, max} }, where the value range is optional.
// If added, this range will automatically be set when the option is selected
// A list of options for parameters to use for color mapping that will appear
// as options in the drop-down menu in the user interface. Per default, the
// first option in the list is selected.
//
// Each option is a table in the form
// `{ Key = \"dataColumn\", Range = {min, max} }`, where the `Range` is optional.
// The specified range (or the min/max values for this data column) will be used
// for color mapping when the option is selected.
std::optional<std::vector<ColorMapParameter>> parameterOptions;
// [[codegen::verbatim(ParameterInfo.description)]]
// The default parameter to use for the color map. The options for this parameter
// are either loaded from the dataset or provided in the `ParameterOptions` list.
// This value can be changed dynamically in the user interface.
std::optional<std::string> parameter;
// [[codegen::verbatim(RangeInfo.description)]]
@@ -216,7 +237,7 @@ ColorMappingComponent::ColorMappingComponent()
: properties::PropertyOwner({ "ColorMapping", "Color Mapping", "" })
, enabled(EnabledInfo, true)
, invert(InvertColorMapInfo, false)
, dataColumn(ParameterInfo, properties::OptionProperty::DisplayType::Dropdown)
, dataColumn(ParameterInfo)
, colorMapFile(FileInfo)
, valueRange(RangeInfo, glm::vec2(0.f))
, setRangeFromData(SetRangeFromDataInfo)
@@ -318,9 +339,7 @@ ColorMappingComponent::ColorMappingComponent(const ghoul::Dictionary& dictionary
_hasBelowRangeColorInAsset = true;
}
if (p.file.has_value()) {
colorMapFile = p.file->string();
}
colorMapFile = p.file.string();
invert = p.invert.value_or(invert);
}

View File

@@ -42,13 +42,20 @@ namespace {
openspace::properties::Property::Visibility::AdvancedUser
};
// This is the base class of all `LightSource` types, which are components that can be
// added to certain `Renderable` types to add lighting effects.
//
// A `LightSource`, in this case, is just a table that describes properties such as
// the location of the light. It _does not physically exist in the scene_, and the
// table of parameters have to be added to each `Renderable` that should be affected
// by the light source. This is commonly done by exporting a light source table from
// an Asset file that represents an illuminating object in the scene, such as the Sun
// in our solar system.
struct [[codegen::Dictionary(LightSource)]] Parameters {
// The type of the light source that is described in this element. The available
// types of light sources depend on the configuration of the application and can
// be written to disk on application startup into the FactoryDocumentation
std::string type [[codegen::annotation("Must name a valid LightSource type")]];
// The type of light source that is described in this element.
std::string type [[codegen::annotation("Must name a valid `LightSource` type")]];
// The identifier of the light source
// The identifier of the light source.
std::string identifier [[codegen::identifier()]];
// [[codegen::verbatim(EnabledInfo.description)]]

View File

@@ -623,6 +623,15 @@ void convertVersion12to13(nlohmann::json& profile) {
} // namespace version12
namespace version13 {
void convertVersion13to14(nlohmann::json& profile) {
// Version 1.4 introduced the ui panel view
profile["version"] = Profile::Version{ 1, 4 };
}
} // namespace version13
Profile::ParsingError::ParsingError(Severity severity_, std::string msg)
: ghoul::RuntimeError(std::move(msg), "profile")
, severity(severity_)
@@ -737,6 +746,9 @@ std::string Profile::serialize() const {
if (!additionalScripts.empty()) {
r["additional_scripts"] = additionalScripts;
}
if (!uiPanelVisibility.empty()) {
r["panel_visibility"] = uiPanelVisibility;
}
return r.dump(2);
}
@@ -779,6 +791,11 @@ Profile::Profile(const std::filesystem::path& path) {
profile["version"].get_to(version);
}
if (version.major == 1 && version.minor == 3) {
version13::convertVersion13to14(profile);
profile["version"].get_to(version);
}
if (profile.find("modules") != profile.end()) {
profile["modules"].get_to(modules);
@@ -827,6 +844,9 @@ Profile::Profile(const std::filesystem::path& path) {
if (profile.find("additional_scripts") != profile.end()) {
profile["additional_scripts"].get_to(additionalScripts);
}
if (profile.find("panel_visibility") != profile.end()) {
profile["panel_visibility"].get_to(uiPanelVisibility);
}
}
catch (const nlohmann::json::exception& e) {
std::string err = e.what();

View File

@@ -32,6 +32,7 @@
#include <openspace/scene/profile.h>
#include <openspace/scene/sceneinitializer.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/lua/lua_helper.h>
#include <stack>

View File

@@ -195,7 +195,7 @@ namespace {
};
constexpr openspace::properties::Property::PropertyInfo GuiFocusableInfo = {
"IsFocusable",
"Focusable",
"Focusable Hint",
"This value serves as a hint to determine if it makes sense to focus the camera "
"on this scene graph node. It only serves as a hint and does not actually "
@@ -924,7 +924,7 @@ void SceneGraphNode::renderDebugSphere(const Camera& camera, double size,
void SceneGraphNode::setParent(SceneGraphNode& parent) {
ghoul_assert(_parent != nullptr, "Node must be attached to a parent");
parent.attachChild(_parent->detachChild(*this));
_parent = &parent;
}
void SceneGraphNode::attachChild(ghoul::mm_unique_ptr<SceneGraphNode> child) {

View File

@@ -0,0 +1,116 @@
{
"version": { "major": 1, "minor": 0 },
"meta": {
"name": "name",
"version": "version",
"description": "description",
"author": "author",
"url": "url",
"license": "license"
},
"modules": [
{ "name": "abs-module" },
{
"name": "def-module",
"loadedInstruction": "instr"
},
{
"name": "ghi-module",
"notLoadedInstruction": "not_instr"
},
{
"name": "jkl-module",
"loadedInstruction": "instr",
"notLoadedInstruction": "not_instr"
}
],
"assets": [
"scene/solarsystem/planets/earth/earth",
"scene/solarsystem/planets/earth/satellites/satellites",
"folder1/folder2/asset",
"folder3/folder4/asset2",
"folder5/folder6/asset3"
],
"properties": [
{
"type": "setPropertyValue",
"name": "{earth_satellites}.Renderable.Enabled",
"value": "false"
},
{
"type": "setPropertyValue",
"name": "property_name_1",
"value": "property_value_1"
},
{
"type": "setPropertyValue",
"name": "property_name_2",
"value": "property_value_2"
},
{
"type": "setPropertyValue",
"name": "property_name_3",
"value": "property_value_3"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_4",
"value": "property_value_4"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_5",
"value": "property_value_5"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_6",
"value": "property_value_6"
}
],
"keybindings": [
{
"key": "T",
"documentation": "T documentation",
"name": "T name",
"gui_path": "T Gui-Path",
"is_local": true,
"script": "T script"
},
{
"key": "U",
"documentation": "U documentation",
"name": "U name",
"gui_path": "U Gui-Path",
"is_local": false,
"script": "U script"
},
{
"key": "CTRL+V",
"documentation": "CTRL+V documentation",
"name": "CTRL+V name",
"gui_path": "CTRL+V Gui-Path",
"is_local": false,
"script": "CTRL+V script"
}
],
"time": {
"type": "relative",
"value": "-1d"
},
"camera": {
"type": "goToGeo",
"anchor": "Earth",
"latitude": 58.5877,
"longitude": 16.1924,
"altitude": 2.0e+07
},
"mark_nodes": [
"Earth", "Mars", "Moon", "Sun"
],
"additional_scripts": [
"script-1",
"script-2",
"script-3"
]
}

View File

@@ -0,0 +1,130 @@
{
"version": { "major": 1, "minor": 1 },
"meta": {
"name": "name",
"version": "version",
"description": "description",
"author": "author",
"url": "url",
"license": "license"
},
"modules": [
{ "name": "abs-module" },
{
"name": "def-module",
"loadedInstruction": "instr"
},
{
"name": "ghi-module",
"notLoadedInstruction": "not_instr"
},
{
"name": "jkl-module",
"loadedInstruction": "instr",
"notLoadedInstruction": "not_instr"
}
],
"assets": [
"scene/solarsystem/planets/earth/earth",
"scene/solarsystem/planets/earth/satellites/satellites",
"folder1/folder2/asset",
"folder3/folder4/asset2",
"folder5/folder6/asset3"
],
"properties": [
{
"type": "setPropertyValue",
"name": "{earth_satellites}.Renderable.Enabled",
"value": "false"
},
{
"type": "setPropertyValue",
"name": "property_name_1",
"value": "property_value_1"
},
{
"type": "setPropertyValue",
"name": "property_name_2",
"value": "property_value_2"
},
{
"type": "setPropertyValue",
"name": "property_name_3",
"value": "property_value_3"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_4",
"value": "property_value_4"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_5",
"value": "property_value_5"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_6",
"value": "property_value_6"
}
],
"actions": [
{
"identifier": "profile.keybind.0",
"documentation": "T documentation",
"name": "T name",
"gui_path": "T Gui-Path",
"is_local": true,
"script": "T script"
},
{
"identifier": "profile.keybind.1",
"documentation": "U documentation",
"name": "U name",
"gui_path": "U Gui-Path",
"is_local": false,
"script": "U script"
},
{
"identifier": "profile.keybind.2",
"documentation": "CTRL+V documentation",
"name": "CTRL+V name",
"gui_path": "CTRL+V Gui-Path",
"is_local": false,
"script": "CTRL+V script"
}
],
"keybindings": [
{
"action": "profile.keybind.0",
"key": "T"
},
{
"action": "profile.keybind.1",
"key": "U"
},
{
"action": "profile.keybind.2",
"key": "CTRL+V"
}
],
"time": {
"type": "relative",
"value": "-1d"
},
"camera": {
"type": "goToGeo",
"anchor": "Earth",
"latitude": 58.5877,
"longitude": 16.1924,
"altitude": 2.0e+07
},
"mark_nodes": [
"Earth", "Mars", "Moon", "Sun"
],
"additional_scripts": [
"script-1",
"script-2",
"script-3"
]
}

View File

@@ -0,0 +1,131 @@
{
"version": { "major": 1, "minor": 2 },
"meta": {
"name": "name",
"version": "version",
"description": "description",
"author": "author",
"url": "url",
"license": "license"
},
"modules": [
{ "name": "abs-module" },
{
"name": "def-module",
"loadedInstruction": "instr"
},
{
"name": "ghi-module",
"notLoadedInstruction": "not_instr"
},
{
"name": "jkl-module",
"loadedInstruction": "instr",
"notLoadedInstruction": "not_instr"
}
],
"assets": [
"scene/solarsystem/planets/earth/earth",
"scene/solarsystem/planets/earth/satellites/satellites",
"folder1/folder2/asset",
"folder3/folder4/asset2",
"folder5/folder6/asset3"
],
"properties": [
{
"type": "setPropertyValue",
"name": "{earth_satellites}.Renderable.Enabled",
"value": "false"
},
{
"type": "setPropertyValue",
"name": "property_name_1",
"value": "property_value_1"
},
{
"type": "setPropertyValue",
"name": "property_name_2",
"value": "property_value_2"
},
{
"type": "setPropertyValue",
"name": "property_name_3",
"value": "property_value_3"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_4",
"value": "property_value_4"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_5",
"value": "property_value_5"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_6",
"value": "property_value_6"
}
],
"actions": [
{
"identifier": "profile.keybind.0",
"documentation": "T documentation",
"name": "T name",
"gui_path": "T Gui-Path",
"is_local": true,
"script": "T script"
},
{
"identifier": "profile.keybind.1",
"documentation": "U documentation",
"name": "U name",
"gui_path": "U Gui-Path",
"is_local": false,
"script": "U script"
},
{
"identifier": "profile.keybind.2",
"documentation": "CTRL+V documentation",
"name": "CTRL+V name",
"gui_path": "CTRL+V Gui-Path",
"is_local": false,
"script": "CTRL+V script"
}
],
"keybindings": [
{
"action": "profile.keybind.0",
"key": "T"
},
{
"action": "profile.keybind.1",
"key": "U"
},
{
"action": "profile.keybind.2",
"key": "CTRL+V"
}
],
"time": {
"type": "relative",
"value": "-1d",
"is_paused": false
},
"camera": {
"type": "goToGeo",
"anchor": "Earth",
"latitude": 58.5877,
"longitude": 16.1924,
"altitude": 2.0e+07
},
"mark_nodes": [
"Earth", "Mars", "Moon", "Sun"
],
"additional_scripts": [
"script-1",
"script-2",
"script-3"
]
}

View File

@@ -0,0 +1,131 @@
{
"version": { "major": 1, "minor": 3 },
"meta": {
"name": "name",
"version": "version",
"description": "description",
"author": "author",
"url": "url",
"license": "license"
},
"modules": [
{ "name": "abs-module" },
{
"name": "def-module",
"loadedInstruction": "instr"
},
{
"name": "ghi-module",
"notLoadedInstruction": "not_instr"
},
{
"name": "jkl-module",
"loadedInstruction": "instr",
"notLoadedInstruction": "not_instr"
}
],
"assets": [
"scene/solarsystem/planets/earth/earth",
"scene/solarsystem/planets/earth/satellites/satellites",
"folder1/folder2/asset",
"folder3/folder4/asset2",
"folder5/folder6/asset3"
],
"properties": [
{
"type": "setPropertyValue",
"name": "{earth_satellites}.Renderable.Enabled",
"value": "false"
},
{
"type": "setPropertyValue",
"name": "property_name_1",
"value": "property_value_1"
},
{
"type": "setPropertyValue",
"name": "property_name_2",
"value": "property_value_2"
},
{
"type": "setPropertyValue",
"name": "property_name_3",
"value": "property_value_3"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_4",
"value": "property_value_4"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_5",
"value": "property_value_5"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_6",
"value": "property_value_6"
}
],
"actions": [
{
"identifier": "profile.keybind.0",
"documentation": "T documentation",
"name": "T name",
"gui_path": "T Gui-Path",
"is_local": true,
"script": "T script"
},
{
"identifier": "profile.keybind.1",
"documentation": "U documentation",
"name": "U name",
"gui_path": "U Gui-Path",
"is_local": false,
"script": "U script"
},
{
"identifier": "profile.keybind.2",
"documentation": "CTRL+V documentation",
"name": "CTRL+V name",
"gui_path": "CTRL+V Gui-Path",
"is_local": false,
"script": "CTRL+V script"
}
],
"keybindings": [
{
"action": "profile.keybind.0",
"key": "T"
},
{
"action": "profile.keybind.1",
"key": "U"
},
{
"action": "profile.keybind.2",
"key": "CTRL+V"
}
],
"time": {
"type": "relative",
"value": "-1d",
"is_paused": false
},
"camera": {
"type": "goToGeo",
"anchor": "Earth",
"latitude": 58.5877,
"longitude": 16.1924,
"altitude": 2.0e+07
},
"mark_nodes": [
"Earth", "Mars", "Moon", "Sun"
],
"additional_scripts": [
"script-1",
"script-2",
"script-3"
]
}

View File

@@ -0,0 +1,131 @@
{
"version": { "major": 1, "minor": 4 },
"meta": {
"name": "name",
"version": "version",
"description": "description",
"author": "author",
"url": "url",
"license": "license"
},
"modules": [
{ "name": "abs-module" },
{
"name": "def-module",
"loadedInstruction": "instr"
},
{
"name": "ghi-module",
"notLoadedInstruction": "not_instr"
},
{
"name": "jkl-module",
"loadedInstruction": "instr",
"notLoadedInstruction": "not_instr"
}
],
"assets": [
"scene/solarsystem/planets/earth/earth",
"scene/solarsystem/planets/earth/satellites/satellites",
"folder1/folder2/asset",
"folder3/folder4/asset2",
"folder5/folder6/asset3"
],
"properties": [
{
"type": "setPropertyValue",
"name": "{earth_satellites}.Renderable.Enabled",
"value": "false"
},
{
"type": "setPropertyValue",
"name": "property_name_1",
"value": "property_value_1"
},
{
"type": "setPropertyValue",
"name": "property_name_2",
"value": "property_value_2"
},
{
"type": "setPropertyValue",
"name": "property_name_3",
"value": "property_value_3"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_4",
"value": "property_value_4"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_5",
"value": "property_value_5"
},
{
"type": "setPropertyValueSingle",
"name": "property_name_6",
"value": "property_value_6"
}
],
"actions": [
{
"identifier": "profile.keybind.0",
"documentation": "T documentation",
"name": "T name",
"gui_path": "T Gui-Path",
"is_local": true,
"script": "T script"
},
{
"identifier": "profile.keybind.1",
"documentation": "U documentation",
"name": "U name",
"gui_path": "U Gui-Path",
"is_local": false,
"script": "U script"
},
{
"identifier": "profile.keybind.2",
"documentation": "CTRL+V documentation",
"name": "CTRL+V name",
"gui_path": "CTRL+V Gui-Path",
"is_local": false,
"script": "CTRL+V script"
}
],
"keybindings": [
{
"action": "profile.keybind.0",
"key": "T"
},
{
"action": "profile.keybind.1",
"key": "U"
},
{
"action": "profile.keybind.2",
"key": "CTRL+V"
}
],
"time": {
"type": "relative",
"value": "-1d",
"is_paused": false
},
"camera": {
"type": "goToGeo",
"anchor": "Earth",
"latitude": 58.5877,
"longitude": 16.1924,
"altitude": 2.0e+07
},
"mark_nodes": [
"Earth", "Mars", "Moon", "Sun"
],
"additional_scripts": [
"script-1",
"script-2",
"script-3"
]
}

View File

@@ -861,6 +861,98 @@ TEST_CASE("Save settings to profile", "[profile]") {
CHECK(profile.time->value == "current-time");
}
TEST_CASE("Version 1.0 -> 1.1", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_10.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_11.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.0 -> 1.2", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_10.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_12.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.0 -> 1.3", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_10.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_13.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.0 -> 1.4", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_10.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_14.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.1 -> 1.2", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_11.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_12.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.1 -> 1.3", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_11.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_13.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.1 -> 1.4", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_11.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_14.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.2 -> 1.3", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_12.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_13.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.2 -> 1.4", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_12.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_14.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
TEST_CASE("Version 1.3 -> 1.4", "[profile]") {
constexpr std::string_view Src = "${TESTDIR}/profile/conversion/version_13.profile";
constexpr std::string_view Dest = "${TESTDIR}/profile/conversion/version_14.profile";
Profile src = Profile(absPath(Src));
Profile dst = Profile(absPath(Dest));
CHECK(src == dst);
}
//
// Error states
//