diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4f8dda2..a12deb9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,9 +4,14 @@ project(shkyeraEngine)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
-set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
+else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -fPIE -no-pie")
+endif()
set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps)
@@ -47,6 +52,7 @@ add_library(
src/ui/settings/cameraSettingsWindow.cpp
src/ui/settings/worldSettingsWindow.cpp
src/ui/settings/planetSettingsWindow.cpp
+ src/ui/settings/exportSettingsWindow.cpp
)
diff --git a/README.md b/README.md
index 32bf0f5..01afc66 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+
+
SHKYERA Engine
@@ -12,12 +14,19 @@
-
-## Building
+## Quick start
+Download appropriate executives for your system from [here]().
+### Linux
+Everything should work from the beginning. Just extract the contents and launch the app.
-### Setup
+### MacOS
+Open Terminal in the extracted directory and run
+```
+./shkyera
+```
+## Building from source
Everything should be taken care of if you just do this.
```sh
@@ -25,3 +34,48 @@ chmod +x ./build.sh
./build.sh -r
```
+
+## Using the Engine
+
+### Camera
+Once you click on the top-left **Render** window. You can move the camera freely using:
+1. W - forward
+2. A - left
+3. S - backwards
+4. D - right
+5. E - up
+6. Q - down
+
+If you hold left mouse button, you can freely rotate the camera.
+
+In the top-right **Camera** window, you can manually select camera's position as well as its:
+1. Depth of Field - "blur effect"
+2. Focus Distance - focus of the camera
+3. Field of View - camera "zoom"
+
+Camera's position can also be manipulated on the plots in the bottom-left window.
+
+### Planets
+Click "Add New Object" in the **World** menu in the bottom-right window to create a new planet.
+
+By clicking on one of the planet's names, you can manipulate its:
+1. Position
+2. Size
+3. Texture
+4. Remove the object
+
+In the _Texture_ Menu, you can choose planet's color and press _Set Color_ to apply changes or choose one of the preloaded textures.
+
+You have to select _Emit Light_ checkbox in order to emit light from that object. You can also change light's color and intensity. If you want to apply these changes, you have to select the texture **again** or _Set Color_.
+
+### Ambient Light
+Change Ambient Light in the **World** window to alter the color that the "unvierse" shines onto the objects.
+
+## Plots
+In the bottom-left window, you can see your world from all three different sides. You can drag and drop the planets as well as the camera. You can also see the focus distance of the camera. This is especially useful if you have high Depth of Field.
+
+## Resources
+1. [_Ray Tracing in One Weekend_](https://raytracing.github.io/books/RayTracingInOneWeekend.html)
+2. [_Atta_](https://github.com/brenocq/atta)
+3. [_The Cherno's raytracing series_](https://www.youtube.com/watch?v=gfW1Fhd9u9Q&list=PLlrATfBNZ98edc5GshdBtREv5asFW3yXl)
+4. [_Planets' textures_](https://www.solarsystemscope.com/textures/)
diff --git a/resources/instructions/image.png b/resources/instructions/image.png
index cca9d4b..73cccd6 100644
Binary files a/resources/instructions/image.png and b/resources/instructions/image.png differ
diff --git a/resources/instructions/ui.png b/resources/instructions/ui.png
new file mode 100644
index 0000000..d7642fa
Binary files /dev/null and b/resources/instructions/ui.png differ
diff --git a/src/core/image.cpp b/src/core/image.cpp
index 4ca972c..09b153b 100644
--- a/src/core/image.cpp
+++ b/src/core/image.cpp
@@ -1,7 +1,9 @@
#include "core/image.hpp"
#define STB_IMAGE_IMPLEMENTATION
+#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image.h"
+#include "stb_image_write.h"
#define GL_SILENCE_DEPRECATION
#if defined(IMGUI_IMPL_OPENGL_ES2)
@@ -29,6 +31,9 @@ image::image(const char *filename) {
if (!raw_data) {
std::cerr << "\033[0;31mCould not load texture from file '" << filename << "'\033[0m" << std::endl;
+#ifdef __APPLE__
+ std::cerr << "\tMake sure you are running the executive from its directory." << std::endl;
+#endif
m_width = 0;
m_height = 0;
return;
@@ -176,6 +181,36 @@ void image::writeColor(std::ostream &out, color pixelColor) const {
color &image::operator()(int x, int y) { return m_data[y][x]; }
color &image::at(int x, int y) { return m_data[y][x]; }
+void image::saveToPng(std::string path) {
+ char *data = new char[3 * width() * height()];
+
+ for (int y = 0; y < height(); ++y) {
+ for (int x = 0; x < width(); ++x) {
+ color c = at(x, y);
+ data[(y * width() + x) * 3 + 0] = static_cast(c[0] * 255);
+ data[(y * width() + x) * 3 + 1] = static_cast(c[1] * 255);
+ data[(y * width() + x) * 3 + 2] = static_cast(c[2] * 255);
+ }
+ }
+
+ stbi_write_png(path.c_str(), width(), height(), 3, data, 3 * width());
+}
+
+void image::saveToJpg(std::string path) {
+ char *data = new char[3 * width() * height()];
+
+ for (int y = 0; y < height(); ++y) {
+ for (int x = 0; x < width(); ++x) {
+ color c = at(x, y);
+ data[(y * width() + x) * 3 + 0] = static_cast(c[0] * 255);
+ data[(y * width() + x) * 3 + 1] = static_cast(c[1] * 255);
+ data[(y * width() + x) * 3 + 2] = static_cast(c[2] * 255);
+ }
+ }
+
+ stbi_write_jpg(path.c_str(), width(), height(), 3, data, 100);
+}
+
std::shared_ptr image::EARTH_DAY_TEXTURE = std::make_shared("resources/textures/earthday.jpg");
std::shared_ptr image::EARTH_NIGHT_TEXTURE = std::make_shared("resources/textures/earthnight.jpg");
std::shared_ptr image::MARS_TEXTURE = std::make_shared("resources/textures/mars.jpg");
diff --git a/src/core/image.hpp b/src/core/image.hpp
index efa91c3..b8bd801 100644
--- a/src/core/image.hpp
+++ b/src/core/image.hpp
@@ -60,6 +60,9 @@ class image {
color &operator()(int x, int y);
color &at(int x, int y);
+ void saveToPng(std::string path);
+ void saveToJpg(std::string path);
+
static constexpr int TEXTURE_BYTES_PER_PIXEL = 3;
static constexpr int ICON_SIZE = 64;
diff --git a/src/core/vec3.cpp b/src/core/vec3.cpp
index c71f866..7f29461 100644
--- a/src/core/vec3.cpp
+++ b/src/core/vec3.cpp
@@ -51,7 +51,7 @@ void vec3::rotateAroundY(double angle) {
}
void vec3::rotateUpAndDown(double angle) {
- float baseAngle = atan2(m_cords[0], m_cords[2]);
+ float baseAngle = atan2(m_cords[2], m_cords[0]);
rotateAroundY(baseAngle);
double new_x = m_cords[0] * cos(angle) - m_cords[1] * sin(angle);
diff --git a/src/main.cpp b/src/main.cpp
index 25adb9e..c8bafa5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -16,8 +16,7 @@
using namespace shkyera;
-int main(int argc, char *argv[]) {
-
+ui prepareUserInterface() {
const auto aspectRatio = 16.0 / 9.0;
const int imageWidth = 600;
const int imageHeight = static_cast(imageWidth / aspectRatio);
@@ -46,8 +45,13 @@ int main(int argc, char *argv[]) {
ui interface(im, r, world, cam);
- interface.init();
+ return interface;
+}
+int main(int argc, char *argv[]) {
+ ui interface = prepareUserInterface();
+
+ interface.init();
while (interface.isOpen()) {
interface.run();
}
diff --git a/src/shapes/material.hpp b/src/shapes/material.hpp
index 2d29dc9..88cd1d7 100644
--- a/src/shapes/material.hpp
+++ b/src/shapes/material.hpp
@@ -14,70 +14,80 @@ typedef struct hitData hitData;
enum MATERIAL_TYPE { LAMBERTIAN, METAL, REFRACTOR, DIFFUSE_LIGHT };
class material {
- public:
- virtual bool scatter(const ray &rayIn, const hitData &data, color &attenuation, ray &rayOut) const = 0;
- virtual color emit(double u, double v, const point3 &p, bool firstHit) const;
- virtual std::shared_ptr getLightMaterial() const;
- virtual color getVisibleColor() const;
+public:
+ virtual bool scatter(const ray &rayIn, const hitData &data,
+ color &attenuation, ray &rayOut) const = 0;
+ virtual color emit(double u, double v, const point3 &p, bool firstHit) const;
+ virtual std::shared_ptr getLightMaterial() const;
+ virtual color getVisibleColor() const;
};
class lambertian : public material {
- public:
- lambertian(const color &c);
- lambertian(shared_ptr c);
+public:
+ lambertian(const color &c);
+ lambertian(shared_ptr c);
- static std::shared_ptr generateFromImage(std::shared_ptr im);
- static std::shared_ptr generateFromImageTextureType(int imageTextureType);
+ static std::shared_ptr
+ generateFromImage(std::shared_ptr im);
+ static std::shared_ptr
+ generateFromImageTextureType(int imageTextureType);
- virtual bool scatter(const ray &rayIn, const hitData &data, color &attenuation, ray &rayOut) const override;
- virtual color getVisibleColor() const;
+ virtual bool scatter(const ray &rayIn, const hitData &data,
+ color &attenuation, ray &rayOut) const override;
+ virtual color getVisibleColor() const override;
- private:
- shared_ptr m_albedo;
+private:
+ shared_ptr m_albedo;
};
class metal : public material {
- public:
- metal(const color &c, double f);
+public:
+ metal(const color &c, double f);
- virtual bool scatter(const ray &rayIn, const hitData &data, color &attenuation, ray &rayOut) const override;
- virtual color getVisibleColor() const;
+ virtual bool scatter(const ray &rayIn, const hitData &data,
+ color &attenuation, ray &rayOut) const override;
+ virtual color getVisibleColor() const override;
- private:
- color m_albedo;
- double m_fuzz;
+private:
+ color m_albedo;
+ double m_fuzz;
};
class refractor : public material {
- public:
- refractor(double eta);
+public:
+ refractor(double eta);
- virtual bool scatter(const ray &rayIn, const hitData &data, color &attenuation, ray &rayOut) const override;
+ virtual bool scatter(const ray &rayIn, const hitData &data,
+ color &attenuation, ray &rayOut) const override;
- private:
- static double reflectance(double cosine, double eta);
+private:
+ static double reflectance(double cosine, double eta);
- double m_eta;
+ double m_eta;
};
class diffuseLight : public material {
- public:
- diffuseLight(shared_ptr text, color lightColor);
- diffuseLight(color even, color odd, color lightColor);
- diffuseLight(color displayColor, color lightColor);
-
- static std::shared_ptr generateFromImage(std::shared_ptr im, color c);
- static std::shared_ptr generateFromImageTextureType(IMAGE_TEXTURE_TYPE imageTextureType, color c);
-
- virtual bool scatter(const ray &rayIn, const hitData &data, color &attenuation, ray &rayOut) const override;
- virtual color emit(double u, double v, const point3 &p, bool firstHit) const override;
-
- virtual std::shared_ptr getLightMaterial() const override;
- virtual color getVisibleColor() const;
-
- private:
- shared_ptr m_textureToDisplay;
- shared_ptr m_lightColor;
+public:
+ diffuseLight(shared_ptr text, color lightColor);
+ diffuseLight(color even, color odd, color lightColor);
+ diffuseLight(color displayColor, color lightColor);
+
+ static std::shared_ptr
+ generateFromImage(std::shared_ptr im, color c);
+ static std::shared_ptr
+ generateFromImageTextureType(IMAGE_TEXTURE_TYPE imageTextureType, color c);
+
+ virtual bool scatter(const ray &rayIn, const hitData &data,
+ color &attenuation, ray &rayOut) const override;
+ virtual color emit(double u, double v, const point3 &p,
+ bool firstHit) const override;
+
+ virtual std::shared_ptr getLightMaterial() const override;
+ virtual color getVisibleColor() const override;
+
+private:
+ shared_ptr m_textureToDisplay;
+ shared_ptr m_lightColor;
};
} // namespace shkyera
diff --git a/src/ui/render/renderWindow.cpp b/src/ui/render/renderWindow.cpp
index bf5ee6a..868ddfb 100644
--- a/src/ui/render/renderWindow.cpp
+++ b/src/ui/render/renderWindow.cpp
@@ -91,7 +91,7 @@ void renderWindow::updateImageTexture() {
m_loadWidth = m_image->width();
m_loadHeight = m_image->height();
- if (m_renderTexture.size() == 0)
+ if (m_renderTexture.size() != 4 * m_loadWidth * m_loadHeight)
m_renderTexture.resize(4 * m_loadWidth * m_loadHeight);
for (size_t y = 0; y < m_loadHeight; ++y) {
@@ -103,6 +103,7 @@ void renderWindow::updateImageTexture() {
m_renderTexture[(y * m_loadWidth + x) * 4 + 3] = 255;
}
}
+
unsigned textureId = m_loadTexId;
if (m_loadedImage)
@@ -122,6 +123,11 @@ void renderWindow::updateImageTexture() {
m_loadTexId = textureId;
}
+void renderWindow::setImage(std::shared_ptr image) {
+ m_image = image;
+ updateImageTexture();
+}
+
std::shared_ptr renderWindow::getImage() const { return m_image; }
} // namespace shkyera
diff --git a/src/ui/render/renderWindow.hpp b/src/ui/render/renderWindow.hpp
index e2f14f8..3802d3b 100644
--- a/src/ui/render/renderWindow.hpp
+++ b/src/ui/render/renderWindow.hpp
@@ -12,6 +12,7 @@ class renderWindow {
point3 render(bool sampleTexture, bool &updated, std::pair &mouseMovement);
+ void setImage(std::shared_ptr image);
std::shared_ptr getImage() const;
private:
diff --git a/src/ui/renderer.cpp b/src/ui/renderer.cpp
index 5a36aa5..2a0cca8 100644
--- a/src/ui/renderer.cpp
+++ b/src/ui/renderer.cpp
@@ -12,18 +12,19 @@ namespace shkyera {
renderer::renderer(std::shared_ptr world, std::shared_ptr cam, std::shared_ptr im,
color backgroundColor)
- : m_world(world), m_cam(cam), m_image(im), m_stop(false) {
- m_imageToDraw = std::make_unique(m_image->width() / SCALING_FACTOR, m_image->height() / SCALING_FACTOR);
+ : m_world(world), m_cam(cam), m_image(im), m_stop(false), m_isExporting(false) {
+ m_imageToDraw = std::make_shared(m_image->width() / SCALING_FACTOR, m_image->height() / SCALING_FACTOR);
m_backgroundColor = backgroundColor;
}
renderer::renderer(std::shared_ptr world, std::shared_ptr cam, std::shared_ptr im)
- : m_world(world), m_cam(cam), m_image(im) {
- m_imageToDraw = std::make_unique(m_image->width() / SCALING_FACTOR, m_image->height() / SCALING_FACTOR);
+ : m_world(world), m_cam(cam), m_image(im), m_isExporting(false) {
+ m_imageToDraw = std::make_shared(m_image->width() / SCALING_FACTOR, m_image->height() / SCALING_FACTOR);
}
void renderer::startRendering() {
m_renderedImage = false;
+ m_samplesTaken = 0;
m_renderingThread = std::thread([this] { render(); });
}
@@ -31,6 +32,25 @@ bool renderer::renderedImage() const { return m_renderedImage; }
void renderer::stopRendering() { m_stop = true; }
+std::shared_ptr renderer::setupImageToExport(exportSettings settings) {
+ m_imageToDraw = std::make_shared(settings.width, settings.height);
+ m_isExporting = true;
+
+ return m_imageToDraw;
+}
+
+bool renderer::isExporting() const { return m_isExporting; }
+
+std::shared_ptr renderer::stopExporting() {
+ m_imageToDraw = std::make_shared(m_image->width() / SCALING_FACTOR, m_image->height() / SCALING_FACTOR);
+ m_isExporting = false;
+ m_cam->setAspectRatio(static_cast(m_imageToDraw->width()) / m_imageToDraw->height());
+
+ return m_image;
+}
+
+unsigned int renderer::getTakenSamples() const { return m_samplesTaken; }
+
std::thread &renderer::renderingThread() { return m_renderingThread; }
void renderer::renderRow(int y) {
diff --git a/src/ui/renderer.hpp b/src/ui/renderer.hpp
index 42e1472..cc366d9 100644
--- a/src/ui/renderer.hpp
+++ b/src/ui/renderer.hpp
@@ -3,6 +3,7 @@
#include "core/image.hpp"
#include "core/utils.hpp"
+#include "ui/settings/exportSettingsWindow.hpp"
#include "world/camera.hpp"
#include "world/visibleWorld.hpp"
@@ -23,8 +24,14 @@ class renderer {
bool renderedImage() const;
+ std::shared_ptr setupImageToExport(exportSettings settings);
+ bool isExporting() const;
+ std::shared_ptr stopExporting();
+
+ unsigned int getTakenSamples() const;
+
static constexpr float SCALING_FACTOR = 2.5;
- static constexpr int MAXIMUM_RAY_DEPTH = 3;
+ static constexpr int MAXIMUM_RAY_DEPTH = 5;
private:
void render();
@@ -37,7 +44,7 @@ class renderer {
color m_backgroundColor;
std::shared_ptr m_image;
- std::unique_ptr m_imageToDraw;
+ std::shared_ptr m_imageToDraw;
std::shared_ptr m_cam;
std::shared_ptr m_world;
@@ -47,6 +54,8 @@ class renderer {
unsigned int m_samplesTaken;
std::thread m_renderingThread;
+
+ bool m_isExporting;
};
} // namespace shkyera
diff --git a/src/ui/settings/cameraSettingsWindow.cpp b/src/ui/settings/cameraSettingsWindow.cpp
index c8b0cf9..6c720a7 100644
--- a/src/ui/settings/cameraSettingsWindow.cpp
+++ b/src/ui/settings/cameraSettingsWindow.cpp
@@ -21,7 +21,7 @@ cameraSettings cameraSettingsWindow::render(bool &updated) {
float fieldOfView = settings.fieldOfView;
float focusDistance = settings.focusDistance;
- ImGui::Begin("Camera");
+ ImGui::Begin("Camera", nullptr, ImGuiWindowFlags_None);
ImGui::PushFont(ui::BOLD_FONT);
ImGui::Text("Position");
@@ -33,9 +33,27 @@ cameraSettings cameraSettingsWindow::render(bool &updated) {
ImGui::PushFont(ui::BOLD_FONT);
ImGui::Text("Visual");
ImGui::PopFont();
- ImGui::SliderFloat("Depth of Field", &depthOfField, 0.0f, 1.0f, "%.2f");
ImGui::SliderFloat("Field of View", &fieldOfView, 10.0f, 140.0f, "%.2f");
+ if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted("The bigger, the more the camera sees.");
+ ImGui::EndTooltip();
+ }
+
+ ImGui::SliderFloat("Depth of Field", &depthOfField, 0.0f, 1.0f, "%.2f");
+ if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted("Size of the camera aperture.\nUse in combination with Focus Distance.");
+ ImGui::EndTooltip();
+ }
+
ImGui::SliderFloat("Focus Distance", &focusDistance, 0.5f, 30.0f, "%.2f");
+ if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted("Camera will be focused at the specified distance.\nEffect only visible if Depth of "
+ "Field is larger than 0.");
+ ImGui::EndTooltip();
+ }
ImGui::End();
diff --git a/src/ui/settings/cameraSettingsWindow.hpp b/src/ui/settings/cameraSettingsWindow.hpp
index 189ddaf..a8a5ccc 100644
--- a/src/ui/settings/cameraSettingsWindow.hpp
+++ b/src/ui/settings/cameraSettingsWindow.hpp
@@ -7,7 +7,7 @@ namespace shkyera {
class cameraSettingsWindow {
public:
- cameraSettingsWindow(std::shared_ptr im);
+ cameraSettingsWindow(std::shared_ptr cam);
cameraSettings render(bool &updated);
diff --git a/src/ui/settings/exportSettingsWindow.cpp b/src/ui/settings/exportSettingsWindow.cpp
new file mode 100644
index 0000000..e134329
--- /dev/null
+++ b/src/ui/settings/exportSettingsWindow.cpp
@@ -0,0 +1,145 @@
+#include
+#include
+#include
+
+#include "ui/settings/exportSettingsWindow.hpp"
+#include "ui/ui.hpp"
+
+#include "imgui.h"
+#include "imgui_internal.h"
+
+namespace shkyera {
+
+exportSettingsWindow::exportSettingsWindow(std::shared_ptr cam) : m_camera(cam) {}
+
+exportSettings exportSettingsWindow::render(RENDER_MODE &mode) {
+ ImGui::Begin("Export", nullptr, ImGuiWindowFlags_None);
+
+ static exportSettings settings = getDefaultExportSettings();
+ int setWidth = settings.width;
+ int setHeight = settings.height;
+
+ float aspectRatio = static_cast(settings.width) / settings.height;
+
+ if (ImGui::BeginCombo("Format", (" " + FILE_EXTENSIONS[settings.extension]).c_str())) {
+ if (ImGui::Selectable("PNG", settings.extension == PNG))
+ settings.extension = PNG;
+ if (ImGui::Selectable("JPG", settings.extension == JPG))
+ settings.extension = JPG;
+ ImGui::EndCombo();
+ }
+
+ static char fileName[128] = "shkyera_render";
+ ImGui::InputText("File Name", fileName, IM_ARRAYSIZE(fileName));
+
+ static const char *localDirectory = settings.path.c_str();
+ static char directory[1024];
+ static bool loadedLocalDirectory = false;
+ if (!loadedLocalDirectory) {
+ strcpy(directory, localDirectory);
+ loadedLocalDirectory = true;
+ }
+
+ ImGui::TextWrapped(directory);
+
+#ifndef __APPLE__
+ if (ImGui::Button("Choose Destination")) {
+ FILE *f = popen("zenity --file-selection --directory --title=\"Choose a directory\"", "r");
+ fgets(directory, 1024, f);
+ std::string stringifiedDirectory(directory);
+ stringifiedDirectory.pop_back();
+ strcpy(directory, stringifiedDirectory.c_str());
+ }
+#endif
+
+ settings.path = directory;
+ settings.path += "/";
+ settings.path += fileName;
+
+ switch (settings.extension) {
+ case PNG:
+ settings.path += ".png";
+ break;
+ case JPG:
+ default:
+ settings.path += ".jpg";
+ break;
+ }
+
+ ImGui::Dummy(ImVec2(0.0f, 5.0f));
+
+ ImGui::Checkbox("Lock Aspect Ratio", &settings.lockAspectRatio);
+
+ ImGui::SliderInt("Width", &setWidth, 128, 4096);
+ ImGui::SliderInt("Height", &setHeight, 128, 4096);
+
+ if (settings.lockAspectRatio) {
+ if (setWidth != settings.width)
+ setHeight = setWidth / aspectRatio;
+ else if (setHeight != settings.height)
+ setWidth = setHeight * aspectRatio;
+ }
+
+ settings.width = std::max(128, setWidth);
+ settings.height = std::max(128, setHeight);
+
+ ImGui::SliderInt("Rays Per Pixel", &settings.raysPerPixel, 10, 200);
+ if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted("Rays shot out for every pixel.\nThe higher, the "
+ "less noisy the image.\nUse high values "
+ "if you have relatively small light sources.");
+ ImGui::EndTooltip();
+ }
+
+ ImGui::Dummy(ImVec2(0, 5.0f));
+
+ if (mode == EDIT) {
+ if (ImGui::Button("Start Preview", ImVec2(130, 0))) {
+ mode = PREVIEW;
+ }
+ if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted("The program might run slower after running preview.");
+ ImGui::EndTooltip();
+ }
+ ImGui::SameLine();
+
+ if (ImGui::Button("Export", ImVec2(130, 0))) {
+ mode = EXPORT;
+ }
+ if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) {
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted("The program might run slower after starting the export.");
+ ImGui::EndTooltip();
+ }
+
+ } else {
+ if (ImGui::Button("Stop Preview", ImVec2(130, 0))) {
+ mode = EDIT;
+ }
+ }
+
+ ImGui::End();
+
+ return settings;
+} // namespace shkyera
+
+exportSettings exportSettingsWindow::getDefaultExportSettings() {
+ exportSettings settings;
+ settings.width = 1920;
+ settings.height = 1080;
+ settings.raysPerPixel = 40;
+ settings.extension = PNG;
+ settings.lockAspectRatio = true;
+
+#ifdef __APPLE__
+ settings.path = std::filesystem::current_path();
+#else
+ settings.path = get_current_dir_name();
+#endif
+
+ return settings;
+}
+
+} // namespace shkyera
diff --git a/src/ui/settings/exportSettingsWindow.hpp b/src/ui/settings/exportSettingsWindow.hpp
new file mode 100644
index 0000000..3fe88e5
--- /dev/null
+++ b/src/ui/settings/exportSettingsWindow.hpp
@@ -0,0 +1,38 @@
+#ifndef EXPORT_SETTINGS_WINDOW_H
+#define EXPORT_SETTINGS_WINDOW_H
+
+#include "world/camera.hpp"
+
+namespace shkyera {
+
+enum RENDER_FILE_EXTENSION { JPG, PNG };
+enum RENDER_MODE { EDIT, PREVIEW, EXPORT };
+
+struct exportSettings {
+ int width;
+ int height;
+ int raysPerPixel;
+
+ std::string path;
+ RENDER_FILE_EXTENSION extension;
+
+ bool lockAspectRatio;
+};
+
+class exportSettingsWindow {
+ public:
+ exportSettingsWindow(std::shared_ptr cam);
+
+ exportSettings render(RENDER_MODE &mode);
+
+ inline static const std::string FILE_EXTENSIONS[2] = {"JPG", "PNG"};
+
+ private:
+ exportSettings getDefaultExportSettings();
+
+ std::shared_ptr m_camera;
+};
+
+} // namespace shkyera
+
+#endif
diff --git a/src/ui/settings/planetSettingsWindow.cpp b/src/ui/settings/planetSettingsWindow.cpp
index 7640fa2..228ab25 100644
--- a/src/ui/settings/planetSettingsWindow.cpp
+++ b/src/ui/settings/planetSettingsWindow.cpp
@@ -37,7 +37,7 @@ planetSettings planetSettingsWindow::render(bool &updated) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(ImColor(82, 3, 23)));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(ImColor(166, 15, 53)));
ImGui::SameLine();
- if (ImGui::Button("Remove", ImVec2((ImGui::GetWindowWidth() - 122) / 2, 0))) {
+ if (ImGui::Button("Remove", ImVec2((ImGui::GetWindowWidth() - 122) * 0.4, 0))) {
settings.remove = true;
}
@@ -76,7 +76,7 @@ std::shared_ptr planetSettingsWindow::renderMaterialSettings() {
static float lightIntensity = 5;
if (ImGui::Button(("Choose Texture##" + m_planet->getName()).c_str(),
- ImVec2((ImGui::GetWindowWidth() - 122) / 2, 0))) {
+ ImVec2((ImGui::GetWindowWidth() - 122) * 0.6, 0))) {
ImGui::OpenPopup((m_planet->getName() + " Texture").c_str());
useSolidColor = false;
}
diff --git a/src/ui/settings/worldSettingsWindow.cpp b/src/ui/settings/worldSettingsWindow.cpp
index ae9e60a..987d3e2 100644
--- a/src/ui/settings/worldSettingsWindow.cpp
+++ b/src/ui/settings/worldSettingsWindow.cpp
@@ -42,7 +42,7 @@ worldSettings worldSettingsWindow::render(bool &updated) {
ImGui::OpenPopup("Add New Object");
}
- if (ImGui::BeginPopupModal("Add New Object", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
+ if (ImGui::BeginPopupModal("Add New Object", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::InputText("Name", planetName, IM_ARRAYSIZE(planetName));
ImGui::Dummy(ImVec2(0.0f, 5.0f));
if (ImGui::Button("Cancel")) {
diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp
index 5e8baa2..1ea701d 100644
--- a/src/ui/ui.cpp
+++ b/src/ui/ui.cpp
@@ -17,313 +17,427 @@
namespace shkyera {
-ui::ui(std::shared_ptr im, std::shared_ptr renderer, std::shared_ptr world,
- std::shared_ptr cam)
- : m_renderWindow(im, cam), m_renderer(renderer), m_camera(cam), m_world(world), m_cameraSettingsWindow(cam),
- m_worldSettingsWindow(world), m_plotWindow(world, cam), m_mouseSensitivity(MOUSE_SENSITIVITY) {}
+ui::ui(std::shared_ptr im, std::shared_ptr renderer,
+ std::shared_ptr world, std::shared_ptr cam)
+ : m_renderWindow(im, cam), m_renderer(renderer), m_camera(cam),
+ m_world(world), m_cameraSettingsWindow(cam), m_worldSettingsWindow(world),
+ m_exportSettingsWindow(cam), m_plotWindow(world, cam),
+ m_mouseSensitivity(MOUSE_SENSITIVITY), m_renderMode(EDIT),
+ m_exported(false) {}
void glfw_error_callback(int error, const char *description) {
- fprintf(stderr, "Glfw Error %d: %s\n", error, description);
+ fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}
void ui::init() { // Setup window
- glfwSetErrorCallback(glfw_error_callback);
- if (!glfwInit())
- return;
+ glfwSetErrorCallback(glfw_error_callback);
+ if (!glfwInit())
+ return;
- // Decide GL+GLSL versions
+ // Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
- // GL ES 2.0 + GLSL 100
- const char *glsl_version = "#version 100";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
- glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+ // GL ES 2.0 + GLSL 100
+ const char *glsl_version = "#version 100";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
#elif defined(__APPLE__)
- // GL 3.2 + GLSL 150
- const char *glsl_version = "#version 150";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
- glfwWindowHint(GLFW_OPENGL_PROFILE,
- GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
+ // GL 3.2 + GLSL 150
+ const char *glsl_version = "#version 150";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_PROFILE,
+ GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#else
- // GL 3.0 + GLSL 130
- const char *glsl_version = "#version 130";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
- // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
- // // 3.2+ only glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
- // // 3.0+ only
+ // GL 3.0 + GLSL 130
+ const char *glsl_version = "#version 130";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ // // 3.2+ only glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+ // // 3.0+ only
#endif
- // Create window with graphics context
- m_window = glfwCreateWindow(1000, 800, "SHKYERA Engine", NULL, NULL);
- if (m_window == NULL)
- return;
-
- m_open = true;
-
- glfwMakeContextCurrent(m_window);
- glfwSwapInterval(1); // Enable vsync
- gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
-
- // Setup Dear ImGui context
- IMGUI_CHECKVERSION();
- ImGui::CreateContext();
- ImPlot::CreateContext();
- ImGuiIO &io = ImGui::GetIO();
- (void)io;
- io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
- io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
-
- // Setup Dear ImGui style
- ImGui::StyleColorsDark();
-
- // Setup Platform/Renderer backends
- ImGui_ImplGlfw_InitForOpenGL(m_window, true);
- ImGui_ImplOpenGL3_Init(glsl_version);
-
- // Our state
- m_clearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
-
- glfwInit();
-
- NORMAL_FONT = io.Fonts->AddFontFromFileTTF("resources/fonts/OpenSansRegular.ttf", 18);
- BOLD_FONT = io.Fonts->AddFontFromFileTTF("resources/fonts/OpenSansBold.ttf", 18);
-
- image::EARTH_DAY_TEXTURE->scaleImage(image::ICON_EARTH_DAY_TEXTURE, true);
- image::EARTH_NIGHT_TEXTURE->scaleImage(image::ICON_EARTH_NIGHT_TEXTURE, true);
- image::MARS_TEXTURE->scaleImage(image::ICON_MARS_TEXTURE, true);
- image::SUN_TEXTURE->scaleImage(image::ICON_SUN_TEXTURE, true);
- image::MOON_TEXTURE->scaleImage(image::ICON_MOON_TEXTURE, true);
- image::CERES_TEXTURE->scaleImage(image::ICON_CERES_TEXTURE, true);
- image::CLOUDY_VENUS_TEXTURE->scaleImage(image::ICON_CLOUDY_VENUS_TEXTURE, true);
- image::ERIS_TEXTURE->scaleImage(image::ICON_ERIS_TEXTURE, true);
- image::HAUMEA_TEXTURE->scaleImage(image::ICON_HAUMEA_TEXTURE, true);
- image::JUPITER_TEXTURE->scaleImage(image::ICON_JUPITER_TEXTURE, true);
- image::MAKE_TEXTURE->scaleImage(image::ICON_MAKE_TEXTURE, true);
- image::MERCURY_TEXTURE->scaleImage(image::ICON_MERCURY_TEXTURE, true);
- image::NEPTUNE_TEXTURE->scaleImage(image::ICON_NEPTUNE_TEXTURE, true);
- image::SATURN_TEXTURE->scaleImage(image::ICON_SATURN_TEXTURE, true);
- image::URANUS_TEXTURE->scaleImage(image::ICON_URANUS_TEXTURE, true);
- image::VENUS_TEXTURE->scaleImage(image::ICON_VENUS_TEXTURE, true);
-
- image::ICON_EARTH_DAY_TEXTURE->updateTextureId();
- image::ICON_EARTH_NIGHT_TEXTURE->updateTextureId();
- image::ICON_MARS_TEXTURE->updateTextureId();
- image::ICON_SUN_TEXTURE->updateTextureId();
- image::ICON_MOON_TEXTURE->updateTextureId();
- image::ICON_CERES_TEXTURE->updateTextureId();
- image::ICON_CLOUDY_VENUS_TEXTURE->updateTextureId();
- image::ICON_ERIS_TEXTURE->updateTextureId();
- image::ICON_HAUMEA_TEXTURE->updateTextureId();
- image::ICON_JUPITER_TEXTURE->updateTextureId();
- image::ICON_MAKE_TEXTURE->updateTextureId();
- image::ICON_MERCURY_TEXTURE->updateTextureId();
- image::ICON_NEPTUNE_TEXTURE->updateTextureId();
- image::ICON_SATURN_TEXTURE->updateTextureId();
- image::ICON_URANUS_TEXTURE->updateTextureId();
- image::ICON_VENUS_TEXTURE->updateTextureId();
+ // Create window with graphics context
+ m_window = glfwCreateWindow(1100, 800, "Shkyera Engine", NULL, NULL);
+ if (m_window == NULL)
+ return;
+
+ m_open = true;
+
+ glfwMakeContextCurrent(m_window);
+ glfwSwapInterval(1); // Enable vsync
+ gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
+
+ // Setup Dear ImGui context
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+ ImPlot::CreateContext();
+ ImGuiIO &io = ImGui::GetIO();
+ (void)io;
+ io.ConfigFlags |=
+ ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
+ io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
+
+ // Setup Dear ImGui style
+ ImGui::StyleColorsDark();
+
+ // Setup Platform/Renderer backends
+ ImGui_ImplGlfw_InitForOpenGL(m_window, true);
+ ImGui_ImplOpenGL3_Init(glsl_version);
+
+ // Our state
+ m_clearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+
+ glfwInit();
+
+ NORMAL_FONT =
+ io.Fonts->AddFontFromFileTTF("resources/fonts/OpenSansRegular.ttf", 18);
+ BOLD_FONT =
+ io.Fonts->AddFontFromFileTTF("resources/fonts/OpenSansBold.ttf", 18);
+
+ image::EARTH_DAY_TEXTURE->scaleImage(image::ICON_EARTH_DAY_TEXTURE, true);
+ image::EARTH_NIGHT_TEXTURE->scaleImage(image::ICON_EARTH_NIGHT_TEXTURE, true);
+ image::MARS_TEXTURE->scaleImage(image::ICON_MARS_TEXTURE, true);
+ image::SUN_TEXTURE->scaleImage(image::ICON_SUN_TEXTURE, true);
+ image::MOON_TEXTURE->scaleImage(image::ICON_MOON_TEXTURE, true);
+ image::CERES_TEXTURE->scaleImage(image::ICON_CERES_TEXTURE, true);
+ image::CLOUDY_VENUS_TEXTURE->scaleImage(image::ICON_CLOUDY_VENUS_TEXTURE,
+ true);
+ image::ERIS_TEXTURE->scaleImage(image::ICON_ERIS_TEXTURE, true);
+ image::HAUMEA_TEXTURE->scaleImage(image::ICON_HAUMEA_TEXTURE, true);
+ image::JUPITER_TEXTURE->scaleImage(image::ICON_JUPITER_TEXTURE, true);
+ image::MAKE_TEXTURE->scaleImage(image::ICON_MAKE_TEXTURE, true);
+ image::MERCURY_TEXTURE->scaleImage(image::ICON_MERCURY_TEXTURE, true);
+ image::NEPTUNE_TEXTURE->scaleImage(image::ICON_NEPTUNE_TEXTURE, true);
+ image::SATURN_TEXTURE->scaleImage(image::ICON_SATURN_TEXTURE, true);
+ image::URANUS_TEXTURE->scaleImage(image::ICON_URANUS_TEXTURE, true);
+ image::VENUS_TEXTURE->scaleImage(image::ICON_VENUS_TEXTURE, true);
+
+ image::ICON_EARTH_DAY_TEXTURE->updateTextureId();
+ image::ICON_EARTH_NIGHT_TEXTURE->updateTextureId();
+ image::ICON_MARS_TEXTURE->updateTextureId();
+ image::ICON_SUN_TEXTURE->updateTextureId();
+ image::ICON_MOON_TEXTURE->updateTextureId();
+ image::ICON_CERES_TEXTURE->updateTextureId();
+ image::ICON_CLOUDY_VENUS_TEXTURE->updateTextureId();
+ image::ICON_ERIS_TEXTURE->updateTextureId();
+ image::ICON_HAUMEA_TEXTURE->updateTextureId();
+ image::ICON_JUPITER_TEXTURE->updateTextureId();
+ image::ICON_MAKE_TEXTURE->updateTextureId();
+ image::ICON_MERCURY_TEXTURE->updateTextureId();
+ image::ICON_NEPTUNE_TEXTURE->updateTextureId();
+ image::ICON_SATURN_TEXTURE->updateTextureId();
+ image::ICON_URANUS_TEXTURE->updateTextureId();
+ image::ICON_VENUS_TEXTURE->updateTextureId();
}
void ui::style() {
- ImGuiStyle &style = ImGui::GetStyle();
- style.WindowMinSize = ImVec2(320, 320);
- style.FramePadding = ImVec2(4, 2);
- style.ItemSpacing = ImVec2(6, 2);
- style.ItemInnerSpacing = ImVec2(6, 4);
- style.Alpha = 0.95f;
- style.WindowRounding = 8.0f;
- style.FrameRounding = 8.0f;
- style.IndentSpacing = 6.0f;
- style.ItemInnerSpacing = ImVec2(2, 4);
- style.ColumnsMinSpacing = 50.0f;
- style.GrabMinSize = 14.0f;
- style.GrabRounding = 16.0f;
- style.ScrollbarSize = 12.0f;
- style.ScrollbarRounding = 16.0f;
-
- ImVec4 BACKGROUND_COLOR(0.08f, 0.1f, 0.12f, 1.0f);
- ImVec4 TEXT_COLOR(0.86f, 0.93f, 0.89f, 0.78f);
- ImVec4 DISABLED_TEXT_COLOR(0.86f, 0.93f, 0.89f, 0.28f);
- ImVec4 ACCENT_COLOR(0.2f, 0.6f, 0.9f, 0.6f);
- ImVec4 STRONG_ACCENT_COLOR(0.2f, 0.6f, 0.9f, 1.0f);
-
- ImVec4 GREY(0.7f, 0.7f, 0.7f, 1.0f);
- ImVec4 LIGHT_GREY(0.8f, 0.8f, 0.8f, 1.0f);
- ImVec4 BLACK(0.0f, 0.0f, 0.0f, 0.0f);
-
- style.Colors[ImGuiCol_Text] = TEXT_COLOR;
- style.Colors[ImGuiCol_TextDisabled] = DISABLED_TEXT_COLOR;
- style.Colors[ImGuiCol_WindowBg] = BACKGROUND_COLOR;
- style.Colors[ImGuiCol_BorderShadow] = BLACK;
- style.Colors[ImGuiCol_FrameBg] = ACCENT_COLOR;
- style.Colors[ImGuiCol_ScrollbarBg] = ACCENT_COLOR;
- style.Colors[ImGuiCol_FrameBgHovered] = ACCENT_COLOR;
- style.Colors[ImGuiCol_FrameBgActive] = STRONG_ACCENT_COLOR;
- style.Colors[ImGuiCol_TitleBgCollapsed] = BACKGROUND_COLOR;
- style.Colors[ImGuiCol_TitleBg] = ACCENT_COLOR;
- style.Colors[ImGuiCol_TitleBgActive] = STRONG_ACCENT_COLOR;
- style.Colors[ImGuiCol_SliderGrab] = ACCENT_COLOR;
- style.Colors[ImGuiCol_SliderGrabActive] = STRONG_ACCENT_COLOR;
- style.Colors[ImGuiCol_Separator] = GREY;
- style.Colors[ImGuiCol_SeparatorHovered] = LIGHT_GREY;
- style.Colors[ImGuiCol_SeparatorActive] = LIGHT_GREY;
-
- ImPlotStyle &plotStyle = ImPlot::GetStyle();
- plotStyle.Colors[ImPlotCol_FrameBg] = BACKGROUND_COLOR;
- plotStyle.Colors[ImPlotCol_PlotBg] = BACKGROUND_COLOR;
- plotStyle.Colors[ImPlotCol_PlotBorder] = BACKGROUND_COLOR;
+ ImGuiStyle &style = ImGui::GetStyle();
+ style.WindowMinSize = ImVec2(320, 320);
+ style.FramePadding = ImVec2(4, 2);
+ style.ItemSpacing = ImVec2(6, 2);
+ style.ItemInnerSpacing = ImVec2(6, 4);
+ style.Alpha = 0.95f;
+ style.WindowRounding = 8.0f;
+ style.FrameRounding = 8.0f;
+ style.IndentSpacing = 6.0f;
+ style.ItemInnerSpacing = ImVec2(2, 4);
+ style.ColumnsMinSpacing = 50.0f;
+ style.GrabMinSize = 14.0f;
+ style.GrabRounding = 16.0f;
+ style.ScrollbarSize = 12.0f;
+ style.ScrollbarRounding = 16.0f;
+
+ ImVec4 BACKGROUND_COLOR(0.08f, 0.1f, 0.12f, 1.0f);
+ ImVec4 TEXT_COLOR(0.86f, 0.93f, 0.89f, 0.78f);
+ ImVec4 DISABLED_TEXT_COLOR(0.86f, 0.93f, 0.89f, 0.28f);
+ ImVec4 ACCENT_COLOR(0.2f, 0.6f, 0.9f, 0.6f);
+ ImVec4 STRONG_ACCENT_COLOR(0.2f, 0.6f, 0.9f, 1.0f);
+
+ ImVec4 GREY(0.7f, 0.7f, 0.7f, 1.0f);
+ ImVec4 LIGHT_GREY(0.8f, 0.8f, 0.8f, 1.0f);
+ ImVec4 BLACK(0.0f, 0.0f, 0.0f, 0.0f);
+
+ style.Colors[ImGuiCol_Text] = TEXT_COLOR;
+ style.Colors[ImGuiCol_TextDisabled] = DISABLED_TEXT_COLOR;
+ style.Colors[ImGuiCol_WindowBg] = BACKGROUND_COLOR;
+ style.Colors[ImGuiCol_BorderShadow] = BLACK;
+ style.Colors[ImGuiCol_FrameBg] = ACCENT_COLOR;
+ style.Colors[ImGuiCol_ScrollbarBg] = ACCENT_COLOR;
+ style.Colors[ImGuiCol_FrameBgHovered] = ACCENT_COLOR;
+ style.Colors[ImGuiCol_FrameBgActive] = STRONG_ACCENT_COLOR;
+ style.Colors[ImGuiCol_TitleBgCollapsed] = BACKGROUND_COLOR;
+ style.Colors[ImGuiCol_TitleBg] = ACCENT_COLOR;
+ style.Colors[ImGuiCol_TitleBgActive] = STRONG_ACCENT_COLOR;
+ style.Colors[ImGuiCol_SliderGrab] = ACCENT_COLOR;
+ style.Colors[ImGuiCol_SliderGrabActive] = STRONG_ACCENT_COLOR;
+ style.Colors[ImGuiCol_Separator] = GREY;
+ style.Colors[ImGuiCol_SeparatorHovered] = LIGHT_GREY;
+ style.Colors[ImGuiCol_SeparatorActive] = LIGHT_GREY;
+ style.WindowMinSize = ImVec2(380, 200);
+
+ ImPlotStyle &plotStyle = ImPlot::GetStyle();
+ plotStyle.Colors[ImPlotCol_FrameBg] = BACKGROUND_COLOR;
+ plotStyle.Colors[ImPlotCol_PlotBg] = BACKGROUND_COLOR;
+ plotStyle.Colors[ImPlotCol_PlotBorder] = BACKGROUND_COLOR;
}
void ui::run() {
- m_open = !glfwWindowShouldClose(m_window);
- // Main loop
- if (m_open) {
- glfwPollEvents();
-
- glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- style();
- ImGui_ImplOpenGL3_NewFrame();
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
-
- const ImGuiViewport *viewport = ImGui::GetMainViewport();
- ImGui::SetNextWindowPos(viewport->WorkPos);
- ImGui::SetNextWindowSize(viewport->WorkSize);
- ImGui::SetNextWindowViewport(viewport->ID);
- ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
- ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
- ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
-
- ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
- ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
- ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus |
- ImGuiWindowFlags_NoBackground;
-
- bool open = true;
- ImGui::Begin("Shkyera Engine", &open, window_flags);
- ImGui::PopStyleVar(3);
-
- if (ImGui::BeginMainMenuBar()) {
- if (ImGui::BeginMenu("File")) {
- if (ImGui::MenuItem("Load scene")) {
- }
- if (ImGui::MenuItem("Save scene")) {
- }
- ImGui::EndMenu();
- }
- ImGui::EndMainMenuBar();
- }
-
- ImGuiIO &io = ImGui::GetIO();
-
- if (io.ConfigFlags) {
- ImGuiID dockspace_id = ImGui::GetID("Shkyera Engine");
- ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f),
- ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_NoWindowMenuButton);
-
- static auto firstTime = true;
- if (firstTime) {
- firstTime = false;
-
- ImGui::DockBuilderRemoveNode(dockspace_id);
- ImGui::DockBuilderAddNode(dockspace_id,
- ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_DockSpace);
- ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
-
- ImGuiID dock_id_left, dock_id_right, dock_id_top_right, dock_id_bottom_right, dock_id_top_left,
- dock_id_bottom_left;
- ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.65f, &dock_id_left, &dock_id_right);
- ImGui::DockBuilderSplitNode(dock_id_right, ImGuiDir_Up, 0.3f, &dock_id_top_right,
- &dock_id_bottom_right);
- ImGui::DockBuilderSplitNode(dock_id_left, ImGuiDir_Up, 0.5f, &dock_id_top_left, &dock_id_bottom_left);
-
- ImGui::DockBuilderDockWindow("Render", dock_id_top_left);
-
- ImGui::DockBuilderDockWindow("Top view", dock_id_bottom_left);
- ImGui::DockBuilderDockWindow("Front view", dock_id_bottom_left);
- ImGui::DockBuilderDockWindow("Side view", dock_id_bottom_left);
-
- ImGui::DockBuilderDockWindow("Camera", dock_id_top_right);
- ImGui::DockBuilderDockWindow("World", dock_id_bottom_right);
-
- ImGui::DockBuilderFinish(dockspace_id);
- }
- }
-
- ImGui::End();
-
- bool updatedSettings = false;
- systemSettings newSystemSettings = m_plotWindow.render(updatedSettings);
-
- cameraSettings newCameraSettings = newSystemSettings.cam;
- worldSettings newWorldSettings = newSystemSettings.world;
-
- if (updatedSettings) {
- m_cameraSettingsWindow.render(updatedSettings);
- m_worldSettingsWindow.render(updatedSettings);
- } else {
- newCameraSettings = m_cameraSettingsWindow.render(updatedSettings);
- newWorldSettings = m_worldSettingsWindow.render(updatedSettings);
- }
-
- point3 cameraTranslation;
- std::pair mouseMovement;
-
- if (m_renderer->renderedImage())
- cameraTranslation = m_renderWindow.render(true, updatedSettings, mouseMovement);
- else
- cameraTranslation = m_renderWindow.render(false, updatedSettings, mouseMovement);
-
- newCameraSettings.direction.rotateAroundY(-m_mouseSensitivity * mouseMovement.first);
- newCameraSettings.direction.rotateUpAndDown(m_mouseSensitivity * mouseMovement.second);
- newCameraSettings.direction = unitVector(newCameraSettings.direction);
-
- newCameraSettings.origin += cameraTranslation;
-
- if (m_renderer->renderedImage() && updatedSettings) {
- m_renderer->stopRendering();
- m_renderer->renderingThread().join();
-
- m_camera->setSettings(newCameraSettings);
- m_world->setSettings(newWorldSettings);
-
- m_renderer->startRendering();
- }
-
- ImGui::Render();
+ m_open = !glfwWindowShouldClose(m_window);
+ // Main loop
+ if (m_open) {
+ glfwPollEvents();
+
+ glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ style();
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ const ImGuiViewport *viewport = ImGui::GetMainViewport();
+ ImGui::SetNextWindowPos(viewport->WorkPos);
+ ImGui::SetNextWindowSize(viewport->WorkSize);
+ ImGui::SetNextWindowViewport(viewport->ID);
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
+
+ ImGuiWindowFlags window_flags =
+ ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
+ ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
+ ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus |
+ ImGuiWindowFlags_NoBackground;
+
+ bool open = true;
+ ImGui::Begin("Shkyera Engine", &open, window_flags);
+ ImGui::PopStyleVar(3);
+
+ ImGuiIO &io = ImGui::GetIO();
+
+ if (io.ConfigFlags) {
+ ImGuiID dockspace_id = ImGui::GetID("Shkyera Engine");
+ ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f),
+ ImGuiDockNodeFlags_PassthruCentralNode |
+ ImGuiDockNodeFlags_NoWindowMenuButton);
+
+ static auto firstTime = true;
+ if (firstTime) {
+ firstTime = false;
+
+ ImGui::DockBuilderRemoveNode(dockspace_id);
+ ImGui::DockBuilderAddNode(dockspace_id,
+ ImGuiDockNodeFlags_PassthruCentralNode |
+ ImGuiDockNodeFlags_DockSpace);
+ ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
+
+ ImGuiID dock_id_left, dock_id_right, dock_id_top_right,
+ dock_id_bottom_right, dock_id_top_left, dock_id_bottom_left;
+ ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.6f,
+ &dock_id_left, &dock_id_right);
+ ImGui::DockBuilderSplitNode(dock_id_right, ImGuiDir_Up, 0.4f,
+ &dock_id_top_right, &dock_id_bottom_right);
+ ImGui::DockBuilderSplitNode(dock_id_left, ImGuiDir_Up, 0.5f,
+ &dock_id_top_left, &dock_id_bottom_left);
+
+ ImGui::DockBuilderDockWindow("Render", dock_id_top_left);
+
+ ImGui::DockBuilderDockWindow("Top view", dock_id_bottom_left);
+ ImGui::DockBuilderDockWindow("Front view", dock_id_bottom_left);
+ ImGui::DockBuilderDockWindow("Side view", dock_id_bottom_left);
+
+ ImGui::DockBuilderDockWindow("Camera", dock_id_top_right);
+ ImGui::DockBuilderDockWindow("Export", dock_id_top_right);
+ ImGui::DockBuilderDockWindow("World", dock_id_bottom_right);
+
+ ImGui::DockBuilderFinish(dockspace_id);
+ }
+ }
- int display_w, display_h;
+ ImGui::End();
- glfwGetFramebufferSize(m_window, &display_w, &display_h);
- glViewport(0, 0, display_w, display_h);
- glClearColor(m_clearColor.x * m_clearColor.w, m_clearColor.y * m_clearColor.w, m_clearColor.z * m_clearColor.w,
- m_clearColor.w);
- glClear(GL_COLOR_BUFFER_BIT);
- ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+ exportSettings settingsExport = m_exportSettingsWindow.render(m_renderMode);
- glfwSwapBuffers(m_window);
+ bool updatedSettings = false;
+ systemSettings newSystemSettings = m_plotWindow.render(updatedSettings);
+
+ cameraSettings newCameraSettings = newSystemSettings.cam;
+ worldSettings newWorldSettings = newSystemSettings.world;
+
+ if (updatedSettings) {
+ m_cameraSettingsWindow.render(updatedSettings);
+ m_worldSettingsWindow.render(updatedSettings);
} else {
- close();
+ newCameraSettings = m_cameraSettingsWindow.render(updatedSettings);
+ newWorldSettings = m_worldSettingsWindow.render(updatedSettings);
}
+
+ point3 cameraTranslation;
+ std::pair mouseMovement;
+
+ if (m_renderer->renderedImage())
+ cameraTranslation =
+ m_renderWindow.render(true, updatedSettings, mouseMovement);
+ else
+ cameraTranslation =
+ m_renderWindow.render(false, updatedSettings, mouseMovement);
+
+ newCameraSettings.direction.rotateAroundY(-m_mouseSensitivity *
+ mouseMovement.first);
+ newCameraSettings.direction.rotateUpAndDown(m_mouseSensitivity *
+ mouseMovement.second);
+ newCameraSettings.direction = unitVector(newCameraSettings.direction);
+
+ newCameraSettings.origin += cameraTranslation;
+
+ if (m_renderer->renderedImage() && updatedSettings) {
+ m_renderer->stopRendering();
+ m_renderer->renderingThread().join();
+
+ m_camera->setSettings(newCameraSettings);
+ m_world->setSettings(newWorldSettings);
+
+ m_renderer->startRendering();
+ }
+
+ switch (m_renderMode) {
+ case EXPORT:
+ if (m_renderer->renderedImage() && !m_renderer->isExporting()) {
+ m_renderer->stopRendering();
+ m_renderer->renderingThread().join();
+
+ m_camera->setAspectRatio(static_cast(settingsExport.width) /
+ settingsExport.height);
+
+ auto previewImage = m_renderer->setupImageToExport(settingsExport);
+ m_renderWindow.setImage(previewImage);
+
+ m_renderer->startRendering();
+
+ m_exported = false;
+ m_startedExportTime = std::chrono::steady_clock::now();
+ ImGui::OpenPopup("Exporting");
+ }
+ exportPopup(settingsExport);
+ break;
+ case PREVIEW:
+ if (m_renderer->renderedImage() && !m_renderer->isExporting()) {
+ m_renderer->stopRendering();
+ m_renderer->renderingThread().join();
+
+ m_camera->setAspectRatio(static_cast(settingsExport.width) /
+ settingsExport.height);
+
+ auto previewImage = m_renderer->setupImageToExport(settingsExport);
+ m_renderWindow.setImage(previewImage);
+
+ m_renderer->startRendering();
+ }
+ break;
+ case EDIT:
+ default:
+ if (m_renderer->renderedImage() && m_renderer->isExporting()) {
+ m_renderer->stopRendering();
+ m_renderer->renderingThread().join();
+
+ auto editImage = m_renderer->stopExporting();
+ m_renderWindow.setImage(editImage);
+
+ m_renderer->startRendering();
+ }
+ break;
+ }
+
+ ImGui::Render();
+
+ int display_w, display_h;
+
+ glfwGetFramebufferSize(m_window, &display_w, &display_h);
+ glViewport(0, 0, display_w, display_h);
+ glClearColor(m_clearColor.x * m_clearColor.w,
+ m_clearColor.y * m_clearColor.w,
+ m_clearColor.z * m_clearColor.w, m_clearColor.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ glfwSwapBuffers(m_window);
+ } else {
+ close();
+ }
+}
+
+void ui::exportPopup(exportSettings settings) {
+ ImGui::SetNextWindowSize(ImVec2(300, 0));
+ if (ImGui::BeginPopupModal("Exporting", nullptr,
+ ImGuiWindowFlags_AlwaysAutoResize)) {
+ std::chrono::steady_clock::time_point currentTime =
+ std::chrono::steady_clock::now();
+ int elapsedNanoseconds =
+ std::chrono::duration_cast(
+ currentTime - m_startedExportTime)
+ .count();
+
+ int nanosecondsPerPass =
+ elapsedNanoseconds / (m_renderer->getTakenSamples() + 0.01f);
+ int nanosecondsToFinish =
+ nanosecondsPerPass *
+ (settings.raysPerPixel - m_renderer->getTakenSamples());
+ int secondsToFinish = nanosecondsToFinish / 1e3;
+
+ ImGui::Dummy(ImVec2(150.0f, 5.0f));
+ ImGui::Text("Progress: %.0f%%",
+ std::min(100.0f, 100.0f * m_renderer->getTakenSamples() /
+ settings.raysPerPixel));
+ ImGui::SameLine();
+ ImGui::Text("\tTime left: %d seconds", std::max(0, secondsToFinish));
+
+ if (m_exported) {
+ ImGui::TextWrapped(
+ ("Succesfully saved the image to:\n" + settings.path).c_str());
+ if (ImGui::Button("OK")) {
+ m_renderMode = EDIT;
+ m_exported = false;
+ ImGui::CloseCurrentPopup();
+ }
+ } else {
+ if (ImGui::Button("Cancel")) {
+ m_renderMode = EDIT;
+ ImGui::CloseCurrentPopup();
+ }
+ }
+
+ if (!m_exported && m_renderer->getTakenSamples() >= settings.raysPerPixel) {
+ switch (settings.extension) {
+ case PNG:
+ m_renderWindow.getImage()->saveToPng(settings.path);
+ break;
+ case JPG:
+ default:
+ m_renderWindow.getImage()->saveToJpg(settings.path);
+ }
+ m_exported = true;
+ }
+
+ ImGui::EndPopup();
+ }
}
bool ui::isOpen() const { return m_open; }
void ui::close() {
- m_renderer->stopRendering();
- m_renderer->renderingThread().join();
+ m_renderer->stopRendering();
+ m_renderer->renderingThread().join();
- ImGui_ImplOpenGL3_Shutdown();
- ImGui_ImplGlfw_Shutdown();
- ImGui::DestroyContext();
- ImPlot::DestroyContext();
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+ ImPlot::DestroyContext();
- glfwDestroyWindow(m_window);
- glfwTerminate();
+ glfwDestroyWindow(m_window);
+ glfwTerminate();
}
} // namespace shkyera
diff --git a/src/ui/ui.hpp b/src/ui/ui.hpp
index f8dd504..ec3aeab 100644
--- a/src/ui/ui.hpp
+++ b/src/ui/ui.hpp
@@ -6,6 +6,7 @@
#include "ui/render/renderWindow.hpp"
#include "ui/renderer.hpp"
#include "ui/settings/cameraSettingsWindow.hpp"
+#include "ui/settings/exportSettingsWindow.hpp"
#include "ui/settings/worldSettingsWindow.hpp"
#include "imgui.h"
@@ -31,6 +32,8 @@ class ui {
private:
void style();
+ void exportPopup(exportSettings settings);
+
std::shared_ptr m_renderer;
std::shared_ptr m_camera;
std::shared_ptr m_world;
@@ -38,14 +41,19 @@ class ui {
renderWindow m_renderWindow;
cameraSettingsWindow m_cameraSettingsWindow;
worldSettingsWindow m_worldSettingsWindow;
+ exportSettingsWindow m_exportSettingsWindow;
plotWindow m_plotWindow;
GLFWwindow *m_window;
ImVec4 m_clearColor;
bool m_open;
-
float m_mouseSensitivity;
+
+ bool m_exported;
+ std::chrono::steady_clock::time_point m_startedExportTime;
+
+ RENDER_MODE m_renderMode;
};
} // namespace shkyera
diff --git a/src/world/camera.cpp b/src/world/camera.cpp
index 09393a0..b3e8c1a 100644
--- a/src/world/camera.cpp
+++ b/src/world/camera.cpp
@@ -83,4 +83,9 @@ void camera::setSettings(const cameraSettings config) {
setDirection(config.direction);
}
+void camera::setAspectRatio(float aspectRatio) {
+ m_aspectRatio = aspectRatio;
+ setSettings(getSettings());
+}
+
} // namespace shkyera
diff --git a/src/world/camera.hpp b/src/world/camera.hpp
index d314b48..fdaeda1 100644
--- a/src/world/camera.hpp
+++ b/src/world/camera.hpp
@@ -37,6 +37,8 @@ class camera {
cameraSettings getSettings() const;
void setSettings(const cameraSettings config);
+ void setAspectRatio(float aspectRatio);
+
private:
point3 m_origin;
point3 m_lowerLeftCorner;