From d8e94c9d3af19c96df91fcd51be056b4ed84eeef Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Tue, 9 Jul 2019 22:49:55 +0300 Subject: [PATCH 01/15] Remove release branches from Appveyor config --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 6e3d3dc..a900522 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,6 @@ branches: only: - master - develop - - /release\/.+/ image: Visual Studio 2017 clone_depth: 1 environment: From 6c1ba52e122d575ba6a2e78cf53a91af75aa5a6e Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Wed, 10 Jul 2019 12:23:51 +0300 Subject: [PATCH 02/15] Fix typo in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15856b7..3a37ffb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added possibility to add preset crystal populations ### Changed -- Disabled "Add population" button when there is only one population in simulation +- Disabled "Remove population" button when there is only one population in simulation - Added multiple crystal populations by default ## 2.1.1 - 2019-07-08 From ee86c225176418de91dc0587115a188b5c597e3e Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Wed, 10 Jul 2019 22:13:26 +0300 Subject: [PATCH 03/15] Add SVG version of hexagon icon --- src/resources/hexagon.svg | 148 ++++++++++++++++++++++++++++++ src/resources/hexagon_256px.png | Bin 14657 -> 0 bytes src/resources/hexagon_appicon.psd | Bin 82538 -> 0 bytes 3 files changed, 148 insertions(+) create mode 100644 src/resources/hexagon.svg delete mode 100644 src/resources/hexagon_256px.png delete mode 100644 src/resources/hexagon_appicon.psd diff --git a/src/resources/hexagon.svg b/src/resources/hexagon.svg new file mode 100644 index 0000000..4caedad --- /dev/null +++ b/src/resources/hexagon.svg @@ -0,0 +1,148 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/resources/hexagon_256px.png b/src/resources/hexagon_256px.png deleted file mode 100644 index 2647abcb3dd965dbcd6407cf3910bb9f66b3e2ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14657 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 qwANxB#zGB7I&ZWbV*eNEPf`;A diff --git a/src/resources/hexagon_appicon.psd b/src/resources/hexagon_appicon.psd deleted file mode 100644 index 57a791a90a487a0a828f91132e4cefa6e4e42767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82538 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 Y%bd#cnd{Q?*R|)WhO27%|EB)_-@Wj~(f|Me From 940ab09009292b3f86008f60df9498ab203aa65c Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Wed, 10 Jul 2019 22:13:37 +0300 Subject: [PATCH 04/15] Add hexagon icon to readme --- README.md | 2 +- images/hexagon_small.png | Bin 0 -> 1048 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 images/hexagon_small.png diff --git a/README.md b/README.md index 14d4864..ae9d1eb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HaloRay +# HaloRay ![](images/hexagon_small.png) [![Build status](https://ci.appveyor.com/api/projects/status/5k9laekby84x2ex1/branch/develop?svg=true)](https://ci.appveyor.com/project/naavis/haloray/branch/develop) HaloRay simulates the reflection and refraction of sun light inside hexagonal diff --git a/images/hexagon_small.png b/images/hexagon_small.png new file mode 100644 index 0000000000000000000000000000000000000000..b2f7408fdad5f643f1a93f7ef43a529b2121ca53 GIT binary patch literal 1048 zcmV+z1n2vSP)ccK~zYI9=p2ZzHk@ukv%1Bj2a<`L%apS zYkVPsC`JsZxdZ}X<83d&gBlNtf{7pok|5E`5-`Z3aR(6vMOb%XS7!&NcjvV;(=**& zT{$>2+3BvH2e}CT(1))7umAh%uloKf;C(mJD&VXTbpE0KNGRHn5KhbZnk}W%$v?l} z8^hzrrT<0X_+;|px`F<$Qx-rOYYUunj;AkPqMTX05uX zEu{ef0)!2Iy5~9otz!a*|C;@HsBg`#+CKq77jaq=m`^M~2vPrMfne;Tv8y}av)zhc zqTXoq4@POjq-Z3BnM?t;&(4)A73A{Eje~@sYh?2^03QM{+;gNAIC5|9&_GYmCil3- zITAdGHzFkcbfp`FddX2$-LUP#onw%kIVpj`zTTgRq+m1IGA!y_G!V+5_6|yY>YCwk zjEqED68LH|dA=tSS>+xtR!k%i_64t5V0AC3>OFIW(#ZC2ul)$uwl|cdD!X4*g?WfQ z&s1R0He^T$h!TKZ@G6gKnn#umKr3ap(iV#b(yaCBYo>Iq0tE`L^Y)>k{bm?d*gNHR%)g}T8)zP-f9gs+EJ45bPr>Pws4 zzxA0_$ldpb6YyfLLvWy?C#588WBI|0bEo3~9H8+G2qQl6rQo*r;-CyxKGQb1UCe`* zmc0%kc;c5!M*-At$cD{*GJZ<8bD1l$q>fnzoYt)vLcr9MQkRuZ{k-t^&DU6QJIeaA z61zK>rmE&W#)Kje?Qe>2W_2k6!FlPQZ@%~dx}HL{{D8a@+U8>Nj5NDrL)fM-xNtbE zrIgfHo}c~!x}IGL4){Mv*v{Lt(<~I;=yvc@(Vqaf-)@zAj%ChI9p1hLKnAXp>DSn~ znS8Z3v}tq-`0rK_rU`BodcJ)c)I!OEr~xbN#q zYIZ6AAbvEq8$e#>Z`R_fvV4o^l#PzTWwPsp0G^DW(yg3+6+(1gdH`lRb;B(?x3zrc zz9CB!!yC8laV(R<;l*}8VOtCFv3&a0000 Date: Fri, 12 Jul 2019 22:14:53 +0300 Subject: [PATCH 05/15] Set icons for Add and Remove Population buttons --- CHANGELOG.md | 5 +++++ README.md | 1 + src/gui/addCrystalPopulationButton.cpp | 5 +++-- src/gui/crystalSettingsWidget.cpp | 15 ++++++++------- src/gui/crystalSettingsWidget.h | 4 ++-- src/gui/mainWindow.cpp | 6 ++++++ src/main.cpp | 2 ++ src/resources/haloray.qrc | 14 ++++++++++---- .../icons/custom-icons/actions/24/list-add.svg | 8 ++++++++ .../icons/custom-icons/actions/24/list-remove.svg | 8 ++++++++ src/resources/icons/custom-icons/index.theme | 9 +++++++++ 11 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 src/resources/icons/custom-icons/actions/24/list-add.svg create mode 100644 src/resources/icons/custom-icons/actions/24/list-remove.svg create mode 100644 src/resources/icons/custom-icons/index.theme diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a37ffb..4ca4312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed +- Replaced Add Population and Remove Population texts with icons + ## 2.2.0 - 2019-07-09 ### Added diff --git a/README.md b/README.md index ae9d1eb..97a7d8d 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ resulting executable: - Qt5Core.dll - Qt5Widgets.dll - Qt5Gui.dll +- Qt5Svg.dll You can also do this automatically with the [windeployqt](https://doc.qt.io/qt-5/windows-deployment.html) tool, which is diff --git a/src/gui/addCrystalPopulationButton.cpp b/src/gui/addCrystalPopulationButton.cpp index 6759a7d..ab66bc7 100644 --- a/src/gui/addCrystalPopulationButton.cpp +++ b/src/gui/addCrystalPopulationButton.cpp @@ -3,7 +3,7 @@ AddCrystalPopulationButton::AddCrystalPopulationButton(QWidget *parent) : QToolButton(parent) { - setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding); + //setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding); mMenu = new QMenu(this); @@ -20,7 +20,8 @@ AddCrystalPopulationButton::AddCrystalPopulationButton(QWidget *parent) mAddLowitz}); setPopupMode(QToolButton::ToolButtonPopupMode::MenuButtonPopup); - setText("Add population"); + setIcon(QIcon::fromTheme("list-add")); + setMenu(mMenu); connect(this, &AddCrystalPopulationButton::clicked, [this]() { emit addPopulation(HaloSim::CrystalPopulationPreset::Random); }); diff --git a/src/gui/crystalSettingsWidget.cpp b/src/gui/crystalSettingsWidget.cpp index 97615ab..1d2f3b7 100644 --- a/src/gui/crystalSettingsWidget.cpp +++ b/src/gui/crystalSettingsWidget.cpp @@ -4,7 +4,7 @@ #include "../simulation/crystalPopulation.h" CrystalSettingsWidget::CrystalSettingsWidget(std::shared_ptr crystalRepository, QWidget *parent) - : QGroupBox("Crystal settings", parent), + : QGroupBox("Crystal population settings", parent), mModel(new CrystalModel(crystalRepository)), mNextPopulationId(1) { @@ -66,7 +66,7 @@ CrystalSettingsWidget::CrystalSettingsWidget(std::shared_ptrcurrentIndex(); if (index == 0) mMapper->toNext(); @@ -112,11 +112,11 @@ void CrystalSettingsWidget::setupUi() mPopulationComboBox->setDuplicatesEnabled(true); mAddPopulationButton = new AddCrystalPopulationButton(); - mAddPopulationButton->setMinimumHeight(30); + mAddPopulationButton->setIconSize(QSize(24, 24)); - mRemovePopulationButton = new QPushButton("Remove population"); - mRemovePopulationButton->setMinimumHeight(30); - mRemovePopulationButton->setStyleSheet("padding: 10px;"); + mRemovePopulationButton = new QToolButton(); + mRemovePopulationButton->setIcon(QIcon::fromTheme("list-remove")); + mRemovePopulationButton->setIconSize(QSize(24, 24)); mCaRatioSlider = new SliderSpinBox(0.0, 15.0); @@ -145,9 +145,10 @@ void CrystalSettingsWidget::setupUi() mWeightSpinBox->setMaximum(10000); auto mainLayout = new QFormLayout(this); - mainLayout->addRow("Crystal population", mPopulationComboBox); + //mainLayout->addRow("Crystal population", mPopulationComboBox); auto populationButtonLayout = new QHBoxLayout(); + populationButtonLayout->addWidget(mPopulationComboBox); populationButtonLayout->addWidget(mAddPopulationButton); populationButtonLayout->addWidget(mRemovePopulationButton); diff --git a/src/gui/crystalSettingsWidget.h b/src/gui/crystalSettingsWidget.h index 4c98075..4f11af2 100644 --- a/src/gui/crystalSettingsWidget.h +++ b/src/gui/crystalSettingsWidget.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include "addCrystalPopulationButton.h" @@ -31,7 +31,7 @@ class CrystalSettingsWidget : public QGroupBox void setRotationVisibility(bool); AddCrystalPopulationButton *mAddPopulationButton; - QPushButton *mRemovePopulationButton; + QToolButton *mRemovePopulationButton; SliderSpinBox *mCaRatioSlider; SliderSpinBox *mCaRatioStdSlider; diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 0a7d4ba..5d3f2a5 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "../simulation/crystalPopulation.h" #include "sliderSpinBox.h" @@ -14,6 +15,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { + if (QIcon::themeName().isEmpty()) + { + QIcon::setThemeName("HaloRayTheme"); + } + mCrystalRepository = std::make_shared(); /* diff --git a/src/main.cpp b/src/main.cpp index f60ebdd..10f2c8a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,5 +43,7 @@ int main(int argc, char *argv[]) MainWindow mainWindow; mainWindow.show(); + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + return app.exec(); } diff --git a/src/resources/haloray.qrc b/src/resources/haloray.qrc index e56f2f1..3cef4e1 100644 --- a/src/resources/haloray.qrc +++ b/src/resources/haloray.qrc @@ -1,5 +1,11 @@ - - - haloray.ico - + + + + haloray.ico + + + icons/custom-icons/actions/24/list-add.svg + icons/custom-icons/actions/24/list-remove.svg + icons/custom-icons/index.theme + diff --git a/src/resources/icons/custom-icons/actions/24/list-add.svg b/src/resources/icons/custom-icons/actions/24/list-add.svg new file mode 100644 index 0000000..fea171f --- /dev/null +++ b/src/resources/icons/custom-icons/actions/24/list-add.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/resources/icons/custom-icons/actions/24/list-remove.svg b/src/resources/icons/custom-icons/actions/24/list-remove.svg new file mode 100644 index 0000000..ee63a42 --- /dev/null +++ b/src/resources/icons/custom-icons/actions/24/list-remove.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/resources/icons/custom-icons/index.theme b/src/resources/icons/custom-icons/index.theme new file mode 100644 index 0000000..194742f --- /dev/null +++ b/src/resources/icons/custom-icons/index.theme @@ -0,0 +1,9 @@ +[Icon Theme] +Name=HaloRayTheme +Comment=Custom icons for HaloRay +Directories=actions/24 + +[actions/24] +Size=24 +Context=Actions +Type=Fixed From 170a5210579836f7f0f5a01eff09da951ce38be9 Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Fri, 12 Jul 2019 22:36:41 +0300 Subject: [PATCH 06/15] Only set theme on Windows --- src/gui/mainWindow.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 5d3f2a5..847f612 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "../simulation/crystalPopulation.h" #include "sliderSpinBox.h" @@ -15,10 +14,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { - if (QIcon::themeName().isEmpty()) - { - QIcon::setThemeName("HaloRayTheme"); - } +#if _WIN32 + QIcon::setThemeName("HaloRayTheme"); +#endif mCrystalRepository = std::make_shared(); From b1402397c319fc53c9aa2a845622895520a307c6 Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 00:56:06 +0300 Subject: [PATCH 07/15] Add menubar with option to save simulation result as image --- src/gui/mainWindow.cpp | 34 ++++++++++++++++++++++++++++++++++ src/gui/mainWindow.h | 5 +++++ 2 files changed, 39 insertions(+) diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 847f612..6188d87 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -6,6 +6,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "../simulation/crystalPopulation.h" #include "sliderSpinBox.h" @@ -73,6 +78,25 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) mEngine->getLightSource().altitude, mEngine->getRaysPerStep(), 600); + + // Signals for menu bar + connect(mQuitAction, &QAction::triggered, QApplication::instance(), &QApplication::quit); + connect(mSaveImageAction, &QAction::triggered, [this]() { + auto image = mOpenGLWidget->grabFramebuffer(); + auto currentTime = QDateTime::currentDateTimeUtc().toString(Qt::DateFormat::ISODate); + auto defaultFilename = QString("haloray_%1.png") + .arg(currentTime) + .replace(":", "-"); + QString filename = QFileDialog::getSaveFileName(this, + tr("Save File"), + defaultFilename, + tr("Images (*.png)")); + + if (!filename.isNull()) + { + image.save(filename, "PNG", 50); + } + }); } void MainWindow::setupUi() @@ -87,6 +111,8 @@ void MainWindow::setupUi() setWindowIcon(QIcon(":/haloray.ico")); + setupMenuBar(); + mOpenGLWidget = new OpenGLWidget(); mProgressBar = setupProgressBar(); mRenderButton = new RenderButton(); @@ -105,6 +131,14 @@ void MainWindow::setupUi() topLayout->addWidget(mOpenGLWidget); } +void MainWindow::setupMenuBar() +{ + auto fileMenu = menuBar()->addMenu(tr("&File")); + mSaveImageAction = fileMenu->addAction(tr("Save image")); + fileMenu->addSeparator(); + mQuitAction = fileMenu->addAction(tr("&Quit")); +} + QScrollArea *MainWindow::setupSideBarScrollArea() { mGeneralSettingsWidget = new GeneralSettingsWidget(); diff --git a/src/gui/mainWindow.h b/src/gui/mainWindow.h index e1b5e9d..c1998df 100644 --- a/src/gui/mainWindow.h +++ b/src/gui/mainWindow.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "renderButton.h" #include "openGLWidget.h" @@ -25,6 +26,7 @@ class MainWindow : public QMainWindow void setupUi(); QScrollArea *setupSideBarScrollArea(); QProgressBar *setupProgressBar(); + void setupMenuBar(); GeneralSettingsWidget *mGeneralSettingsWidget; CrystalSettingsWidget *mCrystalSettingsWidget; @@ -33,6 +35,9 @@ class MainWindow : public QMainWindow RenderButton *mRenderButton; OpenGLWidget *mOpenGLWidget; + QAction *mSaveImageAction; + QAction *mQuitAction; + std::shared_ptr mCrystalRepository; std::shared_ptr mEngine; }; From 3050ad45de061e9c002165a0c7f63a9185b19fcf Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 00:56:28 +0300 Subject: [PATCH 08/15] Make all UI strings translateable --- src/gui/addCrystalPopulationButton.cpp | 10 +++++----- src/gui/crystalSettingsWidget.cpp | 27 +++++++++++++------------- src/gui/generalSettingsWidget.cpp | 8 ++++---- src/gui/renderButton.cpp | 4 ++-- src/gui/viewSettingsWidget.cpp | 24 +++++++++++------------ 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/gui/addCrystalPopulationButton.cpp b/src/gui/addCrystalPopulationButton.cpp index ab66bc7..7028544 100644 --- a/src/gui/addCrystalPopulationButton.cpp +++ b/src/gui/addCrystalPopulationButton.cpp @@ -7,11 +7,11 @@ AddCrystalPopulationButton::AddCrystalPopulationButton(QWidget *parent) mMenu = new QMenu(this); - mAddRandom = new QAction("Add random", this); - mAddPlate = new QAction("Add plate", this); - mAddColumn = new QAction("Add column", this); - mAddParry = new QAction("Add Parry", this); - mAddLowitz = new QAction("Add Lowitz", this); + mAddRandom = new QAction(tr("Add random"), this); + mAddPlate = new QAction(tr("Add plate"), this); + mAddColumn = new QAction(tr("Add column"), this); + mAddParry = new QAction(tr("Add Parry"), this); + mAddLowitz = new QAction(tr("Add Lowitz"), this); mMenu->addActions({mAddRandom, mAddPlate, diff --git a/src/gui/crystalSettingsWidget.cpp b/src/gui/crystalSettingsWidget.cpp index 1d2f3b7..7590ef9 100644 --- a/src/gui/crystalSettingsWidget.cpp +++ b/src/gui/crystalSettingsWidget.cpp @@ -123,21 +123,21 @@ void CrystalSettingsWidget::setupUi() mCaRatioStdSlider = new SliderSpinBox(0.0, 10.0); mTiltDistributionComboBox = new QComboBox(); - mTiltDistributionComboBox->addItems({"Uniform", "Gaussian"}); + mTiltDistributionComboBox->addItems({tr("Uniform"), tr("Gaussian")}); - mTiltAverageLabel = new QLabel("Average"); + mTiltAverageLabel = new QLabel(tr("Average")); mTiltAverageSlider = createAngleSlider(0.0, 180.0); - mTiltStdLabel = new QLabel("Standard deviation"); + mTiltStdLabel = new QLabel(tr("Standard deviation")); mTiltStdSlider = createAngleSlider(0.0, 360.0); mRotationDistributionComboBox = new QComboBox(); - mRotationDistributionComboBox->addItems({"Uniform", "Gaussian"}); + mRotationDistributionComboBox->addItems({tr("Uniform"), tr("Gaussian")}); - mRotationAverageLabel = new QLabel("Average"); + mRotationAverageLabel = new QLabel(tr("Average")); mRotationAverageSlider = createAngleSlider(0.0, 180.0); - mRotationStdLabel = new QLabel("Standard deviation"); + mRotationStdLabel = new QLabel(tr("Standard deviation")); mRotationStdSlider = createAngleSlider(0.0, 360.0); mWeightSpinBox = new QSpinBox(); @@ -145,7 +145,6 @@ void CrystalSettingsWidget::setupUi() mWeightSpinBox->setMaximum(10000); auto mainLayout = new QFormLayout(this); - //mainLayout->addRow("Crystal population", mPopulationComboBox); auto populationButtonLayout = new QHBoxLayout(); populationButtonLayout->addWidget(mPopulationComboBox); @@ -153,22 +152,22 @@ void CrystalSettingsWidget::setupUi() populationButtonLayout->addWidget(mRemovePopulationButton); mainLayout->addRow(populationButtonLayout); - mainLayout->addRow("Population weight", mWeightSpinBox); + mainLayout->addRow(tr("Population weight"), mWeightSpinBox); mainLayout->addItem(new QSpacerItem(0, 10)); - mainLayout->addRow("C/A ratio average", mCaRatioSlider); - mainLayout->addRow("C/A ratio std.", mCaRatioStdSlider); + mainLayout->addRow(tr("C/A ratio average"), mCaRatioSlider); + mainLayout->addRow(tr("C/A ratio std."), mCaRatioStdSlider); - auto tiltGroupBox = new QGroupBox("C-axis tilt"); + auto tiltGroupBox = new QGroupBox(tr("C-axis tilt")); auto tiltLayout = new QFormLayout(tiltGroupBox); mainLayout->addRow(tiltGroupBox); - tiltLayout->addRow("Distribution", mTiltDistributionComboBox); + tiltLayout->addRow(tr("Distribution"), mTiltDistributionComboBox); tiltLayout->addRow(mTiltAverageLabel, mTiltAverageSlider); tiltLayout->addRow(mTiltStdLabel, mTiltStdSlider); - auto rotationGroupBox = new QGroupBox("Rotation around C-axis"); + auto rotationGroupBox = new QGroupBox(tr("Rotation around C-axis")); auto rotationLayout = new QFormLayout(rotationGroupBox); mainLayout->addRow(rotationGroupBox); - rotationLayout->addRow("Distribution", mRotationDistributionComboBox); + rotationLayout->addRow(tr("Distribution"), mRotationDistributionComboBox); rotationLayout->addRow(mRotationAverageLabel, mRotationAverageSlider); rotationLayout->addRow(mRotationStdLabel, mRotationStdSlider); } diff --git a/src/gui/generalSettingsWidget.cpp b/src/gui/generalSettingsWidget.cpp index acf8561..ae4f675 100644 --- a/src/gui/generalSettingsWidget.cpp +++ b/src/gui/generalSettingsWidget.cpp @@ -66,10 +66,10 @@ void GeneralSettingsWidget::setupUi() mMaximumFramesSpinBox->setGroupSeparatorShown(true); auto layout = new QFormLayout(this); - layout->addRow("Sun altitude", mSunAltitudeSlider); - layout->addRow("Sun diameter", mSunDiameterSpinBox); - layout->addRow("Rays per frame", mRaysPerFrameSpinBox); - layout->addRow("Maximum frames", mMaximumFramesSpinBox); + layout->addRow(tr("Sun altitude"), mSunAltitudeSlider); + layout->addRow(tr("Sun diameter"), mSunDiameterSpinBox); + layout->addRow(tr("Rays per frame"), mRaysPerFrameSpinBox); + layout->addRow(tr("Maximum frames"), mMaximumFramesSpinBox); } HaloSim::LightSource GeneralSettingsWidget::stateToLightSource() const diff --git a/src/gui/renderButton.cpp b/src/gui/renderButton.cpp index 7f29676..544cd8e 100644 --- a/src/gui/renderButton.cpp +++ b/src/gui/renderButton.cpp @@ -4,8 +4,8 @@ RenderButton::RenderButton(QWidget *parent) : QPushButton(parent), mRendering(false) { - const QString renderText = "Render"; - const QString stopText = "Stop"; + const QString renderText = tr("Render"); + const QString stopText = tr("Stop"); setText(renderText); setStyleSheet("font: bold;"); diff --git a/src/gui/viewSettingsWidget.cpp b/src/gui/viewSettingsWidget.cpp index 4382109..d8b20c9 100644 --- a/src/gui/viewSettingsWidget.cpp +++ b/src/gui/viewSettingsWidget.cpp @@ -27,11 +27,11 @@ void ViewSettingsWidget::setupUi() setMaximumWidth(400); mCameraProjectionComboBox = new QComboBox(); - mCameraProjectionComboBox->addItems({"Stereographic", - "Rectilinear", - "Equidistant", - "Equal area", - "Orthographic"}); + mCameraProjectionComboBox->addItems({tr("Stereographic"), + tr("Rectilinear"), + tr("Equidistant"), + tr("Equal area"), + tr("Orthographic")}); mPitchSlider = new SliderSpinBox(); mPitchSlider->setMinimum(-90.0); @@ -58,13 +58,13 @@ void ViewSettingsWidget::setupUi() mLockToLightSource = new QCheckBox(); auto layout = new QFormLayout(this); - layout->addRow("Camera projection", mCameraProjectionComboBox); - layout->addRow("Field of view", mFieldOfViewSlider); - layout->addRow("Pitch", mPitchSlider); - layout->addRow("Yaw", mYawSlider); - layout->addRow("Brightness", mBrightnessSlider); - layout->addRow("Hide sub-horizon", mHideSubHorizonCheckBox); - layout->addRow("Lock to light source", mLockToLightSource); + layout->addRow(tr("Camera projection"), mCameraProjectionComboBox); + layout->addRow(tr("Field of view"), mFieldOfViewSlider); + layout->addRow(tr("Pitch"), mPitchSlider); + layout->addRow(tr("Yaw"), mYawSlider); + layout->addRow(tr("Brightness"), mBrightnessSlider); + layout->addRow(tr("Hide sub-horizon"), mHideSubHorizonCheckBox); + layout->addRow(tr("Lock to light source"), mLockToLightSource); } HaloSim::Camera ViewSettingsWidget::stateToCamera() const From 3088cdbdc360afe01aa9431bce33488dabe65e6a Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 01:06:00 +0300 Subject: [PATCH 09/15] Update changelog Added mention of new image saving feature --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca4312..dd5f8c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added +- Added menu bar with option to save simulation result as image + ### Changed - Replaced Add Population and Remove Population texts with icons From 2ac090dbf203d8ba4d3e31fd1076b68fe4489810 Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 16:21:05 +0300 Subject: [PATCH 10/15] Add static factory method for SliderSpinBox with angles --- src/gui/crystalSettingsWidget.cpp | 15 ++++----------- src/gui/sliderSpinBox.cpp | 7 +++++++ src/gui/sliderSpinBox.h | 2 ++ src/gui/viewSettingsWidget.cpp | 17 ++++------------- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/gui/crystalSettingsWidget.cpp b/src/gui/crystalSettingsWidget.cpp index 7590ef9..268bcf9 100644 --- a/src/gui/crystalSettingsWidget.cpp +++ b/src/gui/crystalSettingsWidget.cpp @@ -126,19 +126,19 @@ void CrystalSettingsWidget::setupUi() mTiltDistributionComboBox->addItems({tr("Uniform"), tr("Gaussian")}); mTiltAverageLabel = new QLabel(tr("Average")); - mTiltAverageSlider = createAngleSlider(0.0, 180.0); + mTiltAverageSlider = SliderSpinBox::createAngleSlider(0.0, 180.0); mTiltStdLabel = new QLabel(tr("Standard deviation")); - mTiltStdSlider = createAngleSlider(0.0, 360.0); + mTiltStdSlider = SliderSpinBox::createAngleSlider(0.0, 360.0); mRotationDistributionComboBox = new QComboBox(); mRotationDistributionComboBox->addItems({tr("Uniform"), tr("Gaussian")}); mRotationAverageLabel = new QLabel(tr("Average")); - mRotationAverageSlider = createAngleSlider(0.0, 180.0); + mRotationAverageSlider = SliderSpinBox::createAngleSlider(0.0, 180.0); mRotationStdLabel = new QLabel(tr("Standard deviation")); - mRotationStdSlider = createAngleSlider(0.0, 360.0); + mRotationStdSlider = SliderSpinBox::createAngleSlider(0.0, 360.0); mWeightSpinBox = new QSpinBox(); mWeightSpinBox->setMinimum(0); @@ -172,13 +172,6 @@ void CrystalSettingsWidget::setupUi() rotationLayout->addRow(mRotationStdLabel, mRotationStdSlider); } -SliderSpinBox *CrystalSettingsWidget::createAngleSlider(double min, double max) -{ - auto slider = new SliderSpinBox(min, max); - slider->setSuffix("°"); - return slider; -} - void CrystalSettingsWidget::setTiltVisibility(bool visible) { mTiltAverageSlider->setVisible(visible); diff --git a/src/gui/sliderSpinBox.cpp b/src/gui/sliderSpinBox.cpp index d873114..a930fa5 100644 --- a/src/gui/sliderSpinBox.cpp +++ b/src/gui/sliderSpinBox.cpp @@ -66,3 +66,10 @@ double SliderSpinBox::value() const { return mSpinBox->value(); } + +SliderSpinBox *SliderSpinBox::createAngleSlider(double min, double max) +{ + auto slider = new SliderSpinBox(min, max); + slider->setSuffix("°"); + return slider; +} diff --git a/src/gui/sliderSpinBox.h b/src/gui/sliderSpinBox.h index 3d329a3..f8a4488 100644 --- a/src/gui/sliderSpinBox.h +++ b/src/gui/sliderSpinBox.h @@ -14,6 +14,8 @@ class SliderSpinBox : public QWidget void setSuffix(const QString &suffix); double value() const; + static SliderSpinBox *createAngleSlider(double min, double max); + public slots: void setValue(double value); void setMinimum(double minimum); diff --git a/src/gui/viewSettingsWidget.cpp b/src/gui/viewSettingsWidget.cpp index d8b20c9..c1e4765 100644 --- a/src/gui/viewSettingsWidget.cpp +++ b/src/gui/viewSettingsWidget.cpp @@ -33,21 +33,12 @@ void ViewSettingsWidget::setupUi() tr("Equal area"), tr("Orthographic")}); - mPitchSlider = new SliderSpinBox(); - mPitchSlider->setMinimum(-90.0); - mPitchSlider->setMaximum(90.0); - mPitchSlider->setSuffix("°"); - - mYawSlider = new SliderSpinBox(); - mYawSlider->setMinimum(-360.0); - mYawSlider->setMaximum(360.0); + mPitchSlider = SliderSpinBox::createAngleSlider(-90.0, 90.0); + + mYawSlider = SliderSpinBox::createAngleSlider(-360.0, 360.0); mYawSlider->setWrapping(true); - mYawSlider->setSuffix("°"); - mFieldOfViewSlider = new SliderSpinBox(); - mFieldOfViewSlider->setMinimum(10.0); - mFieldOfViewSlider->setMaximum(360.0); - mFieldOfViewSlider->setSuffix("°"); + mFieldOfViewSlider = SliderSpinBox::createAngleSlider(10.0, 360.0); mBrightnessSlider = new SliderSpinBox(); mBrightnessSlider->setMinimum(0.1); From ad99df4891c94e78627e9de9395eaa1f7e1f7af9 Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 16:21:41 +0300 Subject: [PATCH 11/15] Remove leftover commented out line --- src/gui/addCrystalPopulationButton.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gui/addCrystalPopulationButton.cpp b/src/gui/addCrystalPopulationButton.cpp index 7028544..02d0dcf 100644 --- a/src/gui/addCrystalPopulationButton.cpp +++ b/src/gui/addCrystalPopulationButton.cpp @@ -3,8 +3,6 @@ AddCrystalPopulationButton::AddCrystalPopulationButton(QWidget *parent) : QToolButton(parent) { - //setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding); - mMenu = new QMenu(this); mAddRandom = new QAction(tr("Add random"), this); From c4adff2f8b9a64c98483f45e31c1bf1a84ca532d Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 16:29:13 +0300 Subject: [PATCH 12/15] Move compute shader to Qt resources --- src/resources/haloray.qrc | 1 + src/{simulation => resources}/shaders/raytrace.glsl | 9 --------- src/simulation/simulationEngine.cpp | 3 +-- 3 files changed, 2 insertions(+), 11 deletions(-) rename src/{simulation => resources}/shaders/raytrace.glsl (99%) diff --git a/src/resources/haloray.qrc b/src/resources/haloray.qrc index 3cef4e1..19d54cf 100644 --- a/src/resources/haloray.qrc +++ b/src/resources/haloray.qrc @@ -2,6 +2,7 @@ haloray.ico + shaders/raytrace.glsl icons/custom-icons/actions/24/list-add.svg diff --git a/src/simulation/shaders/raytrace.glsl b/src/resources/shaders/raytrace.glsl similarity index 99% rename from src/simulation/shaders/raytrace.glsl rename to src/resources/shaders/raytrace.glsl index 941c4fc..3ba5eb1 100644 --- a/src/simulation/shaders/raytrace.glsl +++ b/src/resources/shaders/raytrace.glsl @@ -1,10 +1,3 @@ -#pragma once -#include - -namespace HaloSim -{ - -const std::string computeShaderSource = R""( #version 440 core #define DISTRIBUTION_UNIFORM 0 @@ -515,5 +508,3 @@ void main(void) vec3 cieXYZ = daylightEstimate(wavelength) * vec3(xFit_1931(wavelength), yFit_1931(wavelength), zFit_1931(wavelength)); storePixel(pixelCoordinates, cieXYZ); } -)""; -} diff --git a/src/simulation/simulationEngine.cpp b/src/simulation/simulationEngine.cpp index 383e2c2..9fa9529 100644 --- a/src/simulation/simulationEngine.cpp +++ b/src/simulation/simulationEngine.cpp @@ -7,7 +7,6 @@ #include "camera.h" #include "lightSource.h" #include "crystalPopulation.h" -#include "shaders/raytrace.glsl" namespace HaloSim { @@ -176,7 +175,7 @@ void SimulationEngine::initialize() void SimulationEngine::initializeShader() { mSimulationShader = std::make_unique(); - mSimulationShader->addCacheableShaderFromSourceCode(QOpenGLShader::ShaderTypeBit::Compute, computeShaderSource.c_str()); + mSimulationShader->addCacheableShaderFromSourceFile(QOpenGLShader::ShaderTypeBit::Compute, ":/shaders/raytrace.glsl"); if (mSimulationShader->link() == false) { throw std::runtime_error(mSimulationShader->log().toUtf8()); From 30e3e3a8b68ba2533cf5a29cf59db7be6f395bae Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 16:53:32 +0300 Subject: [PATCH 13/15] Add double scattering to simulation Now there is a slider under General Settings that sets the probability for a single light ray to hit two different ice crystals on its way to the camera. --- CHANGELOG.md | 1 + README.md | 7 ++++++- src/gui/generalSettingsWidget.cpp | 24 +++++++++++++++--------- src/gui/generalSettingsWidget.h | 5 ++++- src/gui/mainWindow.cpp | 6 +++++- src/resources/shaders/raytrace.glsl | 22 ++++++++++++++++++++-- src/simulation/simulationEngine.cpp | 14 ++++++++++++++ src/simulation/simulationEngine.h | 4 ++++ 8 files changed, 69 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5f8c2..5b4de9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added menu bar with option to save simulation result as image +- Added slider for setting the probability of double scattering ### Changed - Replaced Add Population and Remove Population texts with icons diff --git a/README.md b/README.md index 97a7d8d..896c236 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,14 @@ Here are some general settings for the whole simulation. - **Rays per frame:** Number of rays traced through individual crystals per rendered frame - If the user interface slows down a lot during rendering, lower this value - - On an NVIDIA GeForce GTX 1070 a good value seems to be 500 000 - 1 000 000 + - On an NVIDIA GeForce GTX 1070 a good value seems to be around 500 000 - The maximum value for this parameter may be limited by your GPU - **Maximum frames:** Simulation stops after rendering this many frames +- **Double scattering:** Probability of a single light ray to scatter from two + different ice crystals + - Note that this slows down the simulation significantly! + - A value of 0.0 means no rays are scattered twice, and 1.0 means all rays + are scattered twice ### Crystal settings diff --git a/src/gui/generalSettingsWidget.cpp b/src/gui/generalSettingsWidget.cpp index ae4f675..646c593 100644 --- a/src/gui/generalSettingsWidget.cpp +++ b/src/gui/generalSettingsWidget.cpp @@ -7,31 +7,35 @@ GeneralSettingsWidget::GeneralSettingsWidget(QWidget *parent) setupUi(); connect(mSunAltitudeSlider, &SliderSpinBox::valueChanged, [this]() { - lightSourceChanged(stateToLightSource()); + emit lightSourceChanged(stateToLightSource()); }); connect(mSunDiameterSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), [this]() { - lightSourceChanged(stateToLightSource()); + emit lightSourceChanged(stateToLightSource()); }); connect(mRaysPerFrameSpinBox, QOverload::of(&QSpinBox::valueChanged), [this](int value) { - numRaysChanged((unsigned int)value); + emit numRaysChanged((unsigned int)value); }); connect(mMaximumFramesSpinBox, QOverload::of(&QSpinBox::valueChanged), [this](int value) { - maximumNumberOfIterationsChanged((unsigned int)value); + emit maximumNumberOfIterationsChanged((unsigned int)value); }); + + connect(mMultipleScattering, &SliderSpinBox::valueChanged, this, &GeneralSettingsWidget::multipleScatteringProbabilityChanged); } void GeneralSettingsWidget::setInitialValues(double sunDiameter, double sunAltitude, unsigned int raysPerFrame, - unsigned int maxNumFrames) + unsigned int maxNumFrames, + double multipleScatteringProbability) { mSunDiameterSpinBox->setValue(sunDiameter); mSunAltitudeSlider->setValue(sunAltitude); mRaysPerFrameSpinBox->setValue(raysPerFrame); mMaximumFramesSpinBox->setValue(maxNumFrames); + mMultipleScattering->setValue(multipleScatteringProbability); } void GeneralSettingsWidget::setupUi() @@ -39,10 +43,7 @@ void GeneralSettingsWidget::setupUi() setMaximumWidth(400); mSunAltitudeSlider = new SliderSpinBox(); - mSunAltitudeSlider->setSuffix("­°"); - mSunAltitudeSlider->setMinimum(-90.0); - mSunAltitudeSlider->setMaximum(90.0); - mSunAltitudeSlider->setValue(0.0); + mSunAltitudeSlider = SliderSpinBox::createAngleSlider(-90.0, 90.0); mSunDiameterSpinBox = new QDoubleSpinBox(); mSunDiameterSpinBox->setSuffix("­°"); @@ -65,11 +66,16 @@ void GeneralSettingsWidget::setupUi() mMaximumFramesSpinBox->setValue(100000000); mMaximumFramesSpinBox->setGroupSeparatorShown(true); + mMultipleScattering = new SliderSpinBox(); + mMultipleScattering->setMinimum(0.0); + mMultipleScattering->setMaximum(1.0); + auto layout = new QFormLayout(this); layout->addRow(tr("Sun altitude"), mSunAltitudeSlider); layout->addRow(tr("Sun diameter"), mSunDiameterSpinBox); layout->addRow(tr("Rays per frame"), mRaysPerFrameSpinBox); layout->addRow(tr("Maximum frames"), mMaximumFramesSpinBox); + layout->addRow(tr("Double scattering"), mMultipleScattering); } HaloSim::LightSource GeneralSettingsWidget::stateToLightSource() const diff --git a/src/gui/generalSettingsWidget.h b/src/gui/generalSettingsWidget.h index 8d4e909..0ecf1c7 100644 --- a/src/gui/generalSettingsWidget.h +++ b/src/gui/generalSettingsWidget.h @@ -14,12 +14,14 @@ class GeneralSettingsWidget : public QGroupBox void setInitialValues(double sunDiameter, double sunAltitude, unsigned int raysPerFrame, - unsigned int maxNumFrames); + unsigned int maxNumFrames, + double multipleScatteringProbability); signals: void lightSourceChanged(HaloSim::LightSource light); void numRaysChanged(unsigned int rays); void maximumNumberOfIterationsChanged(unsigned int iterations); + void multipleScatteringProbabilityChanged(double probability); public slots: void toggleMaxIterationsSpinBoxStatus(); @@ -33,4 +35,5 @@ public slots: QDoubleSpinBox *mSunDiameterSpinBox; QSpinBox *mRaysPerFrameSpinBox; QSpinBox *mMaximumFramesSpinBox; + SliderSpinBox *mMultipleScattering; }; diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp index 6188d87..783b58f 100644 --- a/src/gui/mainWindow.cpp +++ b/src/gui/mainWindow.cpp @@ -73,11 +73,15 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) }); connect(mGeneralSettingsWidget, &GeneralSettingsWidget::maximumNumberOfIterationsChanged, mOpenGLWidget, &OpenGLWidget::setMaxIterations); connect(mGeneralSettingsWidget, &GeneralSettingsWidget::maximumNumberOfIterationsChanged, mProgressBar, &QProgressBar::setMaximum); + connect(mGeneralSettingsWidget, &GeneralSettingsWidget::multipleScatteringProbabilityChanged, [this](double value) { + mEngine->setMultipleScatteringProbability(value); + }); mGeneralSettingsWidget->setInitialValues(mEngine->getLightSource().diameter, mEngine->getLightSource().altitude, mEngine->getRaysPerStep(), - 600); + 600, + mEngine->getMultipleScatteringProbability()); // Signals for menu bar connect(mQuitAction, &QAction::triggered, QApplication::instance(), &QApplication::quit); diff --git a/src/resources/shaders/raytrace.glsl b/src/resources/shaders/raytrace.glsl index 3ba5eb1..4174812 100644 --- a/src/resources/shaders/raytrace.glsl +++ b/src/resources/shaders/raytrace.glsl @@ -44,6 +44,8 @@ uniform struct camera_t int hideSubHorizon; } camera; +uniform float multipleScatteringProbability; + const float PI = 3.1415926535; struct intersection { @@ -463,12 +465,28 @@ void main(void) if (length(resultRay) < 0.0001) return; - resultRay = -rotationMatrix * resultRay; + resultRay = rotationMatrix * resultRay; + + if (multipleScatteringProbability != 0.0 && multipleScatteringProbability > rand()) + { + // Rotation matrix to orient ray/crystal + rotationMatrix = getRotationMatrix(); + + /* The inverse rotation matrix must be applied because we are + rotating the incoming ray and not the crystal itself. */ + rotatedRayDirection = resultRay * rotationMatrix; + + resultRay = castRayThroughCrystal(rotatedRayDirection, wavelength); + + if (length(resultRay) < 0.0001) return; + + resultRay = rotationMatrix * resultRay; + } // Hide subhorizon rays if (camera.hideSubHorizon == 1 && resultRay.y < 0.0) return; - resultRay = getCameraOrientationMatrix() * resultRay; + resultRay = -getCameraOrientationMatrix() * resultRay; ivec2 resolution = imageSize(outputImage); float aspectRatio = float(resolution.y) / float(resolution.x); diff --git a/src/simulation/simulationEngine.cpp b/src/simulation/simulationEngine.cpp index 9fa9529..2ee4a60 100644 --- a/src/simulation/simulationEngine.cpp +++ b/src/simulation/simulationEngine.cpp @@ -26,6 +26,7 @@ SimulationEngine::SimulationEngine( mCamera(Camera::createDefaultCamera()), mLight(LightSource::createDefaultLightSource()), mCameraLockedToLightSource(false), + mMultipleScatteringProbability(0.0), mCrystalRepository(crystalRepository) { } @@ -135,6 +136,8 @@ void SimulationEngine::step() mSimulationShader->setUniformValue("camera.projection", mCamera.projection); mSimulationShader->setUniformValue("camera.hideSubHorizon", mCamera.hideSubHorizon ? 1 : 0); + mSimulationShader->setUniformValue("multipleScatteringProbability", mMultipleScatteringProbability); + glDispatchCompute(numRays, 1, 1); } } @@ -213,4 +216,15 @@ void SimulationEngine::pointCameraToLightSource() mCamera.pitch = mLight.altitude; } +void SimulationEngine::setMultipleScatteringProbability(double probability) +{ + clear(); + mMultipleScatteringProbability = static_cast(std::min(std::max(probability, 0.0), 1.0)); +} + +double SimulationEngine::getMultipleScatteringProbability() const +{ + return static_cast(mMultipleScatteringProbability); +} + } // namespace HaloSim diff --git a/src/simulation/simulationEngine.h b/src/simulation/simulationEngine.h index aec1ab3..e921750 100644 --- a/src/simulation/simulationEngine.h +++ b/src/simulation/simulationEngine.h @@ -37,6 +37,9 @@ class SimulationEngine : protected QOpenGLFunctions_4_4_Core void lockCameraToLightSource(bool locked); + void setMultipleScatteringProbability(double); + double getMultipleScatteringProbability() const; + const unsigned int getOutputTextureHandle() const; void resizeOutputTextureCallback(const unsigned int width, const unsigned int height); @@ -62,6 +65,7 @@ class SimulationEngine : protected QOpenGLFunctions_4_4_Core unsigned int mRaysPerStep; unsigned int mIteration; bool mCameraLockedToLightSource; + float mMultipleScatteringProbability; std::shared_ptr mCrystalRepository; }; From 3295ae237f3c1da7522b494dd48f38c6bda81295 Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 17:03:14 +0300 Subject: [PATCH 14/15] Fix sub-horizon hiding issue introduced by multiple scattering Ticking the "Hide sub-horizon" checkbox was hiding everything above the horizon after introducing multiple scattering to the simulation. This commit fixes the bug. --- src/resources/shaders/raytrace.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/shaders/raytrace.glsl b/src/resources/shaders/raytrace.glsl index 4174812..e3e307b 100644 --- a/src/resources/shaders/raytrace.glsl +++ b/src/resources/shaders/raytrace.glsl @@ -484,7 +484,7 @@ void main(void) } // Hide subhorizon rays - if (camera.hideSubHorizon == 1 && resultRay.y < 0.0) return; + if (camera.hideSubHorizon == 1 && resultRay.y > 0.0) return; resultRay = -getCameraOrientationMatrix() * resultRay; From 5dd0a17cbb754f788028b5904b3728b73de9c789 Mon Sep 17 00:00:00 2001 From: Samuli Vuorinen Date: Sat, 13 Jul 2019 17:10:29 +0300 Subject: [PATCH 15/15] Bump version number --- CHANGELOG.md | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b4de9d..4183b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 2.3.0 - 2019-07-13 ### Added - Added menu bar with option to save simulation result as image diff --git a/appveyor.yml b/appveyor.yml index a900522..fadf82b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '2.2.0-{build}' +version: '2.3.0-{build}' branches: only: - master