diff --git a/src/core/image.cpp b/src/core/image.cpp index 09b153b..179ed47 100644 --- a/src/core/image.cpp +++ b/src/core/image.cpp @@ -181,6 +181,66 @@ 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]; } +std::shared_ptr image::getBlurred(int size) const { + if (size < 3) + throw std::invalid_argument("Image blur size needs to be at least 3."); + + if (size % 2 == 0) + throw std::invalid_argument("Image blur size needs to be odd."); + + auto blurred = std::make_shared(m_width, m_height); + for (int y = 0; y < m_height; ++y) { + for (int x = 0; x < m_width; ++x) { + int lowerBoundY = std::max(0, y - size / 2); + int lowerBoundX = std::max(0, x - size / 2); + int upperBoundY = std::min(m_height, y + size / 2 + 1); + int upperBoundX = std::min(m_width, x + size / 2 + 1); + + color average(0, 0, 0); + int n = 0; + + for (int cy = lowerBoundY; cy < upperBoundY; ++cy) { + for (int cx = lowerBoundX; cx < upperBoundX; ++cx) { + average += m_data[cy][cx]; + n++; + } + } + + average /= n * 255; + blurred->at(x, y) = average; + } + } + + return blurred; +} + +std::shared_ptr image::getNoiseMap() const { + auto blurred = getBlurred(3); + auto noise = std::make_shared(m_width, m_height); + + for (int y = 0; y < m_height; ++y) { + for (int x = 0; x < m_width; ++x) { + double diff = (blurred->at(x, y) - m_data[y][x]).length(); + noise->at(x, y) = color(diff, diff, diff); + } + } + + return noise; +} + +std::vector> image::getPixelsAbove(double threshold) const { + std::vector> coords; + + for (int y = 0; y < m_height; ++y) { + for (int x = 0; x < m_width; ++x) { + if (m_data[y][x].length() > threshold) + coords.push_back({x, y}); + } + } + + return coords; +} + void image::saveToPng(std::string path) { char *data = new char[3 * width() * height()]; diff --git a/src/core/image.hpp b/src/core/image.hpp index b8bd801..4382579 100644 --- a/src/core/image.hpp +++ b/src/core/image.hpp @@ -60,6 +60,11 @@ class image { color &operator()(int x, int y); color &at(int x, int y); + std::shared_ptr getBlurred(int size) const; + std::shared_ptr getNoiseMap() const; + + std::vector> getPixelsAbove(double threshold) const; + void saveToPng(std::string path); void saveToJpg(std::string path); diff --git a/src/core/vec3.hpp b/src/core/vec3.hpp index 6722b66..3371025 100644 --- a/src/core/vec3.hpp +++ b/src/core/vec3.hpp @@ -19,6 +19,8 @@ class vec3 { double y() const; double z() const; + vec3 absolute(); + vec3 operator-() const; double operator[](int i) const; double &operator[](int i); diff --git a/src/ui/renderer.cpp b/src/ui/renderer.cpp index 2a0cca8..d1a4496 100644 --- a/src/ui/renderer.cpp +++ b/src/ui/renderer.cpp @@ -39,12 +39,17 @@ std::shared_ptr renderer::setupImageToExport(exportSettings settings) { return m_imageToDraw; } +void renderer::setDenoiseCoordinates(std::vector> coords) { m_denoiseCoordinates = coords; } + +bool renderer::isDenoising() const { return !m_denoiseCoordinates.empty(); } + 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()); + m_denoiseCoordinates.clear(); return m_image; } @@ -68,6 +73,21 @@ void renderer::renderRow(int y) { } } +void renderer::renderCoordinates(std::vector> coords) { + for (auto [x, y] : coords) { + auto u = (x + randomDouble()) / (m_imageToDraw->width() - 1); + auto v = ((m_imageToDraw->height() - y) + randomDouble()) / (m_imageToDraw->height() - 1); + + ray r = m_cam->getRay(u, v); + color c = rayColor(r, MAXIMUM_RAY_DEPTH); + + color result = m_imageToDraw->at(x, y) * (double(m_samplesTaken) / (m_samplesTaken + 1)) + + (1.0 / (m_samplesTaken + 1)) * c; + + m_imageToDraw->at(x, y) = clamp(result, 0, 1); + } +} + void renderer::render() { const int maxDepth = 5; @@ -86,8 +106,17 @@ void renderer::render() { // Whoever reads this, I'm sorry it's done this way. Nothing else // worked. std::vector renderingThreads; - for (int y = 0; y < m_imageToDraw->height(); ++y) { - renderingThreads.push_back(std::thread([this, y] { renderRow(y); })); + if (m_denoiseCoordinates.empty()) { + for (int y = 0; y < m_imageToDraw->height(); ++y) { + renderingThreads.push_back(std::thread([this, y] { renderRow(y); })); + } + } else { + for (size_t i = 0; i < m_denoiseCoordinates.size(); i += 100) { + size_t last = std::min(i + 100, m_denoiseCoordinates.size()); + std::vector> cords(m_denoiseCoordinates.begin() + i, + m_denoiseCoordinates.begin() + last); + renderingThreads.push_back(std::thread([this, cords] { renderCoordinates(cords); })); + } } for (auto &t : renderingThreads) { diff --git a/src/ui/renderer.hpp b/src/ui/renderer.hpp index cc366d9..08af85c 100644 --- a/src/ui/renderer.hpp +++ b/src/ui/renderer.hpp @@ -25,6 +25,8 @@ class renderer { bool renderedImage() const; std::shared_ptr setupImageToExport(exportSettings settings); + void setDenoiseCoordinates(std::vector> coords); + bool isDenoising() const; bool isExporting() const; std::shared_ptr stopExporting(); @@ -36,6 +38,7 @@ class renderer { private: void render(); void renderRow(int y); + void renderCoordinates(std::vector> coords); void updateScaledImage(); void clearScene(); @@ -56,6 +59,7 @@ class renderer { std::thread m_renderingThread; bool m_isExporting; + std::vector> m_denoiseCoordinates; }; } // namespace shkyera diff --git a/src/ui/settings/exportSettingsWindow.cpp b/src/ui/settings/exportSettingsWindow.cpp index e134329..2056e79 100644 --- a/src/ui/settings/exportSettingsWindow.cpp +++ b/src/ui/settings/exportSettingsWindow.cpp @@ -83,7 +83,7 @@ exportSettings exportSettingsWindow::render(RENDER_MODE &mode) { settings.width = std::max(128, setWidth); settings.height = std::max(128, setHeight); - ImGui::SliderInt("Rays Per Pixel", &settings.raysPerPixel, 10, 200); + ImGui::SliderInt("Rays Per Pixel", &settings.raysPerPixel, 100, 2000); if (ImGui::IsItemHovered() && !ImGui::IsItemActive()) { ImGui::BeginTooltip(); ImGui::TextUnformatted("Rays shot out for every pixel.\nThe higher, the " diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index 1ea701d..781a93f 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -17,427 +17,406 @@ 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_exportSettingsWindow(cam), m_plotWindow(world, cam), - m_mouseSensitivity(MOUSE_SENSITIVITY), m_renderMode(EDIT), - m_exported(false) {} +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(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(); + // 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; - 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; + 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); + 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)); - ImGuiIO &io = ImGui::GetIO(); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | + ImGuiWindowFlags_NoBackground; - 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); - } - } + 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::End(); + 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); + } + } + + ImGui::End(); + + exportSettings settingsExport = m_exportSettingsWindow.render(m_renderMode); + + 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(); + } + + 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); - exportSettings settingsExport = m_exportSettingsWindow.render(m_renderMode); + auto previewImage = m_renderer->setupImageToExport(settingsExport); + m_renderWindow.setImage(previewImage); - bool updatedSettings = false; - systemSettings newSystemSettings = m_plotWindow.render(updatedSettings); + m_renderer->startRendering(); - cameraSettings newCameraSettings = newSystemSettings.cam; - worldSettings newWorldSettings = newSystemSettings.world; + 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(); - if (updatedSettings) { - m_cameraSettingsWindow.render(updatedSettings); - m_worldSettingsWindow.render(updatedSettings); - } else { - newCameraSettings = m_cameraSettingsWindow.render(updatedSettings); - newWorldSettings = m_worldSettingsWindow.render(updatedSettings); - } + m_camera->setAspectRatio(static_cast(settingsExport.width) / settingsExport.height); - point3 cameraTranslation; - std::pair mouseMovement; + auto previewImage = m_renderer->setupImageToExport(settingsExport); + m_renderWindow.setImage(previewImage); - if (m_renderer->renderedImage()) - cameraTranslation = - m_renderWindow.render(true, updatedSettings, mouseMovement); - else - cameraTranslation = - m_renderWindow.render(false, updatedSettings, mouseMovement); + m_renderer->startRendering(); + } + break; + case EDIT: + default: + if (m_renderer->renderedImage() && m_renderer->isExporting()) { + m_renderer->stopRendering(); + m_renderer->renderingThread().join(); - newCameraSettings.direction.rotateAroundY(-m_mouseSensitivity * - mouseMovement.first); - newCameraSettings.direction.rotateUpAndDown(m_mouseSensitivity * - mouseMovement.second); - newCameraSettings.direction = unitVector(newCameraSettings.direction); + auto editImage = m_renderer->stopExporting(); + m_renderWindow.setImage(editImage); - newCameraSettings.origin += cameraTranslation; + m_renderer->startRendering(); + } + break; + } - if (m_renderer->renderedImage() && updatedSettings) { - m_renderer->stopRendering(); - m_renderer->renderingThread().join(); + ImGui::Render(); - m_camera->setSettings(newCameraSettings); - m_world->setSettings(newWorldSettings); + int display_w, display_h; - m_renderer->startRendering(); - } + 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()); - 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; + glfwSwapBuffers(m_window); + } else { + close(); } - - 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::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_renderer->isDenoising() && !m_exported && m_renderer->getTakenSamples() >= settings.raysPerPixel / 5) { + auto noise = m_renderWindow.getImage()->getNoiseMap(); + auto coordsToDenoise = noise->getPixelsAbove(0.1); + m_renderer->setDenoiseCoordinates(coordsToDenoise); + } + + 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(); } - - 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