diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d4304ad6d..32390f3be2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/apps/OpenSpace/ext/launcher/CMakeLists.txt b/apps/OpenSpace/ext/launcher/CMakeLists.txt index e3811a7521..64f8aa3e70 100644 --- a/apps/OpenSpace/ext/launcher/CMakeLists.txt +++ b/apps/OpenSpace/ext/launcher/CMakeLists.txt @@ -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 diff --git a/apps/OpenSpace/ext/launcher/include/launcherwindow.h b/apps/OpenSpace/ext/launcher/include/launcherwindow.h index cbe15c696f..c8b63783de 100644 --- a/apps/OpenSpace/ext/launcher/include/launcherwindow.h +++ b/apps/OpenSpace/ext/launcher/include/launcherwindow.h @@ -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; diff --git a/apps/OpenSpace/ext/launcher/include/profile/profileedit.h b/apps/OpenSpace/ext/launcher/include/profile/profileedit.h index 55ea52c6a2..3aa007bfa9 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/profileedit.h +++ b/apps/OpenSpace/ext/launcher/include/profile/profileedit.h @@ -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; }; diff --git a/apps/OpenSpace/ext/launcher/include/profile/uipanelsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/uipanelsdialog.h new file mode 100644 index 0000000000..d6ea979a74 --- /dev/null +++ b/apps/OpenSpace/ext/launcher/include/profile/uipanelsdialog.h @@ -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 + +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* uiPanels); + +private slots: + void parseSelections(); + +private: + std::map* _uiPanels; + std::map _checkboxToId; +}; + +#endif // __OPENSPACE_UI_LAUNCHER___UIPANELSDIALOG___H__ diff --git a/apps/OpenSpace/ext/launcher/include/sgctedit/windowcontrol.h b/apps/OpenSpace/ext/launcher/include/sgctedit/windowcontrol.h index 4fe3a35baa..a60b0fe86f 100644 --- a/apps/OpenSpace/ext/launcher/include/sgctedit/windowcontrol.h +++ b/apps/OpenSpace/ext/launcher/include/sgctedit/windowcontrol.h @@ -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; }; diff --git a/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp b/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp index 6d39cb20ed..1b562035f6 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp @@ -35,6 +35,7 @@ #include "profile/modulesdialog.h" #include "profile/propertiesdialog.h" #include "profile/timedialog.h" +#include "profile/uipanelsdialog.h" #include #include #include @@ -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")); diff --git a/apps/OpenSpace/ext/launcher/src/profile/uipanelsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/uipanelsdialog.cpp new file mode 100644 index 0000000000..38d3f38e04 --- /dev/null +++ b/apps/OpenSpace/ext/launcher/src/profile/uipanelsdialog.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 loadPanels() { + std::ifstream panelFile = std::ifstream(absPath(DefaultPanelPath)); + const std::string panelContent = std::string( + std::istreambuf_iterator(panelFile), + std::istreambuf_iterator() + ); + const nlohmann::json panel = nlohmann::json::parse(panelContent); + std::map panels = panel.get>(); + + std::vector 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* uiPanels) + : QDialog(parent) + , _uiPanels(uiPanels) +{ + setWindowTitle("User Interface Panels"); + + std::vector 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(); +} diff --git a/apps/OpenSpace/ext/launcher/src/sgctedit/displaywindowunion.cpp b/apps/OpenSpace/ext/launcher/src/sgctedit/displaywindowunion.cpp index 62ea7a1c09..5d69a00042 100644 --- a/apps/OpenSpace/ext/launcher/src/sgctedit/displaywindowunion.cpp +++ b/apps/OpenSpace/ext/launcher/src/sgctedit/displaywindowunion.cpp @@ -194,7 +194,7 @@ void DisplayWindowUnion::initialize(const std::vector& 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& 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); diff --git a/apps/OpenSpace/ext/launcher/src/sgctedit/windowcontrol.cpp b/apps/OpenSpace/ext/launcher/src/sgctedit/windowcontrol.cpp index 96fa09ded0..0a15189d00 100644 --- a/apps/OpenSpace/ext/launcher/src/sgctedit/windowcontrol.cpp +++ b/apps/OpenSpace/ext/launcher/src/sgctedit/windowcontrol.cpp @@ -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(_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(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(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(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() { diff --git a/apps/OpenSpace/ext/sgct b/apps/OpenSpace/ext/sgct index 1418d9beb2..7ac1bd4891 160000 --- a/apps/OpenSpace/ext/sgct +++ b/apps/OpenSpace/ext/sgct @@ -1 +1 @@ -Subproject commit 1418d9beb25ad325f9e9e831acee22317e6b2578 +Subproject commit 7ac1bd4891efb2e34f6574a26e591d4a984e8e6b diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index d403c4fe6a..ba64091dbb 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -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; diff --git a/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable.asset b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable.asset new file mode 100644 index 0000000000..636a0d96b8 --- /dev/null +++ b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable.asset @@ -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) diff --git a/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_axes.asset b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_axes.asset new file mode 100644 index 0000000000..48e264e518 --- /dev/null +++ b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_axes.asset @@ -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) diff --git a/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_model-distance.asset b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_model-distance.asset new file mode 100644 index 0000000000..ec19a4bd1b --- /dev/null +++ b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_model-distance.asset @@ -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) diff --git a/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_model.asset b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_model.asset new file mode 100644 index 0000000000..19abddfac3 --- /dev/null +++ b/data/assets/examples/screenspacerenderable/screenspacerenderablerenderable/renderablerenderable_model.asset @@ -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) diff --git a/data/assets/nightsky/light_pollution.asset b/data/assets/nightsky/light_pollution.asset index e4f502c0c8..0232e76ea3 100644 --- a/data/assets/nightsky/light_pollution.asset +++ b/data/assets/nightsky/light_pollution.asset @@ -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", diff --git a/data/assets/scene/digitaluniverse/allsky_hydrogenalpha.asset b/data/assets/scene/digitaluniverse/allsky_hydrogenalpha.asset index caf7cfde10..fc8419a10b 100644 --- a/data/assets/scene/digitaluniverse/allsky_hydrogenalpha.asset +++ b/data/assets/scene/digitaluniverse/allsky_hydrogenalpha.asset @@ -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", diff --git a/data/assets/scene/digitaluniverse/backgroundradiation.asset b/data/assets/scene/digitaluniverse/backgroundradiation.asset index e1a753a756..8da0862c81 100644 --- a/data/assets/scene/digitaluniverse/backgroundradiation.asset +++ b/data/assets/scene/digitaluniverse/backgroundradiation.asset @@ -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", diff --git a/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset b/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset index 7af1e3f312..82fd1809dd 100644 --- a/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset +++ b/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset @@ -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", diff --git a/data/assets/scene/milkyway/milkyway/eso.asset b/data/assets/scene/milkyway/milkyway/eso.asset index 285e1c75cf..bc86af0c8f 100644 --- a/data/assets/scene/milkyway/milkyway/eso.asset +++ b/data/assets/scene/milkyway/milkyway/eso.asset @@ -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)", diff --git a/data/assets/scene/solarsystem/planets/saturn/titan/layers/colorlayers/cassini_sar_hisar_global_mosaic_351m_sweden.asset b/data/assets/scene/solarsystem/planets/saturn/titan/layers/colorlayers/cassini_sar_hisar_global_mosaic_351m_sweden.asset new file mode 100644 index 0000000000..84df419cd5 --- /dev/null +++ b/data/assets/scene/solarsystem/planets/saturn/titan/layers/colorlayers/cassini_sar_hisar_global_mosaic_351m_sweden.asset @@ -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" +} diff --git a/data/assets/scene/solarsystem/planets/saturn/titan/layers/colorlayers/cassini_sar_hisar_global_mosaic_351m_sweden.wms b/data/assets/scene/solarsystem/planets/saturn/titan/layers/colorlayers/cassini_sar_hisar_global_mosaic_351m_sweden.wms new file mode 100644 index 0000000000..2eddf8c8a3 --- /dev/null +++ b/data/assets/scene/solarsystem/planets/saturn/titan/layers/colorlayers/cassini_sar_hisar_global_mosaic_351m_sweden.wms @@ -0,0 +1,20 @@ + + + http://wms.itn.liu.se/Titan/HiSAR_Global_Mosaic_351m/tile/${z}/${y}/${x} + + + -180.0 + 90.0 + 180.0 + -90.0 + 46080 + 23040 + 7 + top + + EPSG:4326 + 512 + 512 + 1 + 10 + diff --git a/data/assets/util/calibration.asset b/data/assets/util/calibration.asset new file mode 100644 index 0000000000..0e2a879a22 --- /dev/null +++ b/data/assets/util/calibration.asset @@ -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" +} diff --git a/data/profiles/calibrator.profile b/data/profiles/calibrator.profile new file mode 100644 index 0000000000..f6a6a210ad --- /dev/null +++ b/data/profiles/calibrator.profile @@ -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 + } +} \ No newline at end of file diff --git a/data/web/default_ui_panels.json b/data/web/default_ui_panels.json new file mode 100644 index 0000000000..020700b918 --- /dev/null +++ b/data/web/default_ui_panels.json @@ -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 } +} diff --git a/ext/date b/ext/date index 569b2d6785..5bdb7e6f31 160000 --- a/ext/date +++ b/ext/date @@ -1 +1 @@ -Subproject commit 569b2d678547985d0ead3b73ee93a28919000887 +Subproject commit 5bdb7e6f31fac909c090a46dbd9fea27b6e609a4 diff --git a/ext/ghoul b/ext/ghoul index 1ee8a47a1c..8d73afb420 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 1ee8a47a1cb861efd4d446e34142cd45a53819e6 +Subproject commit 8d73afb420eee59881df53ee3dba998fbeaec595 diff --git a/include/openspace/properties/misc/optionproperty.h b/include/openspace/properties/misc/optionproperty.h index 6507d43a45..a833bf4830 100644 --- a/include/openspace/properties/misc/optionproperty.h +++ b/include/openspace/properties/misc/optionproperty.h @@ -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::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