mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-05 11:09:37 -06:00
Merge branch 'master' into feature/horizons-framework
* Solve conflict in launcher/CmakeLists.txt
This commit is contained in:
13
.gitattributes
vendored
13
.gitattributes
vendored
@@ -1,5 +1,18 @@
|
||||
* text=auto
|
||||
|
||||
# Correct GitHub's language detection shenanigans
|
||||
|
||||
# Asset files are not Unity, but Lua instead
|
||||
*.asset linguist-language=Lua
|
||||
# We have some SPICE frame kernels that get misclassified as code
|
||||
*.tf -linguist-detectable
|
||||
# We don't want to index the GDAL csv and xml files
|
||||
modules/globebrowsing/gdal_data/* linguist-vendored
|
||||
# No need to index any external files
|
||||
*/ext/* linguist-vendored
|
||||
# No C allowed
|
||||
*.h linguist-language=C++
|
||||
|
||||
# GitHub files
|
||||
ATTRIBUTION text
|
||||
AUTHORS text
|
||||
|
||||
@@ -28,8 +28,8 @@ project(OpenSpace)
|
||||
|
||||
set(OPENSPACE_VERSION_MAJOR 0)
|
||||
set(OPENSPACE_VERSION_MINOR 17)
|
||||
set(OPENSPACE_VERSION_PATCH -1)
|
||||
set(OPENSPACE_VERSION_STRING "Beta-10 [RC1]")
|
||||
set(OPENSPACE_VERSION_PATCH 1)
|
||||
set(OPENSPACE_VERSION_STRING "Beta-10")
|
||||
|
||||
set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}")
|
||||
set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake")
|
||||
@@ -132,8 +132,6 @@ if (MSVC)
|
||||
set(GHOUL_OPTIMIZATION_ENABLE_OTHER_OPTIMIZATIONS ${OPENSPACE_OPTIMIZATION_ENABLE_OTHER_OPTIMIZATIONS} CACHE BOOL "" FORCE)
|
||||
endif ()
|
||||
|
||||
option(OPENSPACE_WITH_ABUFFER_RENDERER "Compile ABuffer Renderer" OFF)
|
||||
|
||||
if (UNIX)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -stdlib=libc++")
|
||||
|
||||
@@ -4,17 +4,17 @@ Emil Axelsson
|
||||
Kalle Bladin
|
||||
Jonathas Costa
|
||||
Gene Payne
|
||||
Jonas Strandstedt
|
||||
Michal Marcinkowski
|
||||
Elon Olsson
|
||||
Emma Broman
|
||||
Jonas Strandstedt
|
||||
Micah Acinapura
|
||||
Michal Marcinkowski
|
||||
Malin Ejdbo
|
||||
Elon Olsson
|
||||
Joakim Kilby
|
||||
Lovisa Hassler
|
||||
Mikael Petterson
|
||||
Erik Sundén
|
||||
Stefan Lindblad
|
||||
Malin Ejdbo
|
||||
Corrie Roe
|
||||
Eric Myers
|
||||
|
||||
|
||||
50
README.md
50
README.md
@@ -1,13 +1,47 @@
|
||||
[OpenSpace](http://openspaceproject.com) is an open source, non-commercial, and freely available interactive data visualization software designed to visualize the entire known universe and portray our ongoing efforts to investigate the cosmos. Bringing the latest techniques from data visualization research to the general public, OpenSpace supports interactive presentation of dynamic data from observations, simulations, and space mission planning and operations. The software works on multiple operating systems (Windows, Linux, MacOS) with an extensible architecture powering high resolution tiled displays and planetarium domes, making use of the latest graphic card technologies for rapid data throughput. In addition, OpenSpace enables simultaneous connections across the globe creating opportunity for shared experiences among audiences worldwide.
|
||||

|
||||
[OpenSpace](http://openspaceproject.com) is an open source, non-commercial, and freely available interactive data visualization software designed to visualize the entire known universe and portray our ongoing efforts to investigate the cosmos. Bringing the latest techniques from data visualization research to the general public, OpenSpace supports interactive presentation of dynamic data from observations, simulations, and space mission planning and operations. The software works on multiple operating systems (Windows, Linux, MacOS) with an extensible architecture capable of powering both personal computers and also high resolution tiled displays and planetarium domes. In addition, OpenSpace enables simultaneous connections across the globe creating opportunity for shared experiences among audiences worldwide. The target audience of the software reaches from the general public who wishes to explore our universe, enthusiasts interested in hacking the underlying components in OpenSpace to create unique experiences, informal science institutions wishing to create a low-cost, yet powerful exhibition piece, but also scientists desiring to visualize their datasets in a contextualized, powerful software.
|
||||
|
||||
The project stems from the same academic collaboration between Sweden’s [Linköping University](https://www.liu.se) (LiU) and the [American Museum of Natural History](https://www.amnh.org) (AMNH) that led to the creation of Uniview and its parent company [SCISS](http://sciss.se). Development of the software began several years ago through a close collaboration with NASA Goddard’s [Community Coordinated Modeling Center](https://ccmc.gsfc.nasa.gov) (CCMC) to model space weather forecasting and continued with visualizations of NASA’s New Horizons mission to Pluto and ESA’s Rosetta mission. This promising set of preliminary work provided a foundation for recent NASA funding, which has extended the collaboration to include the University of Utah’s [Scientific Computing and Imaging](https://www.sci.utah.edu) (SCI) Institute, [New York University](https://www.nyu.edu)’s Tandon School of Engineering, multiple informal science institutions across the United States, and multiple, international vendors. Current areas of focus within OpenSpace include:
|
||||
[](LICENSE)
|
||||
[](https://www.openspaceproject.com/installation)
|
||||

|
||||
|
||||
- Visualization of dynamic simulations via interactive volumetric rendering, as a priority for communicating research in astrophysics.
|
||||
- Utilization of NASA’s SPICE observational geometry system with its Planetary Data Service (PDS) to enable space mission visualizations that reveal how missions are designed to gather science.
|
||||
- Globe browsing techniques across spatial and temporal scales to examine scientific campaigns on multiple planets, including close up surface exploration.
|
||||
[](https://doi.org/10.1109/TVCG.2019.2934259)
|
||||
[](https://doi.org/10.1109/TVCG.2017.2743958)
|
||||
|
||||
OpenSpace requires graphics support for [OpenGL](https://www.opengl.org/) version 3.3.
|
||||

|
||||

|
||||
|
||||
This repository contains the source code and example scenes for OpenSpace, but does not contain any data. To build and install the client, we refer to the [OpenSpace Wiki](http://wiki.openspaceproject.com/), specifically [building](http://wiki.openspaceproject.com/docs/developers/compiling/general) for [Windows](http://wiki.openspaceproject.com/docs/developers/compiling/windows), [Linux (Ubuntu)](http://wiki.openspaceproject.com/docs/developers/compiling/ubuntu), and [MacOS](http://wiki.openspaceproject.com/docs/developers/compiling/macos). Required preexisting dependencies are: [Boost](http://www.boost.org/) and [Qt](http://www.qt.io/download). Feel free to create issues for missing features, bug reports, or compile problems or contact us via [email](mailto:alexander.bock@me.com?subject=OpenSpace:).
|
||||

|
||||
|
||||
Regarding any issues, you are very welcome on our [Slack support channel](https://openspacesupport.slack.com) to which you can freely [sign-up](https://join.slack.com/t/openspacesupport/shared_invite/zt-37niq6y9-T0JaCIk4UoFLI4VF5U9Vsw).
|
||||
# Background
|
||||
OpenSpace started as a collaboration between Sweden's [Linköping University](https://scivis.github.io) (LiU) and the [American Museum of Natural History](https://www.amnh.org) (AMNH). Development of the software began several years ago through a close collaboration with NASA Goddard's [Community Coordinated Modeling Center](https://ccmc.gsfc.nasa.gov) (CCMC) to model space weather forecasting and continued with visualizations of NASA's New Horizons mission to Pluto and ESA's Rosetta mission to 67P/Churyumov–Gerasimenko. This promising set of preliminary work provided a foundation for continued funding from NASA, the Swedish eScience Research Centre, and the Knut and Alice Wallenberg foundation, which has extended the collaboration to include the University of Utah's [Scientific Computing and Imaging](https://www.sci.utah.edu) (SCI) Institute, [New York University](https://www.nyu.edu)'s Tandon School of Engineering, multiple informal science institutions across the world, and multiple, international vendors.
|
||||
|
||||

|
||||
|
||||
# Features
|
||||
Some of the high-level features supported in OpenSpace are:
|
||||
- AMNH's Digital Universe catalog of extrasolar datasets (stars, galaxies, quasars, ...)
|
||||
- High-resolution planetary images for major objects in the solar system (Earth, Moon, Mars, Venus, ...)
|
||||
- Animated 3D models representing space missions (ISS, New Horizons, JWST, ...)
|
||||
- Support for custom profiles with arbitrary user-defined content
|
||||
- Ability to drive any type of display environment (flat screen, multi-projector, planetariums, ...)
|
||||
- Lua and JavaScript interface into the engine allowing highly customized controls
|
||||
- Native support to export an interactive sessions as individual frames for video export
|
||||
- much much more (see our [Changelog](http://wiki.openspaceproject.com/docs/general/releases))
|
||||
|
||||
OpenSpace requires at least support for [OpenGL](https://www.opengl.org/) version 3.3, some custom components require at least version 4.2.
|
||||
|
||||

|
||||
|
||||
# Getting Started
|
||||
This repository contains the source code and example profiles for OpenSpace, but does not contain any data. To build and install the application, please check out the [OpenSpace Wiki](http://wiki.openspaceproject.com/). Here, you will find two pages, a [build instruction](http://wiki.openspaceproject.com/docs/developers/compiling/general) for all operating systems and then additional instructions for [Windows](http://wiki.openspaceproject.com/docs/developers/compiling/windows), [Linux (Ubuntu)](http://wiki.openspaceproject.com/docs/developers/compiling/ubuntu), and [MacOS](http://wiki.openspaceproject.com/docs/developers/compiling/macos).
|
||||
|
||||
Requirements for compiling are:
|
||||
- CMake version 3.10 or above
|
||||
- C++ compiler supporting C++17 (MSVC 16.10, GCC9, Clang10)
|
||||
- [Boost](http://www.boost.org/)
|
||||
- [Qt](http://www.qt.io/download)
|
||||
|
||||
Feel free to create issues for missing features, bug reports, or compile problems or contact us via [email](mailto:openspace@amnh.org?subject=OpenSpace:). Regarding any issues, you are very welcome on our [Slack support channel](https://openspacesupport.slack.com) to which you can freely [sign-up](https://join.slack.com/t/openspacesupport/shared_invite/zt-37niq6y9-T0JaCIk4UoFLI4VF5U9Vsw).
|
||||
|
||||

|
||||
|
||||
@@ -27,6 +27,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/set_openspace_compile_settings.cmake)
|
||||
set(HEADER_FILES
|
||||
include/filesystemaccess.h
|
||||
include/launcherwindow.h
|
||||
include/profile/actiondialog.h
|
||||
include/profile/additionalscriptsdialog.h
|
||||
include/profile/assetsdialog.h
|
||||
include/profile/assetedit.h
|
||||
@@ -35,7 +36,6 @@ set(HEADER_FILES
|
||||
include/profile/cameradialog.h
|
||||
include/profile/deltatimesdialog.h
|
||||
include/profile/horizonsdialog.h
|
||||
include/profile/keybindingsdialog.h
|
||||
include/profile/scriptlogdialog.h
|
||||
include/profile/line.h
|
||||
include/profile/marknodesdialog.h
|
||||
@@ -49,6 +49,7 @@ set(HEADER_FILES
|
||||
set(SOURCE_FILES
|
||||
src/launcherwindow.cpp
|
||||
src/filesystemaccess.cpp
|
||||
src/profile/actiondialog.cpp
|
||||
src/profile/additionalscriptsdialog.cpp
|
||||
src/profile/assetsdialog.cpp
|
||||
src/profile/assetedit.cpp
|
||||
@@ -57,7 +58,6 @@ set(SOURCE_FILES
|
||||
src/profile/cameradialog.cpp
|
||||
src/profile/deltatimesdialog.cpp
|
||||
src/profile/horizonsdialog.cpp
|
||||
src/profile/keybindingsdialog.cpp
|
||||
src/profile/scriptlogdialog.cpp
|
||||
src/profile/line.cpp
|
||||
src/profile/marknodesdialog.cpp
|
||||
@@ -68,13 +68,9 @@ set(SOURCE_FILES
|
||||
src/profile/propertiesdialog.cpp
|
||||
)
|
||||
|
||||
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
||||
find_package(Qt5 COMPONENTS Network REQUIRED)
|
||||
|
||||
set(MOC_FILES "")
|
||||
qt5_wrap_cpp(
|
||||
MOC_FILES
|
||||
set(HEADER_SOURCE
|
||||
include/launcherwindow.h
|
||||
include/profile/actiondialog.h
|
||||
include/profile/additionalscriptsdialog.h
|
||||
include/profile/assetsdialog.h
|
||||
include/profile/assetedit.h
|
||||
@@ -82,7 +78,6 @@ qt5_wrap_cpp(
|
||||
include/profile/cameradialog.h
|
||||
include/profile/deltatimesdialog.h
|
||||
include/profile/horizonsdialog.h
|
||||
include/profile/keybindingsdialog.h
|
||||
include/profile/scriptlogdialog.h
|
||||
include/profile/marknodesdialog.h
|
||||
include/profile/metadialog.h
|
||||
@@ -92,13 +87,42 @@ qt5_wrap_cpp(
|
||||
include/profile/propertiesdialog.h
|
||||
)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Widgets Network REQUIRED)
|
||||
set(MOC_FILES "")
|
||||
set(RESOURCE_FILES "")
|
||||
qt5_add_resources(RESOURCE_FILES resources/resources.qrc)
|
||||
set(LIBRARIES "")
|
||||
|
||||
if (${QT_VERSION_MAJOR} EQUAL 5)
|
||||
find_package(Qt5 COMPONENTS Core Widgets Network)
|
||||
qt5_wrap_cpp(
|
||||
MOC_FILES
|
||||
${HEADER_SOURCE}
|
||||
)
|
||||
qt5_add_resources(RESOURCE_FILES resources/resources.qrc)
|
||||
set(LIBRARIES )
|
||||
elseif (${QT_VERSION_MAJOR} EQUAL 6)
|
||||
find_package(Qt6 COMPONENTS Core Widgets Network REQUIRED)
|
||||
|
||||
qt6_wrap_cpp(
|
||||
MOC_FILES
|
||||
${HEADER_SOURCE}
|
||||
)
|
||||
qt6_add_resources(RESOURCE_FILES resources/resources.qrc)
|
||||
elseif (NOT DEFINED QT_VERSION_MAJOR)
|
||||
message(FATAL_ERROR "Unable to find Qt version")
|
||||
else ()
|
||||
message(FATAL_ERROR "Unsupported Qt version")
|
||||
endif()
|
||||
|
||||
add_library(openspace-ui-launcher STATIC ${HEADER_FILES} ${SOURCE_FILES} ${MOC_FILES} ${RESOURCE_FILES})
|
||||
set_openspace_compile_settings(openspace-ui-launcher)
|
||||
target_include_directories(openspace-ui-launcher PUBLIC include)
|
||||
target_link_libraries(openspace-ui-launcher PUBLIC Qt5::Core Qt5::Gui Qt5::Network Qt5::Widgets openspace-core)
|
||||
target_link_libraries(
|
||||
openspace-ui-launcher
|
||||
PUBLIC
|
||||
openspace-core
|
||||
Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
set(MSVC_WARNINGS
|
||||
|
||||
@@ -34,10 +34,6 @@ public:
|
||||
*
|
||||
* \param fileExtension string that defines the filter used to find files. Only
|
||||
* files with this extension will be recognized (e.g. '.xml')
|
||||
* \param approvedPaths vector or strings containing directory names to be included
|
||||
* in the search. These are directories at the base level of
|
||||
* the starting point of the search. Any sub-directories within
|
||||
* these directories will be included.
|
||||
* \param hideFileExtensions if true then file extensions will be removed from the
|
||||
* listed files in the output
|
||||
* \param useCheckboxes if true then the text output format will contain a '0' as
|
||||
@@ -45,8 +41,7 @@ public:
|
||||
* used to represent checked ('1'), uncheck ('0') or doesn't
|
||||
* exist in filesystem ('x') states.
|
||||
*/
|
||||
FileSystemAccess(std::string fileExtension,
|
||||
std::vector<std::string> approvedPaths, bool hideFileExtensions,
|
||||
FileSystemAccess(std::string fileExtension, bool hideFileExtensions,
|
||||
bool useCheckboxes);
|
||||
|
||||
/**
|
||||
@@ -54,7 +49,8 @@ public:
|
||||
*
|
||||
* \param dir The directory from which to start the search from
|
||||
*/
|
||||
std::string useQtFileSystemModelToTraverseDir(std::string dir, bool usersAssets = false);
|
||||
std::string useQtFileSystemModelToTraverseDir(std::string dir,
|
||||
bool usersAssets = false);
|
||||
|
||||
private:
|
||||
void parseChildDirElements(QFileInfo item, std::string space, int level,
|
||||
@@ -62,7 +58,6 @@ private:
|
||||
bool userAssets);
|
||||
void parseChildFile(std::string res, bool& hasDirHeaderBeenAdded,
|
||||
std::vector<std::string>& dirNames, std::vector<std::string>& output);
|
||||
bool isApprovedPath(std::string path);
|
||||
|
||||
QFileSystemModel _filesystemModel;
|
||||
QDir::Filters _fileFilterOptions = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot;
|
||||
|
||||
103
apps/OpenSpace/ext/launcher/include/profile/actiondialog.h
Normal file
103
apps/OpenSpace/ext/launcher/include/profile/actiondialog.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_UI_LAUNCHER___ACTIONDIALOG___H__
|
||||
#define __OPENSPACE_UI_LAUNCHER___ACTIONDIALOG___H__
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <openspace/scene/profile.h>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
class QGridLayout;
|
||||
class QLineEdit;
|
||||
class QListWidget;
|
||||
class QPushButton;
|
||||
class QTextEdit;
|
||||
|
||||
class ActionDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ActionDialog(QWidget* parent,
|
||||
std::vector<openspace::Profile::Action>* actions,
|
||||
std::vector<openspace::Profile::Keybinding>* keybindings);
|
||||
|
||||
private:
|
||||
void createWidgets();
|
||||
void createActionWidgets(QGridLayout* layout);
|
||||
void createKeyboardWidgets(QGridLayout* layout);
|
||||
void applyChanges();
|
||||
|
||||
openspace::Profile::Action* selectedAction();
|
||||
void actionAdd();
|
||||
void actionRemove();
|
||||
void actionSelected();
|
||||
void actionSaved();
|
||||
void clearActionFields();
|
||||
void actionRejected();
|
||||
|
||||
openspace::Profile::Keybinding* selectedKeybinding();
|
||||
void keybindingAdd();
|
||||
void keybindingRemove();
|
||||
void keybindingSelected();
|
||||
void keybindingActionSelected(int);
|
||||
void keybindingSaved();
|
||||
void clearKeybindingFields();
|
||||
void keybindingRejected();
|
||||
|
||||
std::vector<openspace::Profile::Action>* _actions = nullptr;
|
||||
std::vector<openspace::Profile::Action> _actionData;
|
||||
std::vector<openspace::Profile::Keybinding>* _keybindings = nullptr;
|
||||
std::vector<openspace::Profile::Keybinding> _keybindingsData;
|
||||
|
||||
struct {
|
||||
QListWidget* list = nullptr;
|
||||
QLineEdit* identifier = nullptr;
|
||||
QLineEdit* name = nullptr;
|
||||
QLineEdit* guiPath = nullptr;
|
||||
QLineEdit* documentation = nullptr;
|
||||
QCheckBox* isLocal = nullptr;
|
||||
QTextEdit* script = nullptr;
|
||||
QPushButton* addButton = nullptr;
|
||||
QPushButton* removeButton = nullptr;
|
||||
QDialogButtonBox* saveButtons = nullptr;
|
||||
} _actionWidgets;
|
||||
|
||||
struct {
|
||||
QListWidget* list = nullptr;
|
||||
QCheckBox* shiftModifier = nullptr;
|
||||
QCheckBox* ctrlModifier = nullptr;
|
||||
QCheckBox* altModifier = nullptr;
|
||||
QComboBox* key = nullptr;
|
||||
QComboBox* action = nullptr;
|
||||
QLineEdit* actionText = nullptr;
|
||||
QPushButton* addButton = nullptr;
|
||||
QPushButton* removeButton = nullptr;
|
||||
QDialogButtonBox* saveButtons = nullptr;
|
||||
} _keybindingWidgets;
|
||||
};
|
||||
|
||||
#endif // __OPENSPACE_UI_LAUNCHER___ACTIONDIALOG___H__
|
||||
@@ -27,11 +27,9 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace openspace { class Profile; }
|
||||
|
||||
class QTextEdit;
|
||||
|
||||
class AdditionalScriptsDialog : public QDialog {
|
||||
class AdditionalScriptsDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -41,7 +39,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
AdditionalScriptsDialog(openspace::Profile& profile, QWidget* parent);
|
||||
AdditionalScriptsDialog(QWidget* parent, std::vector<std::string>* scripts);
|
||||
|
||||
private slots:
|
||||
void parseScript();
|
||||
@@ -57,7 +55,8 @@ private slots:
|
||||
private:
|
||||
void createWidgets();
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::vector<std::string>* _scripts = nullptr;
|
||||
std::vector<std::string> _scriptsData;
|
||||
QTextEdit* _textScripts = nullptr;
|
||||
QPushButton* _chooseScriptsButton = nullptr;
|
||||
};
|
||||
|
||||
@@ -29,12 +29,10 @@
|
||||
|
||||
#include "assettreemodel.h"
|
||||
|
||||
namespace openspace { class Profile; }
|
||||
|
||||
class QTextEdit;
|
||||
class QTreeView;
|
||||
|
||||
class AssetsDialog : public QDialog {
|
||||
class AssetsDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -43,11 +41,12 @@ public:
|
||||
* \param profile The #openspace::Profile object containing all data of the
|
||||
* new or imported profile.
|
||||
* \param assetBasePath The path to the folder in which all of the assets are living
|
||||
* \param userAssetBasePath The path to the folder in which the users' assets are living
|
||||
* \param userAssetBasePath The path to the folder in which the users' assets are
|
||||
* living
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
AssetsDialog(openspace::Profile& profile, const std::string& assetBasePath,
|
||||
const std::string& userAssetBasePath, QWidget* parent);
|
||||
AssetsDialog(QWidget* parent, openspace::Profile* profile,
|
||||
const std::string& assetBasePath, const std::string& userAssetBasePath);
|
||||
|
||||
private slots:
|
||||
void parseSelections();
|
||||
@@ -63,7 +62,7 @@ private:
|
||||
QString createTextSummary();
|
||||
void openAssetEditor(const std::string& asset);
|
||||
|
||||
openspace::Profile& _profile;
|
||||
openspace::Profile* _profile = nullptr;
|
||||
AssetTreeModel _assetTreeModel;
|
||||
QTreeView* _assetTree = nullptr;
|
||||
QTextEdit* _summary = nullptr;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "openspace/scene/profile.h"
|
||||
#include <memory>
|
||||
|
||||
class AssetTreeModel : public QAbstractItemModel {
|
||||
class AssetTreeModel final : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
* function (can be multiple times)
|
||||
* \return QVariant data object
|
||||
*/
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
QVariant data(const QModelIndex& index, int role) const final;
|
||||
|
||||
/**
|
||||
* Returns the header data of the tree view
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
* \return QVariant data object in the header
|
||||
*/
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override;
|
||||
int role = Qt::DisplayRole) const final;
|
||||
|
||||
/**
|
||||
* Returns the index of item in #QModelIndex object form
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
* \return #QModelIndex index of the item at specified position
|
||||
*/
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex& parent = QModelIndex()) const override;
|
||||
const QModelIndex& parent = QModelIndex()) const final;
|
||||
|
||||
/**
|
||||
* Returns the index of the parent of the item specified by input param
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
* \param index of item that is a child of the parent
|
||||
* \return #QModelIndex index of the parent
|
||||
*/
|
||||
QModelIndex parent(const QModelIndex& index) const override;
|
||||
QModelIndex parent(const QModelIndex& index) const final;
|
||||
|
||||
/**
|
||||
* Returns the index of the parent of the item specified by the input params
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
* \param parent #QModelIndex of the parent item
|
||||
* \return number of children/rows of this parent
|
||||
*/
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const final;
|
||||
|
||||
/**
|
||||
* Returns the number of columns of data in each item of the tree
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
* \param parent specified by the #QModelIndex index
|
||||
* \return the number of data columns
|
||||
*/
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const final;
|
||||
|
||||
/**
|
||||
* Return the Qt flags of the item specified by index, which can include
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
* \param index specified by the #QModelIndex index
|
||||
* \return the Qt flags
|
||||
*/
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const final;
|
||||
|
||||
/**
|
||||
* Set data at index \p index
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
* \return true if the data set was successful
|
||||
*/
|
||||
bool setData(const QModelIndex& index, const QVariant& value,
|
||||
int role = Qt::EditRole) override;
|
||||
int role = Qt::EditRole) final;
|
||||
|
||||
/**
|
||||
* Returns a vector of all #Assets selected in the tree view
|
||||
|
||||
@@ -27,13 +27,14 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace openspace { class Profile; }
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <optional>
|
||||
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QTabWidget;
|
||||
|
||||
class CameraDialog : public QDialog {
|
||||
class CameraDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -43,7 +44,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget (optional)
|
||||
*/
|
||||
CameraDialog(openspace::Profile& profile, QWidget* parent);
|
||||
CameraDialog(QWidget* parent, std::optional<openspace::Profile::CameraType>* camera);
|
||||
|
||||
private slots:
|
||||
void approved();
|
||||
@@ -57,7 +58,7 @@ private:
|
||||
void addErrorMsg(QString errorDescription);
|
||||
bool areRequiredFormsFilledAndValid();
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::optional<openspace::Profile::CameraType>* _camera = nullptr;
|
||||
QTabWidget* _tabWidget = nullptr;
|
||||
struct {
|
||||
QLineEdit* anchor = nullptr;
|
||||
|
||||
@@ -27,15 +27,13 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace openspace { class Profile; }
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QListWidget;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
|
||||
class DeltaTimesDialog : public QDialog {
|
||||
class DeltaTimesDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -45,7 +43,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
DeltaTimesDialog(openspace::Profile& profile, QWidget* parent);
|
||||
DeltaTimesDialog(QWidget* parent, std::vector<double>* deltaTimes);
|
||||
|
||||
/**
|
||||
* Returns a text summary of the delta time list for display purposes
|
||||
@@ -87,8 +85,8 @@ private:
|
||||
void setLabelForKey(int index, bool editMode, std::string color);
|
||||
bool isLineEmpty(int index);
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::vector<double> _data;
|
||||
std::vector<double>* _deltaTimes = nullptr;
|
||||
std::vector<double> _deltaTimesData;
|
||||
bool _editModeNewItem = false;
|
||||
|
||||
QListWidget* _listWidget = nullptr;
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_UI_LAUNCHER___KEYBINDINGS___H__
|
||||
#define __OPENSPACE_UI_LAUNCHER___KEYBINDINGS___H__
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <QWidget>
|
||||
#include <QListWidgetItem>
|
||||
|
||||
class QComboBox;
|
||||
class QCheckBox;
|
||||
class QTextEdit;
|
||||
class QDialogButtonBox;
|
||||
class QListWidget;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class KeybindingsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructor for keybindings class
|
||||
*
|
||||
* \param profile The #openspace::Profile object containing all data of the
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget (optional)
|
||||
*/
|
||||
KeybindingsDialog(openspace::Profile& profile, QWidget* parent);
|
||||
|
||||
/**
|
||||
* Handles keypress while the Qt dialog window is open
|
||||
*
|
||||
* \param evt #QKeyEvent object for the key press event
|
||||
*/
|
||||
virtual void keyPressEvent(QKeyEvent* evt) override;
|
||||
|
||||
private slots:
|
||||
void listItemSelected();
|
||||
void listItemAdded();
|
||||
void listItemRemove();
|
||||
void listItemSave();
|
||||
void listItemCancelSave();
|
||||
void transitionToEditMode();
|
||||
void parseSelections();
|
||||
void chooseScripts();
|
||||
void keySelected(int index);
|
||||
|
||||
/**
|
||||
* Adds scripts to the _scriptEdit from outside dialogs
|
||||
*
|
||||
* \param scripts #std::string scripts to be appended
|
||||
*/
|
||||
void appendScriptsToKeybind(std::string scripts);
|
||||
|
||||
private:
|
||||
void createWidgets();
|
||||
void transitionFromEditMode();
|
||||
void editBoxDisabled(bool disabled);
|
||||
int indexInKeyMapping(std::vector<int>& mapVector, int keyInt);
|
||||
bool areRequiredFormsFilled();
|
||||
bool isLineEmpty(int index);
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::vector<openspace::Profile::Keybinding> _data;
|
||||
std::vector<int> _mapModKeyComboBoxIndexToKeyValue;
|
||||
std::vector<int> _mapKeyComboBoxIndexToKeyValue;
|
||||
bool _editModeNewItem = false;
|
||||
|
||||
QListWidget* _list = nullptr;
|
||||
QLabel* _keyModLabel = nullptr;
|
||||
QComboBox* _keyModCombo = nullptr;
|
||||
QLabel* _keyLabel = nullptr;
|
||||
QComboBox* _keyCombo = nullptr;
|
||||
QLabel* _nameLabel = nullptr;
|
||||
QLineEdit* _nameEdit = nullptr;
|
||||
QLabel* _guiPathLabel = nullptr;
|
||||
QLineEdit* _guiPathEdit = nullptr;
|
||||
QLabel* _documentationLabel = nullptr;
|
||||
QLineEdit* _documentationEdit = nullptr;
|
||||
QCheckBox* _localCheck = nullptr;
|
||||
QLabel* _scriptLabel = nullptr;
|
||||
QTextEdit* _scriptEdit = nullptr;
|
||||
|
||||
QPushButton* _addButton = nullptr;
|
||||
QPushButton* _removeButton = nullptr;
|
||||
QPushButton* _chooseScriptsButton = nullptr;
|
||||
QPushButton* _saveButton = nullptr;
|
||||
QPushButton* _cancelButton = nullptr;
|
||||
QDialogButtonBox* _buttonBox = nullptr;
|
||||
|
||||
QLabel* _errorMsg = nullptr;
|
||||
};
|
||||
|
||||
#endif // __OPENSPACE_UI_LAUNCHER___KEYBINDINGS___H__
|
||||
@@ -27,14 +27,12 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace openspace { class Profile; }
|
||||
|
||||
class QLineEdit;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QPushButton;
|
||||
|
||||
class MarkNodesDialog : public QDialog {
|
||||
class MarkNodesDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -44,7 +42,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
MarkNodesDialog(openspace::Profile& profile, QWidget* parent);
|
||||
MarkNodesDialog(QWidget* parent, std::vector<std::string>* markedNodes);
|
||||
|
||||
/**
|
||||
* Handles keypress while the Qt dialog window is open
|
||||
@@ -63,8 +61,8 @@ private:
|
||||
void createWidgets();
|
||||
|
||||
std::vector<QListWidgetItem*> _markedNodesListItems;
|
||||
openspace::Profile& _profile;
|
||||
std::vector<std::string> _data;
|
||||
std::vector<std::string>* _markedNodes;
|
||||
std::vector<std::string> _markedNodesData;
|
||||
|
||||
QListWidget* _list = nullptr;
|
||||
QPushButton* _removeButton = nullptr;
|
||||
|
||||
@@ -27,12 +27,13 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace openspace { class Profile; }
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <optional>
|
||||
|
||||
class QLineEdit;
|
||||
class QTextEdit;
|
||||
|
||||
class MetaDialog : public QDialog {
|
||||
class MetaDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -42,7 +43,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
MetaDialog(openspace::Profile& profile, QWidget* parent);
|
||||
MetaDialog(QWidget* parent, std::optional<openspace::Profile::Meta>* meta);
|
||||
|
||||
private slots:
|
||||
void save();
|
||||
@@ -50,7 +51,7 @@ private slots:
|
||||
private:
|
||||
void createWidgets();
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::optional<openspace::Profile::Meta>* _meta = nullptr;
|
||||
|
||||
QLineEdit* _nameEdit = nullptr;
|
||||
QLineEdit* _versionEdit = nullptr;
|
||||
|
||||
@@ -35,7 +35,7 @@ class QLineEdit;
|
||||
class QListWidget;
|
||||
class QPushButton;
|
||||
|
||||
class ModulesDialog : public QDialog {
|
||||
class ModulesDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
ModulesDialog(openspace::Profile& profile, QWidget* parent);
|
||||
ModulesDialog(QWidget* parent, std::vector<openspace::Profile::Module>* modules);
|
||||
|
||||
/**
|
||||
* Handles keypress while the Qt dialog window is open
|
||||
@@ -71,8 +71,8 @@ private:
|
||||
void editBoxDisabled(bool disabled);
|
||||
bool isLineEmpty(int index) const;
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::vector<openspace::Profile::Module> _data;
|
||||
std::vector<openspace::Profile::Module>* _modules = nullptr;
|
||||
std::vector<openspace::Profile::Module> _moduleData;
|
||||
bool _editModeNewItem = false;
|
||||
|
||||
QListWidget* _list = nullptr;
|
||||
|
||||
@@ -36,7 +36,7 @@ class QLabel;
|
||||
class QLineEdit;
|
||||
class QTextEdit;
|
||||
|
||||
class ProfileEdit : public QDialog {
|
||||
class ProfileEdit final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,7 @@ class QLineEdit;
|
||||
class QListWidget;
|
||||
class QPushButton;
|
||||
|
||||
class PropertiesDialog : public QDialog {
|
||||
class PropertiesDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -46,7 +46,8 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
PropertiesDialog(openspace::Profile& profile, QWidget* parent);
|
||||
PropertiesDialog(QWidget* parent,
|
||||
std::vector<openspace::Profile::Property>* properties);
|
||||
|
||||
/**
|
||||
* Handles keypress while the Qt dialog window is open
|
||||
@@ -73,8 +74,8 @@ private:
|
||||
bool areRequiredFormsFilled();
|
||||
bool isLineEmpty(int index);
|
||||
|
||||
openspace::Profile& _profile;
|
||||
std::vector<openspace::Profile::Property> _data;
|
||||
std::vector<openspace::Profile::Property>* _properties = nullptr;
|
||||
std::vector<openspace::Profile::Property> _propertyData;
|
||||
bool _editModeNewItem = false;
|
||||
|
||||
QListWidget* _list = nullptr;
|
||||
|
||||
@@ -25,11 +25,10 @@
|
||||
#ifndef __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__
|
||||
#define __OPENSPACE_UI_LAUNCHER___SCRIPTLOG___H__
|
||||
|
||||
#include "profile/keybindingsdialog.h"
|
||||
#include <QDialog>
|
||||
#include <QListWidget>
|
||||
|
||||
class ScriptlogDialog : public QDialog {
|
||||
class ScriptlogDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@ class QDateTimeEdit;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
|
||||
class TimeDialog : public QDialog {
|
||||
class TimeDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
* new or imported profile.
|
||||
* \param parent Pointer to parent Qt widget
|
||||
*/
|
||||
TimeDialog(openspace::Profile& profile, QWidget* parent);
|
||||
TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>* time);
|
||||
|
||||
private slots:
|
||||
void enableAccordingToType(int);
|
||||
@@ -54,8 +54,8 @@ private:
|
||||
void createWidgets();
|
||||
void enableFormatForAbsolute(bool enableAbs);
|
||||
|
||||
openspace::Profile& _profile;
|
||||
openspace::Profile::Time _data;
|
||||
std::optional<openspace::Profile::Time>* _time = nullptr;
|
||||
openspace::Profile::Time _timeData;
|
||||
bool _initializedAsAbsolute = true;
|
||||
|
||||
QComboBox* _typeCombo = nullptr;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
QLabel#heading {
|
||||
font-size: 12pt;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
QLabel#error-message {
|
||||
@@ -18,12 +18,12 @@ QLabel#error-message {
|
||||
*/
|
||||
LauncherWindow QLabel {
|
||||
font-family: Segoe UI;
|
||||
font-weight:600;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
LauncherWindow QLabel#label_choose, QLabel#label_options {
|
||||
color: #ddd;
|
||||
font-size:10pt;
|
||||
color: #dddddd;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
LauncherWindow QLabel#clear {
|
||||
@@ -115,15 +115,26 @@ PropertiesDialog QListWidget {
|
||||
*/
|
||||
AssetsDialog QTreeView {
|
||||
min-width: 40em;
|
||||
min-height: 40em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keybindings
|
||||
* ActionDialog
|
||||
*/
|
||||
KeybindingsDialog QListWidget {
|
||||
ActionDialog QListWidget {
|
||||
min-width: 40em;
|
||||
}
|
||||
|
||||
ActionDialog QPushButton#add-button, QPushButton#remove-button {
|
||||
font-weight: bold;
|
||||
|
||||
padding: 0px 0px 0px 0px;
|
||||
min-width: 1.5em;
|
||||
max-width: 1.5em;
|
||||
min-height: 1.5em;
|
||||
max-height: 1.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
* DeltaTimes
|
||||
*/
|
||||
|
||||
@@ -25,15 +25,14 @@
|
||||
#include "filesystemaccess.h"
|
||||
|
||||
FileSystemAccess::FileSystemAccess(std::string fileExtension,
|
||||
std::vector<std::string> approvedPaths,
|
||||
bool hideFileExtensions, bool useCheckboxes)
|
||||
: _fileExtension(std::move(fileExtension))
|
||||
, _approvedPaths(std::move(approvedPaths))
|
||||
, _hideFileExtensions(hideFileExtensions)
|
||||
, _useCheckboxes(useCheckboxes)
|
||||
{}
|
||||
|
||||
std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir, bool userAssets) {
|
||||
std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir,
|
||||
bool userAssets) {
|
||||
_filesystemModel.setRootPath(QString::fromStdString(dir));
|
||||
QModelIndex index = _filesystemModel.index(_filesystemModel.rootPath());
|
||||
QFileInfo fileInfo = _filesystemModel.fileInfo(index);
|
||||
@@ -48,8 +47,10 @@ std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir,
|
||||
}
|
||||
|
||||
void FileSystemAccess::parseChildDirElements(QFileInfo fileInfo, std::string space,
|
||||
int level, std::vector<std::string>& dirNames,
|
||||
std::vector<std::string>& output, bool userAssets)
|
||||
int level,
|
||||
std::vector<std::string>& dirNames,
|
||||
std::vector<std::string>& output,
|
||||
bool userAssets)
|
||||
{
|
||||
QDir dir(fileInfo.filePath());
|
||||
bool hasDirHeaderBeenAdded = false;
|
||||
@@ -62,10 +63,8 @@ void FileSystemAccess::parseChildDirElements(QFileInfo fileInfo, std::string spa
|
||||
res = "${USER_ASSETS}/" + res;
|
||||
}
|
||||
if (fi.isDir()) {
|
||||
if (level != 0 || (level == 0 && (isApprovedPath(res) || userAssets))) {
|
||||
dirNames.push_back(res);
|
||||
parseChildDirElements(fi, (space + " "), level + 1, dirNames, output, userAssets);
|
||||
}
|
||||
dirNames.push_back(res);
|
||||
parseChildDirElements(fi, (space + " "), level + 1, dirNames, output, userAssets);
|
||||
}
|
||||
else {
|
||||
parseChildFile(res, hasDirHeaderBeenAdded, dirNames, output);
|
||||
@@ -77,19 +76,6 @@ void FileSystemAccess::parseChildDirElements(QFileInfo fileInfo, std::string spa
|
||||
}
|
||||
}
|
||||
|
||||
bool FileSystemAccess::isApprovedPath(std::string path) {
|
||||
bool approvedMatch = false;
|
||||
path.erase(0, path.find_first_not_of(" "));
|
||||
|
||||
for (const std::string& p : _approvedPaths) {
|
||||
if (path.substr(0, p.length()).compare(p) == 0) {
|
||||
approvedMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return approvedMatch;
|
||||
}
|
||||
|
||||
void FileSystemAccess::parseChildFile(std::string filename, bool& hasDirHeaderBeenAdded,
|
||||
std::vector<std::string>& dirNames,
|
||||
std::vector<std::string>& output)
|
||||
|
||||
@@ -28,16 +28,18 @@
|
||||
|
||||
#include <openspace/engine/configuration.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <QComboBox>
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QStandardItemModel>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
using namespace openspace;
|
||||
|
||||
@@ -312,8 +314,24 @@ void LauncherWindow::setBackgroundImage(const std::string& syncPath) {
|
||||
std::mt19937 g(rd());
|
||||
std::shuffle(files.begin(), files.end(), g);
|
||||
// We know there has to be at least one folder, so it's fine to just pick the first
|
||||
std::string image = files.front();
|
||||
_backgroundImage->setPixmap(QPixmap(QString::fromStdString(image)));
|
||||
while (!files.empty()) {
|
||||
std::string p = files.front();
|
||||
if (std::filesystem::path(p).extension() == ".png") {
|
||||
// If the top path starts with the png extension, we have found our candidate
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// There shouldn't be any non-png images in here, but you never know. So we
|
||||
// just remove non-image files here
|
||||
files.erase(files.begin());
|
||||
}
|
||||
}
|
||||
|
||||
// There better be at least one file left, but just in in case
|
||||
if (!files.empty()) {
|
||||
std::string image = files.front();
|
||||
_backgroundImage->setPixmap(QPixmap(QString::fromStdString(image)));
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherWindow::populateProfilesList(std::string preset) {
|
||||
@@ -462,7 +480,7 @@ std::string LauncherWindow::selectedWindowConfig() const {
|
||||
int idx = _windowConfigBox->currentIndex();
|
||||
if (idx == 0) {
|
||||
return _sgctConfigName;
|
||||
} else if (idx > _userAssetCount) {
|
||||
} else if (idx > _userConfigCount) {
|
||||
return "${CONFIG}/" + _windowConfigBox->currentText().toStdString();
|
||||
}
|
||||
else {
|
||||
|
||||
725
apps/OpenSpace/ext/launcher/src/profile/actiondialog.cpp
Normal file
725
apps/OpenSpace/ext/launcher/src/profile/actiondialog.cpp
Normal file
@@ -0,0 +1,725 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include "profile/actiondialog.h"
|
||||
|
||||
#include "profile/line.h"
|
||||
#include <openspace/util/keys.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using namespace openspace;
|
||||
|
||||
namespace {
|
||||
void updateListItem(QListWidgetItem* item, const Profile::Action& action) {
|
||||
ghoul_assert(item, "Item must exist at this point");
|
||||
item->setText(
|
||||
action.name.empty() ?
|
||||
QString::fromStdString(action.identifier) :
|
||||
QString::fromStdString(action.name)
|
||||
);
|
||||
}
|
||||
|
||||
void updateListItem(QListWidgetItem* item, const Profile::Keybinding& kb) {
|
||||
ghoul_assert(item, "Item must exist at this point");
|
||||
std::string name = fmt::format("{}\t{}", ghoul::to_string(kb.key), kb.action);
|
||||
item->setText(QString::fromStdString(name));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ActionDialog::ActionDialog(QWidget* parent,
|
||||
std::vector<openspace::Profile::Action>* actions,
|
||||
std::vector<openspace::Profile::Keybinding>* keybindings)
|
||||
: QDialog(parent)
|
||||
, _actions(actions)
|
||||
, _actionData(*_actions)
|
||||
, _keybindings(keybindings)
|
||||
, _keybindingsData(*_keybindings)
|
||||
{
|
||||
setWindowTitle("Actions and Keybindings");
|
||||
createWidgets();
|
||||
}
|
||||
|
||||
void ActionDialog::createWidgets() {
|
||||
// Column 0 Column 1 Column 2
|
||||
// *----------------------*---------------*----------------*
|
||||
// | Actions | Row 0
|
||||
// | | Identifier | [oooooooooooo] | Row 1
|
||||
// | | Name | [oooooooooooo] | Row 2
|
||||
// | | GUI Path | [oooooooooooo] | Row 3
|
||||
// | | Documentation | [oooooooooooo] | Row 4
|
||||
// | | Is Local | [] | Row 5
|
||||
// | | Script | [oooooooooooo] | Row 6
|
||||
// *----------------------*---------------*----------------*
|
||||
// | [+] [-] | | [Save] [Cancel]| Row 7
|
||||
// *----------------------*---------------*----------------*
|
||||
// |=======================================================| Row 8
|
||||
// | Keybindings | Row 9
|
||||
// *----------------------*---------------*----------------|
|
||||
// | | Modifier | []S []C []A | Row 10
|
||||
// | | Key | DDDDDDDDDDDD> | Row 11
|
||||
// | | Add actions | DDDDDDDDDDDD> | Row 12
|
||||
// | | Action | [oooooooooooo] | Row 13
|
||||
// *----------------------*---------------*----------------*
|
||||
// | [+] [-] | | [Save] [Cancel]| Row 14
|
||||
// *----------------------*---------------*----------------*
|
||||
// |=======================================================| Row 14
|
||||
// *----------------------*---------------*----------------*
|
||||
// | | [Save] [Cancel]| Row 15
|
||||
// *----------------------*---------------*----------------*
|
||||
|
||||
QGridLayout* layout = new QGridLayout(this);
|
||||
|
||||
createActionWidgets(layout);
|
||||
clearActionFields();
|
||||
|
||||
layout->addWidget(new Line, 8, 0, 1, 3);
|
||||
|
||||
createKeyboardWidgets(layout);
|
||||
clearKeybindingFields();
|
||||
|
||||
layout->addWidget(new Line, 16, 0, 1, 3);
|
||||
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox;
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
|
||||
QObject::connect(
|
||||
buttonBox, &QDialogButtonBox::accepted,
|
||||
this, &ActionDialog::applyChanges
|
||||
);
|
||||
QObject::connect(
|
||||
buttonBox, &QDialogButtonBox::rejected,
|
||||
this, &ActionDialog::reject
|
||||
);
|
||||
layout->addWidget(buttonBox, 17, 2, Qt::AlignRight);
|
||||
}
|
||||
|
||||
void ActionDialog::createActionWidgets(QGridLayout* layout) {
|
||||
QLabel* title = new QLabel("Actions");
|
||||
title->setObjectName("heading");
|
||||
layout->addWidget(title, 0, 0, 1, 3);
|
||||
|
||||
_actionWidgets.list = new QListWidget;
|
||||
_actionWidgets.list->setToolTip(
|
||||
"The list of all actions currently defined in the profile. Select one to edit it "
|
||||
"or use the + button below to create a new action"
|
||||
);
|
||||
_actionWidgets.list->setAlternatingRowColors(true);
|
||||
_actionWidgets.list->setResizeMode(QListView::Adjust);
|
||||
connect(
|
||||
_actionWidgets.list, &QListWidget::itemSelectionChanged,
|
||||
this, &ActionDialog::actionSelected
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < _actionData.size(); ++i) {
|
||||
const Profile::Action& action = _actionData[i];
|
||||
std::string name = action.name.empty() ? action.identifier : action.name;
|
||||
_actionWidgets.list->addItem(new QListWidgetItem(QString::fromStdString(name)));
|
||||
}
|
||||
|
||||
layout->addWidget(_actionWidgets.list, 1, 0, 6, 1);
|
||||
|
||||
layout->addWidget(new QLabel("Identifier"), 1, 1);
|
||||
_actionWidgets.identifier = new QLineEdit;
|
||||
_actionWidgets.identifier->setToolTip(
|
||||
"The unique identifier for this action. The identifier name cannot be reused "
|
||||
"between different actions and will lead to a failure to load the profile if it "
|
||||
"happens. There are no restrictions on the name of the identifier, but a dot "
|
||||
"separated hierarchical structure is suggested to prevent name clashes"
|
||||
);
|
||||
_actionWidgets.identifier->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.identifier, 1, 2);
|
||||
|
||||
layout->addWidget(new QLabel("Name"), 2, 1);
|
||||
_actionWidgets.name = new QLineEdit;
|
||||
_actionWidgets.name->setToolTip(
|
||||
"The user-facing name of this action. As it is displayed in user interfaces, the "
|
||||
"name should be as concise and informative as possible"
|
||||
);
|
||||
_actionWidgets.name->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.name, 2, 2);
|
||||
|
||||
layout->addWidget(new QLabel("GUI Path"), 3, 1);
|
||||
_actionWidgets.guiPath = new QLineEdit;
|
||||
_actionWidgets.guiPath->setToolTip(
|
||||
"The path under which this action will be shown in user interfaces. The path "
|
||||
"must use the '/' character as separators between folders and start with a '/' "
|
||||
"character that denotes the root folder"
|
||||
);
|
||||
_actionWidgets.guiPath->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.guiPath, 3, 2);
|
||||
|
||||
layout->addWidget(new QLabel("Documentation"), 4, 1);
|
||||
_actionWidgets.documentation = new QLineEdit;
|
||||
_actionWidgets.documentation->setToolTip(
|
||||
"A longer user-facing documentation that describes the action in more detail. "
|
||||
"The user can request the documentation on demand, so it might be longer and "
|
||||
"more descriptive than the name itself and might also explain some optional "
|
||||
"parameters that that action can consume"
|
||||
);
|
||||
_actionWidgets.documentation->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.documentation, 4, 2);
|
||||
|
||||
layout->addWidget(new QLabel("Is Local"), 5, 1);
|
||||
_actionWidgets.isLocal = new QCheckBox;
|
||||
_actionWidgets.isLocal->setToolTip(
|
||||
"If this value is checked, the action will only ever affect the OpenSpace "
|
||||
"instance that is executing it. If running a 'regular' OpenSpace instance, this "
|
||||
"setting will not make any difference, but it is necessary in a clustered "
|
||||
"environment or when using a parallel connection, in which case it determines "
|
||||
"whether a command should be executed only locally or send to all remote "
|
||||
"instances as well"
|
||||
);
|
||||
_actionWidgets.isLocal->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.isLocal, 5, 2);
|
||||
|
||||
layout->addWidget(new QLabel("Script"), 6, 1);
|
||||
_actionWidgets.script = new QTextEdit;
|
||||
_actionWidgets.script->setToolTip(
|
||||
"This is the Lua script that gets executed when this action is triggered. "
|
||||
"Actions can make use of optional arguments which are already defined as the "
|
||||
"`args` variable when this script executes. If no arguments are passed, this "
|
||||
"variable does not exist"
|
||||
);
|
||||
_actionWidgets.script->setEnabled(false);
|
||||
layout->addWidget(_actionWidgets.script, 6, 2);
|
||||
|
||||
|
||||
// + / - buttons
|
||||
QWidget* container = new QWidget;
|
||||
QBoxLayout* containerLayout = new QHBoxLayout(container);
|
||||
_actionWidgets.addButton = new QPushButton("+");
|
||||
_actionWidgets.addButton->setObjectName("add-button");
|
||||
_actionWidgets.addButton->setToolTip("Adds a new action to the list of all actions");
|
||||
QObject::connect(
|
||||
_actionWidgets.addButton, &QPushButton::clicked,
|
||||
this, &ActionDialog::actionAdd
|
||||
);
|
||||
containerLayout->addWidget(_actionWidgets.addButton);
|
||||
|
||||
_actionWidgets.removeButton = new QPushButton("-");
|
||||
_actionWidgets.removeButton->setObjectName("remove-button");
|
||||
_actionWidgets.removeButton->setToolTip("Removes the currently selected action");
|
||||
_actionWidgets.removeButton->setEnabled(false);
|
||||
QObject::connect(
|
||||
_actionWidgets.removeButton, &QPushButton::clicked,
|
||||
this, &ActionDialog::actionRemove
|
||||
);
|
||||
containerLayout->addWidget(_actionWidgets.removeButton);
|
||||
layout->addWidget(container, 7, 0, Qt::AlignLeft);
|
||||
|
||||
|
||||
// Save / Cancel buttons
|
||||
_actionWidgets.saveButtons = new QDialogButtonBox;
|
||||
_actionWidgets.saveButtons->setToolTip(
|
||||
"Saves or discards all changes to the currently selected action"
|
||||
);
|
||||
_actionWidgets.saveButtons->setEnabled(false);
|
||||
_actionWidgets.saveButtons->setStandardButtons(
|
||||
QDialogButtonBox::Save | QDialogButtonBox::Cancel
|
||||
);
|
||||
QObject::connect(
|
||||
_actionWidgets.saveButtons, &QDialogButtonBox::accepted,
|
||||
this, &ActionDialog::actionSaved
|
||||
);
|
||||
QObject::connect(
|
||||
_actionWidgets.saveButtons, &QDialogButtonBox::rejected,
|
||||
this, &ActionDialog::actionRejected
|
||||
);
|
||||
layout->addWidget(_actionWidgets.saveButtons, 7, 2, Qt::AlignRight);
|
||||
}
|
||||
|
||||
void ActionDialog::createKeyboardWidgets(QGridLayout* layout) {
|
||||
QLabel* title = new QLabel("Keybindings");
|
||||
title->setObjectName("heading");
|
||||
layout->addWidget(title);
|
||||
|
||||
_keybindingWidgets.list = new QListWidget;
|
||||
_keybindingWidgets.list->setToolTip(
|
||||
"The list of all keybindings currently assigned in this profile"
|
||||
);
|
||||
_keybindingWidgets.list->setAlternatingRowColors(true);
|
||||
_keybindingWidgets.list->setResizeMode(QListView::Adjust);
|
||||
connect(
|
||||
_keybindingWidgets.list, &QListWidget::itemSelectionChanged,
|
||||
this, &ActionDialog::keybindingSelected
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < _keybindingsData.size(); ++i) {
|
||||
const Profile::Keybinding& kv = _keybindingsData[i];
|
||||
QListWidgetItem* item = new QListWidgetItem;
|
||||
updateListItem(item, kv);
|
||||
_keybindingWidgets.list->addItem(item);
|
||||
}
|
||||
|
||||
layout->addWidget(_keybindingWidgets.list, 10, 0, 4, 1);
|
||||
|
||||
layout->addWidget(new QLabel("Modifier"), 10, 1);
|
||||
{
|
||||
QWidget* container = new QWidget;
|
||||
QBoxLayout* containerLayout = new QHBoxLayout(container);
|
||||
_keybindingWidgets.shiftModifier = new QCheckBox("Shift");
|
||||
_keybindingWidgets.shiftModifier->setEnabled(false);
|
||||
containerLayout->addWidget(_keybindingWidgets.shiftModifier);
|
||||
_keybindingWidgets.ctrlModifier = new QCheckBox("Control");
|
||||
_keybindingWidgets.ctrlModifier->setEnabled(false);
|
||||
containerLayout->addWidget(_keybindingWidgets.ctrlModifier);
|
||||
_keybindingWidgets.altModifier = new QCheckBox("Alt");
|
||||
_keybindingWidgets.altModifier->setEnabled(false);
|
||||
containerLayout->addWidget(_keybindingWidgets.altModifier);
|
||||
layout->addWidget(container, 10, 2);
|
||||
}
|
||||
|
||||
layout->addWidget(new QLabel("Key"), 11, 1);
|
||||
_keybindingWidgets.key = new QComboBox;
|
||||
QStringList keyList;
|
||||
for (const KeyInfo& ki : KeyInfos) {
|
||||
// We don't want to use the Shift, Alt, and Ctrl keys directly since we already
|
||||
// have them as modifier keys
|
||||
if (ki.key == Key::LeftShift || ki.key == Key::RightShift ||
|
||||
ki.key == Key::LeftAlt || ki.key == Key::RightAlt ||
|
||||
ki.key == Key::LeftControl || ki.key == Key::RightControl ||
|
||||
ki.key == Key::LeftSuper || ki.key == Key::RightSuper ||
|
||||
ki.key == Key::Menu || ki.key == Key::NumLock ||
|
||||
ki.key == Key::World1 || ki.key == Key::World2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
keyList += QString::fromStdString(std::string(ki.name));
|
||||
}
|
||||
_keybindingWidgets.key->addItems(keyList);
|
||||
_keybindingWidgets.key->setCurrentIndex(-1);
|
||||
_keybindingWidgets.key->setEnabled(false);
|
||||
connect(
|
||||
_keybindingWidgets.key, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
[this](int newIndex) {
|
||||
_keybindingWidgets.saveButtons->button(QDialogButtonBox::Save)->setEnabled(
|
||||
newIndex > 0
|
||||
);
|
||||
}
|
||||
);
|
||||
layout->addWidget(_keybindingWidgets.key, 11, 2);
|
||||
|
||||
layout->addWidget(new QLabel("Action chooser"), 12, 1);
|
||||
_keybindingWidgets.action = new QComboBox;
|
||||
_keybindingWidgets.action->setToolTip(
|
||||
"You can select any of the actions defined above here to be associated with the "
|
||||
"selected keybind. Selecting an action from this dropdown menu will "
|
||||
"automatically enter it into the text field below and overwrite any value that "
|
||||
"already is entered in there"
|
||||
);
|
||||
for (const Profile::Action& action : _actionData) {
|
||||
_keybindingWidgets.action->addItem(QString::fromStdString(action.identifier));
|
||||
}
|
||||
connect(
|
||||
_keybindingWidgets.action, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &ActionDialog::keybindingActionSelected
|
||||
);
|
||||
|
||||
_keybindingWidgets.action->setEnabled(false);
|
||||
layout->addWidget(_keybindingWidgets.action, 12, 2);
|
||||
|
||||
layout->addWidget(new QLabel("Action"), 13, 1);
|
||||
_keybindingWidgets.actionText = new QLineEdit;
|
||||
_keybindingWidgets.actionText->setToolTip(
|
||||
"This is the action that will be triggered when the keybind is pressed. In the "
|
||||
"majority of cases, you do not need to enter something here manually, but "
|
||||
"instead select the action from the dropdown list above. However, if you know "
|
||||
"that an action with a specific identifier will exist at runtime, for example if "
|
||||
"it is defined in an asset included in this profile, you can enter the "
|
||||
"identifier of that action manually here to associate a key with it. If the "
|
||||
"identifer does not exist, an error will be logged when trying to bind the key "
|
||||
"at startup."
|
||||
);
|
||||
_keybindingWidgets.actionText->setEnabled(false);
|
||||
layout->addWidget(_keybindingWidgets.actionText, 13, 2);
|
||||
|
||||
|
||||
// +/- buttons
|
||||
QWidget* container = new QWidget;
|
||||
QBoxLayout* containerLayout = new QHBoxLayout(container);
|
||||
_keybindingWidgets.addButton = new QPushButton("+");
|
||||
_keybindingWidgets.addButton->setObjectName("add-button");
|
||||
_keybindingWidgets.addButton->setToolTip(
|
||||
"Adds a new keybinding to the list of all keybindings"
|
||||
);
|
||||
QObject::connect(
|
||||
_keybindingWidgets.addButton, &QPushButton::clicked,
|
||||
this, &ActionDialog::keybindingAdd
|
||||
);
|
||||
containerLayout->addWidget(_keybindingWidgets.addButton);
|
||||
|
||||
_keybindingWidgets.removeButton = new QPushButton("-");
|
||||
_keybindingWidgets.removeButton->setObjectName("remove-button");
|
||||
_keybindingWidgets.removeButton->setToolTip(
|
||||
"Removes the currently selected keybinding"
|
||||
);
|
||||
_keybindingWidgets.removeButton->setEnabled(false);
|
||||
QObject::connect(
|
||||
_keybindingWidgets.removeButton, &QPushButton::clicked,
|
||||
this, &ActionDialog::keybindingRemove
|
||||
);
|
||||
containerLayout->addWidget(_keybindingWidgets.removeButton);
|
||||
layout->addWidget(container, 14, 0, Qt::AlignLeft);
|
||||
|
||||
// Save/Cancel
|
||||
_keybindingWidgets.saveButtons = new QDialogButtonBox;
|
||||
_keybindingWidgets.saveButtons->setToolTip(
|
||||
"Saves or discards all changes to the currently selected keybinding"
|
||||
);
|
||||
_keybindingWidgets.saveButtons->setEnabled(false);
|
||||
_keybindingWidgets.saveButtons->setStandardButtons(
|
||||
QDialogButtonBox::Save | QDialogButtonBox::Cancel
|
||||
);
|
||||
QObject::connect(
|
||||
_keybindingWidgets.saveButtons, &QDialogButtonBox::accepted,
|
||||
this, &ActionDialog::keybindingSaved
|
||||
);
|
||||
QObject::connect(
|
||||
_keybindingWidgets.saveButtons, &QDialogButtonBox::rejected,
|
||||
this, &ActionDialog::keybindingRejected
|
||||
);
|
||||
|
||||
layout->addWidget(_keybindingWidgets.saveButtons, 14, 2, Qt::AlignRight);
|
||||
}
|
||||
|
||||
void ActionDialog::applyChanges() {
|
||||
*_actions = std::move(_actionData);
|
||||
*_keybindings = std::move(_keybindingsData);
|
||||
accept();
|
||||
}
|
||||
|
||||
Profile::Action* ActionDialog::selectedAction() {
|
||||
QListWidgetItem* item = _actionWidgets.list->currentItem();
|
||||
const int idx = _actionWidgets.list->row(item);
|
||||
return idx != -1 ? &_actionData[idx] : nullptr;
|
||||
}
|
||||
|
||||
void ActionDialog::actionAdd() {
|
||||
_actionWidgets.list->addItem("");
|
||||
_actionData.push_back(Profile::Action());
|
||||
_actionWidgets.list->setCurrentRow(_actionWidgets.list->count() - 1);
|
||||
}
|
||||
|
||||
void ActionDialog::actionRemove() {
|
||||
const openspace::Profile::Action* action = selectedAction();
|
||||
ghoul_assert(action, "An action must exist at this point");
|
||||
|
||||
ghoul_assert(
|
||||
_actionWidgets.list->count() == static_cast<int>(_actionData.size()),
|
||||
"Action list and data has desynced"
|
||||
);
|
||||
|
||||
// We can't remove an action if it has a keyboard shortcut attached to it
|
||||
for (size_t i = 0; i < _keybindingsData.size(); ++i) {
|
||||
const Profile::Keybinding& kb = _keybindingsData[i];
|
||||
if (kb.action != action->identifier) {
|
||||
continue;
|
||||
}
|
||||
QMessageBox::StandardButton button = QMessageBox::information(
|
||||
this,
|
||||
"Remove action",
|
||||
QString::fromStdString(fmt::format(
|
||||
"Action '{}' is used in the keybind '{}' and cannot be removed unless "
|
||||
"the keybind is removed as well. Do you want to remove the keybind as "
|
||||
"well?",
|
||||
action->identifier, ghoul::to_string(kb.key)
|
||||
)),
|
||||
QMessageBox::StandardButton::Yes,
|
||||
QMessageBox::StandardButton::No
|
||||
);
|
||||
if (button == QMessageBox::StandardButton::Yes) {
|
||||
_keybindingsData.erase(_keybindingsData.begin() + i);
|
||||
delete _keybindingWidgets.list->takeItem(static_cast<int>(i));
|
||||
i--;
|
||||
}
|
||||
else {
|
||||
// If the user chooses 'No' at least once, we have to bail
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < _actionData.size(); ++i) {
|
||||
if (_actionData[i].identifier == action->identifier) {
|
||||
_actionData.erase(_actionData.begin() + i);
|
||||
delete _actionWidgets.list->takeItem(static_cast<int>(i));
|
||||
clearActionFields();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ghoul_assert(false, "We shouldn't be able to get here");
|
||||
}
|
||||
|
||||
void ActionDialog::actionSelected() {
|
||||
const Profile::Action* action = selectedAction();
|
||||
if (action) {
|
||||
// Action selected
|
||||
_actionWidgets.identifier->setText(QString::fromStdString(action->identifier));
|
||||
_actionWidgets.identifier->setEnabled(true);
|
||||
_actionWidgets.name->setText(QString::fromStdString(action->name));
|
||||
_actionWidgets.name->setEnabled(true);
|
||||
_actionWidgets.guiPath->setText(QString::fromStdString(action->guiPath));
|
||||
_actionWidgets.guiPath->setEnabled(true);
|
||||
_actionWidgets.documentation->setText(
|
||||
QString::fromStdString(action->documentation)
|
||||
);
|
||||
_actionWidgets.documentation->setEnabled(true);
|
||||
_actionWidgets.isLocal->setChecked(action->isLocal);
|
||||
_actionWidgets.isLocal->setEnabled(true);
|
||||
_actionWidgets.script->setText(QString::fromStdString(action->script));
|
||||
_actionWidgets.script->setEnabled(true);
|
||||
_actionWidgets.addButton->setEnabled(false);
|
||||
_actionWidgets.removeButton->setEnabled(true);
|
||||
_actionWidgets.saveButtons->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
// No action selected
|
||||
_actionWidgets.addButton->setEnabled(true);
|
||||
_actionWidgets.removeButton->setEnabled(false);
|
||||
_actionWidgets.saveButtons->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionDialog::actionSaved() {
|
||||
std::string newIdentifier = _actionWidgets.identifier->text().toStdString();
|
||||
if (newIdentifier.empty()) {
|
||||
QMessageBox::critical(this, "Empty identifier", "Identifier must not be empty");
|
||||
return;
|
||||
}
|
||||
|
||||
Profile::Action* action = selectedAction();
|
||||
std::string oldIdentifier = action->identifier;
|
||||
if (oldIdentifier != newIdentifier) {
|
||||
// The identifier is a bit special as we need to make sure that we didn't
|
||||
// accidentally create a duplicate while renaming the currently selected action.
|
||||
// Also if we didn't create a duplicate, meaning that we renamed an action to a
|
||||
// new valid identifier, we need to make sure that we update all keybinds that
|
||||
// referenced the old value are updated to use the new name instead
|
||||
|
||||
const auto it = std::find_if(
|
||||
_actionData.begin(), _actionData.end(),
|
||||
[id = newIdentifier](const Profile::Action& a) { return a.identifier == id; }
|
||||
);
|
||||
if (it != _actionData.end()) {
|
||||
QMessageBox::critical(
|
||||
this,
|
||||
"Duplicate identifier",
|
||||
"The chosen identifier was already used in another action. Identifiers "
|
||||
"have to be unique. Please choose a different identfier."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got this far, we have a new identifier and it is a new one, so we need to
|
||||
// update other keybinds now
|
||||
ghoul_assert(
|
||||
_keybindingWidgets.list->count() == _keybindingsData.size(),
|
||||
"The list and data got out of sync"
|
||||
);
|
||||
for (int i = 0; i < _keybindingWidgets.list->count(); ++i) {
|
||||
if (_keybindingsData[i].action == oldIdentifier) {
|
||||
_keybindingsData[i].action = newIdentifier;
|
||||
updateListItem(_keybindingWidgets.list->item(i), _keybindingsData[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _keybindingWidgets.action->count(); ++i) {
|
||||
if (_keybindingWidgets.action->itemText(i).toStdString() == oldIdentifier) {
|
||||
_keybindingWidgets.action->setItemText(
|
||||
i,
|
||||
QString::fromStdString(newIdentifier)
|
||||
);
|
||||
}
|
||||
}
|
||||
action->identifier = newIdentifier;
|
||||
}
|
||||
|
||||
|
||||
action->name = _actionWidgets.name->text().toStdString();
|
||||
action->guiPath = _actionWidgets.guiPath->text().toStdString();
|
||||
action->documentation = _actionWidgets.documentation->text().toStdString();
|
||||
action->isLocal = _actionWidgets.isLocal->isChecked();
|
||||
action->script = _actionWidgets.script->toPlainText().toStdString();
|
||||
|
||||
updateListItem(_actionWidgets.list->currentItem(), *action);
|
||||
clearActionFields();
|
||||
}
|
||||
|
||||
void ActionDialog::clearActionFields() {
|
||||
_actionWidgets.list->setCurrentRow(-1);
|
||||
_actionWidgets.identifier->clear();
|
||||
_actionWidgets.identifier->setEnabled(false);
|
||||
_actionWidgets.name->clear();
|
||||
_actionWidgets.name->setEnabled(false);
|
||||
_actionWidgets.guiPath->clear();
|
||||
_actionWidgets.guiPath->setEnabled(false);
|
||||
_actionWidgets.documentation->clear();
|
||||
_actionWidgets.documentation->setEnabled(false);
|
||||
_actionWidgets.isLocal->setChecked(false);
|
||||
_actionWidgets.isLocal->setEnabled(false);
|
||||
_actionWidgets.script->clear();
|
||||
_actionWidgets.script->setEnabled(false);
|
||||
_actionWidgets.saveButtons->setEnabled(false);
|
||||
}
|
||||
|
||||
void ActionDialog::actionRejected() {
|
||||
if (_actionData.back().identifier.empty()) {
|
||||
// This happens if someone creates a new action and never gave an identifier
|
||||
delete _actionWidgets.list->takeItem(_actionWidgets.list->count() - 1);
|
||||
_actionData.erase(_actionData.begin() + _actionData.size() - 1);
|
||||
}
|
||||
|
||||
clearActionFields();
|
||||
}
|
||||
|
||||
Profile::Keybinding* ActionDialog::selectedKeybinding() {
|
||||
QListWidgetItem* item = _keybindingWidgets.list->currentItem();
|
||||
const int idx = _keybindingWidgets.list->row(item);
|
||||
return idx != -1 ? &_keybindingsData[idx] : nullptr;
|
||||
}
|
||||
|
||||
void ActionDialog::keybindingAdd() {
|
||||
_keybindingWidgets.list->addItem("");
|
||||
_keybindingsData.push_back(Profile::Keybinding());
|
||||
_keybindingWidgets.list->setCurrentRow(_keybindingWidgets.list->count() - 1);
|
||||
}
|
||||
|
||||
void ActionDialog::keybindingRemove() {
|
||||
const Profile::Keybinding* keybinding = selectedKeybinding();
|
||||
ghoul_assert(keybinding, "A keybinding must be selected at this point");
|
||||
|
||||
for (size_t i = 0; i < _keybindingsData.size(); ++i) {
|
||||
if (_keybindingsData[i].key == keybinding->key &&
|
||||
_keybindingsData[i].action == keybinding->action)
|
||||
{
|
||||
_keybindingsData.erase(_keybindingsData.begin() + i);
|
||||
delete _keybindingWidgets.list->takeItem(static_cast<int>(i));
|
||||
clearKeybindingFields();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionDialog::keybindingSelected() {
|
||||
const Profile::Keybinding* keybinding = selectedKeybinding();
|
||||
if (keybinding) {
|
||||
_keybindingWidgets.shiftModifier->setEnabled(true);
|
||||
_keybindingWidgets.shiftModifier->setChecked(
|
||||
hasKeyModifier(keybinding->key.modifier, KeyModifier::Shift)
|
||||
);
|
||||
_keybindingWidgets.ctrlModifier->setEnabled(true);
|
||||
_keybindingWidgets.ctrlModifier->setChecked(
|
||||
hasKeyModifier(keybinding->key.modifier, KeyModifier::Control)
|
||||
);
|
||||
_keybindingWidgets.altModifier->setEnabled(true);
|
||||
_keybindingWidgets.altModifier->setChecked(
|
||||
hasKeyModifier(keybinding->key.modifier, KeyModifier::Alt)
|
||||
);
|
||||
|
||||
std::string key = ghoul::to_string(keybinding->key.key);
|
||||
_keybindingWidgets.key->setCurrentText(QString::fromStdString(key));
|
||||
_keybindingWidgets.key->setEnabled(true);
|
||||
_keybindingWidgets.action->setCurrentText(
|
||||
QString::fromStdString(keybinding->action)
|
||||
);
|
||||
_keybindingWidgets.action->setEnabled(true);
|
||||
_keybindingWidgets.actionText->setText(
|
||||
QString::fromStdString(keybinding->action)
|
||||
);
|
||||
_keybindingWidgets.actionText->setEnabled(true);
|
||||
_keybindingWidgets.addButton->setEnabled(false);
|
||||
_keybindingWidgets.removeButton->setEnabled(true);
|
||||
|
||||
_keybindingWidgets.saveButtons->setEnabled(true);
|
||||
// Only enable the save buttons if a key is selected, otherwise we would get an
|
||||
// exception as the None key cannot be bound
|
||||
_keybindingWidgets.saveButtons->button(QDialogButtonBox::Save)->setEnabled(
|
||||
_keybindingWidgets.key->currentIndex() > 0
|
||||
);
|
||||
}
|
||||
else {
|
||||
// No keybinding selected
|
||||
_keybindingWidgets.addButton->setEnabled(true);
|
||||
_keybindingWidgets.removeButton->setEnabled(false);
|
||||
_keybindingWidgets.saveButtons->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionDialog::keybindingActionSelected(int) {
|
||||
_keybindingWidgets.actionText->setText(_keybindingWidgets.action->currentText());
|
||||
}
|
||||
|
||||
void ActionDialog::keybindingSaved() {
|
||||
Profile::Keybinding* keybinding = selectedKeybinding();
|
||||
ghoul_assert(keybinding, "There must be a selected keybinding at this point");
|
||||
|
||||
KeyModifier km = KeyModifier::None;
|
||||
if (_keybindingWidgets.shiftModifier->isChecked()) {
|
||||
km |= KeyModifier::Shift;
|
||||
}
|
||||
if (_keybindingWidgets.altModifier->isChecked()) {
|
||||
km |= KeyModifier::Alt;
|
||||
}
|
||||
if (_keybindingWidgets.ctrlModifier->isChecked()) {
|
||||
km |= KeyModifier::Control;
|
||||
}
|
||||
|
||||
keybinding->key = stringToKey(_keybindingWidgets.key->currentText().toStdString());
|
||||
keybinding->key.modifier = km;
|
||||
keybinding->action = _keybindingWidgets.actionText->text().toStdString();
|
||||
|
||||
updateListItem(_keybindingWidgets.list->currentItem(), *keybinding);
|
||||
clearKeybindingFields();
|
||||
}
|
||||
|
||||
void ActionDialog::clearKeybindingFields() {
|
||||
_keybindingWidgets.list->setCurrentRow(-1);
|
||||
_keybindingWidgets.shiftModifier->setChecked(false);
|
||||
_keybindingWidgets.shiftModifier->setEnabled(false);
|
||||
_keybindingWidgets.ctrlModifier->setChecked(false);
|
||||
_keybindingWidgets.ctrlModifier->setEnabled(false);
|
||||
_keybindingWidgets.altModifier->setChecked(false);
|
||||
_keybindingWidgets.altModifier->setEnabled(false);
|
||||
_keybindingWidgets.key->setCurrentIndex(-1);
|
||||
_keybindingWidgets.key->setEnabled(false);
|
||||
_keybindingWidgets.action->setCurrentIndex(-1);
|
||||
_keybindingWidgets.action->setEnabled(false);
|
||||
_keybindingWidgets.actionText->clear();
|
||||
_keybindingWidgets.actionText->setEnabled(false);
|
||||
}
|
||||
|
||||
void ActionDialog::keybindingRejected() {
|
||||
clearKeybindingFields();
|
||||
}
|
||||
@@ -33,18 +33,19 @@
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <sstream>
|
||||
|
||||
AdditionalScriptsDialog::AdditionalScriptsDialog(openspace::Profile& profile,
|
||||
QWidget* parent)
|
||||
AdditionalScriptsDialog::AdditionalScriptsDialog(QWidget* parent,
|
||||
std::vector<std::string>* scripts)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _scripts(scripts)
|
||||
, _scriptsData(*_scripts)
|
||||
{
|
||||
setWindowTitle("Additional Scripts");
|
||||
createWidgets();
|
||||
|
||||
std::vector<std::string> scripts = _profile.additionalScripts();
|
||||
std::string scriptText = std::accumulate(
|
||||
scripts.begin(), scripts.end(),
|
||||
_scriptsData.begin(), _scriptsData.end(),
|
||||
std::string(), [](std::string lhs, std::string rhs) { return lhs + rhs + '\n'; }
|
||||
);
|
||||
_textScripts->setText(QString::fromStdString(std::move(scriptText)));
|
||||
@@ -95,7 +96,7 @@ void AdditionalScriptsDialog::parseScript() {
|
||||
std::getline(iss, s);
|
||||
additionalScripts.push_back(std::move(s));
|
||||
}
|
||||
_profile.setAdditionalScripts(additionalScripts);
|
||||
*_scripts = std::move(additionalScripts);
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "profile/line.h"
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
@@ -125,17 +126,15 @@ namespace {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
AssetsDialog::AssetsDialog(openspace::Profile& profile, const std::string& assetBasePath,
|
||||
const std::string& userAssetBasePath, QWidget* parent)
|
||||
AssetsDialog::AssetsDialog(QWidget* parent, openspace::Profile* profile,
|
||||
const std::string& assetBasePath,
|
||||
const std::string& userAssetBasePath)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
{
|
||||
setWindowTitle("Assets");
|
||||
_assetTreeModel.importModelData(assetBasePath, userAssetBasePath);
|
||||
createWidgets();
|
||||
}
|
||||
|
||||
void AssetsDialog::createWidgets() {
|
||||
QBoxLayout* layout = new QVBoxLayout(this);
|
||||
{
|
||||
QGridLayout* container = new QGridLayout;
|
||||
@@ -177,10 +176,10 @@ void AssetsDialog::createWidgets() {
|
||||
connect(_assetTree, &QTreeView::clicked, this, &AssetsDialog::selected);
|
||||
|
||||
|
||||
for (const std::string& a : _profile.assets()) {
|
||||
QModelIndex parent = _assetTreeModel.index(-1, 0);
|
||||
int nRows = _assetTreeModel.rowCount(parent);
|
||||
traverseToFindFilesystemMatch(_assetTreeModel, parent, nRows, a);
|
||||
for (const std::string& a : _profile->assets) {
|
||||
QModelIndex p = _assetTreeModel.index(-1, 0);
|
||||
int nRows = _assetTreeModel.rowCount(p);
|
||||
traverseToFindFilesystemMatch(_assetTreeModel, p, nRows, a);
|
||||
}
|
||||
|
||||
int nRows = _assetTreeModel.rowCount(_assetTreeModel.index(-1, 0));
|
||||
@@ -190,18 +189,19 @@ void AssetsDialog::createWidgets() {
|
||||
nRows,
|
||||
_assetTreeModel.index(-1, 0)
|
||||
);
|
||||
layout->addWidget(_assetTree);
|
||||
layout->addWidget(_assetTree, 4);
|
||||
}
|
||||
{
|
||||
QWidget* box = new QWidget;
|
||||
QBoxLayout* boxLayout = new QVBoxLayout(box);
|
||||
QLabel* summaryHeading = new QLabel("Selection summary");
|
||||
summaryHeading->setObjectName("heading");
|
||||
layout->addWidget(summaryHeading);
|
||||
}
|
||||
{
|
||||
boxLayout->addWidget(summaryHeading);
|
||||
_summary = new QTextEdit;
|
||||
_summary->setReadOnly(true);
|
||||
_summary->setText(createTextSummary());
|
||||
layout->addWidget(_summary);
|
||||
boxLayout->addWidget(_summary);
|
||||
layout->addWidget(box, 1);
|
||||
}
|
||||
|
||||
layout->addWidget(new Line);
|
||||
@@ -247,13 +247,13 @@ void AssetsDialog::openAssetEditor(const std::string& asset) {
|
||||
}
|
||||
|
||||
void AssetsDialog::parseSelections() {
|
||||
_profile.clearAssets();
|
||||
_profile->assets.clear();
|
||||
std::vector<std::string> summaryPaths;
|
||||
std::vector<AssetTreeItem*> summaryItems;
|
||||
_assetTreeModel.getSelectedAssets(summaryPaths, summaryItems);
|
||||
|
||||
for (const std::string& sel : summaryPaths) {
|
||||
_profile.addAsset(sel);
|
||||
_profile->addAsset(sel);
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
@@ -149,9 +149,6 @@ void AssetTreeModel::importModelData(const std::string& assetBasePath,
|
||||
const std::string& userAssetBasePath) {
|
||||
FileSystemAccess assets(
|
||||
".asset",
|
||||
// @TODO (abock, 2021-03-24) We need some better solution for this; what is the
|
||||
// problem of just including all subfolders instead?
|
||||
{ "scene", "global", "customization", "dashboard", "examples", "util" },
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "profile/cameradialog.h"
|
||||
|
||||
#include "profile/line.h"
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleValidator>
|
||||
#include <QFrame>
|
||||
@@ -56,15 +55,16 @@ namespace {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CameraDialog::CameraDialog(openspace::Profile& profile, QWidget *parent)
|
||||
CameraDialog::CameraDialog(QWidget* parent,
|
||||
std::optional<openspace::Profile::CameraType>* camera)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _camera(camera)
|
||||
{
|
||||
setWindowTitle("Set Camera Position");
|
||||
createWidgets();
|
||||
|
||||
if (_profile.camera().has_value()) {
|
||||
openspace::Profile::CameraType type = *_profile.camera();
|
||||
if (_camera->has_value()) {
|
||||
const openspace::Profile::CameraType& type = **_camera;
|
||||
std::visit(overloaded {
|
||||
[this](const openspace::Profile::CameraNavState& nav) {
|
||||
_tabWidget->setCurrentIndex(CameraTypeNav);
|
||||
@@ -406,7 +406,7 @@ void CameraDialog::approved() {
|
||||
else {
|
||||
nav.pitch = std::nullopt;
|
||||
}
|
||||
_profile.setCamera(nav);
|
||||
*_camera = std::move(nav);
|
||||
}
|
||||
else if (_tabWidget->currentIndex() == CameraTypeGeo) {
|
||||
openspace::Profile::CameraGoToGeo geo;
|
||||
@@ -416,7 +416,7 @@ void CameraDialog::approved() {
|
||||
if (!_geoState.altitude->text().isEmpty()) {
|
||||
geo.altitude = _geoState.altitude->text().toDouble();
|
||||
}
|
||||
_profile.setCamera(geo);
|
||||
*_camera = std::move(geo);
|
||||
}
|
||||
|
||||
accept();
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "profile/line.h"
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleValidator>
|
||||
#include <QEvent>
|
||||
@@ -79,16 +80,15 @@ namespace {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent)
|
||||
DeltaTimesDialog::DeltaTimesDialog(QWidget* parent, std::vector<double>* deltaTimes)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _deltaTimes(deltaTimes)
|
||||
, _deltaTimesData(*_deltaTimes)
|
||||
{
|
||||
setWindowTitle("Simulation Time Increments");
|
||||
createWidgets();
|
||||
|
||||
_data = _profile.deltaTimes();
|
||||
|
||||
for (size_t d = 0; d < _data.size(); ++d) {
|
||||
for (size_t d = 0; d < _deltaTimesData.size(); ++d) {
|
||||
std::string summary = createSummaryForDeltaTime(d, true);
|
||||
_listWidget->addItem(new QListWidgetItem(QString::fromStdString(summary)));
|
||||
}
|
||||
@@ -203,7 +203,11 @@ std::string DeltaTimesDialog::createSummaryForDeltaTime(size_t idx, bool forList
|
||||
}
|
||||
|
||||
if (forListView) {
|
||||
s += '\t' + std::to_string(_data.at(idx)) + '\t' + timeDescription(_data.at(idx));
|
||||
s += fmt::format(
|
||||
"\t{}\t{}",
|
||||
std::to_string(_deltaTimesData.at(idx)),
|
||||
timeDescription(_deltaTimesData.at(idx))
|
||||
);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -212,16 +216,16 @@ void DeltaTimesDialog::listItemSelected() {
|
||||
QListWidgetItem *item = _listWidget->currentItem();
|
||||
int index = _listWidget->row(item);
|
||||
|
||||
if (index < (static_cast<int>(_data.size()) - 1)) {
|
||||
if (index < (static_cast<int>(_deltaTimesData.size()) - 1)) {
|
||||
_listWidget->setCurrentRow(index);
|
||||
}
|
||||
|
||||
if (!_data.empty()) {
|
||||
if (_data.at(index) == 0) {
|
||||
if (!_deltaTimesData.empty()) {
|
||||
if (_deltaTimesData.at(index) == 0) {
|
||||
_seconds->clear();
|
||||
}
|
||||
else {
|
||||
_seconds->setText(QString::number(_data.at(index)));
|
||||
_seconds->setText(QString::number(_deltaTimesData.at(index)));
|
||||
}
|
||||
}
|
||||
_editModeNewItem = true;
|
||||
@@ -230,8 +234,8 @@ void DeltaTimesDialog::listItemSelected() {
|
||||
|
||||
void DeltaTimesDialog::setLabelForKey(int index, bool editMode, std::string color) {
|
||||
std::string labelS = "Set Simulation Time Increment for key";
|
||||
if (index >= static_cast<int>(_data.size())) {
|
||||
index = static_cast<int>(_data.size()) - 1;
|
||||
if (index >= static_cast<int>(_deltaTimesData.size())) {
|
||||
index = static_cast<int>(_deltaTimesData.size()) - 1;
|
||||
}
|
||||
if (editMode) {
|
||||
labelS += " '" + createSummaryForDeltaTime(index, false) + "':";
|
||||
@@ -261,7 +265,7 @@ bool DeltaTimesDialog::isLineEmpty(int index) {
|
||||
if (!_listWidget->item(index)->text().isEmpty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
if (!_data.empty() && (_data.at(0) != 0)) {
|
||||
if (!_deltaTimesData.empty() && (_deltaTimesData.at(0) != 0)) {
|
||||
isEmpty = false;
|
||||
}
|
||||
return isEmpty;
|
||||
@@ -275,11 +279,11 @@ void DeltaTimesDialog::addDeltaTimeValue() {
|
||||
// Special case where list is "empty" but really has one line that is blank.
|
||||
// This is done because QListWidget does not seem to like having its sole
|
||||
// remaining item being removed.
|
||||
_data.at(0) = 0;
|
||||
_deltaTimesData.at(0) = 0;
|
||||
_listWidget->item(0)->setText(messageAddValue);
|
||||
}
|
||||
else if (_data.size() < MaxNumberOfKeys) {
|
||||
_data.push_back(0);
|
||||
else if (_deltaTimesData.size() < MaxNumberOfKeys) {
|
||||
_deltaTimesData.push_back(0);
|
||||
_listWidget->addItem(new QListWidgetItem(messageAddValue));
|
||||
}
|
||||
else {
|
||||
@@ -294,8 +298,8 @@ void DeltaTimesDialog::saveDeltaTimeValue() {
|
||||
QListWidgetItem* item = _listWidget->currentItem();
|
||||
if (item != nullptr) {
|
||||
int index = _listWidget->row(item);
|
||||
if (_data.size() > 0) {
|
||||
_data.at(index) = _seconds->text().toDouble();
|
||||
if (_deltaTimesData.size() > 0) {
|
||||
_deltaTimesData.at(index) = _seconds->text().toDouble();
|
||||
std::string summary = createSummaryForDeltaTime(index, true);
|
||||
_listWidget->item(index)->setText(QString::fromStdString(summary));
|
||||
transitionEditMode(index, false);
|
||||
@@ -307,7 +311,7 @@ void DeltaTimesDialog::saveDeltaTimeValue() {
|
||||
void DeltaTimesDialog::discardDeltaTimeValue() {
|
||||
listItemSelected();
|
||||
transitionEditMode(_listWidget->count() - 1, false);
|
||||
if (_editModeNewItem && !_data.empty() && _data.back() == 0) {
|
||||
if (_editModeNewItem && !_deltaTimesData.empty() && _deltaTimesData.back() == 0) {
|
||||
removeDeltaTimeValue();
|
||||
}
|
||||
_editModeNewItem = false;
|
||||
@@ -316,13 +320,13 @@ void DeltaTimesDialog::discardDeltaTimeValue() {
|
||||
void DeltaTimesDialog::removeDeltaTimeValue() {
|
||||
if (_listWidget->count() > 0) {
|
||||
if (_listWidget->count() == 1) {
|
||||
_data.at(0) = 0;
|
||||
_deltaTimesData.at(0) = 0;
|
||||
_listWidget->item(0)->setText("");
|
||||
}
|
||||
else {
|
||||
delete _listWidget->takeItem(_listWidget->count() - 1);
|
||||
if (!_data.empty()) {
|
||||
_data.pop_back();
|
||||
if (!_deltaTimesData.empty()) {
|
||||
_deltaTimesData.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,20 +359,20 @@ void DeltaTimesDialog::transitionEditMode(int index, bool state) {
|
||||
}
|
||||
|
||||
void DeltaTimesDialog::parseSelections() {
|
||||
if ((_data.size() == 1) && (_data.at(0) == 0)) {
|
||||
_data.clear();
|
||||
if ((_deltaTimesData.size() == 1) && (_deltaTimesData.at(0) == 0)) {
|
||||
_deltaTimesData.clear();
|
||||
}
|
||||
int finalNonzeroIndex = static_cast<int>(_data.size()) - 1;
|
||||
int finalNonzeroIndex = static_cast<int>(_deltaTimesData.size()) - 1;
|
||||
for (; finalNonzeroIndex >= 0; --finalNonzeroIndex) {
|
||||
if (_data.at(finalNonzeroIndex) != 0) {
|
||||
if (_deltaTimesData.at(finalNonzeroIndex) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::vector<double> tempDt;
|
||||
for (int i = 0; i < (finalNonzeroIndex + 1); ++i) {
|
||||
tempDt.push_back(_data[i]);
|
||||
tempDt.push_back(_deltaTimesData[i]);
|
||||
}
|
||||
_profile.setDeltaTimes(tempDt);
|
||||
*_deltaTimes = std::move(_deltaTimesData);
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,542 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include "profile/keybindingsdialog.h"
|
||||
|
||||
#include "profile/line.h"
|
||||
#include "profile/scriptlogdialog.h"
|
||||
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <openspace/util/keys.h>
|
||||
#include <qevent.h>
|
||||
#include <algorithm>
|
||||
#include <QKeyEvent>
|
||||
#include <QCheckBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QTextEdit>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
using namespace openspace;
|
||||
|
||||
namespace {
|
||||
const Profile::Keybinding BlankKey= {
|
||||
{ Key::Unknown, KeyModifier::NoModifier },
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
true,
|
||||
""
|
||||
};
|
||||
|
||||
void replaceChars(std::string& src, const std::string& from, const std::string& to) {
|
||||
std::string newString;
|
||||
std::string::size_type found, last = 0;
|
||||
|
||||
while ((found = src.find(from, last)) != std::string::npos) {
|
||||
newString.append(src, last, (found - last));
|
||||
newString += to;
|
||||
last = found + from.length();
|
||||
}
|
||||
newString += src.substr(last);
|
||||
src.swap(newString);
|
||||
}
|
||||
|
||||
std::string truncateString(std::string& s) {
|
||||
const size_t maxLength = 50;
|
||||
replaceChars(s, "\n", ";");
|
||||
if (s.length() > maxLength) {
|
||||
s.resize(maxLength);
|
||||
s += "...";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string createOneLineSummary(Profile::Keybinding k) {
|
||||
std::string summary;
|
||||
|
||||
int keymod = static_cast<int>(k.key.modifier);
|
||||
if (keymod != static_cast<int>(KeyModifier::NoModifier)) {
|
||||
summary += KeyModifierNames.at(keymod) + " ";
|
||||
}
|
||||
int keyname = static_cast<int>(k.key.key);
|
||||
|
||||
summary += KeyNames.at(keyname) + " ";
|
||||
summary += truncateString(k.name) + " (";
|
||||
summary += truncateString(k.documentation) + ") @ ";
|
||||
summary += truncateString(k.guiPath) + " ";
|
||||
summary += (k.isLocal) ? "local" : "remote";
|
||||
summary += " `" + truncateString(k.script) + "`";
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
KeybindingsDialog::KeybindingsDialog(Profile& profile, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _data(_profile.keybindings())
|
||||
{
|
||||
setWindowTitle("Assign Keybindings");
|
||||
createWidgets();
|
||||
|
||||
transitionFromEditMode();
|
||||
}
|
||||
|
||||
void KeybindingsDialog::appendScriptsToKeybind(std::string scripts) {
|
||||
_scriptEdit->append(QString::fromStdString(std::move(scripts)));
|
||||
}
|
||||
|
||||
void KeybindingsDialog::createWidgets() {
|
||||
QBoxLayout* layout = new QVBoxLayout(this);
|
||||
{
|
||||
_list = new QListWidget;
|
||||
connect(
|
||||
_list, &QListWidget::itemSelectionChanged,
|
||||
this, &KeybindingsDialog::listItemSelected
|
||||
);
|
||||
_list->setAlternatingRowColors(true);
|
||||
_list->setMovement(QListView::Free);
|
||||
_list->setResizeMode(QListView::Adjust);
|
||||
|
||||
for (size_t i = 0; i < _data.size(); ++i) {
|
||||
std::string summary = createOneLineSummary(_data[i]);
|
||||
_list->addItem(new QListWidgetItem(QString::fromStdString(summary)));
|
||||
}
|
||||
|
||||
layout->addWidget(_list);
|
||||
}
|
||||
{
|
||||
QBoxLayout* box = new QHBoxLayout;
|
||||
_addButton = new QPushButton("Add new");
|
||||
connect(
|
||||
_addButton, &QPushButton::clicked,
|
||||
this, &KeybindingsDialog::listItemAdded
|
||||
);
|
||||
box->addWidget(_addButton);
|
||||
|
||||
_removeButton = new QPushButton("Remove");
|
||||
connect(
|
||||
_removeButton, &QPushButton::clicked,
|
||||
this, &KeybindingsDialog::listItemRemove
|
||||
);
|
||||
box->addWidget(_removeButton);
|
||||
box->addStretch();
|
||||
layout->addLayout(box);
|
||||
}
|
||||
layout->addWidget(new Line);
|
||||
{
|
||||
QGridLayout* box = new QGridLayout;
|
||||
|
||||
_keyModLabel = new QLabel("Key Modifier");
|
||||
box->addWidget(_keyModLabel, 0, 0);
|
||||
_keyModCombo = new QComboBox;
|
||||
_keyModCombo->setToolTip(
|
||||
"Modifier keys to hold while key is pressed (blank means none)"
|
||||
);
|
||||
|
||||
QStringList comboModKeysStringList;
|
||||
int modIdx = 0;
|
||||
for (const std::pair<const int, std::string>& m : KeyModifierNames) {
|
||||
comboModKeysStringList += QString::fromStdString(m.second);
|
||||
_mapModKeyComboBoxIndexToKeyValue.push_back(modIdx++);
|
||||
}
|
||||
_keyModCombo->addItems(comboModKeysStringList);
|
||||
box->addWidget(_keyModCombo, 0, 1);
|
||||
|
||||
|
||||
_keyLabel = new QLabel("Key");
|
||||
box->addWidget(_keyLabel, 1, 0);
|
||||
_keyCombo = new QComboBox;
|
||||
_keyCombo->setToolTip("Key to press for this keybinding");
|
||||
|
||||
QStringList comboKeysStringList;
|
||||
for (int i = 0; i < static_cast<int>(Key::Last); ++i) {
|
||||
if (KeyNames.find(i) != KeyNames.end()) {
|
||||
comboKeysStringList += QString::fromStdString(KeyNames.at(i));
|
||||
// Create map to relate key combo box to integer value defined in Key
|
||||
_mapKeyComboBoxIndexToKeyValue.push_back(i);
|
||||
}
|
||||
}
|
||||
_keyCombo->addItems(comboKeysStringList);
|
||||
connect(
|
||||
_keyCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &KeybindingsDialog::keySelected
|
||||
);
|
||||
box->addWidget(_keyCombo, 1, 1);
|
||||
|
||||
|
||||
_nameLabel = new QLabel("Name:");
|
||||
box->addWidget(_nameLabel, 2, 0);
|
||||
_nameEdit = new QLineEdit;
|
||||
_nameEdit->setToolTip("Name assigned to this keybinding");
|
||||
box->addWidget(_nameEdit, 2, 1);
|
||||
|
||||
|
||||
_guiPathLabel = new QLabel("GUI Path:");
|
||||
box->addWidget(_guiPathLabel, 3, 0);
|
||||
_guiPathEdit = new QLineEdit;
|
||||
_guiPathEdit->setToolTip(
|
||||
"[OPTIONAL] Path for where this keybinding appears in GUI menu"
|
||||
);
|
||||
box->addWidget(_guiPathEdit, 3, 1);
|
||||
|
||||
|
||||
_documentationLabel = new QLabel("Documentation:");
|
||||
box->addWidget(_documentationLabel, 4, 0);
|
||||
_documentationEdit = new QLineEdit;
|
||||
_documentationEdit->setToolTip(
|
||||
"[OPTIONAL] Documentation entry for keybinding"
|
||||
);
|
||||
box->addWidget(_documentationEdit, 4, 1);
|
||||
|
||||
|
||||
_localCheck = new QCheckBox("Local");
|
||||
_localCheck->setToolTip(
|
||||
"Determines whether the command, when executed, should be shared with "
|
||||
"connected instances or only executed locally"
|
||||
);
|
||||
box->addWidget(_localCheck, 5, 0, 1, 2);
|
||||
|
||||
|
||||
_scriptLabel = new QLabel("Script");
|
||||
box->addWidget(_scriptLabel, 6, 0, 1, 2);
|
||||
|
||||
_chooseScriptsButton = new QPushButton("Choose Scripts");
|
||||
connect(
|
||||
_chooseScriptsButton, &QPushButton::clicked,
|
||||
this, &KeybindingsDialog::chooseScripts
|
||||
);
|
||||
box->addWidget(_chooseScriptsButton, 6, 1, 1, 1);
|
||||
|
||||
_scriptEdit = new QTextEdit;
|
||||
_scriptEdit->setAcceptRichText(false);
|
||||
_scriptEdit->setToolTip("Command(s) to execute at keypress event");
|
||||
_scriptEdit->setTabChangesFocus(true);
|
||||
box->addWidget(_scriptEdit, 7, 0, 1, 2);
|
||||
box->setRowStretch(7, 1);
|
||||
|
||||
QBoxLayout* buttonBox = new QHBoxLayout;
|
||||
_saveButton = new QPushButton("Save");
|
||||
connect(
|
||||
_saveButton, &QPushButton::clicked,
|
||||
this, &KeybindingsDialog::listItemSave
|
||||
);
|
||||
buttonBox->addWidget(_saveButton);
|
||||
|
||||
_cancelButton = new QPushButton("Cancel");
|
||||
connect(
|
||||
_cancelButton, &QPushButton::clicked,
|
||||
this, &KeybindingsDialog::listItemCancelSave
|
||||
);
|
||||
buttonBox->addWidget(_cancelButton);
|
||||
buttonBox->addStretch();
|
||||
box->addLayout(buttonBox, 8, 1, 1, 2);
|
||||
layout->addLayout(box);
|
||||
}
|
||||
layout->addWidget(new Line);
|
||||
{
|
||||
QBoxLayout* footerLayout = new QHBoxLayout;
|
||||
|
||||
_errorMsg = new QLabel;
|
||||
_errorMsg->setObjectName("error-message");
|
||||
_errorMsg->setWordWrap(true);
|
||||
footerLayout->addWidget(_errorMsg);
|
||||
|
||||
_buttonBox = new QDialogButtonBox;
|
||||
_buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
|
||||
QObject::connect(
|
||||
_buttonBox, &QDialogButtonBox::accepted,
|
||||
this, &KeybindingsDialog::parseSelections
|
||||
);
|
||||
QObject::connect(
|
||||
_buttonBox, &QDialogButtonBox::rejected,
|
||||
this, &KeybindingsDialog::reject
|
||||
);
|
||||
footerLayout->addWidget(_buttonBox);
|
||||
layout->addLayout(footerLayout);
|
||||
}
|
||||
}
|
||||
|
||||
void KeybindingsDialog::listItemSelected() {
|
||||
QListWidgetItem *item = _list->currentItem();
|
||||
int index = _list->row(item);
|
||||
|
||||
if (_data.size() > 0) {
|
||||
Profile::Keybinding& k = _data[index];
|
||||
const int modifierKey = indexInKeyMapping(
|
||||
_mapModKeyComboBoxIndexToKeyValue,
|
||||
static_cast<int>(k.key.modifier)
|
||||
);
|
||||
_keyModCombo->setCurrentIndex(modifierKey);
|
||||
|
||||
if (k.key.key == Key::Unknown) {
|
||||
_keyCombo->setCurrentIndex(0);
|
||||
}
|
||||
else {
|
||||
const int key = indexInKeyMapping(
|
||||
_mapKeyComboBoxIndexToKeyValue,
|
||||
static_cast<int>(k.key.key)
|
||||
);
|
||||
_keyCombo->setCurrentIndex(key);
|
||||
}
|
||||
|
||||
// Do key here
|
||||
_nameEdit->setText(QString::fromStdString(k.name));
|
||||
_guiPathEdit->setText(QString::fromStdString(k.guiPath));
|
||||
_documentationEdit->setText(QString::fromStdString(k.documentation));
|
||||
_localCheck->setChecked(k.isLocal);
|
||||
_scriptEdit->setText(QString::fromStdString(k.script));
|
||||
}
|
||||
transitionToEditMode();
|
||||
}
|
||||
|
||||
void KeybindingsDialog::keySelected(int index) {
|
||||
const QString numKeyWarning = "Warning: Using a number key may conflict with the "
|
||||
"keybindings for simulation time increments.";
|
||||
QString errorContents = _errorMsg->text();
|
||||
bool alreadyContainsWarning = (errorContents.length() >= numKeyWarning.length() &&
|
||||
errorContents.left(numKeyWarning.length()) == numKeyWarning);
|
||||
if (_mapKeyComboBoxIndexToKeyValue[index] >= static_cast<int>(Key::Num0)
|
||||
&& _mapKeyComboBoxIndexToKeyValue[index] <= static_cast<int>(Key::Num9))
|
||||
{
|
||||
if (!alreadyContainsWarning) {
|
||||
errorContents = numKeyWarning + errorContents;
|
||||
_errorMsg->setText(errorContents);
|
||||
}
|
||||
}
|
||||
else if (alreadyContainsWarning) {
|
||||
_errorMsg->setText(errorContents.mid(numKeyWarning.length()));
|
||||
}
|
||||
}
|
||||
|
||||
int KeybindingsDialog::indexInKeyMapping(std::vector<int>& mapVector, int keyInt) {
|
||||
const auto it = std::find(mapVector.cbegin(), mapVector.cend(), keyInt);
|
||||
return static_cast<int>(std::distance(mapVector.cbegin(), it));
|
||||
}
|
||||
|
||||
bool KeybindingsDialog::isLineEmpty(int index) {
|
||||
bool isEmpty = true;
|
||||
if (!_list->item(index)->text().isEmpty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
if (!_data.empty() && !_data.at(0).name.empty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
void KeybindingsDialog::listItemAdded() {
|
||||
_data.push_back(BlankKey);
|
||||
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
|
||||
// Scroll down to that blank line highlighted
|
||||
_list->setCurrentRow(_list->count() - 1);
|
||||
|
||||
// Blank-out the 2 text fields, set combo box to index 0
|
||||
_keyModCombo->setCurrentIndex(static_cast<int>(_data.back().key.modifier));
|
||||
if (_data.back().key.key == Key::Unknown) {
|
||||
_keyCombo->setCurrentIndex(0);
|
||||
}
|
||||
else {
|
||||
_keyCombo->setCurrentIndex(static_cast<int>(_data.back().key.key));
|
||||
}
|
||||
_keyModCombo->setFocus(Qt::OtherFocusReason);
|
||||
_nameEdit->setText(QString::fromStdString(_data.back().name));
|
||||
_guiPathEdit->setText("/");
|
||||
_documentationEdit->setText(QString::fromStdString(_data.back().documentation));
|
||||
_localCheck->setChecked(false);
|
||||
_scriptEdit->setText(QString::fromStdString(_data.back().script));
|
||||
|
||||
_editModeNewItem = true;
|
||||
}
|
||||
|
||||
void KeybindingsDialog::listItemSave() {
|
||||
if (!areRequiredFormsFilled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem* item = _list->currentItem();
|
||||
int index = _list->row(item);
|
||||
|
||||
if (!_data.empty()) {
|
||||
int keyModIdx = _mapModKeyComboBoxIndexToKeyValue.at(
|
||||
_keyModCombo->currentIndex());
|
||||
_data[index].key.modifier = static_cast<KeyModifier>(keyModIdx);
|
||||
int keyIdx = _mapKeyComboBoxIndexToKeyValue.at(_keyCombo->currentIndex());
|
||||
_data[index].key.key = static_cast<Key>(keyIdx);
|
||||
_data[index].name = _nameEdit->text().toStdString();
|
||||
_data[index].guiPath = _guiPathEdit->text().toStdString();
|
||||
_data[index].documentation = _documentationEdit->text().toStdString();
|
||||
_data[index].script = _scriptEdit->toPlainText().toStdString();
|
||||
_data[index].isLocal = (_localCheck->isChecked());
|
||||
std::string summary = createOneLineSummary(_data[index]);
|
||||
_list->item(index)->setText(QString::fromStdString(summary));
|
||||
}
|
||||
transitionFromEditMode();
|
||||
}
|
||||
|
||||
bool KeybindingsDialog::areRequiredFormsFilled() {
|
||||
bool requiredFormsFilled = true;
|
||||
std::string errors;
|
||||
if (_keyCombo->currentIndex() < 0) {
|
||||
errors += "Missing key";
|
||||
requiredFormsFilled = false;
|
||||
}
|
||||
if (_nameEdit->text().length() == 0) {
|
||||
if (!errors.empty()) {
|
||||
errors += ", ";
|
||||
}
|
||||
errors += "Missing keybinding name";
|
||||
requiredFormsFilled = false;
|
||||
}
|
||||
if (_scriptEdit->toPlainText().isEmpty()) {
|
||||
if (!errors.empty()) {
|
||||
errors += ", ";
|
||||
}
|
||||
errors += "Missing script";
|
||||
requiredFormsFilled = false;
|
||||
}
|
||||
_errorMsg->setText(QString::fromStdString(errors));
|
||||
return requiredFormsFilled;
|
||||
}
|
||||
|
||||
void KeybindingsDialog::listItemCancelSave() {
|
||||
listItemSelected();
|
||||
transitionFromEditMode();
|
||||
if (_editModeNewItem && !_data.empty() &&
|
||||
(_data.back().name.length() == 0 || _data.back().script.length() == 0 ||
|
||||
_data.back().key.key == Key::Unknown))
|
||||
{
|
||||
listItemRemove();
|
||||
}
|
||||
_editModeNewItem = false;
|
||||
}
|
||||
|
||||
void KeybindingsDialog::listItemRemove() {
|
||||
if (_list->count() > 0) {
|
||||
if (_list->count() == 1) {
|
||||
// Special case where last remaining item is being removed (QListWidget does
|
||||
// not like the final item being removed so instead clear it & leave it)
|
||||
_data.at(0) = BlankKey;
|
||||
_list->item(0)->setText("");
|
||||
}
|
||||
else {
|
||||
int index = _list->currentRow();
|
||||
if (index >= 0 && index < _list->count()) {
|
||||
_list->takeItem(index);
|
||||
if (!_data.empty()) {
|
||||
_data.erase(_data.begin() + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_list->clearSelection();
|
||||
transitionFromEditMode();
|
||||
}
|
||||
|
||||
void KeybindingsDialog::transitionToEditMode() {
|
||||
_saveButton->setDisabled(true);
|
||||
_cancelButton->setDisabled(true);
|
||||
_buttonBox->setDisabled(true);
|
||||
_keyLabel->setText("<font color='black'>Key</font>");
|
||||
_keyModLabel->setText("<font color='black'>Key Modifier</font>");
|
||||
_nameLabel->setText("<font color='black'>Name</font>");
|
||||
_scriptLabel->setText("<font color='black'>Script</font>");
|
||||
_guiPathLabel->setText("<font color='black'>GUI Path</font>");
|
||||
_documentationLabel->setText("<font color='black'>Documentation</font>");
|
||||
|
||||
editBoxDisabled(false);
|
||||
_errorMsg->setText("");
|
||||
}
|
||||
|
||||
void KeybindingsDialog::transitionFromEditMode() {
|
||||
_list->setDisabled(false);
|
||||
_addButton->setDisabled(false);
|
||||
_removeButton->setDisabled(false);
|
||||
_saveButton->setDisabled(false);
|
||||
_cancelButton->setDisabled(false);
|
||||
_buttonBox->setDisabled(false);
|
||||
|
||||
_keyLabel->setText("<font color='light gray'>Key</font>");
|
||||
_keyModLabel->setText("<font color='light gray'>Key Modifier</font>");
|
||||
_nameLabel->setText("<font color='light gray'>Name</font>");
|
||||
_scriptLabel->setText("<font color='light gray'>Script</font>");
|
||||
_guiPathLabel->setText("<font color='light gray'>GUI Path</font>");
|
||||
_documentationLabel->setText("<font color='light gray'>Documentation</font>");
|
||||
editBoxDisabled(true);
|
||||
_errorMsg->setText("");
|
||||
}
|
||||
|
||||
void KeybindingsDialog::editBoxDisabled(bool disabled) {
|
||||
_keyLabel->setDisabled(disabled);
|
||||
_keyCombo->setDisabled(disabled);
|
||||
_keyModLabel->setDisabled(disabled);
|
||||
_keyModCombo->setDisabled(disabled);
|
||||
_nameLabel->setDisabled(disabled);
|
||||
_nameEdit->setDisabled(disabled);
|
||||
_guiPathLabel->setDisabled(disabled);
|
||||
_guiPathEdit->setDisabled(disabled);
|
||||
_documentationLabel->setDisabled(disabled);
|
||||
_documentationEdit->setDisabled(disabled);
|
||||
_localCheck->setDisabled(disabled);
|
||||
_scriptLabel->setDisabled(disabled);
|
||||
_scriptEdit->setDisabled(disabled);
|
||||
_cancelButton->setDisabled(disabled);
|
||||
_saveButton->setDisabled(disabled);
|
||||
_chooseScriptsButton->setDisabled(disabled);
|
||||
}
|
||||
|
||||
void KeybindingsDialog::parseSelections() {
|
||||
// Handle case with only one remaining but empty line
|
||||
if ((_data.size() == 1) && (_data.at(0).name.empty())) {
|
||||
_data.clear();
|
||||
}
|
||||
_profile.setKeybindings(_data);
|
||||
accept();
|
||||
}
|
||||
|
||||
void KeybindingsDialog::chooseScripts() {
|
||||
_errorMsg->clear();
|
||||
ScriptlogDialog d(this);
|
||||
connect(&d, &ScriptlogDialog::scriptsSelected, this, &KeybindingsDialog::appendScriptsToKeybind);
|
||||
d.exec();
|
||||
}
|
||||
|
||||
void KeybindingsDialog::keyPressEvent(QKeyEvent* evt) {
|
||||
if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) {
|
||||
return;
|
||||
}
|
||||
else if (evt->key() == Qt::Key_Escape) {
|
||||
if (_editModeNewItem) {
|
||||
listItemCancelSave();
|
||||
return;
|
||||
}
|
||||
}
|
||||
QDialog::keyPressEvent(evt);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
MarkNodesDialog::MarkNodesDialog(openspace::Profile& profile, QWidget* parent)
|
||||
MarkNodesDialog::MarkNodesDialog(QWidget* parent, std::vector<std::string>* markedNodes)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _data(_profile.markNodes())
|
||||
, _markedNodes(markedNodes)
|
||||
, _markedNodesData(*_markedNodes)
|
||||
{
|
||||
setWindowTitle("Mark Interesting Nodes");
|
||||
createWidgets();
|
||||
@@ -55,9 +55,9 @@ void MarkNodesDialog::createWidgets() {
|
||||
_list->setMovement(QListView::Free);
|
||||
_list->setResizeMode(QListView::Adjust);
|
||||
|
||||
for (size_t i = 0; i < _data.size(); ++i) {
|
||||
for (size_t i = 0; i < _markedNodesData.size(); ++i) {
|
||||
_markedNodesListItems.push_back(
|
||||
new QListWidgetItem(QString::fromStdString(_data[i]))
|
||||
new QListWidgetItem(QString::fromStdString(_markedNodesData[i]))
|
||||
);
|
||||
_list->addItem(_markedNodesListItems[i]);
|
||||
}
|
||||
@@ -110,12 +110,17 @@ void MarkNodesDialog::listItemAdded() {
|
||||
}
|
||||
|
||||
std::string itemToAdd = _newNode->text().toStdString();
|
||||
const auto it = std::find(_data.cbegin(), _data.cend(), itemToAdd);
|
||||
if (it != _data.end()) {
|
||||
_list->setCurrentRow(static_cast<int>(std::distance(_data.cbegin(), it)));
|
||||
const auto it = std::find(
|
||||
_markedNodesData.cbegin(), _markedNodesData.cend(),
|
||||
itemToAdd
|
||||
);
|
||||
if (it != _markedNodesData.end()) {
|
||||
_list->setCurrentRow(
|
||||
static_cast<int>(std::distance(_markedNodesData.cbegin(), it))
|
||||
);
|
||||
}
|
||||
else {
|
||||
_data.push_back(itemToAdd);
|
||||
_markedNodesData.push_back(itemToAdd);
|
||||
_markedNodesListItems.push_back(new QListWidgetItem(_newNode->text()));
|
||||
_list->addItem(_markedNodesListItems.back());
|
||||
|
||||
@@ -136,12 +141,12 @@ void MarkNodesDialog::listItemRemove() {
|
||||
}
|
||||
|
||||
_list->takeItem(index);
|
||||
_data.erase(_data.begin() + index);
|
||||
_markedNodesData.erase(_markedNodesData.begin() + index);
|
||||
_markedNodesListItems.erase(_markedNodesListItems.begin() + index);
|
||||
}
|
||||
|
||||
void MarkNodesDialog::parseSelections() {
|
||||
_profile.setMarkNodes(_data);
|
||||
*_markedNodes = std::move(_markedNodesData);
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,32 +34,32 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <algorithm>
|
||||
|
||||
MetaDialog::MetaDialog(openspace::Profile& profile, QWidget *parent)
|
||||
MetaDialog::MetaDialog(QWidget* parent, std::optional<openspace::Profile::Meta>* meta)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _meta(meta)
|
||||
{
|
||||
setWindowTitle("Meta");
|
||||
createWidgets();
|
||||
|
||||
if (_profile.meta().has_value()) {
|
||||
openspace::Profile::Meta meta = *_profile.meta();
|
||||
if (meta.name.has_value()) {
|
||||
_nameEdit->setText(QString::fromStdString(*meta.name));
|
||||
if (_meta->has_value()) {
|
||||
const openspace::Profile::Meta& m = **_meta;
|
||||
if (m.name.has_value()) {
|
||||
_nameEdit->setText(QString::fromStdString(*m.name));
|
||||
}
|
||||
if (meta.version.has_value()) {
|
||||
_versionEdit->setText(QString::fromStdString(*meta.version));
|
||||
if (m.version.has_value()) {
|
||||
_versionEdit->setText(QString::fromStdString(*m.version));
|
||||
}
|
||||
if (meta.description.has_value()) {
|
||||
_descriptionEdit->setText(QString::fromStdString(*meta.description));
|
||||
if (m.description.has_value()) {
|
||||
_descriptionEdit->setText(QString::fromStdString(*m.description));
|
||||
}
|
||||
if (meta.author.has_value()) {
|
||||
_authorEdit->setText(QString::fromStdString(*meta.author));
|
||||
if (m.author.has_value()) {
|
||||
_authorEdit->setText(QString::fromStdString(*m.author));
|
||||
}
|
||||
if (meta.url.has_value()) {
|
||||
_urlEdit->setText(QString::fromStdString(*meta.url));
|
||||
if (m.url.has_value()) {
|
||||
_urlEdit->setText(QString::fromStdString(*m.url));
|
||||
}
|
||||
if (meta.license.has_value()) {
|
||||
_licenseEdit->setText(QString::fromStdString(*meta.license));
|
||||
if (m.license.has_value()) {
|
||||
_licenseEdit->setText(QString::fromStdString(*m.license));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,10 +127,10 @@ void MetaDialog::save() {
|
||||
if (!_licenseEdit->text().isEmpty()) {
|
||||
m.license = _licenseEdit->text().toStdString();
|
||||
}
|
||||
_profile.setMeta(m);
|
||||
*_meta = std::move(m);
|
||||
}
|
||||
else {
|
||||
_profile.clearMeta();
|
||||
*_meta = std::nullopt;
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
@@ -40,10 +40,11 @@ namespace {
|
||||
const Profile::Module Blank = { "", "", "" };
|
||||
} // namespace
|
||||
|
||||
ModulesDialog::ModulesDialog(Profile& profile, QWidget *parent)
|
||||
ModulesDialog::ModulesDialog(QWidget* parent,
|
||||
std::vector<openspace::Profile::Module>* modules)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _data(_profile.modules())
|
||||
, _modules(modules)
|
||||
, _moduleData(*_modules)
|
||||
{
|
||||
setWindowTitle("Modules");
|
||||
createWidgets();
|
||||
@@ -63,7 +64,7 @@ void ModulesDialog::createWidgets() {
|
||||
_list->setMovement(QListView::Free);
|
||||
_list->setResizeMode(QListView::Adjust);
|
||||
|
||||
for (const Profile::Module& m : _data) {
|
||||
for (const Profile::Module& m : _moduleData) {
|
||||
_list->addItem(new QListWidgetItem(createOneLineSummary(m)));
|
||||
}
|
||||
layout->addWidget(_list);
|
||||
@@ -176,8 +177,8 @@ void ModulesDialog::listItemSelected() {
|
||||
QListWidgetItem* item = _list->currentItem();
|
||||
int index = _list->row(item);
|
||||
|
||||
if (!_data.empty()) {
|
||||
const Profile::Module& m = _data[index];
|
||||
if (!_moduleData.empty()) {
|
||||
const Profile::Module& m = _moduleData[index];
|
||||
_moduleEdit->setText(QString::fromStdString(m.name));
|
||||
if (m.loadedInstruction.has_value()) {
|
||||
_loadedEdit->setText(QString::fromStdString(*m.loadedInstruction));
|
||||
@@ -200,7 +201,7 @@ bool ModulesDialog::isLineEmpty(int index) const {
|
||||
if (!_list->item(index)->text().isEmpty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
if (!_data.empty() && !_data.at(0).name.empty()) {
|
||||
if (!_moduleData.empty() && !_moduleData.at(0).name.empty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
return isEmpty;
|
||||
@@ -213,13 +214,13 @@ void ModulesDialog::listItemAdded() {
|
||||
// Special case where list is "empty" but really has one line that is blank.
|
||||
// This is done because QListWidget does not seem to like having its sole
|
||||
// remaining item being removed.
|
||||
_data.at(0) = Blank;
|
||||
_moduleData.at(0) = Blank;
|
||||
_list->item(0)->setText(" (Enter details below & click 'Save')");
|
||||
_list->setCurrentRow(0);
|
||||
transitionToEditMode();
|
||||
}
|
||||
else {
|
||||
_data.push_back(Blank);
|
||||
_moduleData.push_back(Blank);
|
||||
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
|
||||
//Scroll down to that blank line highlighted
|
||||
_list->setCurrentRow(_list->count() - 1);
|
||||
@@ -227,16 +228,18 @@ void ModulesDialog::listItemAdded() {
|
||||
}
|
||||
|
||||
// Blank-out the 2 text fields, set combo box to index 0
|
||||
_moduleEdit->setText(QString::fromStdString(_data.back().name));
|
||||
if (_data.back().loadedInstruction.has_value()) {
|
||||
_loadedEdit->setText(QString::fromStdString(*_data.back().loadedInstruction));
|
||||
_moduleEdit->setText(QString::fromStdString(_moduleData.back().name));
|
||||
if (_moduleData.back().loadedInstruction.has_value()) {
|
||||
_loadedEdit->setText(QString::fromStdString(
|
||||
*_moduleData.back().loadedInstruction
|
||||
));
|
||||
}
|
||||
else {
|
||||
_loadedEdit->clear();
|
||||
}
|
||||
if (_data.back().notLoadedInstruction.has_value()) {
|
||||
if (_moduleData.back().notLoadedInstruction.has_value()) {
|
||||
_notLoadedEdit->setText(
|
||||
QString::fromStdString(*_data.back().notLoadedInstruction)
|
||||
QString::fromStdString(*_moduleData.back().notLoadedInstruction)
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -255,11 +258,11 @@ void ModulesDialog::listItemSave() {
|
||||
QListWidgetItem* item = _list->currentItem();
|
||||
int index = _list->row(item);
|
||||
|
||||
if ( _data.size() > 0) {
|
||||
_data[index].name = _moduleEdit->text().toStdString();
|
||||
_data[index].loadedInstruction = _loadedEdit->text().toStdString();
|
||||
_data[index].notLoadedInstruction = _notLoadedEdit->text().toStdString();
|
||||
_list->item(index)->setText(createOneLineSummary(_data[index]));
|
||||
if (_moduleData.size() > 0) {
|
||||
_moduleData[index].name = _moduleEdit->text().toStdString();
|
||||
_moduleData[index].loadedInstruction = _loadedEdit->text().toStdString();
|
||||
_moduleData[index].notLoadedInstruction = _notLoadedEdit->text().toStdString();
|
||||
_list->item(index)->setText(createOneLineSummary(_moduleData[index]));
|
||||
}
|
||||
transitionFromEditMode();
|
||||
_editModeNewItem = false;
|
||||
@@ -267,7 +270,7 @@ void ModulesDialog::listItemSave() {
|
||||
|
||||
void ModulesDialog::listItemCancelSave() {
|
||||
transitionFromEditMode();
|
||||
if (_editModeNewItem && !_data.empty() && _data.back().name.empty()) {
|
||||
if (_editModeNewItem && !_moduleData.empty() && _moduleData.back().name.empty()) {
|
||||
listItemRemove();
|
||||
}
|
||||
_editModeNewItem = false;
|
||||
@@ -279,15 +282,15 @@ void ModulesDialog::listItemRemove() {
|
||||
if (_list->count() == 1) {
|
||||
// Special case where last remaining item is being removed (QListWidget
|
||||
// doesn't like the final item being removed so instead clear it)
|
||||
_data.at(0) = Blank;
|
||||
_moduleData.at(0) = Blank;
|
||||
_list->item(0)->setText("");
|
||||
}
|
||||
else {
|
||||
int index = _list->currentRow();
|
||||
if (index >= 0 && index < _list->count()) {
|
||||
delete _list->takeItem(index);
|
||||
if (!_data.empty()) {
|
||||
_data.erase(_data.begin() + index);
|
||||
if (!_moduleData.empty()) {
|
||||
_moduleData.erase(_moduleData.begin() + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,10 +339,10 @@ void ModulesDialog::editBoxDisabled(bool disabled) {
|
||||
|
||||
void ModulesDialog::parseSelections() {
|
||||
// Handle case with only one remaining but empty line
|
||||
if ((_data.size() == 1) && (_data.at(0).name.empty())) {
|
||||
_data.clear();
|
||||
if ((_moduleData.size() == 1) && (_moduleData.at(0).name.empty())) {
|
||||
_moduleData.clear();
|
||||
}
|
||||
_profile.setModules(_data);
|
||||
*_modules = std::move(_moduleData);
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
#include "profile/profileedit.h"
|
||||
|
||||
#include "profile/actiondialog.h"
|
||||
#include "profile/additionalscriptsdialog.h"
|
||||
#include "profile/assetsdialog.h"
|
||||
#include "profile/cameradialog.h"
|
||||
#include "profile/deltatimesdialog.h"
|
||||
#include "profile/keybindingsdialog.h"
|
||||
#include "profile/line.h"
|
||||
#include "profile/marknodesdialog.h"
|
||||
#include "profile/metadialog.h"
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "profile/propertiesdialog.h"
|
||||
#include "profile/timedialog.h"
|
||||
#include <openspace/scene/profile.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
@@ -69,17 +70,18 @@ namespace {
|
||||
return results;
|
||||
}
|
||||
|
||||
std::string summarizeKeybindings(const std::vector<Profile::Keybinding>& keybindings)
|
||||
std::string summarizeKeybindings(const std::vector<Profile::Keybinding>& keybindings,
|
||||
const std::vector<Profile::Action>& actions)
|
||||
{
|
||||
std::string results;
|
||||
for (Profile::Keybinding k : keybindings) {
|
||||
results += k.name + " (";
|
||||
int keymod = static_cast<int>(k.key.modifier);
|
||||
if (keymod != static_cast<int>(openspace::KeyModifier::NoModifier)) {
|
||||
results += openspace::KeyModifierNames.at(keymod) + "+";
|
||||
}
|
||||
results += openspace::KeyNames.at(static_cast<int>(k.key.key));
|
||||
results += ")\n";
|
||||
const auto it = std::find_if(
|
||||
actions.cbegin(), actions.cend(),
|
||||
[id = k.action](const Profile::Action& a) { return a.identifier == id; }
|
||||
);
|
||||
|
||||
std::string name = it != actions.end() ? it->name : "Unknown action";
|
||||
results += fmt::format("{} ({})\n", name, ghoul::to_string(k.key));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -87,7 +89,7 @@ namespace {
|
||||
std::string summarizeProperties(const std::vector<Profile::Property>& properties) {
|
||||
std::string results;
|
||||
for (openspace::Profile::Property p : properties) {
|
||||
results += p.name + " = " + p.value + '\n';
|
||||
results += fmt::format("{} = {}\n", p.name, p.value);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@@ -333,26 +335,26 @@ void ProfileEdit::createWidgets(const std::string& profileName) {
|
||||
}
|
||||
|
||||
void ProfileEdit::initSummaryTextForEachCategory() {
|
||||
_modulesLabel->setText(labelText(_profile.modules().size(), "Modules"));
|
||||
_modulesLabel->setText(labelText(_profile.modules.size(), "Modules"));
|
||||
|
||||
_assetsLabel->setText(labelText(_profile.assets().size(), "Assets"));
|
||||
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets())));
|
||||
_assetsLabel->setText(labelText(_profile.assets.size(), "Assets"));
|
||||
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets)));
|
||||
|
||||
_propertiesLabel->setText(labelText(_profile.properties().size(), "Properties"));
|
||||
_propertiesLabel->setText(labelText(_profile.properties.size(), "Properties"));
|
||||
_propertiesEdit->setText(
|
||||
QString::fromStdString(summarizeProperties(_profile.properties()))
|
||||
QString::fromStdString(summarizeProperties(_profile.properties))
|
||||
);
|
||||
|
||||
_keybindingsLabel->setText(labelText(_profile.keybindings().size(), "Keybindings"));
|
||||
_keybindingsEdit->setText(
|
||||
QString::fromStdString(summarizeKeybindings(_profile.keybindings()))
|
||||
);
|
||||
_keybindingsLabel->setText(labelText(_profile.keybindings.size(), "Keybindings"));
|
||||
_keybindingsEdit->setText(QString::fromStdString(
|
||||
summarizeKeybindings(_profile.keybindings, _profile.actions)
|
||||
));
|
||||
|
||||
_deltaTimesLabel->setText(
|
||||
labelText(_profile.deltaTimes().size(), "Simulation Time Increments")
|
||||
labelText(_profile.deltaTimes.size(), "Simulation Time Increments")
|
||||
);
|
||||
_interestingNodesLabel->setText(
|
||||
labelText(_profile.markNodes().size(), "Mark Interesting Nodes")
|
||||
labelText(_profile.markNodes.size(), "Mark Interesting Nodes")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -401,68 +403,68 @@ void ProfileEdit::duplicateProfile() {
|
||||
|
||||
void ProfileEdit::openMeta() {
|
||||
_errorMsg->clear();
|
||||
MetaDialog(_profile, this).exec();
|
||||
MetaDialog(this, &_profile.meta).exec();
|
||||
}
|
||||
|
||||
void ProfileEdit::openModules() {
|
||||
_errorMsg->clear();
|
||||
ModulesDialog(_profile, this).exec();
|
||||
_modulesLabel->setText(labelText(_profile.modules().size(), "Modules"));
|
||||
ModulesDialog(this, &_profile.modules).exec();
|
||||
_modulesLabel->setText(labelText(_profile.modules.size(), "Modules"));
|
||||
}
|
||||
|
||||
void ProfileEdit::openProperties() {
|
||||
_errorMsg->clear();
|
||||
PropertiesDialog(_profile, this).exec();
|
||||
_propertiesLabel->setText(labelText(_profile.properties().size(), "Properties"));
|
||||
PropertiesDialog(this, &_profile.properties).exec();
|
||||
_propertiesLabel->setText(labelText(_profile.properties.size(), "Properties"));
|
||||
_propertiesEdit->setText(
|
||||
QString::fromStdString(summarizeProperties(_profile.properties()))
|
||||
QString::fromStdString(summarizeProperties(_profile.properties))
|
||||
);
|
||||
}
|
||||
|
||||
void ProfileEdit::openKeybindings() {
|
||||
_errorMsg->clear();
|
||||
KeybindingsDialog(_profile, this).exec();
|
||||
_keybindingsLabel->setText(labelText(_profile.keybindings().size(), "Keybindings"));
|
||||
_keybindingsEdit->setText(
|
||||
QString::fromStdString(summarizeKeybindings(_profile.keybindings()))
|
||||
);
|
||||
ActionDialog(this, &_profile.actions, &_profile.keybindings).exec();
|
||||
_keybindingsLabel->setText(labelText(_profile.keybindings.size(), "Keybindings"));
|
||||
_keybindingsEdit->setText(QString::fromStdString(
|
||||
summarizeKeybindings(_profile.keybindings, _profile.actions)
|
||||
));
|
||||
}
|
||||
|
||||
void ProfileEdit::openAssets() {
|
||||
_errorMsg->clear();
|
||||
AssetsDialog(_profile, _assetBasePath, _userAssetBasePath, this).exec();
|
||||
_assetsLabel->setText(labelText(_profile.assets().size(), "Assets"));
|
||||
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets())));
|
||||
AssetsDialog(this, &_profile, _assetBasePath, _userAssetBasePath).exec();
|
||||
_assetsLabel->setText(labelText(_profile.assets.size(), "Assets"));
|
||||
_assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets)));
|
||||
}
|
||||
|
||||
void ProfileEdit::openTime() {
|
||||
_errorMsg->clear();
|
||||
TimeDialog(_profile, this).exec();
|
||||
TimeDialog(this, &_profile.time).exec();
|
||||
}
|
||||
|
||||
void ProfileEdit::openDeltaTimes() {
|
||||
_errorMsg->clear();
|
||||
DeltaTimesDialog(_profile, this).exec();
|
||||
DeltaTimesDialog(this, &_profile.deltaTimes).exec();
|
||||
_deltaTimesLabel->setText(
|
||||
labelText(_profile.deltaTimes().size(), "Simulation Time Increments")
|
||||
labelText(_profile.deltaTimes.size(), "Simulation Time Increments")
|
||||
);
|
||||
}
|
||||
|
||||
void ProfileEdit::openAddedScripts() {
|
||||
_errorMsg->clear();
|
||||
AdditionalScriptsDialog(_profile, this).exec();
|
||||
AdditionalScriptsDialog(this, &_profile.additionalScripts).exec();
|
||||
}
|
||||
|
||||
void ProfileEdit::openCamera() {
|
||||
_errorMsg->clear();
|
||||
CameraDialog(_profile, this).exec();
|
||||
CameraDialog(this, &_profile.camera).exec();
|
||||
}
|
||||
|
||||
void ProfileEdit::openMarkNodes() {
|
||||
_errorMsg->clear();
|
||||
MarkNodesDialog(_profile, this).exec();
|
||||
MarkNodesDialog(this, &_profile.markNodes).exec();
|
||||
_interestingNodesLabel->setText(
|
||||
labelText(_profile.markNodes().size(), "Mark Interesting Nodes")
|
||||
labelText(_profile.markNodes.size(), "Mark Interesting Nodes")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,10 +46,11 @@ namespace {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
PropertiesDialog::PropertiesDialog(Profile& profile, QWidget *parent)
|
||||
PropertiesDialog::PropertiesDialog(QWidget* parent,
|
||||
std::vector<openspace::Profile::Property>* properties)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _data(_profile.properties())
|
||||
, _properties(properties)
|
||||
, _propertyData(*_properties)
|
||||
{
|
||||
setWindowTitle("Set Property Values");
|
||||
createWidgets();
|
||||
@@ -65,8 +66,8 @@ void PropertiesDialog::createWidgets() {
|
||||
_list, &QListWidget::itemSelectionChanged,
|
||||
this, &PropertiesDialog::listItemSelected
|
||||
);
|
||||
for (size_t i = 0; i < _data.size(); ++i) {
|
||||
_list->addItem(new QListWidgetItem(createOneLineSummary(_data[i])));
|
||||
for (size_t i = 0; i < _propertyData.size(); ++i) {
|
||||
_list->addItem(new QListWidgetItem(createOneLineSummary(_propertyData[i])));
|
||||
}
|
||||
layout->addWidget(_list);
|
||||
}
|
||||
@@ -172,8 +173,8 @@ void PropertiesDialog::listItemSelected() {
|
||||
QListWidgetItem* item = _list->currentItem();
|
||||
int index = _list->row(item);
|
||||
|
||||
if (_data.size() > 0) {
|
||||
Profile::Property& p = _data[index];
|
||||
if (_propertyData.size() > 0) {
|
||||
Profile::Property& p = _propertyData[index];
|
||||
if (p.setType == Profile::Property::SetType::SetPropertyValueSingle) {
|
||||
_commandCombo->setCurrentIndex(0);
|
||||
}
|
||||
@@ -191,7 +192,7 @@ bool PropertiesDialog::isLineEmpty(int index) {
|
||||
if (!_list->item(index)->text().isEmpty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
if (!_data.empty() && !_data.at(0).name.empty()) {
|
||||
if (!_propertyData.empty() && !_propertyData.at(0).name.empty()) {
|
||||
isEmpty = false;
|
||||
}
|
||||
return isEmpty;
|
||||
@@ -204,13 +205,13 @@ void PropertiesDialog::listItemAdded() {
|
||||
// Special case where list is "empty" but really has one line that is blank.
|
||||
// This is done because QListWidget does not seem to like having its sole
|
||||
// remaining item being removed.
|
||||
_data.at(0) = Blank;
|
||||
_propertyData.at(0) = Blank;
|
||||
_list->item(0)->setText(" (Enter details below & click 'Save')");
|
||||
_list->setCurrentRow(0);
|
||||
transitionToEditMode();
|
||||
}
|
||||
else {
|
||||
_data.push_back(Blank);
|
||||
_propertyData.push_back(Blank);
|
||||
_list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')"));
|
||||
//Scroll down to that blank line highlighted
|
||||
_list->setCurrentRow(_list->count() - 1);
|
||||
@@ -218,8 +219,8 @@ void PropertiesDialog::listItemAdded() {
|
||||
|
||||
// Blank-out the 2 text fields, set combo box to index 0
|
||||
_commandCombo->setCurrentIndex(0);
|
||||
_propertyEdit->setText(QString::fromStdString(_data.back().name));
|
||||
_valueEdit->setText(QString::fromStdString(_data.back().value));
|
||||
_propertyEdit->setText(QString::fromStdString(_propertyData.back().name));
|
||||
_valueEdit->setText(QString::fromStdString(_propertyData.back().value));
|
||||
_commandCombo->setFocus(Qt::OtherFocusReason);
|
||||
_editModeNewItem = true;
|
||||
}
|
||||
@@ -232,16 +233,17 @@ void PropertiesDialog::listItemSave() {
|
||||
QListWidgetItem* item = _list->currentItem();
|
||||
int index = _list->row(item);
|
||||
|
||||
if ( _data.size() > 0) {
|
||||
if (_propertyData.size() > 0) {
|
||||
if (_commandCombo->currentIndex() == 0) {
|
||||
_data[index].setType = Profile::Property::SetType::SetPropertyValueSingle;
|
||||
_propertyData[index].setType =
|
||||
Profile::Property::SetType::SetPropertyValueSingle;
|
||||
}
|
||||
else {
|
||||
_data[index].setType = Profile::Property::SetType::SetPropertyValue;
|
||||
_propertyData[index].setType = Profile::Property::SetType::SetPropertyValue;
|
||||
}
|
||||
_data[index].name = _propertyEdit->text().toStdString();
|
||||
_data[index].value = _valueEdit->text().toStdString();
|
||||
_list->item(index)->setText(createOneLineSummary(_data[index]));
|
||||
_propertyData[index].name = _propertyEdit->text().toStdString();
|
||||
_propertyData[index].value = _valueEdit->text().toStdString();
|
||||
_list->item(index)->setText(createOneLineSummary(_propertyData[index]));
|
||||
}
|
||||
transitionFromEditMode();
|
||||
_editModeNewItem = false;
|
||||
@@ -269,8 +271,10 @@ void PropertiesDialog::listItemCancelSave() {
|
||||
listItemSelected();
|
||||
transitionFromEditMode();
|
||||
if (_editModeNewItem) {
|
||||
if (_data.size() > 0) {
|
||||
if (_data.back().name.length() == 0 || _data.back().value.length() == 0) {
|
||||
if (_propertyData.size() > 0) {
|
||||
if (_propertyData.back().name.length() == 0 ||
|
||||
_propertyData.back().value.length() == 0)
|
||||
{
|
||||
listItemRemove();
|
||||
}
|
||||
}
|
||||
@@ -284,15 +288,15 @@ void PropertiesDialog::listItemRemove() {
|
||||
if (_list->count() == 1) {
|
||||
//Special case where last remaining item is being removed (QListWidget
|
||||
// doesn't like the final item being removed so instead clear it)
|
||||
_data.at(0) = Blank;
|
||||
_propertyData.at(0) = Blank;
|
||||
_list->item(0)->setText("");
|
||||
}
|
||||
else {
|
||||
int index = _list->currentRow();
|
||||
if (index >= 0 && index < _list->count()) {
|
||||
delete _list->takeItem(index);
|
||||
if (_data.size() > 0) {
|
||||
_data.erase(_data.begin() + index);
|
||||
if (_propertyData.size() > 0) {
|
||||
_propertyData.erase(_propertyData.begin() + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,10 +345,10 @@ void PropertiesDialog::editBoxDisabled(bool disabled) {
|
||||
|
||||
void PropertiesDialog::parseSelections() {
|
||||
// Handle case with only one remaining but empty line
|
||||
if ((_data.size() == 1) && (_data.at(0).name.compare("") == 0)) {
|
||||
_data.clear();
|
||||
if ((_propertyData.size() == 1) && (_propertyData.at(0).name.compare("") == 0)) {
|
||||
_propertyData.clear();
|
||||
}
|
||||
_profile.setProperties(_data);
|
||||
*_properties = std::move(_propertyData);
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
@@ -37,20 +37,20 @@
|
||||
|
||||
using namespace openspace;
|
||||
|
||||
TimeDialog::TimeDialog(openspace::Profile& profile, QWidget* parent)
|
||||
TimeDialog::TimeDialog(QWidget* parent, std::optional<openspace::Profile::Time>* time)
|
||||
: QDialog(parent)
|
||||
, _profile(profile)
|
||||
, _time(time)
|
||||
{
|
||||
setWindowTitle("Time");
|
||||
createWidgets();
|
||||
|
||||
QStringList types = { "Absolute", "Relative" };
|
||||
_typeCombo->addItems(types);
|
||||
if (_profile.time().has_value()) {
|
||||
_data = *_profile.time();
|
||||
if (_data.type == Profile::Time::Type::Relative) {
|
||||
if (_data.value == "") {
|
||||
_data.value = "now";
|
||||
if (_time->has_value()) {
|
||||
_timeData = **_time;
|
||||
if (_timeData.type == Profile::Time::Type::Relative) {
|
||||
if (_timeData.value == "") {
|
||||
_timeData.value = "0d";
|
||||
}
|
||||
_relativeEdit->setSelection(0, _relativeEdit->text().length());
|
||||
}
|
||||
@@ -59,11 +59,11 @@ TimeDialog::TimeDialog(openspace::Profile& profile, QWidget* parent)
|
||||
}
|
||||
}
|
||||
else {
|
||||
_data.type = Profile::Time::Type::Relative;
|
||||
_data.value = "now";
|
||||
_timeData.type = Profile::Time::Type::Relative;
|
||||
_timeData.value = "0d";
|
||||
}
|
||||
_initializedAsAbsolute = (_data.type == Profile::Time::Type::Absolute);
|
||||
enableAccordingToType(static_cast<int>(_data.type));
|
||||
_initializedAsAbsolute = (_timeData.type == Profile::Time::Type::Absolute);
|
||||
enableAccordingToType(static_cast<int>(_timeData.type));
|
||||
}
|
||||
|
||||
void TimeDialog::createWidgets() {
|
||||
@@ -114,18 +114,18 @@ void TimeDialog::enableAccordingToType(int idx) {
|
||||
if (comboIdx == Profile::Time::Type::Relative) {
|
||||
_relativeEdit->setText("<font color='black'>Relative Time:</font>");
|
||||
if (_initializedAsAbsolute) {
|
||||
_relativeEdit->setText("now");
|
||||
_relativeEdit->setText("0d");
|
||||
}
|
||||
else {
|
||||
_relativeEdit->setText(QString::fromStdString(_data.value));
|
||||
_relativeEdit->setText(QString::fromStdString(_timeData.value));
|
||||
}
|
||||
_relativeEdit->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
else {
|
||||
_relativeEdit->setText("<font color='gray'>Relative Time:</font>");
|
||||
size_t tIdx = _data.value.find_first_of('T', 0);
|
||||
QString importDate = QString::fromStdString(_data.value.substr(0, tIdx));
|
||||
QString importTime = QString::fromStdString(_data.value.substr(tIdx + 1));
|
||||
size_t tIdx = _timeData.value.find_first_of('T', 0);
|
||||
QString importDate = QString::fromStdString(_timeData.value.substr(0, tIdx));
|
||||
QString importTime = QString::fromStdString(_timeData.value.substr(tIdx + 1));
|
||||
_absoluteEdit->setDate(QDate::fromString(importDate, Qt::DateFormat::ISODate));
|
||||
_absoluteEdit->setTime(QTime::fromString(importTime));
|
||||
_relativeEdit->clear();
|
||||
@@ -144,13 +144,13 @@ void TimeDialog::approved() {
|
||||
constexpr const int Relative = static_cast<int>(Profile::Time::Type::Relative);
|
||||
if (_typeCombo->currentIndex() == Relative) {
|
||||
if (_relativeEdit->text().isEmpty()) {
|
||||
_profile.clearTime();
|
||||
*_time = std::nullopt;
|
||||
}
|
||||
else {
|
||||
Profile::Time t;
|
||||
t.type = Profile::Time::Type::Relative;
|
||||
t.value = _relativeEdit->text().toStdString();
|
||||
_profile.setTime(t);
|
||||
*_time = t;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -161,7 +161,7 @@ void TimeDialog::approved() {
|
||||
_absoluteEdit->date().toString("yyyy-MM-dd").toStdString(),
|
||||
_absoluteEdit->time().toString().toStdString()
|
||||
);
|
||||
_profile.setTime(t);
|
||||
*_time = t;
|
||||
}
|
||||
accept();
|
||||
}
|
||||
|
||||
Submodule apps/OpenSpace/ext/sgct updated: 2a3ef78f72...0a4596ff2e
@@ -960,7 +960,7 @@ void checkCommandLineForSettings(int& argc, char** argv, bool& hasSGCT, bool& ha
|
||||
}
|
||||
|
||||
std::string setWindowConfigPresetForGui(const std::string labelFromCfgFile,
|
||||
const std::string xmlExt, bool haveCliSGCTConfig,
|
||||
bool haveCliSGCTConfig,
|
||||
const std::string& sgctFunctionName)
|
||||
{
|
||||
configuration::Configuration& config = *global::configuration;
|
||||
@@ -997,8 +997,7 @@ std::string selectedSgctProfileFromLauncher(LauncherWindow& lw, bool hasCliSGCTC
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( (config.length() >= xmlExt.length())
|
||||
&& (0 == config.compare(config.length() - xmlExt.length(), xmlExt.length(), xmlExt)) ) {
|
||||
if (std::filesystem::path(config).extension() == ".xml") {
|
||||
//user customzied sgct config
|
||||
}
|
||||
else {
|
||||
@@ -1046,8 +1045,9 @@ int main(int argc, char* argv[]) {
|
||||
//
|
||||
// Parse commandline arguments
|
||||
//
|
||||
char* prgName = argv[0];
|
||||
ghoul::cmdparser::CommandlineParser parser(
|
||||
std::string(argv[0]),
|
||||
std::string(prgName),
|
||||
ghoul::cmdparser::CommandlineParser::AllowUnknownCommands::Yes
|
||||
);
|
||||
|
||||
@@ -1159,7 +1159,6 @@ int main(int argc, char* argv[]) {
|
||||
const std::string xmlExt = ".xml";
|
||||
std::string windowCfgPreset = setWindowConfigPresetForGui(
|
||||
labelFromCfgFile,
|
||||
xmlExt,
|
||||
hasSGCTConfig,
|
||||
sgctFunctionName
|
||||
);
|
||||
|
||||
@@ -129,4 +129,4 @@ int main(int argc, char** argv) {
|
||||
LINFO("Server stopped");
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Display swapInterval="0" />
|
||||
</Settings>
|
||||
<Node address="localhost" port="20401">
|
||||
<Window fullScreen="true" numberOfSamples="4" name="OpenSpace">
|
||||
<Window fullscreen="true" numberOfSamples="4" name="OpenSpace">
|
||||
<Stereo type="none" />
|
||||
<Size x="1920" y="1080" />
|
||||
<Pos x="0" y="0" />
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
<?xml version="1.0" ?>
|
||||
<Cluster masterAddress="localhost">
|
||||
<Cluster masterAddress="localhost" externalControlPort="20500">
|
||||
<Settings>
|
||||
<Display swapInterval="0" />
|
||||
</Settings>
|
||||
<Node address="localhost" port="20401">
|
||||
<Window fullScreen="false">
|
||||
<Stereo type="none" />
|
||||
<Pos x="200" y="300" />
|
||||
<!-- 16:9 aspect ratio -->
|
||||
<Size x="1280" y="360" />
|
||||
<Viewport eye="left">
|
||||
<Window fullScreen="false" name="OpenSpace">
|
||||
<Stereo type="side_by_side" />
|
||||
<Size x="1280" y="720" />
|
||||
<Pos x="50" y="50" />
|
||||
<Viewport tracked="true">
|
||||
<Pos x="0.0" y="0.0" />
|
||||
<Size x="0.5" y="1.0" />
|
||||
<PlanarProjection>
|
||||
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
|
||||
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
|
||||
</PlanarProjection>
|
||||
</Viewport>
|
||||
<Viewport eye="right">
|
||||
<Pos x="0.5" y="0.0" />
|
||||
<Size x="0.5" y="1.0" />
|
||||
<Size x="1.0" y="1.0" />
|
||||
<PlanarProjection>
|
||||
<FOV down="25.267007923362" left="40.0" right="40.0" up="25.267007923362" />
|
||||
<Orientation heading="0.0" pitch="0.0" roll="0.0" />
|
||||
@@ -24,7 +18,7 @@
|
||||
</Viewport>
|
||||
</Window>
|
||||
</Node>
|
||||
<User eyeSeparation="0.06">
|
||||
<User eyeSeparation="0.065">
|
||||
<Pos x="0.0" y="0.0" z="0.0" />
|
||||
</User>
|
||||
</Cluster>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<Viewport name="Spout">
|
||||
<Pos x="0.0" y="0.0" />
|
||||
<Size x="1.0" y="1.0" />
|
||||
<SpoutOutputProjection quality="1.5k">
|
||||
<SpoutOutputProjection quality="1k" mappingSpoutName="OpenSpace">
|
||||
<Background r="0.1" g="0.1" b="0.1" a="1.0" />
|
||||
</SpoutOutputProjection>
|
||||
</Viewport>
|
||||
|
||||
105
data/assets/actions/toggle_trail.asset
Normal file
105
data/assets/actions/toggle_trail.asset
Normal file
@@ -0,0 +1,105 @@
|
||||
local toggle_trail = {
|
||||
Identifier = "os.toggle_trail",
|
||||
Name = "Toggle Trail",
|
||||
Command = [[
|
||||
local node
|
||||
if is_declared("args") then
|
||||
node = args.Node
|
||||
else
|
||||
node = openspace.navigation.getNavigationState().Anchor
|
||||
end
|
||||
|
||||
local trail
|
||||
if openspace.hasSceneGraphNode(node .. "Trail") then
|
||||
trail = node .. "Trail"
|
||||
elseif openspace.hasSceneGraphNode(node .. "_trail") then
|
||||
trail = node .. "_trail"
|
||||
else
|
||||
-- No trail found, so nothing more to do here
|
||||
return
|
||||
end
|
||||
|
||||
local visibility
|
||||
if is_declared("args") then
|
||||
if args.Transition == "Approaching" then
|
||||
visibility = false
|
||||
elseif args.Transition == "Exiting" then
|
||||
visibility = true
|
||||
else
|
||||
return
|
||||
end
|
||||
else
|
||||
visibility = not openspace.getPropertyValue("Scene." .. trail .. ".Renderable.Enabled")
|
||||
end
|
||||
|
||||
openspace.setPropertyValueSingle("Scene." .. trail .. ".Renderable.Enabled", visibility)
|
||||
]],
|
||||
Documentation = [[Toggles the visibility of the associated trail of a scene graph node.
|
||||
This action takes optional arguments to 1) determine which trail to hide (as the
|
||||
'Node') and 2) the transition direction (as 'After' and 'Before').]],
|
||||
GuiPath = "/Trails",
|
||||
IsLocal = true
|
||||
}
|
||||
asset.export("toggle_trail", toggle_trail.Identifier)
|
||||
|
||||
local hide_trail = {
|
||||
Identifier = "os.hide_trail",
|
||||
Name = "Hide Trail",
|
||||
Command = [[
|
||||
local node
|
||||
if is_declared("args") then
|
||||
node = args.Node
|
||||
else
|
||||
node = openspace.navigation.getNavigationState().Anchor
|
||||
end
|
||||
|
||||
if openspace.hasSceneGraphNode(node .. "Trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "Trail.Renderable.Enabled", false)
|
||||
elseif openspace.hasSceneGraphNode(node .. "_trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "_trail.Renderable.Enabled", false)
|
||||
end
|
||||
]],
|
||||
Documentation = [[Hides the associated trail of a scene graph node. This action takes an
|
||||
optional argument to determine whose trail to hide. If no argument is provided, the
|
||||
current focus node is used instead]],
|
||||
GuiPath = "/Trails",
|
||||
IsLocal = true
|
||||
}
|
||||
asset.export("hide_trail", hide_trail.Identifier)
|
||||
|
||||
local show_trail = {
|
||||
Identifier = "os.show_trail",
|
||||
Name = "Show Trail",
|
||||
Command = [[
|
||||
local node
|
||||
if is_declared("args") then
|
||||
node = args.Node
|
||||
else
|
||||
node = openspace.navigation.getNavigationState().Anchor
|
||||
end
|
||||
|
||||
if openspace.hasSceneGraphNode(node .. "Trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "Trail.Renderable.Enabled", true)
|
||||
elseif openspace.hasSceneGraphNode(node .. "_trail") then
|
||||
openspace.setPropertyValue("Scene." .. node .. "_trail.Renderable.Enabled", true)
|
||||
end
|
||||
]],
|
||||
Documentation = [[Shows the associated trail of a scene graph node. This action takes an
|
||||
optional argument to determine whose trail to hide. If no argument is provided, the
|
||||
current focus node is used instead]],
|
||||
GuiPath = "/Trails",
|
||||
IsLocal = true
|
||||
}
|
||||
asset.export("show_trail", show_trail.Identifier)
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.action.registerAction(toggle_trail)
|
||||
openspace.action.registerAction(show_trail)
|
||||
openspace.action.registerAction(hide_trail)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.action.removeAction(toggle_trail.Identifier)
|
||||
openspace.action.removeAction(show_trail.Identifier)
|
||||
openspace.action.removeAction(hide_trail.Identifier)
|
||||
end)
|
||||
@@ -1,7 +1,6 @@
|
||||
-- This is a blank scene that that just sets up the default menus/dasboard/keys, etc.
|
||||
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sceneHelper = asset.require('util/scene_helper')
|
||||
local propertyHelper = asset.require('util/property_helper')
|
||||
|
||||
-- Specifying which other assets should be loaded in this scene
|
||||
@@ -14,36 +13,47 @@ asset.require('util/default_keybindings')
|
||||
-- Load web gui
|
||||
local webGui = asset.require('util/webgui')
|
||||
|
||||
-- Keybindings that are specific for this scene
|
||||
local Keybindings = {
|
||||
{
|
||||
Key = "h",
|
||||
Name="Toggle Trails",
|
||||
Command = "local list = openspace.getProperty('{planetTrail_solarSystem}.Renderable.Enabled'); for _,v in pairs(list) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end\n" ..
|
||||
"local moonlist = openspace.getProperty('{moonTrail_solarSystem}.Renderable.Enabled'); for _,v in pairs(moonlist) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end",
|
||||
Documentation = "Toggles the visibility of planet and moon trails",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
},
|
||||
{
|
||||
Key = "l",
|
||||
Name = "Toggle planet labels",
|
||||
Command = "local list = openspace.getProperty('{solarsystem_labels}.Renderable.Enabled'); for _,v in pairs(list) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end",
|
||||
Documentation = "Turns on visibility for all solar system labels",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
}
|
||||
local toggle_trails = {
|
||||
Identifier = "os_default.toggle_trails",
|
||||
Name = "Toggle Trails",
|
||||
Command = "local list = openspace.getProperty('{planetTrail_solarSystem}.Renderable.Enabled'); for _,v in pairs(list) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end\n" ..
|
||||
"local moonlist = openspace.getProperty('{moonTrail_solarSystem}.Renderable.Enabled'); for _,v in pairs(moonlist) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end",
|
||||
Documentation = "Toggles the visibility of planet and moon trails",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false,
|
||||
|
||||
Key = "h"
|
||||
}
|
||||
|
||||
local toggle_planet_labels = {
|
||||
Identifier = "os_default.toggle_planet_labels",
|
||||
Name = "Toggle planet labels",
|
||||
Command = "local list = openspace.getProperty('{solarsystem_labels}.Renderable.Enabled'); for _,v in pairs(list) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end",
|
||||
Documentation = "Turns on visibility for all solar system labels",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false,
|
||||
|
||||
Key = "l"
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
webGui.setCefRoute("onscreen")
|
||||
|
||||
sceneHelper.bindKeys(Keybindings)
|
||||
openspace.action.registerAction(toggle_trails)
|
||||
openspace.bindKey(toggle_trails.Key, toggle_trails.Identifier)
|
||||
|
||||
openspace.action.registerAction(toggle_planet_labels)
|
||||
openspace.bindKey(toggle_planet_labels.Key, toggle_planet_labels.Identifier)
|
||||
|
||||
openspace.setDefaultGuiSorting()
|
||||
|
||||
openspace.setPropertyValueSingle("RenderEngine.VerticalLogOffset", 0.100000)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
sceneHelper.unbindKeys(Keybindings)
|
||||
openspace.action.removeAction(toggle_trails.Identifier)
|
||||
openspace.clearKey(toggle_trails.Key)
|
||||
|
||||
openspace.action.removeAction(toggle_planet_labels.Identifier)
|
||||
openspace.clearKey(toggle_planet_labels.Key)
|
||||
end)
|
||||
|
||||
27
data/assets/events/toggle_trail.asset
Normal file
27
data/assets/events/toggle_trail.asset
Normal file
@@ -0,0 +1,27 @@
|
||||
local action = asset.require('actions/toggle_trail')
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.event.registerEventAction(
|
||||
"CameraFocusTransition",
|
||||
action.show_trail,
|
||||
{ Transition = "Exiting" }
|
||||
);
|
||||
openspace.event.registerEventAction(
|
||||
"CameraFocusTransition",
|
||||
action.hide_trail,
|
||||
{ Transition = "Approaching" }
|
||||
);
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.event.unregisterEventAction(
|
||||
"CameraFocusTransition",
|
||||
action.show_trail,
|
||||
{ Transition = "Exiting" }
|
||||
);
|
||||
openspace.event.unregisterEventAction(
|
||||
"CameraFocusTransition",
|
||||
action.hide_trail,
|
||||
{ Transition = "Approaching" }
|
||||
);
|
||||
end)
|
||||
68
data/assets/examples/approachevents.asset
Normal file
68
data/assets/examples/approachevents.asset
Normal file
@@ -0,0 +1,68 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sunTransforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
local transforms = asset.require('scene/solarsystem/planets/earth/transforms')
|
||||
|
||||
local generic_action = {
|
||||
Identifier = "os.example.generic",
|
||||
Name = "Generic Example",
|
||||
Command = [[
|
||||
openspace.printInfo("Node: " .. args.Node)
|
||||
openspace.printInfo("Transition: " .. args.Transition)
|
||||
]],
|
||||
Documentation = "Prints the argument information for camera transitions to the log",
|
||||
GuiPath = "/Examples/Events",
|
||||
IsLocal = true
|
||||
}
|
||||
|
||||
local model = asset.syncedResource({
|
||||
Name = "Animated Box",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "animated_box",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local obj = {
|
||||
Identifier = "ExampleEventModel",
|
||||
Parent = transforms.EarthCenter.Identifier,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "StaticTranslation",
|
||||
Position = { 0.0, 11E7, 0.0 }
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = model .. "/BoxAnimated.glb",
|
||||
ModelScale = 1.0,
|
||||
LightSources = {
|
||||
{
|
||||
Type = "SceneGraphLightSource",
|
||||
Identifier = "Sun",
|
||||
Node = sunTransforms.SolarSystemBarycenter.Identifier,
|
||||
Intensity = 1.0
|
||||
}
|
||||
},
|
||||
PerformShading = true,
|
||||
DisableFaceCulling = true
|
||||
},
|
||||
InteractionSphere = 1000.0,
|
||||
OnApproach = { "os.example.generic" },
|
||||
OnReach = { "os.example.generic" },
|
||||
OnRecede = { "os.example.generic" },
|
||||
OnExit = { "os.example.generic" },
|
||||
GUI = {
|
||||
Name = "Example Event Model",
|
||||
Path = "/Example",
|
||||
Description = "",
|
||||
}
|
||||
}
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.action.registerAction(generic_action)
|
||||
openspace.addSceneGraphNode(obj)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.removeSceneGraphNode(obj.Identifier)
|
||||
openspace.action.removeAction(generic_action.Identifier)
|
||||
end)
|
||||
@@ -1,5 +1,4 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sceneHelper = asset.require('util/scene_helper')
|
||||
local propertyHelper = asset.require('util/property_helper')
|
||||
local debugHelper = asset.require('util/debug_helper')
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
local transforms = asset.require('scene/solarsystem/planets/earth/transforms')
|
||||
local debugHelper = asset.require('util/debug_helper')
|
||||
local sun_transforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
|
||||
local sunRadius = 6.957E8
|
||||
local earthRadius = 6.371E6
|
||||
|
||||
debugHelper.registerCartesianAxes(asset, {
|
||||
@@ -17,3 +19,13 @@ debugHelper.registerCartesianAxes(asset, {
|
||||
Parent = transforms.EarthIAU.Identifier,
|
||||
Scale = earthRadius * 1.5
|
||||
})
|
||||
|
||||
debugHelper.registerCartesianAxes(asset, {
|
||||
Parent = sun_transforms.SunIAU.Identifier,
|
||||
Scale = sunRadius * 300
|
||||
})
|
||||
|
||||
debugHelper.registerCartesianAxes(asset, {
|
||||
Parent = sun_transforms.SolarSystemBarycenter.Identifier,
|
||||
Scale = sunRadius * 300
|
||||
})
|
||||
|
||||
47
data/assets/examples/globerotation.asset
Normal file
47
data/assets/examples/globerotation.asset
Normal file
@@ -0,0 +1,47 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local earth = asset.require('scene/solarsystem/planets/earth/earth')
|
||||
local sunTransforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
|
||||
local models = asset.syncedResource({
|
||||
Name = "New Horizons Model",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "newhorizons_model",
|
||||
Version = 2
|
||||
})
|
||||
|
||||
local lat = 40.7306
|
||||
local long = -73.9352
|
||||
|
||||
local Example_GlobeRotation = {
|
||||
Identifier = "Example_GlobeRotation",
|
||||
Parent = earth.Earth.Identifier,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "GlobeTranslation",
|
||||
Globe = earth.Earth.Identifier,
|
||||
Latitude = lat,
|
||||
Longitude = long,
|
||||
Altitude = 6,
|
||||
UseHeightmap = true
|
||||
},
|
||||
Rotation = {
|
||||
Type = "GlobeRotation",
|
||||
Globe = earth.Earth.Identifier,
|
||||
Latitude = lat,
|
||||
Longitude = long
|
||||
-- Can be used to to put flat on leaning surfaces, but also leads to updating
|
||||
-- the rotation every frame
|
||||
--UseHeightmap = true
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
Body = "NEW HORIZONS",
|
||||
GeometryFile = models .. "/NewHorizonsCleanModel.obj"
|
||||
},
|
||||
GUI = {
|
||||
Path = "/Example"
|
||||
}
|
||||
}
|
||||
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { Example_GlobeRotation })
|
||||
@@ -2,19 +2,11 @@ local assetHelper = asset.require('util/asset_helper')
|
||||
local earth = asset.require('scene/solarsystem/planets/earth/earth')
|
||||
local sunTransforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
|
||||
|
||||
local textures = asset.syncedResource({
|
||||
Name = "New Horizons Textures",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "newhorizons_textures",
|
||||
Version = 3
|
||||
})
|
||||
|
||||
local models = asset.syncedResource({
|
||||
Name = "New Horizons Model",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "newhorizons_model",
|
||||
Version = 1
|
||||
Version = 2
|
||||
})
|
||||
|
||||
local Example_Fixed_Height = {
|
||||
@@ -24,19 +16,15 @@ local Example_Fixed_Height = {
|
||||
Translation = {
|
||||
Type = "GlobeTranslation",
|
||||
Globe = earth.Earth.Identifier,
|
||||
Longitude = 0.0,
|
||||
Latitude = 0.0,
|
||||
FixedAltitude = 10000000.0
|
||||
Latitude = 40.7128,
|
||||
Longitude = -74.006,
|
||||
Altitude = 100000.0
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
Body = "NEW HORIZONS",
|
||||
Geometry = {{
|
||||
Type = "MultiModelGeometry",
|
||||
GeometryFile = models .. "/NewHorizonsCleanModel.obj",
|
||||
ColorTexture = textures .. "/NHTexture.jpg"
|
||||
}}
|
||||
GeometryFile = models .. "/NewHorizonsCleanModel.obj"
|
||||
},
|
||||
GUI = {
|
||||
Path = "/Example"
|
||||
@@ -50,18 +38,15 @@ local Example_Adaptive_Height = {
|
||||
Translation = {
|
||||
Type = "GlobeTranslation",
|
||||
Globe = earth.Earth.Identifier,
|
||||
Latitude = 40.7128,
|
||||
Longitude = -74.006,
|
||||
Latitude = 40.7128
|
||||
UseHeightmap = true
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
Body = "NEW HORIZONS",
|
||||
Geometry = {{
|
||||
Type = "MultiModelGeometry",
|
||||
GeometryFile = models .. "/NewHorizonsCleanModel.obj",
|
||||
ColorTexture = textures .. "/NHTexture.jpg"
|
||||
}}
|
||||
GeometryFile = models .. "/NewHorizonsCleanModel.obj"
|
||||
},
|
||||
GUI = {
|
||||
Path = "/Example"
|
||||
|
||||
47
data/assets/examples/luastatemachine.asset
Normal file
47
data/assets/examples/luastatemachine.asset
Normal file
@@ -0,0 +1,47 @@
|
||||
local stateMachineHelper = asset.require('util/lua_state_machine_helper')
|
||||
|
||||
local states = {
|
||||
{
|
||||
Title = "Highlight EarthTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 2, 1)
|
||||
end
|
||||
},
|
||||
{
|
||||
Title = "Highlight MarsTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.Appearance.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.Appearance.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.Appearance.LineWidth", 10, 1)
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
local stateMachine
|
||||
|
||||
function next()
|
||||
stateMachine.goToNextState()
|
||||
end
|
||||
|
||||
function previous()
|
||||
stateMachine.goToPreviousState()
|
||||
end
|
||||
|
||||
asset.onInitialize(function ()
|
||||
stateMachine = stateMachineHelper.createStateMachine(states)
|
||||
openspace.bindKey('RIGHT', 'next()')
|
||||
openspace.bindKey('LEFT', 'previous()')
|
||||
end)
|
||||
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
stateMachine = nil
|
||||
openspace.clearKey('RIGHT')
|
||||
openspace.clearKey('LEFT')
|
||||
end)
|
||||
75
data/assets/examples/modelshader/model_fs.glsl
Normal file
75
data/assets/examples/modelshader/model_fs.glsl
Normal file
@@ -0,0 +1,75 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#include "fragment.glsl"
|
||||
|
||||
in vec2 vs_st;
|
||||
in vec3 vs_normalViewSpace;
|
||||
in vec4 vs_positionCameraSpace;
|
||||
in float vs_screenSpaceDepth;
|
||||
in mat3 TBN;
|
||||
|
||||
uniform float ambientIntensity = 0.2;
|
||||
uniform float diffuseIntensity = 1.0;
|
||||
uniform float specularIntensity = 1.0;
|
||||
|
||||
uniform bool performShading = true;
|
||||
uniform bool use_forced_color = false;
|
||||
uniform bool has_texture_diffuse;
|
||||
uniform bool has_texture_normal;
|
||||
uniform bool has_texture_specular;
|
||||
uniform bool has_color_specular;
|
||||
|
||||
uniform bool opacityBlending = false;
|
||||
|
||||
uniform sampler2D texture_diffuse;
|
||||
uniform sampler2D texture_normal;
|
||||
uniform sampler2D texture_specular;
|
||||
|
||||
uniform vec3 color_diffuse;
|
||||
uniform vec3 color_specular;
|
||||
|
||||
uniform int nLightSources;
|
||||
uniform vec3 lightDirectionsViewSpace[8];
|
||||
uniform float lightIntensities[8];
|
||||
|
||||
uniform float opacity = 1.0;
|
||||
|
||||
Fragment getFragment() {
|
||||
Fragment frag;
|
||||
|
||||
if (has_texture_normal) {
|
||||
vec3 normalAlbedo = texture(texture_normal, vs_st).rgb;
|
||||
normalAlbedo = normalize(normalAlbedo * 2.0 - 1.0);
|
||||
frag.color.rgb = normalize(TBN * normalAlbedo);
|
||||
}
|
||||
else {
|
||||
frag.color.rgb = normalize(vs_normalViewSpace);
|
||||
}
|
||||
frag.color.a = 1.0;
|
||||
frag.gPosition = vs_positionCameraSpace;
|
||||
frag.gNormal = vec4(vs_normalViewSpace, 0.0);
|
||||
frag.disableLDR2HDR = true;
|
||||
return frag;
|
||||
}
|
||||
68
data/assets/examples/modelshader/model_vs.glsl
Normal file
68
data/assets/examples/modelshader/model_vs.glsl
Normal file
@@ -0,0 +1,68 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
#include "PowerScaling/powerScaling_vs.hglsl"
|
||||
|
||||
layout(location = 0) in vec4 in_position;
|
||||
layout(location = 1) in vec2 in_st;
|
||||
layout(location = 2) in vec3 in_normal;
|
||||
layout(location = 3) in vec3 in_tangent;
|
||||
|
||||
out vec2 vs_st;
|
||||
out vec3 vs_normalViewSpace;
|
||||
out float vs_screenSpaceDepth;
|
||||
out vec4 vs_positionCameraSpace;
|
||||
out mat3 TBN;
|
||||
|
||||
uniform mat4 modelViewTransform;
|
||||
uniform mat4 projectionTransform;
|
||||
uniform mat4 normalTransform;
|
||||
uniform mat4 meshTransform;
|
||||
uniform mat4 meshNormalTransform;
|
||||
|
||||
void main() {
|
||||
vs_positionCameraSpace = modelViewTransform * (meshTransform * in_position);
|
||||
vec4 positionClipSpace = projectionTransform * vs_positionCameraSpace;
|
||||
vec4 positionScreenSpace = z_normalization(positionClipSpace);
|
||||
|
||||
gl_Position = positionScreenSpace;
|
||||
vs_st = in_st;
|
||||
vs_screenSpaceDepth = positionScreenSpace.w;
|
||||
|
||||
vs_normalViewSpace = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_normal));
|
||||
|
||||
// TBN matrix for normal mapping
|
||||
vec3 T = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_tangent));
|
||||
vec3 N = normalize(mat3(normalTransform) * (mat3(meshNormalTransform) * in_normal));
|
||||
|
||||
// Re-orthogonalize T with respect to N
|
||||
T = normalize(T - dot(T, N) * N);
|
||||
|
||||
// Retrieve perpendicular vector B with cross product of T and N
|
||||
vec3 B = normalize(cross(N, T));
|
||||
|
||||
TBN = mat3(T, B, N);
|
||||
}
|
||||
45
data/assets/examples/modelshader/modelshader.asset
Normal file
45
data/assets/examples/modelshader/modelshader.asset
Normal file
@@ -0,0 +1,45 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sunTransforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
local transforms = asset.require('scene/solarsystem/planets/earth/transforms')
|
||||
|
||||
local model = asset.syncedResource({
|
||||
Name = "Animated Box",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "animated_box",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local model = {
|
||||
Identifier = "modelshader",
|
||||
Parent = transforms.EarthCenter.Identifier,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "StaticTranslation",
|
||||
Position = { 11E7, 0.0, 0.0 }
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = model .. "/BoxAnimated.glb",
|
||||
ModelScale = 3E7,
|
||||
LightSources = {
|
||||
{
|
||||
Type = "SceneGraphLightSource",
|
||||
Identifier = "Sun",
|
||||
Node = sunTransforms.SolarSystemBarycenter.Identifier,
|
||||
Intensity = 1.0
|
||||
}
|
||||
},
|
||||
PerformShading = true,
|
||||
DisableFaceCulling = true,
|
||||
VertexShader = asset.localResource("model_vs.glsl"),
|
||||
FragmentShader = asset.localResource("model_fs.glsl"),
|
||||
},
|
||||
GUI = {
|
||||
Name = "Model Shader",
|
||||
Path = "/Example",
|
||||
Description = "Simple box model with a custom shader",
|
||||
}
|
||||
}
|
||||
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { model })
|
||||
@@ -15,22 +15,51 @@ asset.onInitialize(function ()
|
||||
|
||||
local interpolationDuration = 0.5
|
||||
|
||||
function nextSlide()
|
||||
helper.goToNextSlide(deck, interpolationDuration)
|
||||
end
|
||||
-- Add global functions for controlling slide deck and bind to keys
|
||||
rawset(_G, "nextSlide", function()
|
||||
helper.goToNextSlide(deck, interpolationDuration)
|
||||
end)
|
||||
|
||||
function previousSlide()
|
||||
rawset(_G, "previousSlide", function()
|
||||
helper.goToPreviousSlide(deck, interpolationDuration)
|
||||
end
|
||||
end)
|
||||
|
||||
function toggleSlides()
|
||||
rawset(_G, "toggleSlides", function()
|
||||
helper.toggleSlides(deck, interpolationDuration)
|
||||
end
|
||||
end)
|
||||
|
||||
helper.setCurrentSlide(deck, 1)
|
||||
openspace.bindKey("KP_6", "nextSlide()", "Next slide", "Next slide", "/Slides")
|
||||
openspace.bindKey("KP_4", "previousSlide()", "Previous slide", "Previous slide", "/Slides")
|
||||
openspace.bindKey("KP_0", "toggleSlides()", "Toggle slides", "Toggle slides", "/Slides")
|
||||
|
||||
openspace.action.registerAction({
|
||||
Identifier = "slide_deck.prevslide",
|
||||
Name = "Previous slide",
|
||||
Command = "previousSlide()",
|
||||
Documentation = "Previous slide",
|
||||
GuiPath = "/Slides",
|
||||
IsLocal = false
|
||||
})
|
||||
openspace.bindKey("KP_4", "slide_deck.prevslide");
|
||||
|
||||
openspace.action.registerAction({
|
||||
Identifier = "slide_deck.nextslide",
|
||||
Name = "Next slide",
|
||||
Command = "nextSlide()",
|
||||
Documentation = "Next slide",
|
||||
GuiPath = "/Slides",
|
||||
IsLocal = false
|
||||
})
|
||||
openspace.bindKey("KP_6", "slide_deck.nextslide")
|
||||
|
||||
openspace.action.registerAction({
|
||||
Identifier = "slide_deck.toggleslides",
|
||||
Name = "Toggle slides",
|
||||
Command = "toggleSlides()",
|
||||
Documentation = "Toggle slides",
|
||||
GuiPath = "/Slides",
|
||||
IsLocal = false
|
||||
})
|
||||
openspace.bindKey("KP_0", "slide_deck.toggleslides")
|
||||
|
||||
end)
|
||||
|
||||
|
||||
|
||||
@@ -1,47 +1,72 @@
|
||||
local stateMachineHelper = asset.require('util/state_machine_helper')
|
||||
-- Create a state machine with a few different states. The state machine can be controlled through
|
||||
-- the scripting commands from the state machine module.
|
||||
|
||||
states = {
|
||||
local targetNode = function(nodeIdentifier)
|
||||
return [[
|
||||
openspace.setPropertyValueSingle("NavigationHandler.OrbitalNavigator.RetargetAnchor", nil)
|
||||
openspace.setPropertyValueSingle(
|
||||
"NavigationHandler.OrbitalNavigator.Anchor",
|
||||
']] .. nodeIdentifier .. [['
|
||||
)
|
||||
openspace.setPropertyValueSingle("NavigationHandler.OrbitalNavigator.Aim", '')
|
||||
]]
|
||||
end
|
||||
|
||||
local states = {
|
||||
{
|
||||
Title = "Highlight EarthTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 2, 1)
|
||||
end
|
||||
},
|
||||
Identifier = "Constellations",
|
||||
Enter = [[
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Opacity', 1.0, 1.0)
|
||||
]],
|
||||
Exit = [[
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Opacity', 0.0, 1.0)
|
||||
]]
|
||||
},
|
||||
{
|
||||
Title = "Highlight MarsTrail",
|
||||
Play = function ()
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.LineWidth", 10, 1)
|
||||
end,
|
||||
Rewind = function ()
|
||||
openspace.setPropertyValue("Scene.MarsTrail.Renderable.LineWidth", 2, 1)
|
||||
openspace.setPropertyValue("Scene.EarthTrail.Renderable.LineWidth", 10, 1)
|
||||
end
|
||||
Identifier = "Earth",
|
||||
Enter = "openspace.setPropertyValueSingle('Scene.EarthLabel.Renderable.Enabled', true)",
|
||||
Exit = "openspace.setPropertyValueSingle('Scene.EarthLabel.Renderable.Enabled', false)"
|
||||
},
|
||||
{
|
||||
Identifier = "Moon"
|
||||
}
|
||||
}
|
||||
|
||||
local stateMachine
|
||||
local transitions = {
|
||||
{
|
||||
From = "Earth",
|
||||
To = "Moon",
|
||||
Action = targetNode("Moon")
|
||||
},
|
||||
{
|
||||
From = "Moon",
|
||||
To = "Earth",
|
||||
Action = targetNode("Earth")
|
||||
},
|
||||
{
|
||||
From = "Earth",
|
||||
To = "Constellations",
|
||||
},
|
||||
{
|
||||
From = "Constellations",
|
||||
To = "Earth"
|
||||
},
|
||||
{
|
||||
From = "Moon",
|
||||
To = "Constellations",
|
||||
Action = targetNode("Earth")
|
||||
},
|
||||
{
|
||||
From = "Constellations",
|
||||
To = "Moon",
|
||||
Action = targetNode("Moon")
|
||||
}
|
||||
}
|
||||
|
||||
function next()
|
||||
stateMachine.goToNextState()
|
||||
end
|
||||
asset.onInitialize(function()
|
||||
-- Setup (could be done in the profile)
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Enabled', true)
|
||||
openspace.setPropertyValueSingle('Scene.Constellations.Renderable.Opacity', 0.0)
|
||||
|
||||
function previous()
|
||||
stateMachine.goToPreviousState()
|
||||
end
|
||||
|
||||
asset.onInitialize(function ()
|
||||
stateMachine = stateMachineHelper.createStateMachine(states)
|
||||
openspace.bindKey('RIGHT', 'next()')
|
||||
openspace.bindKey('LEFT', 'previous()')
|
||||
end)
|
||||
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
stateMachine = nil
|
||||
openspace.clearKey('RIGHT')
|
||||
openspace.clearKey('LEFT')
|
||||
openspace.statemachine.createStateMachine(states, transitions, "Earth")
|
||||
end)
|
||||
|
||||
39
data/assets/examples/temperature_land_highres.asset
Normal file
39
data/assets/examples/temperature_land_highres.asset
Normal file
@@ -0,0 +1,39 @@
|
||||
local globeIdentifier = asset.require("scene/solarsystem/planets/earth/earth.asset").Earth.Identifier
|
||||
|
||||
local path = asset.syncedResource({
|
||||
Name = "Earth Textures Climate",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "earth_textures_climate",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local layer = {
|
||||
Identifier = "ERA5_Land_HighRes_Monthly_2M_Temperature_Temporal",
|
||||
Name = "ERA5 Land HighRes Monthly 2M Temperature (Temporal)",
|
||||
Type = "TemporalTileLayer",
|
||||
FilePath =
|
||||
"<OpenSpaceTemporalGDALDataset>" ..
|
||||
"<OpenSpaceTimeStart>1981-01-01</OpenSpaceTimeStart>" ..
|
||||
"<OpenSpaceTimeEnd>1990-10-01</OpenSpaceTimeEnd>" ..
|
||||
"<OpenSpaceTimeResolution>1M</OpenSpaceTimeResolution>" ..
|
||||
"<OpenSpaceTimeIdFormat>YYYY-MM-DD</OpenSpaceTimeIdFormat>" ..
|
||||
"<OpenSpaceTransferFunction>" .. path .. "/rainbow.png</OpenSpaceTransferFunction>" ..
|
||||
"<OpenSpaceTimeInterpolation>linear</OpenSpaceTimeInterpolation>" ..
|
||||
"<FilePath>" .. path .. "/${OpenSpaceTimeId}-land.png</FilePath>" ..
|
||||
"</OpenSpaceTemporalGDALDataset>",
|
||||
Description = [[ Temporal coverage: 01 Jan 1981 - 31 Dec 2020.]]
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.globebrowsing.addLayer(globeIdentifier, "ColorLayers", layer)
|
||||
end)
|
||||
|
||||
asset.export("layer", layer)
|
||||
asset.meta = {
|
||||
Name = "Climate Earth Layers",
|
||||
Version = "1.0",
|
||||
Description = [[ ERA5 data ]],
|
||||
Author = "OpenSpace Team",
|
||||
URL = "http://openspaceproject.com",
|
||||
License = "MIT license"
|
||||
}
|
||||
@@ -35,12 +35,6 @@ asset.syncedResource({
|
||||
Override = true
|
||||
})
|
||||
|
||||
asset.syncedResource({
|
||||
Name = "Example No ident",
|
||||
Type = "UrlSynchronization",
|
||||
Url = "http://ipv4.download.thinkbroadband.com/5MB.zip"
|
||||
})
|
||||
|
||||
asset.syncedResource({
|
||||
Name = "Example No Hash",
|
||||
Type = "UrlSynchronization",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local circle = asset.require('util/circle').circle
|
||||
|
||||
local circle = asset.syncedResource({
|
||||
Name = "Circle",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "circle_image",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local HUDFSpeck = asset.syncedResource({
|
||||
Name = "HUDF Speck",
|
||||
|
||||
@@ -78,7 +78,7 @@ local sunstar = {
|
||||
}
|
||||
},
|
||||
GUI = {
|
||||
Name = "Sun",
|
||||
Name = "Sun Star",
|
||||
Path = "/Milky Way/Stars",
|
||||
Description = [[Individual star to represent the sun when outside of the solar
|
||||
system]]
|
||||
|
||||
@@ -78,6 +78,60 @@ local createConstellations = function (baseIdentifier, guiPath, constellationfil
|
||||
end
|
||||
|
||||
|
||||
local show_art = {
|
||||
Identifier = "constellation_art.show_art",
|
||||
Name = "Show Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0);" ..
|
||||
"openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Enabled', true);" ..
|
||||
"openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0.2, 2);",
|
||||
Documentation = "Enables and fades up constellation art work",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false
|
||||
}
|
||||
asset.export("ShowArtAction", show_art)
|
||||
|
||||
local hide_art = {
|
||||
Identifier = "constellation_art.hide_art",
|
||||
Name = "Hide Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0, 2);",
|
||||
Documentation = "Fades out constellation artwork",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false
|
||||
}
|
||||
asset.export("HideArtAction", hide_art)
|
||||
|
||||
local disable_art = {
|
||||
Identifier = "constellation_art.disable_art",
|
||||
Name = "Disable Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Enabled', false);",
|
||||
Documentation = "Disable constellation artwork",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false
|
||||
}
|
||||
asset.export("DisableArtAction", disable_art)
|
||||
|
||||
local show_zodiac_art = {
|
||||
Identifier = "constellation_art.show_zodiac_art",
|
||||
Name = "Show Zodiac Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0);" ..
|
||||
"openspace.setPropertyValue('{zodiac}.Renderable.Enabled',true);" ..
|
||||
"openspace.setPropertyValue('{zodiac}.Renderable.Opacity', 1, 2);",
|
||||
Documentation = "Enables and fades up zodiac art work",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false
|
||||
}
|
||||
asset.export("ShowZodiacArt", show_zodiac_art)
|
||||
|
||||
local hide_zodiac_art = {
|
||||
Identifier = "constellation_art.hide_zodiac_art",
|
||||
Name = "Hide Zodiac Art",
|
||||
Command = "openspace.setPropertyValue('{zodiac}.Renderable.Opacity', 0, 2);",
|
||||
Documentation = "fades down zodiac art work",
|
||||
GuiPath = "/Rendering",
|
||||
IsLocal = false
|
||||
}
|
||||
asset.export("HideZodiacArt", hide_zodiac_art)
|
||||
|
||||
local nodes = {}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
@@ -87,12 +141,20 @@ asset.onInitialize(function ()
|
||||
for _, n in ipairs(nodes) do
|
||||
openspace.addSceneGraphNode(n);
|
||||
end
|
||||
|
||||
openspace.action.registerAction(show_art)
|
||||
openspace.action.registerAction(hide_art)
|
||||
openspace.action.registerAction(disable_art)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
for _, n in ipairs(nodes) do
|
||||
openspace.removeSceneGraphNode(n.Identifier);
|
||||
end
|
||||
|
||||
openspace.action.removeAction(show_art.Identifier)
|
||||
openspace.action.removeAction(hide_art.Identifier)
|
||||
openspace.action.removeAction(disable_art.Identifier)
|
||||
end)
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +1,13 @@
|
||||
local scene_helper = asset.require('util/scene_helper')
|
||||
|
||||
local Keybindings = {
|
||||
{
|
||||
Key = "v",
|
||||
Name = "Show Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0);" ..
|
||||
"openspace.setPropertyValue('{zodiac}.Renderable.Enabled',true);" ..
|
||||
"openspace.setPropertyValue('{zodiac}.Renderable.Opacity', 1, 2);",
|
||||
Documentation = "Enables and fades up zodiac art work",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
},
|
||||
{
|
||||
Key = "Shift+v",
|
||||
Name = "Show Constellation Art",
|
||||
Command = "openspace.setPropertyValue('{zodiac}.Renderable.Opacity', 0, 2);",
|
||||
Documentation = "fades down zodiac art work",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
},
|
||||
}
|
||||
local mainAsset = asset.require('./constellation_art')
|
||||
|
||||
asset.onInitialize(function ()
|
||||
scene_helper.bindKeys(Keybindings)
|
||||
openspace.bindKey("v", mainAsset.ShowZodiacArt.Identifier)
|
||||
openspace.bindKey("Shift+v", mainAsset.HideZodiacArt.Identifier)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
scene_helper.unbindKeys(Keybindings)
|
||||
openspace.clearKey("v")
|
||||
openspace.clearKey("Shift+v")
|
||||
end)
|
||||
|
||||
asset.meta = {
|
||||
|
||||
@@ -1,40 +1,15 @@
|
||||
local scene_helper = asset.require('util/scene_helper')
|
||||
|
||||
local Keybindings = {
|
||||
{
|
||||
Key = "c",
|
||||
Name = "Show Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0);" ..
|
||||
"openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Enabled', true);" ..
|
||||
"openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0.2, 2);",
|
||||
Documentation = "Enables and fades up constellation art work",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
},
|
||||
{
|
||||
Key = "SHIFT+c",
|
||||
Name = "Hide Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Opacity', 0, 2);",
|
||||
Documentation = "Fades out constellation artwork",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
},
|
||||
{
|
||||
Key = "CTRL+c",
|
||||
Name = "Disable Constellation Art",
|
||||
Command = "openspace.setPropertyValue('Scene.ConstellationArt*.Renderable.Enabled', false);",
|
||||
Documentation = "Disable constellation artwork",
|
||||
GuiPath = "/Rendering",
|
||||
Local = false
|
||||
}
|
||||
}
|
||||
local mainAsset = asset.require('./constellation_art')
|
||||
|
||||
asset.onInitialize(function ()
|
||||
scene_helper.bindKeys(Keybindings)
|
||||
openspace.bindKey("c", mainAsset.ShowArtAction.Identifier)
|
||||
openspace.bindKey("SHIFT+c", mainAsset.HideArtAction.Identifier)
|
||||
openspace.bindKey("CTRL+c", mainAsset.DisableArtAction.Identifier)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
scene_helper.unbindKeys(Keybindings)
|
||||
openspace.clearKey("c")
|
||||
openspace.clearKey("SHIFT+c")
|
||||
openspace.clearKey("CTRL+c")
|
||||
end)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
local habitableZoneTextures =
|
||||
asset.require('./../habitable_zones/habitable_zone_textures').TexturesPath
|
||||
|
||||
local sunTextures = asset.require('scene/solarsystem/sun/sun_textures').TexturesPath
|
||||
local sunTextures = asset.syncedResource({
|
||||
Type = "HttpSynchronization",
|
||||
Name = "Sun textures",
|
||||
Identifier = "sun_textures",
|
||||
Version = 4
|
||||
})
|
||||
|
||||
local TexturesPath = asset.syncedResource({
|
||||
Name = "Exoplanet Textures",
|
||||
|
||||
@@ -2,10 +2,16 @@ local assetHelper = asset.require('util/asset_helper')
|
||||
local transforms = asset.require('./../transforms')
|
||||
asset.require("spice/base")
|
||||
asset.require('../trail')
|
||||
local labelsPath = asset.require('./../pluto_globelabels').LabelsPath
|
||||
|
||||
|
||||
|
||||
local labelsPath = asset.syncedResource({
|
||||
Name = "Pluto Labels",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "pluto_labels",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local Charon = {
|
||||
Identifier = "Charon",
|
||||
Parent = transforms.PlutoBarycenter.Identifier,
|
||||
|
||||
@@ -21,7 +21,7 @@ local Hydra = {
|
||||
SegmentsPerPatch = 64,
|
||||
Layers = {}
|
||||
},
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto" },
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto", "moon_minor" },
|
||||
GUI = {
|
||||
Path = "/Solar System/Dwarf Planets/Pluto"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ local Kerberos = {
|
||||
SegmentsPerPatch = 64,
|
||||
Layers = {}
|
||||
},
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto" },
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto", "moon_minor" },
|
||||
GUI = {
|
||||
Path = "/Solar System/Dwarf Planets/Pluto"
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ local Nix = {
|
||||
SegmentsPerPatch = 64,
|
||||
Layers = {}
|
||||
},
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto" },
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto", "moon_minor" },
|
||||
GUI = {
|
||||
Path = "/Solar System/Dwarf Planets/Pluto"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ local Styx = {
|
||||
SegmentsPerPatch = 64,
|
||||
Layers = {}
|
||||
},
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto" },
|
||||
Tag = { "moon_solarSystem", "moon_dwarf", "moon_pluto", "moon_minor" },
|
||||
GUI = {
|
||||
Path = "/Solar System/Dwarf Planets/Pluto"
|
||||
}
|
||||
|
||||
@@ -2,9 +2,14 @@ local assetHelper = asset.require('util/asset_helper')
|
||||
local transforms = asset.require('./transforms')
|
||||
asset.require("spice/base")
|
||||
asset.require('./trail')
|
||||
local labelsPath = asset.require('./pluto_globelabels').LabelsPath
|
||||
|
||||
|
||||
local labelsPath = asset.syncedResource({
|
||||
Name = "Pluto Labels",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "pluto_labels",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local Pluto = {
|
||||
Identifier = "Pluto",
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
local LabelsPath = asset.syncedResource({
|
||||
Name = "Pluto Labels",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "pluto_labels",
|
||||
Version = 1
|
||||
})
|
||||
asset.export("LabelsPath", LabelsPath)
|
||||
@@ -3,33 +3,35 @@ local transforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
asset.require("spice/base")
|
||||
local kernels = asset.require('./kernels').PlutoKernels
|
||||
local OneAU = 1.496e+8
|
||||
|
||||
|
||||
local PlutoKeplerianTrail = {
|
||||
Identifier = "PlutoKeplerianTrail",
|
||||
Parent = transforms.SunECLIPJ2000.Identifier,
|
||||
Renderable = {
|
||||
Type = "RenderableTrailOrbit",
|
||||
Translation = {
|
||||
Type = "KeplerTranslation",
|
||||
Eccentricity = 2.543033082909471E-01,
|
||||
SemiMajorAxis = 3.974407237841206E+01 * OneAU,
|
||||
Inclination = 1.736609481151430E+01,
|
||||
AscendingNode = 1.102099981996057E+02,
|
||||
ArgumentOfPeriapsis = 1.142248569189779E+02,
|
||||
MeanAnomaly = 14.53,
|
||||
Epoch = '2000 01 01 00:00',
|
||||
Period = 7.82438e+9
|
||||
},
|
||||
Color = { 0.2, 0.8, 0.3 },
|
||||
Period = 90487.27692706819,
|
||||
Resolution = 1000,
|
||||
Enabled = false
|
||||
Identifier = "PlutoKeplerianTrail",
|
||||
Parent = transforms.SunECLIPJ2000.Identifier,
|
||||
Renderable = {
|
||||
Type = "RenderableTrailOrbit",
|
||||
Translation = {
|
||||
Type = "KeplerTranslation",
|
||||
Eccentricity = 2.543033082909471E-01,
|
||||
SemiMajorAxis = 3.974407237841206E+01 * OneAU,
|
||||
Inclination = 1.736609481151430E+01,
|
||||
AscendingNode = 1.102099981996057E+02,
|
||||
ArgumentOfPeriapsis = 1.142248569189779E+02,
|
||||
MeanAnomaly = 14.53,
|
||||
Epoch = '2000 01 01 00:00',
|
||||
Period = 7.82438e+9
|
||||
},
|
||||
Tag = { "planetTrail_dwarf" },
|
||||
GUI = {
|
||||
Name = "Pluto Keplerian Trail",
|
||||
Path = "/Solar System/Dwarf Planets/Pluto",
|
||||
Description = "Keplerian trail of Pluto. Contains full orbit."
|
||||
}
|
||||
Color = { 0.2, 0.8, 0.3 },
|
||||
Period = 90487.27692706819,
|
||||
Resolution = 1000,
|
||||
Enabled = false
|
||||
},
|
||||
Tag = { "planetTrail_dwarf" },
|
||||
GUI = {
|
||||
Name = "Pluto Keplerian Trail",
|
||||
Path = "/Solar System/Dwarf Planets/Pluto",
|
||||
Description = "Keplerian trail of Pluto. Contains full orbit."
|
||||
}
|
||||
}
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { PlutoKeplerianTrail })
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
local CarringtonLongitudeToHEEQ180Rotation = {
|
||||
-- This is a rotation matrix to go from Carrington longitude referens frame to HEEQ180
|
||||
-- reference fram. At the reference time, MAS_seq = 0, 2000-07-14T08:33:37.105 the
|
||||
-- Carrington longitude was 309.3 degrees.
|
||||
-- Difference from HEEQ => 360-309.3=50.7
|
||||
-- (or 0-309.3 = -309.3 However this leads to the same rotation matrix in the end)
|
||||
-- Since OpenSpace supports HEEQ180 and not HEEQ, 180 was added or subtracted
|
||||
-- => a1 = -129.3 and a2 = 230.7
|
||||
-- Rotation matrix: (cos a, -sin a, 0)(sin a, cos a, 0)(0, 0, 1) leads to the result.
|
||||
Type = "FixedRotation",
|
||||
XAxis = { -0.63338087262755016203262119192353, -0.77384020972650618518999944537717, 0.0 },
|
||||
YAxis = { 0.77384020972650618518999944537717, -0.63338087262755016203262119192353, 0.0 },
|
||||
ZAxis = { 0.0, 0.0, 1.0 }
|
||||
}
|
||||
|
||||
asset.export("CarringtonLongitudeToHEEQ180Rotation", CarringtonLongitudeToHEEQ180Rotation)
|
||||
@@ -0,0 +1,73 @@
|
||||
local assetHelper = asset.require("util/asset_helper")
|
||||
local propertyHelper = asset.require('util/property_helper')
|
||||
local sunTransforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
|
||||
local densityDirectory = asset.syncedResource({
|
||||
Name = "Bastille Day MAS Density",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "bastille_day_mas_density",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local sunRadius = 6.957E8
|
||||
|
||||
local densityVolume = {
|
||||
Identifier = "MAS-MHD-Density-bastille-day-2000",
|
||||
Parent = sunTransforms.SunIAU.Identifier,
|
||||
Transform = {
|
||||
Scale = {
|
||||
Type = "StaticScale",
|
||||
Scale = sunRadius
|
||||
},
|
||||
Rotation = {
|
||||
Type = "FixedRotation",
|
||||
XAxis = { -1.0, 0.0, 0.0 },
|
||||
YAxis = { 0.0, 1.0, 0.0 },
|
||||
ZAxis = { 0.0, 0.0, -1.0 }
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableTimeVaryingVolume",
|
||||
StepSize = 0.004,
|
||||
Opacity = 0.3,
|
||||
TransferFunction = asset.localResource("transferfunctions/mas-mhd-r-squared-old.txt"),
|
||||
SourceDirectory = densityDirectory,
|
||||
GridType = "Spherical",
|
||||
SecondsBefore = 24*60*60,
|
||||
SecondsAfter = 24*60*60,
|
||||
Dimensions = { 100, 100, 128 },
|
||||
InvertDataAtZ = true
|
||||
},
|
||||
GUI = {
|
||||
Name = "MAS Density",
|
||||
Path = "/Solar System/Heliosphere/Bastille Day 2000",
|
||||
Description = "Volumetric rendering for the bastille day CME event. This asset contain data from 2000-07-14 08:33 to 2000-07-14 11:49"
|
||||
}
|
||||
}
|
||||
|
||||
local toggle_volume = {
|
||||
Identifier = "density_volume.toggle_volume",
|
||||
Name = "Toggle volume",
|
||||
Command = propertyHelper.invert("Scene.MAS-MHD-Density-bastille-day-2000.Renderable.Enabled"),
|
||||
Documentation = "Toggle volume rendering of CME",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.action.registerAction(toggle_volume)
|
||||
end)
|
||||
asset.onDeinitialize(function ()
|
||||
openspace.action.removeAction(toggle_volume.Identifier)
|
||||
end)
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { densityVolume })
|
||||
|
||||
asset.meta = {
|
||||
Name = "Predictive Science Inc. Volume Rendering Bastille Day",
|
||||
Version = "1.0",
|
||||
Description = [[Volumetric rendering for the bastille day CME event]],
|
||||
Author = "CCMC, OpenSpace team",
|
||||
URL = "https://dx.doi.org/10.3847/1538-4357/aab36d",
|
||||
License = "CC-BY",
|
||||
Identifier = {"MAS-MHD-Density-bastille-day-2000"}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local heliosphereTransforms = asset.require('scene/solarsystem/sun/transforms_heliosphere')
|
||||
local propertyHelper = asset.require('util/property_helper')
|
||||
local rot = asset.require('./carringtonToHEEQRotation.asset')
|
||||
|
||||
local fieldlinesDirectory = asset.syncedResource({
|
||||
Name = "Bastille Day MAS Fieldlines",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "bastille_day_mas_fieldlines",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local sunRadius = 6.957E8
|
||||
|
||||
-- Fieldlies from binaries
|
||||
local fieldlines = {
|
||||
Identifier = "MAS-MHD-Fieldlines-bastille-day-2000",
|
||||
Parent = heliosphereTransforms.HEEQ180ReferenceFrame.Identifier,
|
||||
Transform = {
|
||||
Scale = {
|
||||
Type = "StaticScale",
|
||||
Scale = sunRadius
|
||||
},
|
||||
Rotation = rot.CarringtonLongitudeToHEEQ180Rotation
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableFieldlinesSequence",
|
||||
SourceFolder = fieldlinesDirectory,
|
||||
AlphaBlendlingEnabled = false,
|
||||
InputFileType = "Osfls",
|
||||
ColorTablePaths = {
|
||||
asset.localResource("transferfunctions/density-fieldlines.txt"),
|
||||
asset.localResource("transferfunctions/velocity-fieldlines.txt")
|
||||
},
|
||||
ColorTableMinMax = {
|
||||
{ 0, 1000000 },
|
||||
{ 100, 2000 }
|
||||
},
|
||||
SimulationModel = "mas",
|
||||
Color = {
|
||||
Uniform = { 0.0, 0.725, 0.75, 1.0 }
|
||||
}
|
||||
},
|
||||
GUI = {
|
||||
Path = "/Solar System/Heliosphere/Bastille Day 2000",
|
||||
Name = "MAS Fieldlines",
|
||||
Description = "Magnetic fieldlines for the bastille day CME event. This asset contains data from 2000-07-14 08:33 to 2000-07-14 11:49"
|
||||
}
|
||||
}
|
||||
|
||||
local toggle_fieldlines = {
|
||||
Identifier = "fieldlines.toggle_fieldlines",
|
||||
Name = "Toggle Fieldlines",
|
||||
Command = propertyHelper.invert("Scene.MAS-MHD-Fieldlines-bastille-day-2000.Renderable.Enabled"),
|
||||
Documentation = "Toggle fieldline rendering of CME",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.action.registerAction(toggle_fieldlines)
|
||||
end)
|
||||
asset.onDeinitialize(function ()
|
||||
openspace.action.removeAction(toggle_fieldlines.Identifier)
|
||||
end)
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { fieldlines })
|
||||
|
||||
asset.meta = {
|
||||
Name = "Predictive Science Inc. Fieldlines Bastille Day",
|
||||
Version = "1.0",
|
||||
Description = [[Magnetic fieldlines for the bastille day CME event]],
|
||||
Author = "CCMC, Jonathan Grangien, Matthias Berg",
|
||||
URL = "https://dx.doi.org/10.3847/1538-4357/aab36d",
|
||||
License = "CC-BY",
|
||||
Identifier = {"MAS-MHD-Fieldlines-bastille-day-2000"}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local heliosphereTransforms = asset.require('scene/solarsystem/sun/transforms_heliosphere')
|
||||
local propertyHelper = asset.require('util/property_helper')
|
||||
local rot = asset.require('./carringtonToHEEQRotation.asset')
|
||||
|
||||
local fluxNodesBinaries = asset.syncedResource({
|
||||
Name = "Bastille day Flux nodes binaries",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "bastille_day_streamnodes_binaries",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
-- FluxNodes from binaries
|
||||
local fluxNodes = {
|
||||
Identifier = "MAS-MHD-FluxNodes-bastille-day-2000",
|
||||
Parent = heliosphereTransforms.HEEQ180ReferenceFrame.Identifier,
|
||||
Transform = {
|
||||
Rotation = rot.CarringtonLongitudeToHEEQ180Rotation
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableFluxNodes",
|
||||
SourceFolder = fluxNodesBinaries,
|
||||
ColorTablePath = asset.localResource("transferfunctions/CMR.txt"),
|
||||
ColorTableRange = {-2.0, 4.0}
|
||||
},
|
||||
GUI = {
|
||||
Path = "/Solar System/Heliosphere/Bastille Day 2000",
|
||||
Name = "Flux Nodes",
|
||||
Description = "Flux nodes for the bastille day CME event. This asset contains data from 2000-07-14 08:38 to 2000-07-14 19:48"
|
||||
}
|
||||
}
|
||||
|
||||
local toggle_fluxnodes = {
|
||||
Identifier = "fluxnodes.toggle_fluxnodes",
|
||||
Name = "Toggle flux nodes",
|
||||
Command = propertyHelper.invert("Scene.MAS-MHD-FluxNodes-bastille-day-2000.Renderable.Enabled"),
|
||||
Documentation = "Toggle flux node rendering of CME",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.action.registerAction(toggle_fluxnodes)
|
||||
end)
|
||||
asset.onDeinitialize(function ()
|
||||
openspace.action.removeAction(toggle_fluxnodes.Identifier)
|
||||
end)
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { fluxNodes })
|
||||
|
||||
asset.meta = {
|
||||
Name = "Predictive Science Inc. Flux nodes Bastille Day",
|
||||
Version = "1.0",
|
||||
Description = [[Flux nodes for the bastille day CME event]],
|
||||
Author = "CCMC, Christian Adamsson, Emilie Ho",
|
||||
URL = "https://dx.doi.org/10.3847/1538-4357/aab36d",
|
||||
License = "CC-BY",
|
||||
Identifier = {"MAS-MHD-FluxNodes-bastille-day-2000"}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
local assetHelper = asset.require("util/asset_helper")
|
||||
local propertyHelper = asset.require("util/property_helper")
|
||||
local transforms = asset.require('scene/solarsystem/sun/transforms_heliosphere')
|
||||
local rot = asset.require('./carringtonToHEEQRotation.asset')
|
||||
|
||||
local TexturesPathEquitorial = asset.syncedResource({
|
||||
Type = "HttpSynchronization",
|
||||
Name = "cutplanes_textures",
|
||||
Identifier = "cutplanes_textures",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local TexturesPathMeridial = asset.syncedResource({
|
||||
Type = "HttpSynchronization",
|
||||
Name = "cutplane_meridial_textures",
|
||||
Identifier = "cutplane_meridial_textures",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local EquatorialCutplane = {
|
||||
Identifier = "EquatorialCutplane-bastille-day-2000",
|
||||
Parent = transforms.HEEQ180ReferenceFrame.Identifier,
|
||||
Transform = {
|
||||
Rotation = rot.CarringtonLongitudeToHEEQ180Rotation
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderablePlaneTimeVaryingImage",
|
||||
Size = 1.57E11,
|
||||
Enabled = true,
|
||||
SourceFolder = TexturesPathEquitorial,
|
||||
BlendMode = "Normal",
|
||||
MirrorBackside = false,
|
||||
Opacity = 0.7
|
||||
},
|
||||
GUI = {
|
||||
Name = "Cutplane Equitorial",
|
||||
Path = "/Solar System/Heliosphere/Bastille Day 2000",
|
||||
Description = "Equatorial cutplane sequence for the bastille day CME event. This asset contains data from 2000-07-14 08:38 to 2000-07-14 12:00"
|
||||
}
|
||||
}
|
||||
|
||||
local MeridialCutplane = {
|
||||
Identifier = "MeridialCutplane-bastille-day-2000",
|
||||
Parent = transforms.HEEQ180ReferenceFrame.Identifier,
|
||||
Transform = {
|
||||
Rotation = {
|
||||
Type = "StaticRotation",
|
||||
Rotation = { -math.pi/2, -math.pi, 0.0 }
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderablePlaneTimeVaryingImage",
|
||||
Size = 1.57E11,
|
||||
Enabled = true,
|
||||
SourceFolder = TexturesPathMeridial,
|
||||
BlendMode = "Normal",
|
||||
MirrorBackside = false,
|
||||
Opacity = 0.7
|
||||
},
|
||||
GUI = {
|
||||
Name = "Cutplane Meridial",
|
||||
Path = "/Solar System/Heliosphere/Bastille Day 2000",
|
||||
Description = "Meridial cutplane sequence for the bastille day CME event. This asset contains data from 2000-07-14 08:38 to 2000-07-14 12:00"
|
||||
}
|
||||
}
|
||||
|
||||
local toggle_equatorial = {
|
||||
Identifier = "fluxnodescutplane.toggle_equatorial",
|
||||
Name = "Toggle equatorial cutplane",
|
||||
Command = propertyHelper.invert("Scene.EquatorialCutplane-bastille-day-2000.Renderable.Enabled"),
|
||||
Documentation = "Toggle equatorial cutplane of CME",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
local toggle_meridial = {
|
||||
Identifier = "fluxnodescutplane.toggle_meridial",
|
||||
Name = "Toggle meridial cutplane",
|
||||
Command = propertyHelper.invert("Scene.MeridialCutplane-bastille-day-2000.Renderable.Enabled"),
|
||||
Documentation = "Toggle meridial cutplane of CME",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.action.registerAction(toggle_equatorial)
|
||||
openspace.action.registerAction(toggle_meridial)
|
||||
end)
|
||||
asset.onDeinitialize(function ()
|
||||
openspace.action.removeAction(toggle_equatorial.Identifier)
|
||||
openspace.action.removeAction(toggle_meridial.Identifier)
|
||||
end)
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { EquatorialCutplane, MeridialCutplane})
|
||||
|
||||
asset.meta = {
|
||||
Name = "Predictive Science Inc. Cutplanes Bastille Days",
|
||||
Version = "1.0",
|
||||
Description = [[Cutplanes for the bastille day CME event]],
|
||||
Author = "CCMC, Christian Adamsson, Emilie Ho",
|
||||
URL = "https://dx.doi.org/10.3847/1538-4357/aab36d",
|
||||
License = "CC-BY",
|
||||
Identifier = {"MeridialCutplane-bastille-day-2000", "EquatorialCutplane-bastille-day-2000"}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
asset.require("spice/base")
|
||||
local assetHelper = asset.require("util/asset_helper")
|
||||
|
||||
local TexturesPath = asset.syncedResource({
|
||||
Type = "HttpSynchronization",
|
||||
Name = "Streamnodes textures",
|
||||
Identifier = "streamnodes_legend",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local legend = {
|
||||
Identifier = "LegendFluxNodes-bastille-day-2000",
|
||||
Type = "ScreenSpaceImageLocal",
|
||||
TexturePath = TexturesPath .. "/CMR_transparent_white_text.png",
|
||||
Scale = 0.15,
|
||||
CartesianPosition = { 0.677970, 0.0, -1.049180 }
|
||||
}
|
||||
|
||||
local show_legend = {
|
||||
Identifier = "fluxnodelegend.show_legend",
|
||||
Name = "Show the legend image",
|
||||
Command = "openspace.setPropertyValueSingle('ScreenSpace.LegendFluxNodes-bastille-day-2000.Opacity', 0.0);" ..
|
||||
"openspace.setPropertyValueSingle('ScreenSpace.LegendFluxNodes-bastille-day-2000.Enabled', true);" ..
|
||||
"openspace.setPropertyValueSingle('ScreenSpace.LegendFluxNodes-bastille-day-2000.Opacity', 1.0, 4);",
|
||||
Documentation = "Show the legend image",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
local hide_legend = {
|
||||
Identifier = "fluxnodelegend.hide_legend",
|
||||
Name = "Hides the legend image",
|
||||
Command = "openspace.setPropertyValueSingle('ScreenSpace.LegendFluxNodes-bastille-day-2000.Opacity', 0.0, 2);",
|
||||
Documentation = "Hides the legend image",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.addScreenSpaceRenderable(legend)
|
||||
-- These numbers are there to put the legend on the side of the screen.
|
||||
openspace.setPropertyValueSingle('ScreenSpace.LegendFluxNodes-bastille-day-2000.Rotation', { 0.0, 0.0, 0.56652 });
|
||||
openspace.action.registerAction(show_legend)
|
||||
openspace.action.registerAction(hide_legend)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
openspace.removeScreenSpaceRenderable(legend.Identifier)
|
||||
openspace.action.removeAction(show_legend.Identifier)
|
||||
openspace.action.removeAction(hide_legend.Identifier)
|
||||
end)
|
||||
|
||||
asset.meta = {
|
||||
Name = "Predictive Science Inc. Flux nodes legend for Bastille Days",
|
||||
Version = "1.0",
|
||||
Description = [[Screen space renderable image legend of flux nodes for the bastille day CME event]],
|
||||
Author = "Christian Adamsson, Emilie Ho",
|
||||
URL = "https://dx.doi.org/10.3847/1538-4357/aab36d",
|
||||
License = "MIT",
|
||||
Identifier = {"LegendFluxNodes-bastille-day-2000"}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sunTransforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
local earthTransforms = asset.require('scene/solarsystem/planets/earth/transforms')
|
||||
|
||||
local travelSpeedIndicator = {
|
||||
Identifier = "TravelSpeedIndicator-2000",
|
||||
--SunIAU adds an extra rotation. Using barycenter, then offsetting to SunIAU
|
||||
Parent = sunTransforms.SolarSystemBarycenter.Identifier,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "SpiceTranslation",
|
||||
Target = "SUN",
|
||||
Observer = "SSB"
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableTravelSpeed",
|
||||
Target = earthTransforms.EarthCenter.Identifier, --name of scene graph node. Earth by default
|
||||
LineWidth = 4,
|
||||
IndicatorLength = 10,
|
||||
FadeLength = 10
|
||||
},
|
||||
GUI = {
|
||||
Path = "/Solar System/Heliosphere",
|
||||
Name = "Speed indicator",
|
||||
Description = "Speed of light indicator from sun to earth"
|
||||
}
|
||||
}
|
||||
|
||||
assetHelper.registerSceneGraphNodesAndExport(asset, { travelSpeedIndicator })
|
||||
|
||||
asset.meta = {
|
||||
Name = "Light travel from sun to earth",
|
||||
Version = "1.0",
|
||||
Description = [[Speed of light indicator from sun to earth]],
|
||||
Author = "CCMC, Christian Adamsson, Emilie Ho",
|
||||
URL = "",
|
||||
License = "MIT",
|
||||
Identifier = {"TravelSpeedIndicator-2000"}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
local TexturesPath = asset.syncedResource({
|
||||
Name = "Bastille Day Magnetogram",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "bastille_day_magnetogram",
|
||||
Version = 5
|
||||
})
|
||||
|
||||
asset.export("TexturesPath", TexturesPath)
|
||||
|
||||
-- Torok, T., Downs, C., Linker, J. A., Lionello, R., Titov, V. S., Mikic, Z., Riley, P., Caplan, R. M., and Wijaya, J. (2018).
|
||||
-- Sun-to-Earth MHD Simulation of the 2000 July 14 Bastille Day Eruption.
|
||||
-- ApJ,
|
||||
-- 856:75.
|
||||
asset.meta = {
|
||||
Name = "Predictive Science Inc. MAS Bastille Day Textures",
|
||||
Version = "1.0",
|
||||
Description = [[Magnetograms of the sun as texture color layers]],
|
||||
Author = "Jonathan Grangien, Matthias Berg",
|
||||
URL = "https://dx.doi.org/10.3847/1538-4357/aab36d",
|
||||
License = "CC-BY",
|
||||
Identifier = {"Sun"}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sun = asset.require('scene/solarsystem/sun/sun')
|
||||
|
||||
-- synced directories
|
||||
local magnetogramDirectory = asset.require('./magnetogram').TexturesPath;
|
||||
local magnetograms;
|
||||
|
||||
local switch_color_layer = {
|
||||
Identifier = "magnetogram_texture.switch_color_layer",
|
||||
Name = "Next sun texture",
|
||||
Command = [[
|
||||
local textureList = openspace.globebrowsing.getLayers('Sun', 'ColorLayers');
|
||||
if (magnetogramsTextureIndex == -1) then
|
||||
magnetogramsTextureIndex = 2;
|
||||
end;
|
||||
magnetogramsTextureIndex = magnetogramsTextureIndex + 1;
|
||||
if (magnetogramsTextureIndex >= #textureList) then
|
||||
magnetogramsTextureIndex = 0;
|
||||
end
|
||||
openspace.setPropertyValue("Scene.Sun.Renderable.Layers.ColorLayers.*.Enabled", false);
|
||||
if (magnetogramsTextureIndex == 0) then
|
||||
openspace.setPropertyValueSingle("Scene.Sun.Renderable.Layers.ColorLayers.Texture.Enabled", true);
|
||||
else
|
||||
str = "Scene.Sun.Renderable.Layers.ColorLayers.magnetogram-" .. magnetogramsTextureIndex .. ".Enabled";
|
||||
openspace.setPropertyValueSingle(str, true);
|
||||
end;
|
||||
]],
|
||||
Documentation = "Display next sun texture in list of textures",
|
||||
GuiPath = "/Bastille-Day 2000",
|
||||
IsLocal = false
|
||||
}
|
||||
|
||||
asset.onInitialize(function ()
|
||||
magnetograms = openspace.walkDirectoryFiles(magnetogramDirectory);
|
||||
rawset(_G, "magnetogramsTextureIndex", -1)
|
||||
for i, imagename in ipairs(magnetograms) do
|
||||
openspace.globebrowsing.addLayer(
|
||||
'Sun',
|
||||
'ColorLayers',
|
||||
{
|
||||
Identifier = "magnetogram-" .. i,
|
||||
Name = imagename, --"Magnetogram-" .. i,
|
||||
Description = "",
|
||||
FilePath = imagename,
|
||||
Enabled = false
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
openspace.action.registerAction(switch_color_layer)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
for i, imagename in ipairs(magnetograms) do
|
||||
openspace.globebrowsing.deleteLayer(
|
||||
'Sun', 'ColorLayers', 'magnetogram-' .. i
|
||||
)
|
||||
end
|
||||
openspace.action.removeAction(switch_color_layer.Identifier)
|
||||
end)
|
||||
@@ -0,0 +1,14 @@
|
||||
width 10
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.0 17 17 56 255
|
||||
mappingkey 0.083 47 38 142 255
|
||||
mappingkey 0.166 80 39 189 255
|
||||
mappingkey 0.332 139 49 139 255
|
||||
mappingkey 0.415 221 60 68 255
|
||||
mappingkey 0.498 243 95 20 255
|
||||
mappingkey 0.664 230 147 8 255
|
||||
mappingkey 0.830 230 195 34 255
|
||||
mappingkey 0.915 230 226 118 255
|
||||
mappingkey 1.0 255 255 255 255
|
||||
@@ -0,0 +1,14 @@
|
||||
width 10
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.0 60 38 164 255
|
||||
mappingkey 0.083 106 43 167 255
|
||||
mappingkey 0.166 173 54 110 255
|
||||
mappingkey 0.332 253 64 40 255
|
||||
mappingkey 0.415 234 117 6 255
|
||||
mappingkey 0.498 230 169 16 255
|
||||
mappingkey 0.664 236 236 159 255
|
||||
mappingkey 0.830 251 251 235 255
|
||||
mappingkey 0.915 255 255 255 255
|
||||
mappingkey 1.0 255 255 255 255
|
||||
@@ -0,0 +1,14 @@
|
||||
width 10
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.0 0 0 0 0
|
||||
mappingkey 0.083 30 30 100 255
|
||||
mappingkey 0.166 60 38 164 255
|
||||
mappingkey 0.332 106 43 167 255
|
||||
mappingkey 0.415 173 54 110 255
|
||||
mappingkey 0.498 253 64 40 255
|
||||
mappingkey 0.664 234 117 6 255
|
||||
mappingkey 0.830 230 169 16 255
|
||||
mappingkey 0.915 236 236 159 255
|
||||
mappingkey 1.0 251 251 235 255
|
||||
@@ -0,0 +1,9 @@
|
||||
width 6
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
mappingkey 0.0 0 0 255 250
|
||||
mappingkey 0.075 0 0 255 250
|
||||
mappingkey 0.4 0 255 255 250
|
||||
mappingkey 0.58 0 255 0 250
|
||||
mappingkey 0.73 255 255 0 250
|
||||
mappingkey 1.0 255 0 0 250
|
||||
@@ -0,0 +1,11 @@
|
||||
width 7
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.0 153 51 255 255
|
||||
mappingkey 0.166 51 51 255 255
|
||||
mappingkey 0.332 51 255 255 255
|
||||
mappingkey 0.498 51 255 51 255
|
||||
mappingkey 0.664 255 255 51 255
|
||||
mappingkey 0.830 255 153 51 255
|
||||
mappingkey 1.0 255 51 51 255
|
||||
@@ -0,0 +1,7 @@
|
||||
width 7
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.0 45 45 45 255
|
||||
mappingkey 0.5 45 45 45 255
|
||||
mappingkey 1.0 45 45 45 255
|
||||
@@ -0,0 +1,14 @@
|
||||
width 10
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.0 127 0 255 255
|
||||
mappingkey 0.083 89 59 253 255
|
||||
mappingkey 0.166 55 90 246 255
|
||||
mappingkey 0.332 23 152 242 255
|
||||
mappingkey 0.415 13 194 224 255
|
||||
mappingkey 0.498 138 254 174 255
|
||||
mappingkey 0.664 227 197 105 255
|
||||
mappingkey 0.830 255 128 66 255
|
||||
mappingkey 0.915 251 79 37 255
|
||||
mappingkey 1.0 255 0 0 255
|
||||
@@ -0,0 +1,14 @@
|
||||
width 2048
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.005 0 54 135 0
|
||||
mappingkey 0.06 0 67 168 10
|
||||
mappingkey 0.1 100 180 200 70
|
||||
mappingkey 0.2 180 50 100 100
|
||||
mappingkey 0.3 180 90 25 150
|
||||
mappingkey 0.4 200 53 18 255
|
||||
mappingkey 0.5 255 177 42 255
|
||||
mappingkey 0.6 255 22 15 255
|
||||
mappingkey 0.7 255 5 5 255
|
||||
mappingkey 1.0 255 0 0 255
|
||||
@@ -0,0 +1,18 @@
|
||||
width 2048
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.000001 50 50 250 2
|
||||
mappingkey 0.000015 250 250 250 250
|
||||
mappingkey 0.00025 70 170 255 250
|
||||
mappingkey 0.004 120 230 255 250
|
||||
mappingkey 0.0045 255 220 180 250
|
||||
mappingkey 0.06 255 150 120 250
|
||||
mappingkey 0.1 255 50 20 250
|
||||
|
||||
mappingkey 0.15 250 0 0 200
|
||||
mappingkey 0.20 250 0 0 200
|
||||
mappingkey 0.35 250 20 0 200
|
||||
mappingkey 0.40 250 0 0 200
|
||||
mappingkey 0.60 250 0 0 200
|
||||
mappingkey 0.80 250 0 0 255
|
||||
@@ -0,0 +1,18 @@
|
||||
width 2048
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.005 0 54 135 0
|
||||
mappingkey 0.06 0 85 125 11
|
||||
mappingkey 0.1 140 30 70 50
|
||||
mappingkey 0.13 160 160 0 45
|
||||
mappingkey 0.16 150 150 150 150
|
||||
mappingkey 0.2 180 210 255 190
|
||||
mappingkey 0.3 190 200 255 250
|
||||
mappingkey 0.4 255 255 255 0
|
||||
mappingkey 0.5 0 0 0 0
|
||||
mappingkey 0.6 137 223 255 0
|
||||
mappingkey 0.7 94 255 214 0
|
||||
mappingkey 0.8 255 200 180 0
|
||||
mappingkey 0.9 249 199 150 0
|
||||
mappingkey 1.0 255 140 100 0
|
||||
@@ -0,0 +1,18 @@
|
||||
width 2048
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.005 0 54 135 0
|
||||
mappingkey 0.06 0 145 255 15
|
||||
mappingkey 0.1 0 130 255 70
|
||||
mappingkey 0.13 0 200 255 120
|
||||
mappingkey 0.16 0 255 255 255
|
||||
mappingkey 0.2 90 210 255 130
|
||||
mappingkey 0.3 90 200 255 250
|
||||
mappingkey 0.4 200 200 230 150
|
||||
mappingkey 0.5 92 177 242 200
|
||||
mappingkey 0.6 137 223 255 0
|
||||
mappingkey 0.7 94 255 214 0
|
||||
mappingkey 0.8 255 200 180 0
|
||||
mappingkey 0.9 249 199 150 0
|
||||
mappingkey 1.0 255 140 100 0
|
||||
@@ -0,0 +1,16 @@
|
||||
width 2048
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
|
||||
mappingkey 0.005 0 54 135 0
|
||||
mappingkey 0.06 0 67 168 3
|
||||
mappingkey 0.1 90 180 255 75
|
||||
mappingkey 0.2 0 180 255 55
|
||||
mappingkey 0.3 0 180 255 70
|
||||
mappingkey 0.4 7 93 183 50
|
||||
mappingkey 0.5 0 177 242 10
|
||||
mappingkey 0.6 1 223 255 9
|
||||
mappingkey 0.7 94 255 214 0
|
||||
mappingkey 0.8 255 200 114 0
|
||||
mappingkey 0.9 249 199 97 0
|
||||
mappingkey 1.0 255 140 48 0
|
||||
@@ -0,0 +1,9 @@
|
||||
width 5
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
mappingkey 0.0 0 0 0 255
|
||||
mappingkey 0.25 255 0 0 255
|
||||
mappingkey 0.5 255 140 0 255
|
||||
mappingkey 0.75 255 255 0 255
|
||||
mappingkey 1.0 255 255 255 255
|
||||
|
||||
@@ -5,10 +5,21 @@ local moon_transforms = asset.require('scene/solarsystem/planets/earth/moon/moon
|
||||
local descentKeyframes = asset.require('./lem_descent.asset')
|
||||
local descentRotationKeyframes = asset.require('./lem_descent_rotation.asset')
|
||||
|
||||
local lem_model = asset.require('scene/solarsystem/missions/apollo/lem_model')
|
||||
local kernels = asset.require('./kernels').kernels
|
||||
|
||||
local models = asset.require('./models').models
|
||||
local models = asset.syncedResource({
|
||||
Name = "Apollo Models",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_11_models",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local lem_model = asset.syncedResource({
|
||||
Name = "Apollo Lem Models",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_lem_model",
|
||||
Version = 4
|
||||
})
|
||||
|
||||
asset.require('spice/base')
|
||||
|
||||
@@ -171,7 +182,7 @@ local Apollo11LemDescentModel = {
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = lem_model.modelFolder .. "/lmremoved.obj",
|
||||
GeometryFile = lem_model .. "/lmremoved.obj",
|
||||
SpecularIntensity = 0.0,
|
||||
RotationVector = { 273.750,28.0,309.85 },
|
||||
LightSources = asset_helper.getDefaultLightSources(sun_transforms.SolarSystemBarycenter.Identifier)
|
||||
@@ -197,7 +208,7 @@ local Apollo11LemLandedModel = {
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = lem_model.modelFolder .. "/LM-2_ver2clean.obj",
|
||||
GeometryFile = lem_model .. "/LM-2_ver2clean.obj",
|
||||
SpecularIntensity = 0.0,
|
||||
RotationVector = { 273.750,28.0,309.85 },
|
||||
LightSources = asset_helper.getDefaultLightSources(sun_transforms.SolarSystemBarycenter.Identifier)
|
||||
|
||||
@@ -3,8 +3,13 @@ local asset_helper = asset.require('util/asset_helper')
|
||||
local sun_transforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
local moon_asset = asset.require('scene/solarsystem/planets/earth/moon/moon')
|
||||
|
||||
local lem_model = asset.require('scene/solarsystem/missions/apollo/lem_model')
|
||||
|
||||
local lem_model = asset.syncedResource({
|
||||
Name = "Apollo Lem Models",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_lem_model",
|
||||
Version = 4
|
||||
})
|
||||
|
||||
local Apollo11Lem = {
|
||||
Identifier = "Apollo11Lem",
|
||||
@@ -36,7 +41,7 @@ local Apollo11LemModel = {
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = lem_model.modelFolder .. "/LM-2_ver2clean.obj",
|
||||
GeometryFile = lem_model .. "/LM-2_ver2clean.obj",
|
||||
RotationVector = { 91.044090,171.229706,111.666664 },
|
||||
LightSources = asset_helper.getDefaultLightSources(sun_transforms.SolarSystemBarycenter.Identifier)
|
||||
},
|
||||
|
||||
@@ -14,21 +14,42 @@ local vrts = asset.syncedResource({
|
||||
Version = 1
|
||||
})
|
||||
|
||||
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(vrts, assetGlobe);
|
||||
flipbook = helper.createFlipbook(assetPrefix, assetGlobe, 19);
|
||||
|
||||
rawset(_G, "nextFlip", function() helper.nextFlipbookPage(flipbook) end)
|
||||
rawset(_G, "previousFlip", function() helper.previousFlipbookPage(flipbook) end)
|
||||
openspace.action.registerAction({
|
||||
Identifier = "lem_flipbook.next_flip",
|
||||
Name = "Next A11 flip",
|
||||
Command = "nextFlip()",
|
||||
Documentation = "Show the next Apollo 11 flipbook image",
|
||||
GuiPath = "/Missions/Apollo/11",
|
||||
IsLocal = false
|
||||
})
|
||||
|
||||
openspace.bindKey("p", "nextFlip()", "Show the next Apollo 11 flipbook image.", "Next A11 flip", "/Missions/Apollo/11")
|
||||
openspace.bindKey("o", "previousFlip()","Show the previous Apollo 11 flipbook image.", "Prev A11 flip", "/Missions/Apollo/11")
|
||||
rawset(_G, "previousFlip", function() helper.previousFlipbookPage(flipbook) end)
|
||||
openspace.action.registerAction({
|
||||
Identifier = "lem_flipbook.prev_flip",
|
||||
Name = "Prev A11 flip",
|
||||
Command = "previousFlip()",
|
||||
Documentation = "Show the previous Apollo 11 flipbook image",
|
||||
GuiPath = "/Missions/Apollo/11",
|
||||
IsLocal = false
|
||||
})
|
||||
|
||||
openspace.bindKey("p", "lem_flipbook.next_flip")
|
||||
openspace.bindKey("o", "lem_flipbook.prev_flip")
|
||||
end)
|
||||
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
flipbook = nil;
|
||||
|
||||
openspace.action.removeAction("lem_flipbook.next_flip")
|
||||
openspace.action.removeAction("lem_flipbook.prev_flip")
|
||||
openspace.clearKey("o")
|
||||
openspace.clearKey("p")
|
||||
end)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
local modelFolder = asset.syncedResource({
|
||||
Name = "Apollo Models",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_11_models",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
asset.export('models', modelFolder)
|
||||
@@ -2,7 +2,13 @@ local assetHelper = asset.require('util/asset_helper')
|
||||
local moon_transforms = asset.require('scene/solarsystem/planets/earth/moon/moon')
|
||||
local sun_transforms = asset.require('scene/solarsystem/sun/transforms')
|
||||
asset.require('spice/base')
|
||||
local models = asset.require('scene/solarsystem/missions/apollo/csm_model').models
|
||||
|
||||
local models = asset.syncedResource({
|
||||
Name = "Apollo Models",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_models",
|
||||
Version = 4
|
||||
})
|
||||
|
||||
local kernels = asset.require('scene/solarsystem/missions/apollo/15/kernels').kernels
|
||||
|
||||
@@ -50,6 +56,7 @@ local Apollo15 = {
|
||||
Renderable = {
|
||||
Type = "RenderableModel",
|
||||
GeometryFile = models .. "/ApolloCSM.osmodel",
|
||||
ModelScale = 0.0001,
|
||||
LightSources = {
|
||||
{
|
||||
Type = "SceneGraphLightSource",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
local models = asset.syncedResource({
|
||||
Name = "Apollo Boulders Models",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_boulders",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
asset.export('models', models)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user