diff --git a/CHANGELOG.md b/CHANGELOG.md index 15856b7..4183b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ 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). +## 2.3.0 - 2019-07-13 + +### 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 + ## 2.2.0 - 2019-07-09 ### Added @@ -12,7 +21,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 diff --git a/README.md b/README.md index 14d4864..896c236 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 @@ -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 @@ -139,6 +144,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/appveyor.yml b/appveyor.yml index 6e3d3dc..fadf82b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,8 @@ -version: '2.2.0-{build}' +version: '2.3.0-{build}' branches: only: - master - develop - - /release\/.+/ image: Visual Studio 2017 clone_depth: 1 environment: diff --git a/images/hexagon_small.png b/images/hexagon_small.png new file mode 100644 index 0000000..b2f7408 Binary files /dev/null and b/images/hexagon_small.png differ diff --git a/src/gui/addCrystalPopulationButton.cpp b/src/gui/addCrystalPopulationButton.cpp index 6759a7d..02d0dcf 100644 --- a/src/gui/addCrystalPopulationButton.cpp +++ b/src/gui/addCrystalPopulationButton.cpp @@ -3,15 +3,13 @@ AddCrystalPopulationButton::AddCrystalPopulationButton(QWidget *parent) : QToolButton(parent) { - setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding); - 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, @@ -20,7 +18,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..268bcf9 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,73 +112,66 @@ 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); mCaRatioStdSlider = new SliderSpinBox(0.0, 10.0); mTiltDistributionComboBox = new QComboBox(); - mTiltDistributionComboBox->addItems({"Uniform", "Gaussian"}); + mTiltDistributionComboBox->addItems({tr("Uniform"), tr("Gaussian")}); - mTiltAverageLabel = new QLabel("Average"); - mTiltAverageSlider = createAngleSlider(0.0, 180.0); + mTiltAverageLabel = new QLabel(tr("Average")); + mTiltAverageSlider = SliderSpinBox::createAngleSlider(0.0, 180.0); - mTiltStdLabel = new QLabel("Standard deviation"); - mTiltStdSlider = createAngleSlider(0.0, 360.0); + mTiltStdLabel = new QLabel(tr("Standard deviation")); + mTiltStdSlider = SliderSpinBox::createAngleSlider(0.0, 360.0); mRotationDistributionComboBox = new QComboBox(); - mRotationDistributionComboBox->addItems({"Uniform", "Gaussian"}); + mRotationDistributionComboBox->addItems({tr("Uniform"), tr("Gaussian")}); - mRotationAverageLabel = new QLabel("Average"); - mRotationAverageSlider = createAngleSlider(0.0, 180.0); + mRotationAverageLabel = new QLabel(tr("Average")); + mRotationAverageSlider = SliderSpinBox::createAngleSlider(0.0, 180.0); - mRotationStdLabel = new QLabel("Standard deviation"); - mRotationStdSlider = createAngleSlider(0.0, 360.0); + mRotationStdLabel = new QLabel(tr("Standard deviation")); + mRotationStdSlider = SliderSpinBox::createAngleSlider(0.0, 360.0); mWeightSpinBox = new QSpinBox(); mWeightSpinBox->setMinimum(0); mWeightSpinBox->setMaximum(10000); auto mainLayout = new QFormLayout(this); - mainLayout->addRow("Crystal population", mPopulationComboBox); auto populationButtonLayout = new QHBoxLayout(); + populationButtonLayout->addWidget(mPopulationComboBox); populationButtonLayout->addWidget(mAddPopulationButton); 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); } -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/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/generalSettingsWidget.cpp b/src/gui/generalSettingsWidget.cpp index acf8561..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("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); + 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 0a7d4ba..783b58f 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" @@ -14,6 +19,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { +#if _WIN32 + QIcon::setThemeName("HaloRayTheme"); +#endif + mCrystalRepository = std::make_shared(); /* @@ -64,11 +73,34 @@ 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); + 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() @@ -83,6 +115,8 @@ void MainWindow::setupUi() setWindowIcon(QIcon(":/haloray.ico")); + setupMenuBar(); + mOpenGLWidget = new OpenGLWidget(); mProgressBar = setupProgressBar(); mRenderButton = new RenderButton(); @@ -101,6 +135,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; }; 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/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 4382109..c1e4765 100644 --- a/src/gui/viewSettingsWidget.cpp +++ b/src/gui/viewSettingsWidget.cpp @@ -27,27 +27,18 @@ void ViewSettingsWidget::setupUi() setMaximumWidth(400); mCameraProjectionComboBox = new QComboBox(); - mCameraProjectionComboBox->addItems({"Stereographic", - "Rectilinear", - "Equidistant", - "Equal area", - "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); + mCameraProjectionComboBox->addItems({tr("Stereographic"), + tr("Rectilinear"), + tr("Equidistant"), + tr("Equal area"), + tr("Orthographic")}); + + 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); @@ -58,13 +49,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 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..19d54cf 100644 --- a/src/resources/haloray.qrc +++ b/src/resources/haloray.qrc @@ -1,5 +1,12 @@ - - - haloray.ico - + + + + haloray.ico + shaders/raytrace.glsl + + + 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/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 2647abc..0000000 Binary files a/src/resources/hexagon_256px.png and /dev/null differ diff --git a/src/resources/hexagon_appicon.psd b/src/resources/hexagon_appicon.psd deleted file mode 100644 index 57a791a..0000000 Binary files a/src/resources/hexagon_appicon.psd and /dev/null differ 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 diff --git a/src/simulation/shaders/raytrace.glsl b/src/resources/shaders/raytrace.glsl similarity index 94% rename from src/simulation/shaders/raytrace.glsl rename to src/resources/shaders/raytrace.glsl index 941c4fc..e3e307b 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 @@ -51,6 +44,8 @@ uniform struct camera_t int hideSubHorizon; } camera; +uniform float multipleScatteringProbability; + const float PI = 3.1415926535; struct intersection { @@ -470,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; + 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); @@ -515,5 +526,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..2ee4a60 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 { @@ -27,6 +26,7 @@ SimulationEngine::SimulationEngine( mCamera(Camera::createDefaultCamera()), mLight(LightSource::createDefaultLightSource()), mCameraLockedToLightSource(false), + mMultipleScatteringProbability(0.0), mCrystalRepository(crystalRepository) { } @@ -136,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); } } @@ -176,7 +178,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()); @@ -214,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; };