From dcafcd68d34bf235d85fa6c094571fc346bf7d61 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Thu, 30 May 2019 01:45:02 -0700 Subject: [PATCH 1/6] display final render in opengl viewport widget --- src/appleseed.studio/CMakeLists.txt | 26 +- src/appleseed.studio/main/main.cpp | 2 +- .../mainwindow/mainwindow.cpp | 182 +++--- src/appleseed.studio/mainwindow/mainwindow.h | 22 +- ...{lightpathswidget.cpp => glscenelayer.cpp} | 501 +++----------- .../{lightpathswidget.h => glscenelayer.h} | 64 +- .../mainwindow/rendering/lightpathslayer.cpp | 614 ++++++++++++++++++ .../mainwindow/rendering/lightpathslayer.h | 158 +++++ .../rendering/lightpathspickinghandler.cpp | 45 +- .../rendering/lightpathspickinghandler.h | 12 +- ...stab.cpp => lightpathsviewportmanager.cpp} | 221 +++---- ...pathstab.h => lightpathsviewportmanager.h} | 45 +- .../mainwindow/rendering/qttilecallback.cpp | 29 +- .../mainwindow/rendering/qttilecallback.h | 6 +- .../mainwindow/rendering/renderingmanager.cpp | 37 +- .../mainwindow/rendering/renderingmanager.h | 9 +- .../{renderwidget.cpp => renderlayer.cpp} | 136 ++-- .../{renderwidget.h => renderlayer.h} | 34 +- ...cpp => viewportregionselectionhandler.cpp} | 12 +- ...ler.h => viewportregionselectionhandler.h} | 6 +- .../{rendertab.cpp => viewporttab.cpp} | 229 +++++-- .../rendering/{rendertab.h => viewporttab.h} | 97 +-- .../mainwindow/rendering/viewportwidget.cpp | 415 ++++++++++++ .../mainwindow/rendering/viewportwidget.h | 165 +++++ src/appleseed.studio/resources/resources.qrc | 3 + .../resources/shaders/final_render.frag | 41 ++ .../resources/shaders/fullscreen_tri.vert | 48 ++ .../resources/shaders/lightpaths.frag | 32 +- .../resources/shaders/lightpaths.vert | 92 ++- .../resources/shaders/oit_resolve.frag | 52 ++ .../resources/shaders/scene.frag | 2 +- .../resources/shaders/scene.vert | 2 +- src/appleseed.studio/utility/gl.cpp | 149 +++++ src/appleseed.studio/utility/gl.h | 75 +++ .../utility/miscellaneous.cpp | 10 - src/appleseed.studio/utility/miscellaneous.h | 3 - .../lighting/directlightingintegrator.cpp | 2 + .../kernel/lighting/lightpathrecorder.cpp | 4 + .../kernel/lighting/lightpathrecorder.h | 1 + .../kernel/lighting/lightpathstream.cpp | 19 +- .../kernel/lighting/lightpathstream.h | 9 +- .../kernel/lighting/pt/ptlightingengine.cpp | 3 +- .../modeling/camera/orthographiccamera.cpp | 22 +- .../modeling/camera/perspectivecamera.cpp | 28 +- 44 files changed, 2637 insertions(+), 1027 deletions(-) rename src/appleseed.studio/mainwindow/rendering/{lightpathswidget.cpp => glscenelayer.cpp} (53%) rename src/appleseed.studio/mainwindow/rendering/{lightpathswidget.h => glscenelayer.h} (70%) create mode 100644 src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp create mode 100644 src/appleseed.studio/mainwindow/rendering/lightpathslayer.h rename src/appleseed.studio/mainwindow/rendering/{lightpathstab.cpp => lightpathsviewportmanager.cpp} (60%) rename src/appleseed.studio/mainwindow/rendering/{lightpathstab.h => lightpathsviewportmanager.h} (73%) rename src/appleseed.studio/mainwindow/rendering/{renderwidget.cpp => renderlayer.cpp} (83%) rename src/appleseed.studio/mainwindow/rendering/{renderwidget.h => renderlayer.h} (85%) rename src/appleseed.studio/mainwindow/rendering/{renderregionhandler.cpp => viewportregionselectionhandler.cpp} (91%) rename src/appleseed.studio/mainwindow/rendering/{renderregionhandler.h => viewportregionselectionhandler.h} (95%) rename src/appleseed.studio/mainwindow/rendering/{rendertab.cpp => viewporttab.cpp} (66%) rename src/appleseed.studio/mainwindow/rendering/{rendertab.h => viewporttab.h} (57%) create mode 100644 src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp create mode 100644 src/appleseed.studio/mainwindow/rendering/viewportwidget.h create mode 100644 src/appleseed.studio/resources/shaders/final_render.frag create mode 100644 src/appleseed.studio/resources/shaders/fullscreen_tri.vert create mode 100644 src/appleseed.studio/resources/shaders/oit_resolve.frag create mode 100644 src/appleseed.studio/utility/gl.cpp create mode 100644 src/appleseed.studio/utility/gl.h diff --git a/src/appleseed.studio/CMakeLists.txt b/src/appleseed.studio/CMakeLists.txt index 827b7ccb4a..93b65560f0 100644 --- a/src/appleseed.studio/CMakeLists.txt +++ b/src/appleseed.studio/CMakeLists.txt @@ -248,12 +248,14 @@ source_group ("mainwindow\\pythonconsole" FILES set (mainwindow_rendering_sources mainwindow/rendering/cameracontroller.cpp mainwindow/rendering/cameracontroller.h + mainwindow/rendering/glscenelayer.h + mainwindow/rendering/glscenelayer.cpp + mainwindow/rendering/lightpathslayer.cpp + mainwindow/rendering/lightpathslayer.h mainwindow/rendering/lightpathspickinghandler.cpp mainwindow/rendering/lightpathspickinghandler.h - mainwindow/rendering/lightpathstab.cpp - mainwindow/rendering/lightpathstab.h - mainwindow/rendering/lightpathswidget.cpp - mainwindow/rendering/lightpathswidget.h + mainwindow/rendering/lightpathsviewportmanager.cpp + mainwindow/rendering/lightpathsviewportmanager.h mainwindow/rendering/materialdrophandler.cpp mainwindow/rendering/materialdrophandler.h mainwindow/rendering/pixelcolortracker.cpp @@ -268,14 +270,16 @@ set (mainwindow_rendering_sources mainwindow/rendering/renderclipboardhandler.h mainwindow/rendering/renderingmanager.cpp mainwindow/rendering/renderingmanager.h - mainwindow/rendering/renderregionhandler.cpp - mainwindow/rendering/renderregionhandler.h - mainwindow/rendering/rendertab.cpp - mainwindow/rendering/rendertab.h - mainwindow/rendering/renderwidget.cpp - mainwindow/rendering/renderwidget.h + mainwindow/rendering/renderlayer.cpp + mainwindow/rendering/renderlayer.h mainwindow/rendering/scenepickinghandler.cpp mainwindow/rendering/scenepickinghandler.h + mainwindow/rendering/viewportregionselectionhandler.cpp + mainwindow/rendering/viewportregionselectionhandler.h + mainwindow/rendering/viewporttab.cpp + mainwindow/rendering/viewporttab.h + mainwindow/rendering/viewportwidget.cpp + mainwindow/rendering/viewportwidget.h ) list (APPEND appleseed.studio_sources ${mainwindow_rendering_sources} @@ -360,6 +364,8 @@ set (utility_sources utility/doubleslider.h utility/foldablepanelwidget.cpp utility/foldablepanelwidget.h + utility/gl.cpp + utility/gl.h utility/inputwidgetproxies.cpp utility/inputwidgetproxies.h utility/interop.h diff --git a/src/appleseed.studio/main/main.cpp b/src/appleseed.studio/main/main.cpp index cc88731f9c..c8e8414375 100644 --- a/src/appleseed.studio/main/main.cpp +++ b/src/appleseed.studio/main/main.cpp @@ -328,7 +328,7 @@ int main(int argc, char* argv[]) // Set default surface format before creating application instance. This is // required on macOS in order to use an OpenGL Core profile context. QSurfaceFormat default_format; - default_format.setVersion(3, 3); + default_format.setVersion(4, 1); default_format.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(default_format); diff --git a/src/appleseed.studio/mainwindow/mainwindow.cpp b/src/appleseed.studio/mainwindow/mainwindow.cpp index 26421b137f..bb4997cb31 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.cpp +++ b/src/appleseed.studio/mainwindow/mainwindow.cpp @@ -40,9 +40,9 @@ #include "mainwindow/project/attributeeditor.h" #include "mainwindow/project/projectexplorer.h" #include "mainwindow/pythonconsole/pythonconsolewidget.h" -#include "mainwindow/rendering/lightpathstab.h" #include "mainwindow/rendering/materialdrophandler.h" -#include "mainwindow/rendering/renderwidget.h" +#include "mainwindow/rendering/renderlayer.h" +#include "mainwindow/rendering/viewportwidget.h" #include "utility/interop.h" #include "utility/miscellaneous.h" #include "utility/settingskeys.h" @@ -201,7 +201,7 @@ bool MainWindow::open_project(const QString& filepath) m_rendering_manager.wait_until_rendering_end(); } - remove_render_tabs(); + remove_viewport_tabs(); set_file_widgets_enabled(false, NotRendering); set_project_explorer_enabled(false); @@ -216,7 +216,7 @@ bool MainWindow::open_project(const QString& filepath) } else { - recreate_render_tabs(); + recreate_viewport_tabs(); update_workspace(); } @@ -233,7 +233,7 @@ void MainWindow::open_project_async(const QString& filepath) m_rendering_manager.wait_until_rendering_end(); } - remove_render_tabs(); + remove_viewport_tabs(); set_file_widgets_enabled(false, NotRendering); set_project_explorer_enabled(false); @@ -741,11 +741,18 @@ void MainWindow::update_workspace() update_pause_resume_checkbox(false); m_ui->attribute_editor_scrollarea_contents->setEnabled(true); - // Add/remove light paths tab. + // Enable/disable light paths if (m_project_manager.is_project_open() && m_project_manager.get_project()->get_light_path_recorder().get_light_path_count() > 0) - add_light_paths_tab(); - else remove_light_paths_tab(); + { + for (const_each i = m_viewport_tabs; i; ++i) + i->second->set_light_paths_toggle_enabled(true); + } + else + { + for (const_each i = m_viewport_tabs; i; ++i) + i->second->set_light_paths_toggle_enabled(false); + } } void MainWindow::update_project_explorer() @@ -886,25 +893,25 @@ void MainWindow::set_rendering_widgets_enabled(const bool is_enabled, const Rend // Rendering -> Render Settings. m_ui->action_rendering_rendering_settings->setEnabled(allow_start); - // Render tab buttons. + // Viewport tab buttons. const int current_tab_index = m_ui->tab_render_channels->currentIndex(); if (current_tab_index != -1) { - const auto render_tab_it = m_tab_index_to_render_tab.find(current_tab_index); - if (render_tab_it != m_tab_index_to_render_tab.end()) + const auto viewport_tab_it = m_tab_index_to_viewport_tab.find(current_tab_index); + if (viewport_tab_it != m_tab_index_to_viewport_tab.end()) { - RenderTab* render_tab = render_tab_it->second; + ViewportTab* viewport_tab = viewport_tab_it->second; // Clear frame. - render_tab->set_clear_frame_button_enabled( + viewport_tab->set_clear_frame_button_enabled( is_enabled && is_project_open && rendering_mode == NotRendering); // Set/clear rendering region. - render_tab->set_render_region_buttons_enabled( + viewport_tab->set_render_region_buttons_enabled( is_enabled && is_project_open && rendering_mode != FinalRendering); // Scene picker. - render_tab->get_scene_picking_handler()->set_enabled( + viewport_tab->get_scene_picking_handler()->set_enabled( is_enabled && is_project_open && rendering_mode != FinalRendering); } } @@ -924,18 +931,18 @@ void MainWindow::save_state_before_project_open() m_state_before_project_open->m_is_rendering = m_rendering_manager.is_rendering(); - for (const_each i = m_render_tabs; i; ++i) - m_state_before_project_open->m_render_tab_states[i->first] = i->second->save_state(); + for (const_each i = m_viewport_tabs; i; ++i) + m_state_before_project_open->m_viewport_tab_states[i->first] = i->second->save_state(); } void MainWindow::restore_state_after_project_open() { if (m_state_before_project_open.get()) { - for (const_each i = m_render_tabs; i; ++i) + for (const_each i = m_viewport_tabs; i; ++i) { - const RenderTabStateCollection& tab_states = m_state_before_project_open->m_render_tab_states; - const RenderTabStateCollection::const_iterator tab_state_it = tab_states.find(i->first); + const ViewportTabStateCollection& tab_states = m_state_before_project_open->m_viewport_tab_states; + const ViewportTabStateCollection::const_iterator tab_state_it = tab_states.find(i->first); if (tab_state_it != tab_states.end()) i->second->load_state(tab_state_it->second); @@ -946,112 +953,78 @@ void MainWindow::restore_state_after_project_open() } } -void MainWindow::recreate_render_tabs() +void MainWindow::recreate_viewport_tabs() { - remove_render_tabs(); + remove_viewport_tabs(); if (m_project_manager.is_project_open()) - add_render_tab("RGB"); + add_viewport_tab("RGB"); } -void MainWindow::remove_render_tabs() +void MainWindow::remove_viewport_tabs() { - for (const_each i = m_render_tabs; i; ++i) + for (const_each i = m_viewport_tabs; i; ++i) delete i->second; - m_render_tabs.clear(); - m_tab_index_to_render_tab.clear(); + m_viewport_tabs.clear(); + m_tab_index_to_viewport_tab.clear(); while (m_ui->tab_render_channels->count() > 0) m_ui->tab_render_channels->removeTab(0); } -void MainWindow::add_render_tab(const QString& label) +void MainWindow::add_viewport_tab(const QString& label) { // Create render tab. - RenderTab* render_tab = - new RenderTab( + ViewportTab* viewport_tab = + new ViewportTab( *m_project_explorer, *m_project_manager.get_project(), m_rendering_manager, - m_ocio_config); + m_ocio_config, + m_application_settings); // Connect the render tab to the main window and the rendering manager. connect( - render_tab, SIGNAL(signal_render_widget_context_menu(const QPoint&)), - SLOT(slot_render_widget_context_menu(const QPoint&))); + viewport_tab, SIGNAL(signal_viewport_widget_context_menu(const QPoint&)), + SLOT(slot_viewport_widget_context_menu(const QPoint&))); connect( - render_tab, SIGNAL(signal_set_render_region(const QRect&)), + viewport_tab, SIGNAL(signal_set_render_region(const QRect&)), SLOT(slot_set_render_region(const QRect&))); connect( - render_tab, SIGNAL(signal_clear_render_region()), + viewport_tab, SIGNAL(signal_clear_render_region()), SLOT(slot_clear_render_region())); connect( - render_tab, SIGNAL(signal_save_frame_and_aovs()), + viewport_tab, SIGNAL(signal_save_frame_and_aovs()), SLOT(slot_save_frame_and_aovs())); connect( - render_tab, SIGNAL(signal_quicksave_frame_and_aovs()), + viewport_tab, SIGNAL(signal_quicksave_frame_and_aovs()), SLOT(slot_quicksave_frame_and_aovs())); connect( - render_tab, SIGNAL(signal_reset_zoom()), + viewport_tab, SIGNAL(signal_reset_zoom()), SLOT(slot_reset_zoom())); connect( - render_tab, SIGNAL(signal_clear_frame()), + viewport_tab, SIGNAL(signal_clear_frame()), SLOT(slot_clear_frame())); connect( - render_tab, SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)), + viewport_tab, SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)), SLOT(slot_clear_filter())); connect( - render_tab, SIGNAL(signal_camera_change_begin()), + viewport_tab, SIGNAL(signal_camera_change_begin()), &m_rendering_manager, SLOT(slot_camera_change_begin())); connect( - render_tab, SIGNAL(signal_camera_change_end()), + viewport_tab, SIGNAL(signal_camera_change_end()), &m_rendering_manager, SLOT(slot_camera_change_end())); connect( - render_tab, SIGNAL(signal_camera_changed()), + viewport_tab, SIGNAL(signal_camera_changed()), &m_rendering_manager, SLOT(slot_camera_changed())); // Add the render tab to the tab bar. - const int tab_index = m_ui->tab_render_channels->addTab(render_tab, label); + const int tab_index = m_ui->tab_render_channels->addTab(viewport_tab, label); // Update mappings. - m_render_tabs[label.toStdString()] = render_tab; - m_tab_index_to_render_tab[tab_index] = render_tab; -} - -void MainWindow::add_light_paths_tab() -{ - if (m_light_paths_tab == nullptr) - { - // Create light paths tab. - m_light_paths_tab = - new LightPathsTab( - *m_project_manager.get_project(), - m_application_settings); - - // Connect render tabs to the light paths tab. - for (const auto& kv : m_render_tabs) - { - connect( - kv.second, SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)), - m_light_paths_tab, SLOT(slot_entity_picked(renderer::ScenePicker::PickingResult))); - connect( - kv.second, SIGNAL(signal_rectangle_selection(const QRect&)), - m_light_paths_tab, SLOT(slot_rectangle_selection(const QRect&))); - } - - // Add the light paths tab to the tab bar. - m_ui->tab_render_channels->addTab(m_light_paths_tab, "Light Paths"); - } -} - -void MainWindow::remove_light_paths_tab() -{ - if (m_light_paths_tab != nullptr) - { - delete m_light_paths_tab; - m_light_paths_tab = nullptr; - } + m_viewport_tabs[label.toStdString()] = viewport_tab; + m_tab_index_to_viewport_tab[tab_index] = viewport_tab; } ParamArray MainWindow::get_project_params(const char* configuration_name) const @@ -1127,7 +1100,7 @@ bool MainWindow::can_close_project() void MainWindow::on_project_change() { update_project_explorer(); - recreate_render_tabs(); + recreate_viewport_tabs(); update_override_shading_menu_item(); m_false_colors_window.reset(); @@ -1230,9 +1203,6 @@ void MainWindow::start_rendering(const RenderingMode rendering_mode) set_diagnostics_widgets_enabled(rendering_mode == InteractiveRendering, rendering_mode); m_ui->attribute_editor_scrollarea_contents->setEnabled(rendering_mode == InteractiveRendering); - // Remove light paths tab. - remove_light_paths_tab(); - // Stop monitoring the project file in Final rendering mode. if (rendering_mode == FinalRendering) { @@ -1246,10 +1216,9 @@ void MainWindow::start_rendering(const RenderingMode rendering_mode) frame->clear_main_and_aov_images(); // Darken render widgets. - for (const_each i = m_render_tabs; i; ++i) + for (const_each i = m_viewport_tabs; i; ++i) { - i->second->darken(); - i->second->update(); + i->second->render_began(); } // Retrieve the appropriate rendering configuration. @@ -1264,7 +1233,7 @@ void MainWindow::start_rendering(const RenderingMode rendering_mode) rendering_mode == InteractiveRendering ? RenderingManager::InteractiveRendering : RenderingManager::FinalRendering, - m_render_tabs["RGB"]); + m_viewport_tabs["RGB"]); } void MainWindow::apply_false_colors_settings() @@ -1302,10 +1271,10 @@ void MainWindow::apply_false_colors_settings() else { // Blit the regular frame into the render widget. - for (const_each i = m_render_tabs; i; ++i) + for (const_each i = m_viewport_tabs; i; ++i) { - i->second->get_render_widget()->blit_frame(*frame); - i->second->get_render_widget()->update(); + i->second->get_viewport_widget()->get_render_layer()->blit_frame(*frame); + i->second->get_viewport_widget()->get_render_layer()->update(); } } } @@ -1325,10 +1294,10 @@ void MainWindow::apply_post_processing_stage( stage.execute(working_frame); // Blit the frame copy into the render widget. - for (const_each i = m_render_tabs; i; ++i) + for (const_each i = m_viewport_tabs; i; ++i) { - i->second->get_render_widget()->blit_frame(working_frame); - i->second->get_render_widget()->update(); + i->second->get_viewport_widget()->get_render_layer()->blit_frame(working_frame); + i->second->get_viewport_widget()->get_render_layer()->update(); } } } @@ -1376,7 +1345,7 @@ void MainWindow::closeEvent(QCloseEvent* event) if (m_benchmark_window.get()) m_benchmark_window->close(); - remove_render_tabs(); + remove_viewport_tabs(); m_project_manager.close_project(); @@ -1496,7 +1465,7 @@ void MainWindow::slot_open_project_complete(const QString& filepath, const bool else { show_project_file_loading_failed_message_box(this, filepath); - recreate_render_tabs(); + recreate_viewport_tabs(); update_workspace(); } } @@ -1932,7 +1901,7 @@ void MainWindow::slot_set_render_region(const QRect& rect) } } -void MainWindow::slot_render_widget_context_menu(const QPoint& point) +void MainWindow::slot_viewport_widget_context_menu(const QPoint& point) { if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier)) return; @@ -2072,7 +2041,7 @@ void MainWindow::slot_save_render_widget_content() return; // todo: this is sketchy. The render tab should be retrieved from the signal. - m_render_tabs["RGB"]->get_render_widget()->capture().save(filepath); + m_viewport_tabs["RGB"]->get_viewport_widget()->get_render_layer()->capture().save(filepath); RENDERER_LOG_INFO("wrote image file %s.", filepath.toStdString().c_str()); } @@ -2083,16 +2052,19 @@ void MainWindow::slot_clear_frame() frame->clear_main_and_aov_images(); // In the UI, clear all render widgets to black. - for (const_each i = m_render_tabs; i; ++i) - i->second->clear(); + for (const_each i = m_viewport_tabs; i; ++i) + { + i->second->get_viewport_widget()->get_render_layer()->clear(); + i->second->get_viewport_widget()->repaint(); + } } void MainWindow::slot_reset_zoom() { const int current_tab_index = m_ui->tab_render_channels->currentIndex(); - const auto render_tab_it = m_tab_index_to_render_tab.find(current_tab_index); - if (render_tab_it != m_tab_index_to_render_tab.end()) - render_tab_it->second->reset_zoom(); + const auto viewport_tab_it = m_tab_index_to_viewport_tab.find(current_tab_index); + if (viewport_tab_it != m_tab_index_to_viewport_tab.end()) + viewport_tab_it->second->reset_zoom(); } void MainWindow::slot_filter_text_changed(const QString& pattern) @@ -2108,7 +2080,7 @@ void MainWindow::slot_clear_filter() void MainWindow::slot_frame_modified() { - for (each i = m_render_tabs; i; ++i) + for (each i = m_viewport_tabs; i; ++i) i->second->update_size(); } diff --git a/src/appleseed.studio/mainwindow/mainwindow.h b/src/appleseed.studio/mainwindow/mainwindow.h index c81dcb6703..19af943b88 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.h +++ b/src/appleseed.studio/mainwindow/mainwindow.h @@ -37,7 +37,7 @@ #include "mainwindow/project/projectmanager.h" #include "mainwindow/qtlogtarget.h" #include "mainwindow/rendering/renderingmanager.h" -#include "mainwindow/rendering/rendertab.h" +#include "mainwindow/rendering/viewporttab.h" #include "mainwindow/renderingsettingswindow.h" #include "mainwindow/statusbar.h" @@ -159,17 +159,17 @@ class MainWindow AttributeEditor* m_attribute_editor; RenderingManager m_rendering_manager; - typedef std::map RenderTabCollection; - typedef std::map RenderTabStateCollection; + typedef std::map ViewportTabCollection; + typedef std::map ViewportTabStateCollection; - RenderTabCollection m_render_tabs; - std::map m_tab_index_to_render_tab; + ViewportTabCollection m_viewport_tabs; + std::map m_tab_index_to_viewport_tab; LightPathsTab* m_light_paths_tab; struct StateBeforeProjectOpen { bool m_is_rendering; - RenderTabStateCollection m_render_tab_states; + ViewportTabStateCollection m_viewport_tab_states; }; std::unique_ptr m_state_before_project_open; @@ -206,11 +206,9 @@ class MainWindow void restore_state_after_project_open(); // Render tabs. - void recreate_render_tabs(); - void remove_render_tabs(); - void add_render_tab(const QString& label); - void add_light_paths_tab(); - void remove_light_paths_tab(); + void recreate_viewport_tabs(); + void remove_viewport_tabs(); + void add_viewport_tab(const QString& label); // Project file handling. renderer::ParamArray get_project_params(const char* configuration_name) const; @@ -287,7 +285,7 @@ class MainWindow void slot_set_render_region(const QRect& rect); // Render widget actions. - void slot_render_widget_context_menu(const QPoint& point); + void slot_viewport_widget_context_menu(const QPoint& point); void slot_save_frame(); void slot_save_frame_and_aovs(); void slot_quicksave_frame_and_aovs(); diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathswidget.cpp b/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp similarity index 53% rename from src/appleseed.studio/mainwindow/rendering/lightpathswidget.cpp rename to src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp index 72a53a700c..a95e6f36d5 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathswidget.cpp +++ b/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp @@ -27,10 +27,10 @@ // // Interface header. -#include "lightpathswidget.h" +#include "glscenelayer.h" // appleseed.studio headers. -#include "utility/miscellaneous.h" +#include "utility/gl.h" // appleseed.renderer headers. #include "renderer/api/camera.h" @@ -51,7 +51,8 @@ // Qt headers. #include -#include +#include +#include #include #include @@ -128,116 +129,56 @@ namespace }; } -LightPathsWidget::LightPathsWidget( - const Project& project, - const size_t width, - const size_t height) +GLSceneLayer::GLSceneLayer( + const Project& project, + const size_t width, + const size_t height) : m_project(project) , m_camera(*m_project.get_uncached_active_camera()) , m_backface_culling_enabled(false) - , m_selected_light_path_index(-1) - , m_gl_initialized(false) + , m_initialized(false) { - setFocusPolicy(Qt::StrongFocus); - setFixedWidth(static_cast(width)); - setFixedHeight(static_cast(height)); - + m_camera.transform_sequence().prepare(); const float time = m_camera.get_shutter_middle_time(); set_transform(m_camera.transform_sequence().evaluate(time)); } -QImage LightPathsWidget::capture() -{ - return grabFramebuffer(); -} - -void LightPathsWidget::set_transform(const Transformd& transform) -{ - m_camera_matrix = transform.get_parent_to_local(); - m_gl_view_matrix = transpose(m_camera_matrix); - m_camera_position = Vector3f(m_camera_matrix.extract_translation()); -} - -void LightPathsWidget::set_light_paths(const LightPathArray& light_paths) -{ - m_light_paths = light_paths; - - if (m_light_paths.size() > 1) - { - // Sort paths by descending radiance at the camera. - const auto& light_path_recorder = m_project.get_light_path_recorder(); - std::sort( - &m_light_paths[0], - &m_light_paths[0] + m_light_paths.size(), - [&light_path_recorder](const LightPath& lhs, const LightPath& rhs) - { - LightPathVertex lhs_v; - light_path_recorder.get_light_path_vertex(lhs.m_vertex_end_index - 1, lhs_v); - - LightPathVertex rhs_v; - light_path_recorder.get_light_path_vertex(rhs.m_vertex_end_index - 1, rhs_v); - - return - sum_value(Color3f::from_array(lhs_v.m_radiance)) > - sum_value(Color3f::from_array(rhs_v.m_radiance)); - }); - } - - // Display all paths by default. - set_selected_light_path_index(-1); - load_light_paths_data(); -} - -void LightPathsWidget::set_selected_light_path_index(const int selected_light_path_index) +GLSceneLayer::~GLSceneLayer() { - m_selected_light_path_index = selected_light_path_index; - - dump_selected_light_path(); - update(); - - emit signal_light_path_selection_changed( - m_selected_light_path_index, - static_cast(m_light_paths.size())); -} - -void LightPathsWidget::slot_display_all_light_paths() -{ - if (m_selected_light_path_index > -1) - set_selected_light_path_index(-1); + cleanup_gl_data(); } -void LightPathsWidget::slot_display_previous_light_path() +void GLSceneLayer::set_gl_functions(QOpenGLFunctions_4_1_Core* functions) { - if (m_selected_light_path_index > -1) - set_selected_light_path_index(m_selected_light_path_index - 1); + m_gl = functions; } -void LightPathsWidget::slot_display_next_light_path() +void GLSceneLayer::set_transform(const Transformd& transform) { - if (m_selected_light_path_index < static_cast(m_light_paths.size()) - 1) - set_selected_light_path_index(m_selected_light_path_index + 1); + m_camera_matrix = transform.get_parent_to_local(); + m_gl_view_matrix = transpose(m_camera_matrix); + m_camera_position = Vector3f(m_camera_matrix.extract_translation()); } -void LightPathsWidget::slot_toggle_backface_culling(const bool checked) +void GLSceneLayer::slot_toggle_backface_culling(const bool checked) { m_backface_culling_enabled = checked; - update(); } -void LightPathsWidget::slot_synchronize_camera() +void GLSceneLayer::slot_synchronize_camera() { m_camera.transform_sequence().clear(); m_camera.transform_sequence().set_transform(0.0f, Transformd::from_local_to_parent(inverse(m_camera_matrix))); } -void LightPathsWidget::load_object_instance( +void GLSceneLayer::load_object_instance( const ObjectInstance& object_instance, const Matrix4f& assembly_transform_matrix) { Object* object = object_instance.find_object(); - // This would already be logged in LightPathsWidget::load_scene_data + // This would already be logged in GLSceneLayer::load_scene_data if (object == nullptr) return; @@ -264,13 +205,13 @@ void LightPathsWidget::load_object_instance( reinterpret_cast(&gl_matrix[0])); } -void LightPathsWidget::load_assembly_instance( +void GLSceneLayer::load_assembly_instance( const AssemblyInstance& assembly_instance, const float time) { const Assembly* assembly = assembly_instance.find_assembly(); - // This would already be logged in LightPathsWidget::load_scene_data + // This would already be logged in GLSceneLayer::load_scene_data if (assembly == nullptr) return; @@ -285,7 +226,7 @@ void LightPathsWidget::load_assembly_instance( load_assembly_instance(child_assembly_instance, time); } -void LightPathsWidget::load_object_data(const Object& object) +void GLSceneLayer::load_object_data(const Object& object) { const std::string obj_name = std::string(object.get_name()); RENDERER_LOG_DEBUG("opengl: uploading mesh data for object \"%s\"...", obj_name.c_str()); @@ -334,7 +275,7 @@ void LightPathsWidget::load_object_data(const Object& object) } } -void LightPathsWidget::load_assembly_data(const Assembly& assembly) +void GLSceneLayer::load_assembly_data(const Assembly& assembly) { for (const auto& object : assembly.objects()) load_object_data(object); @@ -343,7 +284,7 @@ void LightPathsWidget::load_assembly_data(const Assembly& assembly) load_assembly_data(child_assembly); } -void LightPathsWidget::load_scene_data() +void GLSceneLayer::load_scene_data() { const float time = m_camera.get_shutter_middle_time(); @@ -434,190 +375,34 @@ void LightPathsWidget::load_scene_data() load_assembly_instance(assembly_instance, time); } -void LightPathsWidget::load_light_paths_data() -{ - m_light_paths_index_offsets.clear(); - if (!m_light_paths.empty()) - { - m_light_paths_index_offsets.push_back(0); - - const auto& light_path_recorder = m_project.get_light_path_recorder(); - - const size_t total_gl_vertex_count = 2 * (light_path_recorder.get_vertex_count() - 2) + 2; - - GLsizei total_index_count = 0; - std::vector buffer; - buffer.reserve(total_gl_vertex_count * LightPathVertexFloatStride); - for (size_t light_path_idx = 0; light_path_idx < m_light_paths.size(); light_path_idx++) - { - const auto& path = m_light_paths[light_path_idx]; - assert(path.m_vertex_end_index - path.m_vertex_begin_index >= 2); - - LightPathVertex prev; - light_path_recorder.get_light_path_vertex(path.m_vertex_begin_index, prev); - for (size_t vertex_idx = path.m_vertex_begin_index + 1; vertex_idx < path.m_vertex_end_index; vertex_idx++) - { - LightPathVertex curr; - light_path_recorder.get_light_path_vertex(vertex_idx, curr); - - auto piece_radiance = Color3f::from_array(curr.m_radiance); - piece_radiance /= piece_radiance + Color3f(1.0f); // Reinhard tone mapping - piece_radiance = linear_rgb_to_srgb(piece_radiance); - - const float temp_store[LightPathVertexLineFloatStride] = - { - prev.m_position[0], prev.m_position[1], prev.m_position[2], - piece_radiance[0], piece_radiance[1], piece_radiance[2], - - curr.m_position[0], curr.m_position[1], curr.m_position[2], - piece_radiance[0], piece_radiance[1], piece_radiance[2], - }; - buffer.insert(buffer.end(), temp_store, temp_store + 12); - - total_index_count += 2; - prev = curr; - } - m_light_paths_index_offsets.push_back(total_index_count); - } - - m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_light_paths_vbo); - m_gl->glBufferData( - GL_ARRAY_BUFFER, - buffer.size() * sizeof(float), - reinterpret_cast(&buffer[0]), - GL_STATIC_DRAW); - } -} - -namespace -{ - std::string shader_kind_to_string(const GLint shader_kind) - { - switch (shader_kind) - { - case GL_VERTEX_SHADER: - return "Vertex"; - case GL_FRAGMENT_SHADER: - return "Fragment"; - default: - return "Unknown Kind"; - } - } - - void compile_shader( - QOpenGLFunctions_3_3_Core* f, - const GLuint shader, - const GLsizei count, - const GLchar** src_string, - const GLint* length) - { - f->glShaderSource(shader, count, src_string, length); - f->glCompileShader(shader); - GLint success; - f->glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - - if (!success) - { - char info_log[1024]; - f->glGetShaderInfoLog(shader, 1024, NULL, info_log); - - GLint shader_kind; - f->glGetShaderiv(shader, GL_SHADER_TYPE, &shader_kind); - const std::string shader_kind_string = shader_kind_to_string(shader_kind); - RENDERER_LOG_ERROR("opengl: %s shader compilation failed:\n%s", shader_kind_string.c_str(), info_log); - } - } - - void link_shader_program( - QOpenGLFunctions_3_3_Core* f, - const GLuint program, - const GLuint vert, - const GLuint frag) - { - f->glAttachShader(program, vert); - f->glAttachShader(program, frag); - f->glLinkProgram(program); - - GLint success; - f->glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (!success) - { - char info_log[1024]; - f->glGetProgramInfoLog(program, 1024, NULL, info_log); - RENDERER_LOG_ERROR("opengl: shader program linking failed:\n%s", info_log); - } - } - - void create_shader_program( - QOpenGLFunctions_3_3_Core* f, - GLuint& program, - const QByteArray& vert_source, - const QByteArray& frag_source) - { - GLuint vert = f->glCreateShader(GL_VERTEX_SHADER); - GLuint frag = f->glCreateShader(GL_FRAGMENT_SHADER); - - auto gl_vert_source = static_cast(vert_source.constData()); - auto gl_vert_source_length = static_cast(vert_source.size()); - - auto gl_frag_source = static_cast(frag_source.constData()); - auto gl_frag_source_length = static_cast(frag_source.size()); - - compile_shader(f, vert, 1, &gl_vert_source, &gl_vert_source_length); - compile_shader(f, frag, 1, &gl_frag_source, &gl_frag_source_length); - - program = f->glCreateProgram(); - link_shader_program(f, program, vert, frag); - - f->glDeleteShader(vert); - f->glDeleteShader(frag); - } -} - -void LightPathsWidget::initializeGL() +void GLSceneLayer::init_gl(QSurfaceFormat format) { - m_gl = QOpenGLContext::currentContext()->versionFunctions(); // If there was already previous data, clean up - LightPathsWidget::cleanup_gl_data(); - - const auto qgl_format = format(); + GLSceneLayer::cleanup_gl_data(); - if (!m_gl->initializeOpenGLFunctions()) - { - const int major_version = qgl_format.majorVersion(); - const int minor_version = qgl_format.minorVersion(); - RENDERER_LOG_ERROR( - "opengl: could not load required gl functions: loaded version %d.%d, required version 3.3.", - major_version, - minor_version); - m_gl_initialized = false; - return; - } - - glEnable(GL_DEPTH_TEST); - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + auto vertex_shader = load_gl_shader("scene.vert"); + auto fragment_shader = load_gl_shader("scene.frag"); create_shader_program( m_gl, m_scene_shader_program, - load_gl_shader("scene.vert"), - load_gl_shader("scene.frag")); - - create_shader_program( - m_gl, - m_light_paths_shader_program, - load_gl_shader("lightpaths.vert"), - load_gl_shader("lightpaths.frag")); + &vertex_shader, + &fragment_shader); m_scene_view_mat_location = m_gl->glGetUniformLocation(m_scene_shader_program, "u_view"); m_scene_proj_mat_location = m_gl->glGetUniformLocation(m_scene_shader_program, "u_proj"); m_scene_camera_pos_location = m_gl->glGetUniformLocation(m_scene_shader_program, "u_camera_pos"); - m_light_paths_view_mat_location = m_gl->glGetUniformLocation(m_light_paths_shader_program, "u_view"); - m_light_paths_proj_mat_location = m_gl->glGetUniformLocation(m_light_paths_shader_program, "u_proj"); + create_shader_program( + m_gl, + m_depthonly_shader_program, + &vertex_shader, + nullptr); + + m_depthonly_view_mat_location = m_gl->glGetUniformLocation(m_depthonly_shader_program, "u_view"); + m_depthonly_proj_mat_location = m_gl->glGetUniformLocation(m_depthonly_shader_program, "u_proj"); + m_depthonly_camera_pos_location = m_gl->glGetUniformLocation(m_depthonly_shader_program, "u_camera_pos"); const float z_near = 0.01f; const float z_far = 1000.0f; @@ -639,41 +424,12 @@ void LightPathsWidget::initializeGL() // from the FBO to the default framebuffer, which flips the image. m_gl_proj_matrix = transpose(Matrix4f::make_frustum(top, bottom, left, right, z_near, z_far)); - GLuint temp_light_paths_vao = 0; - GLuint temp_light_paths_vbo = 0; - m_gl->glGenVertexArrays(1, &temp_light_paths_vao); - m_gl->glGenBuffers(1, &temp_light_paths_vbo); - m_light_paths_vao = temp_light_paths_vao; - m_light_paths_vbo = temp_light_paths_vbo; - - m_gl->glBindVertexArray(m_light_paths_vao); - m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_light_paths_vbo); - m_gl->glVertexAttribPointer( - 0, - 3, - GL_FLOAT, - GL_FALSE, - LightPathVertexByteStride, - reinterpret_cast(0)); - m_gl->glVertexAttribPointer( - 1, - 3, - GL_FLOAT, - GL_FALSE, - LightPathVertexByteStride, - reinterpret_cast(LightPathVertexByteStride / 2)); - m_gl->glEnableVertexAttribArray(0); - m_gl->glEnableVertexAttribArray(1); - - m_gl->glBindVertexArray(0); - load_scene_data(); - load_light_paths_data(); - m_gl_initialized = true; + m_initialized = true; } -void LightPathsWidget::cleanup_gl_data() +void GLSceneLayer::cleanup_gl_data() { if (!m_scene_object_vaos.empty()) { @@ -700,9 +456,9 @@ void LightPathsWidget::cleanup_gl_data() { m_gl->glDeleteProgram(m_scene_shader_program); } - if (m_light_paths_shader_program != 0) + if (m_depthonly_shader_program != 0) { - m_gl->glDeleteProgram(m_light_paths_shader_program); + m_gl->glDeleteProgram(m_depthonly_shader_program); } m_scene_object_index_map.clear(); m_scene_object_data_index_counts.clear(); @@ -710,54 +466,20 @@ void LightPathsWidget::cleanup_gl_data() m_scene_object_current_instances.clear(); } -void LightPathsWidget::resizeGL(int w, int h) -{ - glViewport(0, 0, static_cast(w), static_cast(h)); -} - -void LightPathsWidget::paintGL() +void GLSceneLayer::draw() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - if (!m_gl_initialized) + if (!m_initialized) return; if (m_backface_culling_enabled) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - render_geometry(); - render_light_paths(); -} - -void LightPathsWidget::keyPressEvent(QKeyEvent* event) -{ - switch (event->key()) - { - // Home key: display all paths. - case Qt::Key_Home: - slot_display_all_light_paths(); - break; - - // Left key: display previous path. - case Qt::Key_Left: - slot_display_previous_light_path(); - break; - - // Right key: display next path. - case Qt::Key_Right: - slot_display_next_light_path(); - break; - - default: - QOpenGLWidget::keyPressEvent(event); - break; - } -} - -void LightPathsWidget::render_geometry() -{ + m_gl->glDepthMask(GL_FALSE); + m_gl->glEnable(GL_DEPTH_TEST); + m_gl->glDepthFunc(GL_LEQUAL); m_gl->glUseProgram(m_scene_shader_program); + m_gl->glUniformMatrix4fv( m_scene_view_mat_location, 1, @@ -772,7 +494,49 @@ void LightPathsWidget::render_geometry() m_scene_camera_pos_location, 1, const_cast(&m_camera_position[0])); + + render_scene(); + + m_gl->glDepthMask(GL_TRUE); +} + +void GLSceneLayer::draw_depth_only() +{ + if (!m_initialized) + return; + + if (m_backface_culling_enabled) + glEnable(GL_CULL_FACE); + else glDisable(GL_CULL_FACE); + + m_gl->glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + m_gl->glDepthMask(GL_TRUE); + m_gl->glEnable(GL_DEPTH_TEST); + m_gl->glDepthFunc(GL_LEQUAL); + m_gl->glUseProgram(m_depthonly_shader_program); + + m_gl->glUniformMatrix4fv( + m_depthonly_view_mat_location, + 1, + false, + const_cast(&m_gl_view_matrix[0])); + m_gl->glUniformMatrix4fv( + m_depthonly_proj_mat_location, + 1, + false, + const_cast(&m_gl_proj_matrix[0])); + m_gl->glUniform3fv( + m_depthonly_camera_pos_location, + 1, + const_cast(&m_camera_position[0])); + + render_scene(); + m_gl->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +void GLSceneLayer::render_scene() +{ for (size_t i = 0; i < m_scene_object_data_vbos.size(); i++) { GLuint vao = m_scene_object_vaos[i]; @@ -780,7 +544,6 @@ void LightPathsWidget::render_geometry() int instance_count = m_scene_object_instance_counts[i]; m_gl->glBindVertexArray(vao); - m_gl->glDrawArraysInstanced( GL_TRIANGLES, 0, @@ -789,81 +552,5 @@ void LightPathsWidget::render_geometry() } } -void LightPathsWidget::render_light_paths() -{ - if (m_light_paths_index_offsets.size() > 1) - { - m_gl->glUseProgram(m_light_paths_shader_program); - m_gl->glUniformMatrix4fv( - m_light_paths_view_mat_location, - 1, - false, - const_cast(&m_gl_view_matrix[0])); - m_gl->glUniformMatrix4fv( - m_light_paths_proj_mat_location, - 1, - false, - const_cast(&m_gl_proj_matrix[0])); - - m_gl->glBindVertexArray(m_light_paths_vao); - - GLint first; - GLsizei count; - assert(m_selected_light_path_index >= -1); - if (m_selected_light_path_index == -1) - { - first = 0; - count = m_light_paths_index_offsets[m_light_paths_index_offsets.size() - 1]; - } - else - { - first = static_cast(m_light_paths_index_offsets[m_selected_light_path_index]); - count = m_light_paths_index_offsets[m_selected_light_path_index + 1] - first; - } - glDrawArrays(GL_LINES, first, count); - } -} - -void LightPathsWidget::dump_selected_light_path() const -{ - if (m_selected_light_path_index == -1) - { - if (m_light_paths.empty()) - RENDERER_LOG_INFO("no light path to display."); - else - { - RENDERER_LOG_INFO("displaying all %s light path%s.", - pretty_uint(m_light_paths.size()).c_str(), - m_light_paths.size() > 1 ? "s" : ""); - } - } - else - { - RENDERER_LOG_INFO("displaying light path %s:", - pretty_int(m_selected_light_path_index + 1).c_str()); - - const auto& light_path_recorder = m_project.get_light_path_recorder(); - const auto& path = m_light_paths[m_selected_light_path_index]; - - for (size_t i = path.m_vertex_begin_index; i < path.m_vertex_end_index; ++i) - { - LightPathVertex v; - light_path_recorder.get_light_path_vertex(i, v); - - const std::string entity_name = - v.m_entity != nullptr - ? foundation::format("\"{0}\"", v.m_entity->get_path().c_str()) - : "n/a"; - - RENDERER_LOG_INFO(" vertex " FMT_SIZE_T ": entity: %s - position: (%f, %f, %f) - radiance: (%f, %f, %f) - total radiance: %f", - i - path.m_vertex_begin_index + 1, - entity_name.c_str(), - v.m_position[0], v.m_position[1], v.m_position[2], - v.m_radiance[0], v.m_radiance[1], v.m_radiance[2], - v.m_radiance[0] + v.m_radiance[1] + v.m_radiance[2]); - } - } -} - } // namespace studio } // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathswidget.h b/src/appleseed.studio/mainwindow/rendering/glscenelayer.h similarity index 70% rename from src/appleseed.studio/mainwindow/rendering/lightpathswidget.h rename to src/appleseed.studio/mainwindow/rendering/glscenelayer.h index 7614f75263..372208a261 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathswidget.h +++ b/src/appleseed.studio/mainwindow/rendering/glscenelayer.h @@ -5,7 +5,7 @@ // // This software is released under the MIT license. // -// Copyright (c) 2018 Francois Beaune, The appleseedhq Organization +// Copyright (c) 2018 Gray Olson, The appleseedhq Organization // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -58,47 +58,42 @@ namespace renderer { class ObjectInstance; } namespace renderer { class Project; } class QKeyEvent; class QImage; -class QOpenGLFunctions_3_3_Core; +class QOpenGLFunctions_4_1_Core; +class QSurfaceFormat; namespace appleseed { namespace studio { // -// A widget providing an hardware-accelerated visualization of recorded light paths. +// A widget providing an hardware-accelerated visualization of the loaded scene. // -class LightPathsWidget - : public QOpenGLWidget - , public ICapturableWidget +class GLSceneLayer + : public QObject { Q_OBJECT public: - LightPathsWidget( + GLSceneLayer( const renderer::Project& project, const size_t width, const size_t height); - QImage capture() override; + ~GLSceneLayer(); + + void init_gl( + QSurfaceFormat format); void set_transform( const foundation::Transformd& transform); - void set_light_paths( - const renderer::LightPathArray& light_paths); - - void set_selected_light_path_index( - const int selected_light_path_index); + void set_gl_functions( + QOpenGLFunctions_4_1_Core* functions); - signals: - void signal_light_path_selection_changed( - const int selected_light_path_index, - const int total_light_paths) const; + void draw(); + void draw_depth_only(); public slots: - void slot_display_all_light_paths(); - void slot_display_previous_light_path(); - void slot_display_next_light_path(); void slot_toggle_backface_culling(const bool checked); void slot_synchronize_camera(); @@ -110,10 +105,7 @@ class LightPathsWidget bool m_backface_culling_enabled; - renderer::LightPathArray m_light_paths; - int m_selected_light_path_index; // -1 == display all paths - - QOpenGLFunctions_3_3_Core* m_gl; + QOpenGLFunctions_4_1_Core* m_gl; std::vector m_scene_object_data_vbos; std::vector m_scene_object_data_index_counts; @@ -126,24 +118,17 @@ class LightPathsWidget GLint m_scene_view_mat_location; GLint m_scene_proj_mat_location; GLint m_scene_camera_pos_location; - GLuint m_light_paths_vbo; - std::vector m_light_paths_index_offsets; - GLuint m_light_paths_vao; - GLuint m_light_paths_shader_program; - GLint m_light_paths_view_mat_location; - GLint m_light_paths_proj_mat_location; + GLuint m_depthonly_shader_program; + GLint m_depthonly_view_mat_location; + GLint m_depthonly_proj_mat_location; + GLint m_depthonly_camera_pos_location; foundation::Matrix4f m_gl_view_matrix; foundation::Matrix4f m_gl_proj_matrix; - bool m_gl_initialized; - - void initializeGL() override; - void resizeGL(int w, int h) override; - void paintGL() override; - void keyPressEvent(QKeyEvent* event) override; + bool m_initialized; + void render_scene(); void cleanup_gl_data(); void load_scene_data(); - void load_light_paths_data(); void load_assembly_data( const renderer::Assembly& object); @@ -158,11 +143,6 @@ class LightPathsWidget void load_object_instance( const renderer::ObjectInstance& object_instance, const foundation::Matrix4f& assembly_transform_matrix); - - void render_geometry(); - void render_light_paths(); - - void dump_selected_light_path() const; }; } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp new file mode 100644 index 0000000000..7a27c8e90a --- /dev/null +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp @@ -0,0 +1,614 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Francois Beaune, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "lightpathslayer.h" + +// appleseed.studio headers. +#include "utility/gl.h" + +// appleseed.renderer headers. +#include "renderer/api/camera.h" +#include "renderer/api/entity.h" +#include "renderer/api/object.h" +#include "renderer/api/project.h" +#include "renderer/api/rasterization.h" +#include "renderer/api/scene.h" +#include "renderer/api/utility.h" + +// appleseed.foundation headers. +#include "foundation/image/color.h" +#include "foundation/image/colorspace.h" +#include "foundation/math/scalar.h" +#include "foundation/utility/api/apistring.h" +#include "foundation/utility/string.h" + +// Qt headers. +#include +#include +#include +#include + +// Standard headers. +#include +#include +#include + +using namespace foundation; +using namespace renderer; +using namespace std; + +namespace appleseed { +namespace studio { + +namespace { + // Byte stride of a vec3. + const size_t Vec3ByteStride = sizeof(float) * 3; + + // Struct of an element of the "others" vertex buffer + struct OtherAttributes { + GLfloat v_luminance; + GLint v_distance_start_end; + GLfloat v_color[3]; + GLfloat v_surface_normal[3]; + }; +} + +LightPathsLayer::LightPathsLayer( + const Project& project, + const size_t width, + const size_t height) + : m_project(project) + , m_camera(*m_project.get_uncached_active_camera()) + , m_selected_light_path_index(-1) + , m_gl_initialized(false) + , m_width(width) + , m_height(height) + , m_max_thickness(4.0f) + , m_min_thickness(1.0f) + , m_max_luminance(0.0f) +{ + + const float time = m_camera.get_shutter_middle_time(); + set_transform(m_camera.transform_sequence().evaluate(time)); +} + +void LightPathsLayer::resize(const size_t width, const size_t height) +{ + m_width = width, + m_height = height; +} + +void LightPathsLayer::set_gl_functions(QOpenGLFunctions_4_1_Core* functions) +{ + m_gl = functions; +} + +void LightPathsLayer::update_render_camera_transform() +{ + const float time = m_camera.get_shutter_middle_time(); + m_render_camera_matrix = m_camera.transform_sequence().evaluate(time).get_parent_to_local(); + m_gl_render_view_matrix = transpose(m_render_camera_matrix); +} + +void LightPathsLayer::set_transform(const Transformd& transform) +{ + m_camera_matrix = transform.get_parent_to_local(); + m_gl_view_matrix = transpose(m_camera_matrix); +} + +void LightPathsLayer::set_light_paths(const LightPathArray& light_paths) +{ + m_light_paths = light_paths; + + if (m_light_paths.size() > 1) + { + // Sort paths by descending radiance at the camera. + const auto& light_path_recorder = m_project.get_light_path_recorder(); + sort( + &m_light_paths[0], + &m_light_paths[0] + m_light_paths.size(), + [&light_path_recorder](const LightPath& lhs, const LightPath& rhs) + { + LightPathVertex lhs_v; + light_path_recorder.get_light_path_vertex(lhs.m_vertex_end_index - 1, lhs_v); + + LightPathVertex rhs_v; + light_path_recorder.get_light_path_vertex(rhs.m_vertex_end_index - 1, rhs_v); + + return + sum_value(Color3f::from_array(lhs_v.m_radiance)) > + sum_value(Color3f::from_array(rhs_v.m_radiance)); + }); + } + + // Display all paths by default. + set_selected_light_path_index(-1); + load_light_paths_data(); +} + +void LightPathsLayer::set_selected_light_path_index(const int selected_light_path_index) +{ + m_selected_light_path_index = selected_light_path_index; + + dump_selected_light_path(); + + emit signal_light_path_selection_changed( + m_selected_light_path_index, + static_cast(m_light_paths.size())); +} + +void LightPathsLayer::slot_display_all_light_paths() +{ + if (m_selected_light_path_index > -1) + set_selected_light_path_index(-1); +} + +void LightPathsLayer::slot_display_previous_light_path() +{ + if (m_selected_light_path_index > -1) + set_selected_light_path_index(m_selected_light_path_index - 1); +} + +void LightPathsLayer::slot_display_next_light_path() +{ + if (m_selected_light_path_index < static_cast(m_light_paths.size()) - 1) + set_selected_light_path_index(m_selected_light_path_index + 1); +} + +void LightPathsLayer::slot_synchronize_camera() +{ + m_camera.transform_sequence().clear(); + m_camera.transform_sequence().set_transform(0.0f, + Transformd::from_local_to_parent(inverse(m_camera_matrix))); +} + +void LightPathsLayer::load_light_paths_data() +{ + m_path_terminator_vertex_indices.clear(); + m_max_luminance = 0.0f; + if (!m_light_paths.empty()) + { + m_path_terminator_vertex_indices.push_back(0); + + const auto& light_path_recorder = m_project.get_light_path_recorder(); + + m_total_triangle_count = 0; + + // Vertex count * 4 since we will be adding two vertices for each point along the line and will + // be adding each point twice for the beginning and end parts of each segment + // Add two because we need extra at the front and back for the extra 'previous' and 'next' attributes + const size_t total_gl_vertex_count = 2 * (light_path_recorder.get_vertex_count() + 2); + + vector indices; + vector positions_buffer; + // * 3 since we want 3 floats per position + positions_buffer.reserve(total_gl_vertex_count * 3); + indices.reserve(total_gl_vertex_count); + + // Add an empty vertex at the beginning to serve as first 'previous' + array empty_positions = + { + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + }; + positions_buffer.insert( + positions_buffer.end(), + empty_positions.begin(), + empty_positions.end()); + + vector others_buffer; + others_buffer.reserve(total_gl_vertex_count); + array others; + + for (size_t light_path_idx = 0; light_path_idx < m_light_paths.size(); light_path_idx++) + { + const auto& path = m_light_paths[light_path_idx]; + assert(path.m_vertex_end_index - path.m_vertex_begin_index >= 2); + + LightPathVertex prev; + light_path_recorder.get_light_path_vertex(path.m_vertex_begin_index, prev); + for (size_t vertex_idx = path.m_vertex_begin_index + 1; vertex_idx < path.m_vertex_end_index; vertex_idx++) + { + LightPathVertex vert; + light_path_recorder.get_light_path_vertex(vertex_idx, vert); + + auto piece_radiance = Color3f::from_array(vert.m_radiance); + piece_radiance = linear_rgb_to_srgb(piece_radiance); + auto piece_luminance = luminance(piece_radiance); + m_max_luminance = max(m_max_luminance, piece_luminance); + piece_radiance /= piece_luminance; + //piece_radiance /= piece_radiance + Color3f(1.0); + + array positions_temp_store = { + prev.m_position[0], prev.m_position[1], prev.m_position[2], + prev.m_position[0], prev.m_position[1], prev.m_position[2], + vert.m_position[0], vert.m_position[1], vert.m_position[2], + vert.m_position[0], vert.m_position[1], vert.m_position[2], + }; + positions_buffer.insert( + positions_buffer.end(), + positions_temp_store.begin(), + positions_temp_store.end()); + + unsigned int start_index = static_cast(others_buffer.size()); + + GLint start = 2; + GLint end = 4; + + others = {{ + { + piece_luminance, + start, + {piece_radiance[0], piece_radiance[1], piece_radiance[2]}, + {prev.m_surface_normal[0], prev.m_surface_normal[1], prev.m_surface_normal[2]} + }, + { + piece_luminance, + 1 | start, + {piece_radiance[0], piece_radiance[1], piece_radiance[2]}, + {prev.m_surface_normal[0], prev.m_surface_normal[1], prev.m_surface_normal[2]} + }, + { + piece_luminance, + end, + {piece_radiance[0], piece_radiance[1], piece_radiance[2]}, + {vert.m_surface_normal[0], vert.m_surface_normal[1], vert.m_surface_normal[2]} + }, + { + piece_luminance, + 1 | end, + {piece_radiance[0], piece_radiance[1], piece_radiance[2]}, + {vert.m_surface_normal[0], vert.m_surface_normal[1], vert.m_surface_normal[2]} + }, + }}; + others_buffer.insert( + others_buffer.end(), + others.begin(), + others.end()); + + array indices_scratch = { + start_index, start_index + 1, start_index + 2, + start_index + 1, start_index + 2, start_index + 3, + }; + indices.insert( + indices.end(), + indices_scratch.begin(), + indices_scratch.end()); + + m_total_triangle_count += 6; + prev = vert; + } + m_path_terminator_vertex_indices.push_back(static_cast(others_buffer.size())); + } + + // Add an empty vertex at the end to serve as last 'next' + positions_buffer.insert( + positions_buffer.end(), + empty_positions.begin(), + empty_positions.end()); + + // Upload the data to the buffers + m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_positions_vbo); + m_gl->glBufferData( + GL_ARRAY_BUFFER, + positions_buffer.size() * sizeof(float), + reinterpret_cast(&positions_buffer[0]), + GL_STATIC_DRAW); + m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_others_vbo); + m_gl->glBufferData( + GL_ARRAY_BUFFER, + others_buffer.size() * sizeof(OtherAttributes), + reinterpret_cast(&others_buffer[0]), + GL_STATIC_DRAW); + m_gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indices_ebo); + m_gl->glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + indices.size() * sizeof(unsigned int), + reinterpret_cast(&indices[0]), + GL_STATIC_DRAW); + } +} + +void LightPathsLayer::init_gl(QSurfaceFormat format) +{ + // If there was already previous data, clean up + LightPathsLayer::cleanup_gl_data(); + + glEnable(GL_DEPTH_TEST); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + auto vertex_shader = load_gl_shader("lightpaths.vert"); + auto fragment_shader = load_gl_shader("lightpaths.frag"); + + create_shader_program( + m_gl, + m_shader_program, + &vertex_shader, + &fragment_shader); + + m_view_mat_loc = m_gl->glGetUniformLocation(m_shader_program, "u_view"); + m_proj_mat_loc = m_gl->glGetUniformLocation(m_shader_program, "u_proj"); + m_res_loc = m_gl->glGetUniformLocation(m_shader_program, "u_res"); + m_max_luminance_loc = m_gl->glGetUniformLocation(m_shader_program, "u_max_luminance"); + m_max_thickness_loc = m_gl->glGetUniformLocation(m_shader_program, "u_max_thickness"); + m_min_thickness_loc = m_gl->glGetUniformLocation(m_shader_program, "u_min_thickness"); + m_first_selected_loc = m_gl->glGetUniformLocation(m_shader_program, "u_first_selected"); + m_last_selected_loc = m_gl->glGetUniformLocation(m_shader_program, "u_last_selected"); + + const float z_near = 0.01f; + const float z_far = 1000.0f; + + const auto& rc = m_camera.get_rasterization_camera(); + + const float fy = tan(rc.m_hfov / rc.m_aspect_ratio * 0.5) * z_near; + const float fx = fy * rc.m_aspect_ratio; + + const float shift_x = rc.m_shift_x * 2.0 * fx; + const float shift_y = rc.m_shift_y * 2.0 * fy; + + const float left = -fx + shift_x; + const float right = fx + shift_x; + const float top = -fy + shift_y; + const float bottom = fy + shift_y; + + // Top and bottom are flipped because QOpenGLWidget draws to a framebuffer object and then blits + // from the FBO to the default framebuffer, which flips the image. + m_gl_proj_matrix = transpose(Matrix4f::make_frustum(top, bottom, left, right, z_near, z_far)); + + GLuint temp_light_paths_vao = 0; + m_gl->glGenVertexArrays(1, &temp_light_paths_vao); + m_light_paths_vao = temp_light_paths_vao; + + GLuint temp_vbos[3] = {0, 0, 0}; + m_gl->glGenBuffers(3, &temp_vbos[0]); + m_positions_vbo = temp_vbos[0]; + m_others_vbo = temp_vbos[1]; + m_indices_ebo = temp_vbos[2]; + + m_gl->glBindVertexArray(m_light_paths_vao); + + m_gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indices_ebo); + + // v_previous, v_position, and v_next all reference offsets into the same vertex data buffer. + // v_previous is at vertex offset 0, v_position is at vertex offset 2 and v_next is at vertex offset 4. + // See http://codeflow.org/entries/2012/aug/05/webgl-rendering-of-solid-trails/ for reference. + m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_positions_vbo); + + // vec3 v_previous; + m_gl->glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + Vec3ByteStride, + reinterpret_cast(0)); + m_gl->glEnableVertexAttribArray(0); + + // vec3 v_position; + m_gl->glVertexAttribPointer( + 1, + 3, + GL_FLOAT, + GL_FALSE, + Vec3ByteStride, + reinterpret_cast(Vec3ByteStride * 2)); + m_gl->glEnableVertexAttribArray(1); + + // vec3 v_next; + m_gl->glVertexAttribPointer( + 2, + 3, + GL_FLOAT, + GL_FALSE, + Vec3ByteStride, + reinterpret_cast(Vec3ByteStride * 4)); + m_gl->glEnableVertexAttribArray(2); + + // The rest of the attributes are stored in a separate data buffer, interleaved. + m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_others_vbo); + + // float v_thickness; + m_gl->glVertexAttribPointer( + 3, + 1, + GL_FLOAT, + GL_FALSE, + sizeof(OtherAttributes), + reinterpret_cast(0)); + m_gl->glEnableVertexAttribArray(3); + + // This attribute stores 3 bools: + // bit 0: normal direction which maps to -1.0 (false) or 1.0 (true) + // bit 1: whether this is the start vertex of a new path + // bit 2: whether this is the end vertex of a path + // int v_direction_start_end; + m_gl->glVertexAttribIPointer( + 4, + 1, + GL_INT, + sizeof(OtherAttributes), + reinterpret_cast(sizeof(GLfloat))); + m_gl->glEnableVertexAttribArray(4); + + // vec3 v_color; + m_gl->glVertexAttribPointer( + 5, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(OtherAttributes), + reinterpret_cast(sizeof(GLfloat) + sizeof(GLint))); + m_gl->glEnableVertexAttribArray(5); + + // vec3 v_surface_normal; + m_gl->glVertexAttribPointer( + 6, + 3, + GL_FLOAT, + GL_FALSE, + sizeof(OtherAttributes), + reinterpret_cast(sizeof(GLfloat) + sizeof(GLint) + 3 * sizeof(GLfloat))); + m_gl->glEnableVertexAttribArray(6); + + m_gl->glBindVertexArray(0); + m_gl->glBindBuffer(GL_ARRAY_BUFFER, 0); + + load_light_paths_data(); + + m_gl_initialized = true; +} + +void LightPathsLayer::cleanup_gl_data() +{ + if (m_shader_program != 0) + { + m_gl->glDeleteProgram(m_shader_program); + } +} + +void LightPathsLayer::draw_render_camera() const +{ + auto gl_view_matrix = const_cast(&m_gl_render_view_matrix[0]); + + render_scene(gl_view_matrix); +} + +void LightPathsLayer::draw() const +{ + auto gl_view_matrix = const_cast(&m_gl_view_matrix[0]); + + render_scene(gl_view_matrix); +} + +void LightPathsLayer::render_scene(const GLfloat* gl_view_matrix) const +{ + if (!m_gl_initialized) + return; + + if (m_total_triangle_count > 1) + { + GLint first_selected; + GLint last_selected; + assert(m_selected_light_path_index >= -1); + if (m_selected_light_path_index == -1) + { + first_selected = 0; + last_selected = static_cast(m_path_terminator_vertex_indices[m_path_terminator_vertex_indices.size() - 1]); + } + else + { + first_selected = static_cast(m_path_terminator_vertex_indices[m_selected_light_path_index + 1]); + last_selected = static_cast(m_path_terminator_vertex_indices[m_selected_light_path_index + 2]); + } + + m_gl->glUseProgram(m_shader_program); + + m_gl->glUniformMatrix4fv( + m_view_mat_loc, + 1, + false, + gl_view_matrix); + m_gl->glUniformMatrix4fv( + m_proj_mat_loc, + 1, + false, + const_cast(&m_gl_proj_matrix[0])); + m_gl->glUniform1f( + m_max_luminance_loc, + m_max_luminance); + m_gl->glUniform1f( + m_max_thickness_loc, + m_max_thickness); + m_gl->glUniform1f( + m_min_thickness_loc, + m_min_thickness); + m_gl->glUniform2f( + m_res_loc, + (GLfloat)m_width, + (GLfloat)m_height); + m_gl->glUniform1i( + m_first_selected_loc, + first_selected); + m_gl->glUniform1i( + m_last_selected_loc, + last_selected); + + m_gl->glBindVertexArray(m_light_paths_vao); + + + m_gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indices_ebo); + m_gl->glDrawElements(GL_TRIANGLES, m_total_triangle_count, GL_UNSIGNED_INT, static_cast(0)); + m_gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +} + +void LightPathsLayer::dump_selected_light_path() const +{ + if (m_selected_light_path_index == -1) + { + if (m_light_paths.empty()) + RENDERER_LOG_INFO("no light path to display."); + else + { + RENDERER_LOG_INFO("displaying all %s light path%s.", + pretty_uint(m_light_paths.size()).c_str(), + m_light_paths.size() > 1 ? "s" : ""); + } + } + else + { + RENDERER_LOG_INFO("displaying light path %s:", + pretty_int(m_selected_light_path_index + 1).c_str()); + + const auto& light_path_recorder = m_project.get_light_path_recorder(); + const auto& path = m_light_paths[m_selected_light_path_index]; + + for (size_t i = path.m_vertex_begin_index; i < path.m_vertex_end_index; ++i) + { + LightPathVertex v; + light_path_recorder.get_light_path_vertex(i, v); + + const string entity_name = + v.m_entity != nullptr + ? foundation::format("\"{0}\"", v.m_entity->get_path().c_str()) + : "n/a"; + + RENDERER_LOG_INFO(" vertex " FMT_SIZE_T ": entity: %s - position: (%f, %f, %f) - radiance: (%f, %f, %f) - total radiance: %f", + i - path.m_vertex_begin_index + 1, + entity_name.c_str(), + v.m_position[0], v.m_position[1], v.m_position[2], + v.m_radiance[0], v.m_radiance[1], v.m_radiance[2], + v.m_radiance[0] + v.m_radiance[1] + v.m_radiance[2]); + } + } +} + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h new file mode 100644 index 0000000000..41246d5691 --- /dev/null +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h @@ -0,0 +1,158 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Francois Beaune, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// appleseed.studio headers. +#include "mainwindow/rendering/renderclipboardhandler.h" + +// appleseed.renderer headers. +#include "renderer/api/lighting.h" + +// appleseed.foundation headers. +#include "foundation/math/matrix.h" +#include "foundation/math/transform.h" +#include "foundation/math/vector.h" + +// Qt headers. +#include +#include + +// Standard headers. +#include +#include +#include +#include + +// Forward declarations. +namespace renderer { class Assembly; } +namespace renderer { class AssemblyInstance; } +namespace renderer { class Camera; } +namespace renderer { class Object; } +namespace renderer { class ObjectInstance; } +namespace renderer { class Project; } +class QKeyEvent; +class QImage; +class QOpenGLFunctions_4_1_Core; + +namespace appleseed { +namespace studio { + +// +// A widget providing an hardware-accelerated visualization of recorded light paths. +// + +class LightPathsLayer: public QObject +{ + Q_OBJECT + + public: + LightPathsLayer( + const renderer::Project& project, + const size_t width, + const size_t height); + + void resize(const size_t width, const size_t height); + + void update_render_camera_transform(); + + void set_transform( + const foundation::Transformd& transform); + + void set_light_paths( + const renderer::LightPathArray& light_paths); + + void set_selected_light_path_index( + const int selected_light_path_index); + + void set_gl_functions( + QOpenGLFunctions_4_1_Core* functions); + + void init_gl(QSurfaceFormat format); + + void draw() const; + + void draw_render_camera() const; + + signals: + void signal_light_path_selection_changed( + const int selected_light_path_index, + const int total_light_paths) const; + + public slots: + void slot_display_all_light_paths(); + void slot_display_previous_light_path(); + void slot_display_next_light_path(); + void slot_synchronize_camera(); + + private: + const renderer::Project& m_project; + renderer::Camera& m_camera; + foundation::Matrix4d m_camera_matrix; + foundation::Matrix4d m_render_camera_matrix; + + renderer::LightPathArray m_light_paths; + int m_selected_light_path_index; // -1 == display all paths + float m_max_luminance; + float m_max_thickness; + float m_min_thickness; + size_t m_width; + size_t m_height; + + QOpenGLFunctions_4_1_Core* m_gl; + + GLuint m_positions_vbo; + GLuint m_others_vbo; + GLuint m_indices_ebo; + std::vector m_path_terminator_vertex_indices; + GLsizei m_total_triangle_count; + GLuint m_light_paths_vao; + GLuint m_shader_program; + GLint m_view_mat_loc; + GLint m_proj_mat_loc; + GLint m_res_loc; + GLint m_max_luminance_loc; + GLint m_min_thickness_loc; + GLint m_max_thickness_loc; + GLint m_first_selected_loc; + GLint m_last_selected_loc; + foundation::Matrix4f m_gl_render_view_matrix; + foundation::Matrix4f m_gl_view_matrix; + foundation::Matrix4f m_gl_proj_matrix; + bool m_gl_initialized; + + void cleanup_gl_data(); + void load_light_paths_data(); + + void render_scene(const GLfloat* gl_view_matrix) const; + + void dump_selected_light_path() const; +}; + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp index 4071653db7..3f8e1cf8bd 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp @@ -30,7 +30,8 @@ #include "lightpathspickinghandler.h" // appleseed.studio headers. -#include "mainwindow/rendering/lightpathswidget.h" +#include "mainwindow/rendering/lightpathslayer.h" +#include "mainwindow/rendering/viewportwidget.h" #include "utility/mousecoordinatestracker.h" // appleseed.renderer headers. @@ -57,20 +58,13 @@ namespace appleseed { namespace studio { LightPathsPickingHandler::LightPathsPickingHandler( - LightPathsWidget* light_paths_widget, + ViewportWidget* viewport_widget, const MouseCoordinatesTracker& mouse_tracker, const Project& project) - : m_light_paths_widget(light_paths_widget) - , m_mouse_tracker(mouse_tracker) - , m_project(project) + : m_project(project) , m_enabled(true) + , m_viewport_widget(viewport_widget) { - m_light_paths_widget->installEventFilter(this); -} - -LightPathsPickingHandler::~LightPathsPickingHandler() -{ - m_light_paths_widget->removeEventFilter(this); } void LightPathsPickingHandler::set_enabled(const bool enabled) @@ -108,8 +102,8 @@ void LightPathsPickingHandler::pick(const Vector2i& pixel) const pixel.y); } - m_light_paths_widget->set_light_paths(light_paths); - m_light_paths_widget->update(); + m_viewport_widget->get_light_paths_layer()->set_light_paths(light_paths); + m_viewport_widget->update(); } } @@ -156,31 +150,10 @@ void LightPathsPickingHandler::pick(const AABB2i& rect) const final_rect.max.y); } - m_light_paths_widget->set_light_paths(light_paths); - m_light_paths_widget->update(); + m_viewport_widget->get_light_paths_layer()->set_light_paths(light_paths); + m_viewport_widget->update(); } } -bool LightPathsPickingHandler::eventFilter(QObject* object, QEvent* event) -{ - if (m_enabled) - { - if (event->type() == QEvent::MouseButtonPress) - { - const QMouseEvent* mouse_event = static_cast(event); - if (!(mouse_event->modifiers() & (Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier))) - { - if (mouse_event->button() == Qt::LeftButton) - { - pick(m_mouse_tracker.widget_to_pixel(mouse_event->pos())); - return true; - } - } - } - } - - return QObject::eventFilter(object, event); -} - } // namespace studio } // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h index 5858fe6e4d..736c7eac29 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h @@ -36,8 +36,9 @@ #include // Forward declarations. -namespace appleseed { namespace studio { class LightPathsWidget; } } +namespace appleseed { namespace studio { class LightPathsLayer; } } namespace appleseed { namespace studio { class MouseCoordinatesTracker; } } +namespace appleseed { namespace studio { class ViewportWidget; } } namespace renderer { class Project; } class QEvent; @@ -51,24 +52,19 @@ class LightPathsPickingHandler public: LightPathsPickingHandler( - LightPathsWidget* light_paths_widget, + ViewportWidget* viewport_widget, const MouseCoordinatesTracker& mouse_tracker, const renderer::Project& project); - ~LightPathsPickingHandler() override; - void set_enabled(const bool enabled); void pick(const foundation::Vector2i& pixel) const; void pick(const foundation::AABB2i& rect) const; private: - LightPathsWidget* m_light_paths_widget; - const MouseCoordinatesTracker& m_mouse_tracker; + ViewportWidget* m_viewport_widget; const renderer::Project& m_project; bool m_enabled; - - bool eventFilter(QObject* object, QEvent* event) override; }; } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathstab.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp similarity index 60% rename from src/appleseed.studio/mainwindow/rendering/lightpathstab.cpp rename to src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp index 125d50a05c..76fbc596a2 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathstab.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp @@ -27,11 +27,12 @@ // // Interface header. -#include "lightpathstab.h" +#include "lightpathsviewportmanager.h" // appleseed.studio headers. #include "mainwindow/rendering/lightpathspickinghandler.h" -#include "mainwindow/rendering/lightpathswidget.h" +#include "mainwindow/rendering/lightpathslayer.h" +#include "mainwindow/rendering/viewporttab.h" #include "utility/miscellaneous.h" #include "utility/settingskeys.h" @@ -70,84 +71,133 @@ namespace appleseed { namespace studio { // -// LightPathsTab class implementation. +// LightPathsViewportManager class implementation. // -LightPathsTab::LightPathsTab(Project& project, ParamArray& settings) - : m_project(project) +LightPathsViewportManager::LightPathsViewportManager( + ViewportTab* viewport_tab, + Project* project, + ParamArray& settings) + : m_enabled(false) + , m_picking_enabled(true) + , m_paths_display_active(false) + , m_project(project) , m_settings(settings) + , m_viewport_tab(viewport_tab) { - setObjectName("render_widget_tab"); - setLayout(new QGridLayout()); - layout()->setSpacing(0); - layout()->setMargin(0); + LightPathsLayer* light_paths_layer = m_viewport_tab->get_viewport_widget()->get_light_paths_layer(); + connect( + light_paths_layer, SIGNAL(signal_light_path_selection_changed(const int, const int)), + SLOT(slot_light_path_selection_changed(const int, const int))); - create_light_paths_widget(); create_toolbar(); - create_scrollarea(); - - layout()->addWidget(m_toolbar); - layout()->addWidget(m_scroll_area); recreate_handlers(); } -void LightPathsTab::slot_entity_picked(const ScenePicker::PickingResult& result) +void LightPathsViewportManager::reset(renderer::Project* project) +{ + set_enabled(false); + set_display_enabled(false); + set_picking_enabled(true); + m_project = project; +} + +void LightPathsViewportManager::slot_base_layer_changed(const ViewportWidget::BaseLayer layer) +{ + if (layer == ViewportWidget::BaseLayer::FinalRender) + set_picking_enabled(true); + else + set_picking_enabled(false); +} + +void LightPathsViewportManager::set_enabled(const bool enabled) { - const CanvasProperties& props = m_project.get_frame()->image().properties(); + m_enabled = enabled; + if (enabled) + { + m_toolbar->show(); + } + else + { + set_display_enabled(false); + m_toolbar->hide(); + } + m_toolbar->setDisabled(!enabled); + + emit signal_should_display(m_enabled && m_paths_display_active); +} + +void LightPathsViewportManager::set_display_enabled(const bool enabled) +{ + m_paths_display_active = enabled; + + emit signal_should_display(m_enabled && m_paths_display_active); +} + +void LightPathsViewportManager::set_picking_enabled(const bool enabled) +{ + m_picking_enabled = enabled; + m_screen_space_paths_picking_handler->set_enabled(enabled); +} + +QToolBar* LightPathsViewportManager::toolbar() const +{ + return m_toolbar; +} +void LightPathsViewportManager::slot_entity_picked(const ScenePicker::PickingResult& result) +{ + if (!m_picking_enabled || !m_enabled) return; + + set_display_enabled(true); + + const CanvasProperties& props = m_project->get_frame()->image().properties(); m_screen_space_paths_picking_handler->pick( Vector2i( result.m_ndc[0] * static_cast(props.m_canvas_width), result.m_ndc[1] * static_cast(props.m_canvas_height))); } -void LightPathsTab::slot_rectangle_selection(const QRect& rect) +void LightPathsViewportManager::slot_light_paths_display_toggled(const bool active) { + set_enabled(active); +} + +void LightPathsViewportManager::slot_rectangle_selection(const QRect& rect) +{ + if (!m_picking_enabled || !m_enabled) return; + + set_display_enabled(true); m_screen_space_paths_picking_handler->pick( AABB2i( Vector2i(rect.x(), rect.y()), Vector2i(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1))); } -void LightPathsTab::slot_light_path_selection_changed( +void LightPathsViewportManager::slot_light_path_selection_changed( const int selected_light_path_index, - const int total_light_paths) const + const int total_light_paths) { if (total_light_paths > 0) { + set_display_enabled(true); m_prev_path_button->setEnabled(selected_light_path_index > -1); m_next_path_button->setEnabled(selected_light_path_index < total_light_paths - 1); } else { + set_display_enabled(false); m_prev_path_button->setEnabled(false); m_next_path_button->setEnabled(false); } } -void LightPathsTab::slot_context_menu(const QPoint& point) -{ - if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier)) - return; - - QMenu* menu = new QMenu(this); - - const auto light_path_count = m_project.get_light_path_recorder().get_light_path_count(); - menu->addAction( - QString("Save %1 Light Path%2...") - .arg(QString::fromStdString(pretty_uint(light_path_count))) - .arg(light_path_count > 1 ? "s" : ""), - this, SLOT(slot_save_light_paths())); - - menu->exec(m_light_paths_widget->mapToGlobal(point)); -} - -void LightPathsTab::slot_save_light_paths() +void LightPathsViewportManager::slot_save_light_paths() { QString filepath = get_save_filename( - this, + m_viewport_tab, "Save Light Paths As...", "Light Paths Files (*.aspaths);;All Files (*.*)", m_settings, @@ -162,37 +212,18 @@ void LightPathsTab::slot_save_light_paths() filepath = QDir::toNativeSeparators(filepath); // Write light paths to disk. - m_project.get_light_path_recorder().write(filepath.toUtf8().constData()); + m_project->get_light_path_recorder().write(filepath.toUtf8().constData()); } -void LightPathsTab::slot_camera_changed() +void LightPathsViewportManager::slot_camera_changed() { - m_light_paths_widget->set_transform(m_camera_controller->get_transform()); - m_light_paths_widget->update(); + if (!m_enabled) return; } -void LightPathsTab::create_light_paths_widget() +void LightPathsViewportManager::create_toolbar() { - // Create the OpenGL widget. - const CanvasProperties& props = m_project.get_frame()->image().properties(); - m_light_paths_widget = - new LightPathsWidget( - m_project, - props.m_canvas_width, - props.m_canvas_height); - - // Enable context menu on the OpenGL widget. - m_light_paths_widget->setContextMenuPolicy(Qt::CustomContextMenu); - connect( - m_light_paths_widget, SIGNAL(signal_light_path_selection_changed(const int, const int)), - SLOT(slot_light_path_selection_changed(const int, const int))); - connect( - m_light_paths_widget, SIGNAL(customContextMenuRequested(const QPoint&)), - SLOT(slot_context_menu(const QPoint&))); -} + LightPathsLayer* light_paths_layer = m_viewport_tab->get_viewport_widget()->get_light_paths_layer(); -void LightPathsTab::create_toolbar() -{ // Create the render toolbar. m_toolbar = new QToolBar(); m_toolbar->setObjectName("render_toolbar"); @@ -201,7 +232,7 @@ void LightPathsTab::create_toolbar() // Save Light Paths button. QToolButton* save_light_paths_button = new QToolButton(); save_light_paths_button->setIcon(load_icons("lightpathstab_save_light_paths")); - const auto light_path_count = m_project.get_light_path_recorder().get_light_path_count(); + const auto light_path_count = m_project->get_light_path_recorder().get_light_path_count(); save_light_paths_button->setToolTip( QString("Save %1 Light Path%2...") .arg(QString::fromStdString(pretty_uint(light_path_count))) @@ -220,7 +251,7 @@ void LightPathsTab::create_toolbar() m_prev_path_button->setEnabled(false); connect( m_prev_path_button, SIGNAL(clicked()), - m_light_paths_widget, SLOT(slot_display_previous_light_path())); + light_paths_layer, SLOT(slot_display_previous_light_path())); m_toolbar->addWidget(m_prev_path_button); // Next Light Path button. @@ -230,7 +261,7 @@ void LightPathsTab::create_toolbar() m_next_path_button->setEnabled(false); connect( m_next_path_button, SIGNAL(clicked()), - m_light_paths_widget, SLOT(slot_display_next_light_path())); + light_paths_layer, SLOT(slot_display_next_light_path())); m_toolbar->addWidget(m_next_path_button); m_toolbar->addSeparator(); @@ -243,7 +274,7 @@ void LightPathsTab::create_toolbar() backface_culling_button->setChecked(false); connect( backface_culling_button, SIGNAL(toggled(bool)), - m_light_paths_widget, SLOT(slot_toggle_backface_culling(const bool))); + light_paths_layer, SLOT(slot_toggle_backface_culling(bool))); m_toolbar->addWidget(backface_culling_button); // Synchronize Camera button. @@ -252,7 +283,7 @@ void LightPathsTab::create_toolbar() sync_camera_button->setToolTip("Synchronize the rendering camera with this camera"); connect( sync_camera_button, SIGNAL(clicked()), - m_light_paths_widget, SLOT(slot_synchronize_camera())); + light_paths_layer, SLOT(slot_synchronize_camera())); m_toolbar->addWidget(sync_camera_button); // Add stretchy spacer. @@ -265,50 +296,19 @@ void LightPathsTab::create_toolbar() m_info_label = new QLabel(); m_info_label->setObjectName("info_label"); m_toolbar->addWidget(m_info_label); -} -void LightPathsTab::create_scrollarea() -{ - // Encapsulate the OpenGL widget into another widget that adds a margin around it. - QWidget* gl_widget_wrapper = new QWidget(); - gl_widget_wrapper->setObjectName("render_widget_wrapper"); - gl_widget_wrapper->setLayout(new QGridLayout()); - gl_widget_wrapper->layout()->setSizeConstraint(QLayout::SetFixedSize); - gl_widget_wrapper->layout()->setContentsMargins(20, 20, 20, 20); - gl_widget_wrapper->layout()->addWidget(m_light_paths_widget); - - // Wrap the OpenGL widget in a scroll area. - m_scroll_area = new QScrollArea(); - m_scroll_area->setObjectName(QString::fromUtf8("render_widget_scrollarea")); - m_scroll_area->setAlignment(Qt::AlignCenter); - m_scroll_area->setWidget(gl_widget_wrapper); + m_toolbar->setDisabled(true); + m_toolbar->hide(); } -void LightPathsTab::recreate_handlers() +void LightPathsViewportManager::recreate_handlers() { - // Handler for zooming the render widget in and out with the keyboard or the mouse wheel. - m_zoom_handler.reset( - new WidgetZoomHandler( - m_scroll_area, - m_light_paths_widget)); - - // Handler for panning the render widget with the mouse. - m_pan_handler.reset( - new ScrollAreaPanHandler( - m_scroll_area)); - - // Handler for tracking and displaying mouse coordinates. - m_mouse_tracker.reset( - new MouseCoordinatesTracker( - m_light_paths_widget, - m_info_label)); - // The screen-space paths picking handler is used to pick paths from the render widget. m_screen_space_paths_picking_handler.reset( new LightPathsPickingHandler( - m_light_paths_widget, + m_viewport_tab->get_viewport_widget(), *m_mouse_tracker.get(), - m_project)); + *m_project)); m_screen_space_paths_picking_handler->set_enabled(false); // The world-space paths picking handler is used to pick paths in the light paths widget. @@ -320,21 +320,10 @@ void LightPathsTab::recreate_handlers() // m_project)); // Camera handler. - m_light_paths_widget->setMouseTracking(true); - m_camera_controller.reset( - new CameraController( - m_light_paths_widget, - m_project, - m_project.get_uncached_active_camera())); - connect( - m_camera_controller.get(), SIGNAL(signal_camera_changed()), - SLOT(slot_camera_changed())); - - // Clipboard handler. - m_clipboard_handler.reset(new RenderClipboardHandler(m_light_paths_widget, m_light_paths_widget)); + m_viewport_tab->get_viewport_widget()->setMouseTracking(true); } } // namespace studio } // namespace appleseed -#include "mainwindow/rendering/moc_cpp_lightpathstab.cxx" +//#include "mainwindow/rendering/moc_cpp_lightpathsviewportmanager.cxx" diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathstab.h b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h similarity index 73% rename from src/appleseed.studio/mainwindow/rendering/lightpathstab.h rename to src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h index 223c755325..5894a83c97 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathstab.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h @@ -32,6 +32,7 @@ #include "mainwindow/rendering/cameracontroller.h" #include "mainwindow/rendering/lightpathspickinghandler.h" #include "mainwindow/rendering/renderclipboardhandler.h" +#include "mainwindow/rendering/viewportwidget.h" #include "utility/mousecoordinatestracker.h" #include "utility/scrollareapanhandler.h" #include "utility/widgetzoomhandler.h" @@ -47,7 +48,7 @@ #include // Forward declarations. -namespace appleseed { namespace studio { class LightPathsWidget; } } +namespace appleseed { namespace studio { class ViewportTab; } } namespace renderer { class ParamArray; } namespace renderer { class Project; } class QLabel; @@ -61,54 +62,64 @@ namespace appleseed { namespace studio { // -// A tab providing an hardware-accelerated visualization of recorded light paths. +// Manager for the light paths display overlay into the viewport // -class LightPathsTab - : public QWidget +class LightPathsViewportManager + : public QObject { Q_OBJECT public: - LightPathsTab( - renderer::Project& project, + LightPathsViewportManager( + ViewportTab* viewport_tab, + renderer::Project* project, renderer::ParamArray& settings); + void reset(renderer::Project* project); + + QToolBar* toolbar() const; + + void set_enabled(const bool enabled); + void set_picking_enabled(const bool enabled); + void set_display_enabled(const bool enabled); + + signals: + void signal_should_display(const bool should_display); + public slots: void slot_entity_picked(const renderer::ScenePicker::PickingResult& result); void slot_rectangle_selection(const QRect& rect); + void slot_light_paths_display_toggled(const bool active); private slots: + void slot_base_layer_changed(const ViewportWidget::BaseLayer layer); void slot_light_path_selection_changed( const int selected_light_path_index, - const int total_light_paths) const; - void slot_context_menu(const QPoint& point); + const int total_light_paths); void slot_save_light_paths(); void slot_camera_changed(); private: - renderer::Project& m_project; + bool m_enabled; + bool m_picking_enabled; + bool m_paths_display_active; + + renderer::Project* m_project; renderer::ParamArray& m_settings; - LightPathsWidget* m_light_paths_widget; - QScrollArea* m_scroll_area; + ViewportTab* m_viewport_tab; QToolBar* m_toolbar; QToolButton* m_prev_path_button; QToolButton* m_next_path_button; QLabel* m_info_label; - std::unique_ptr m_zoom_handler; std::unique_ptr m_pan_handler; std::unique_ptr m_mouse_tracker; - std::unique_ptr m_camera_controller; std::unique_ptr m_screen_space_paths_picking_handler; std::unique_ptr m_world_space_paths_picking_handler; - std::unique_ptr m_clipboard_handler; - void create_light_paths_widget(); void create_toolbar(); - void create_scrollarea(); void recreate_handlers(); - }; } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/qttilecallback.cpp b/src/appleseed.studio/mainwindow/rendering/qttilecallback.cpp index 7984d7e39e..bd8520a40e 100644 --- a/src/appleseed.studio/mainwindow/rendering/qttilecallback.cpp +++ b/src/appleseed.studio/mainwindow/rendering/qttilecallback.cpp @@ -31,7 +31,8 @@ #include "qttilecallback.h" // appleseed.studio headers. -#include "mainwindow/rendering/renderwidget.h" +#include "mainwindow/rendering/renderlayer.h" +#include "mainwindow/rendering/viewportwidget.h" // Qt headers. #include @@ -59,12 +60,12 @@ namespace Q_OBJECT public: - explicit QtTileCallback(RenderWidget* render_widget) - : m_render_widget(render_widget) + explicit QtTileCallback(ViewportWidget* viewport_widget) + : m_render_layer(viewport_widget->get_render_layer()) { connect( this, SIGNAL(signal_update()), - m_render_widget, SLOT(update()), + viewport_widget, SLOT(repaint()), Qt::QueuedConnection); } @@ -78,8 +79,8 @@ namespace const size_t tile_x, const size_t tile_y) override { - assert(m_render_widget); - m_render_widget->highlight_tile(*frame, tile_x, tile_y); + assert(m_render_layer); + m_render_layer->highlight_tile(*frame, tile_x, tile_y); emit signal_update(); } @@ -88,16 +89,16 @@ namespace const size_t tile_x, const size_t tile_y) override { - assert(m_render_widget); - m_render_widget->blit_tile(*frame, tile_x, tile_y); + assert(m_render_layer); + m_render_layer->blit_tile(*frame, tile_x, tile_y); emit signal_update(); } void on_progressive_frame_update( const Frame* frame) override { - assert(m_render_widget); - m_render_widget->blit_frame(*frame); + assert(m_render_layer); + m_render_layer->blit_frame(*frame); emit signal_update(); } @@ -105,7 +106,7 @@ namespace void signal_update(); private: - RenderWidget* m_render_widget; + RenderLayer* m_render_layer; }; } @@ -114,8 +115,8 @@ namespace // QtTileCallbackFactory class implementation. // -QtTileCallbackFactory::QtTileCallbackFactory(RenderWidget* render_widget) - : m_render_widget(render_widget) +QtTileCallbackFactory::QtTileCallbackFactory(ViewportWidget* viewport_widget) + : m_viewport_widget(viewport_widget) { } @@ -126,7 +127,7 @@ void QtTileCallbackFactory::release() ITileCallback* QtTileCallbackFactory::create() { - return new QtTileCallback(m_render_widget); + return new QtTileCallback(m_viewport_widget); } } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/qttilecallback.h b/src/appleseed.studio/mainwindow/rendering/qttilecallback.h index 1366a341f5..91c7e3bc3b 100644 --- a/src/appleseed.studio/mainwindow/rendering/qttilecallback.h +++ b/src/appleseed.studio/mainwindow/rendering/qttilecallback.h @@ -36,7 +36,7 @@ #include "foundation/platform/compiler.h" // Forward declarations. -namespace appleseed { namespace studio { class RenderWidget; } } +namespace appleseed { namespace studio { class ViewportWidget; } } namespace appleseed { namespace studio { @@ -46,7 +46,7 @@ class QtTileCallbackFactory { public: // Constructor. - explicit QtTileCallbackFactory(RenderWidget* render_widget); + explicit QtTileCallbackFactory(ViewportWidget* viewport_widget); // Delete this instance. void release() override; @@ -55,7 +55,7 @@ class QtTileCallbackFactory renderer::ITileCallback* create() override; private: - RenderWidget* m_render_widget; + ViewportWidget* m_viewport_widget; }; } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/renderingmanager.cpp b/src/appleseed.studio/mainwindow/rendering/renderingmanager.cpp index ad56767ca0..2ed6679014 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderingmanager.cpp +++ b/src/appleseed.studio/mainwindow/rendering/renderingmanager.cpp @@ -33,8 +33,8 @@ // appleseed.studio headers. #include "mainwindow/rendering/cameracontroller.h" #include "mainwindow/rendering/qttilecallback.h" -#include "mainwindow/rendering/rendertab.h" -#include "mainwindow/rendering/renderwidget.h" +#include "mainwindow/rendering/viewporttab.h" +#include "mainwindow/rendering/viewportwidget.h" #include "mainwindow/statusbar.h" // appleseed.shared headers. @@ -122,7 +122,7 @@ namespace RenderingManager::RenderingManager(StatusBar& status_bar) : m_status_bar(status_bar) , m_project(nullptr) - , m_render_tab(nullptr) + , m_viewport_tab(nullptr) { Application::initialize_resource_search_paths(m_resource_search_paths); @@ -189,25 +189,30 @@ RenderingManager::~RenderingManager() clear_sticky_actions(); } +RenderingManager::RenderingMode RenderingManager::get_rendering_mode() const +{ + return m_rendering_mode; +} + void RenderingManager::start_rendering( Project* project, const ParamArray& params, const RenderingMode rendering_mode, - RenderTab* render_tab) + ViewportTab* viewport_tab) { m_project = project; m_params = params; m_rendering_mode = rendering_mode; - m_render_tab = render_tab; + m_viewport_tab = viewport_tab; - m_render_tab->get_render_widget()->start_render(); + m_viewport_tab->get_viewport_widget()->get_render_layer()->start_render(); TileCallbackCollectionFactory* tile_callback_collection_factory = new TileCallbackCollectionFactory(); tile_callback_collection_factory->insert( new QtTileCallbackFactory( - m_render_tab->get_render_widget())); + m_viewport_tab->get_viewport_widget())); tile_callback_collection_factory->insert( new ProgressTileCallbackFactory( @@ -434,7 +439,7 @@ void RenderingManager::slot_rendering_begin() run_scheduled_actions(); if (m_rendering_mode == InteractiveRendering) - m_render_tab->get_camera_controller()->set_enabled(true); + m_viewport_tab->get_camera_controller()->set_enabled(true); m_has_camera_changed = false; @@ -461,10 +466,10 @@ void RenderingManager::slot_rendering_end() { // Disable camera interaction. if (m_rendering_mode == InteractiveRendering) - m_render_tab->get_camera_controller()->set_enabled(false); + m_viewport_tab->get_camera_controller()->set_enabled(false); - // Save the controller target point into the camera. - m_render_tab->get_camera_controller()->save_camera_target(); + // Save the controller target point into the camera when rendering ends. + m_viewport_tab->get_camera_controller()->save_camera_target(); // Stop printing rendering time in the status bar. m_status_bar.stop_rendering_time_display(); @@ -484,10 +489,10 @@ void RenderingManager::slot_rendering_failed() { // Disable camera interaction. if (m_rendering_mode == InteractiveRendering) - m_render_tab->get_camera_controller()->set_enabled(false); + m_viewport_tab->get_camera_controller()->set_enabled(false); - // Save the controller target point into the camera. - m_render_tab->get_camera_controller()->save_camera_target(); + // Save the controller target point into the camera when rendering ends. + m_viewport_tab->get_camera_controller()->save_camera_target(); // Stop printing rendering time in the status bar. m_status_bar.stop_rendering_time_display(); @@ -498,7 +503,7 @@ void RenderingManager::slot_frame_begin() // Update the scene's camera before rendering the frame. if (m_has_camera_changed) { - m_render_tab->get_camera_controller()->update_camera_transform(); + m_viewport_tab->get_camera_controller()->update_camera_transform(); m_has_camera_changed = false; } } @@ -506,7 +511,7 @@ void RenderingManager::slot_frame_begin() void RenderingManager::slot_frame_end() { // Ensure that the render widget is up-to-date. - m_render_tab->get_render_widget()->update(); + m_viewport_tab->get_viewport_widget()->update(); } void RenderingManager::slot_camera_change_begin() diff --git a/src/appleseed.studio/mainwindow/rendering/renderingmanager.h b/src/appleseed.studio/mainwindow/rendering/renderingmanager.h index 9318be3cf7..71f150cab4 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderingmanager.h +++ b/src/appleseed.studio/mainwindow/rendering/renderingmanager.h @@ -55,7 +55,7 @@ #include // Forward declarations. -namespace appleseed { namespace studio { class RenderTab; } } +namespace appleseed { namespace studio { class ViewportTab; } } namespace appleseed { namespace studio { class StatusBar; } } namespace foundation { class IAbortSwitch; } namespace renderer { class Frame; } @@ -87,7 +87,10 @@ class RenderingManager renderer::Project* project, const renderer::ParamArray& params, const RenderingMode rendering_mode, - RenderTab* render_tab); + ViewportTab* viewport_tab); + + // Get current rendering mode + RenderingMode get_rendering_mode() const; // Return true if currently rendering, false otherwise. bool is_rendering() const; @@ -165,7 +168,7 @@ class RenderingManager renderer::ParamArray m_params; foundation::SearchPaths m_resource_search_paths; RenderingMode m_rendering_mode; - RenderTab* m_render_tab; + ViewportTab* m_viewport_tab; std::unique_ptr m_tile_callback_factory; diff --git a/src/appleseed.studio/mainwindow/rendering/renderwidget.cpp b/src/appleseed.studio/mainwindow/rendering/renderlayer.cpp similarity index 83% rename from src/appleseed.studio/mainwindow/rendering/renderwidget.cpp rename to src/appleseed.studio/mainwindow/rendering/renderlayer.cpp index 6d2bbccc96..5df5e6ae1b 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderwidget.cpp +++ b/src/appleseed.studio/mainwindow/rendering/renderlayer.cpp @@ -28,7 +28,7 @@ // // Interface header. -#include "renderwidget.h" +#include "renderlayer.h" // appleseed.renderer headers. #include "renderer/api/frame.h" @@ -39,6 +39,8 @@ #include "foundation/image/nativedrawing.h" #include "foundation/image/tile.h" #include "foundation/math/scalar.h" +#include "foundation/platform/types.h" +#include "utility/gl.h" // Qt headers. #include @@ -47,6 +49,9 @@ #include #include #include +#include +#include +#include #include // Standard headers. @@ -61,10 +66,10 @@ namespace appleseed { namespace studio { // -// RenderWidget class implementation. +// RenderLayer class implementation. // -RenderWidget::RenderWidget( +RenderLayer::RenderLayer( const size_t width, const size_t height, OCIO::ConstConfigRcPtr ocio_config, @@ -72,11 +77,14 @@ RenderWidget::RenderWidget( : QWidget(parent) , m_mutex(QMutex::Recursive) , m_ocio_config(ocio_config) + , m_gl_initialized(false) { setFocusPolicy(Qt::StrongFocus); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent, true); + m_gl_tex = new QOpenGLTexture(QOpenGLTexture::Target2D); + resize(width, height); const char* display_name = m_ocio_config->getDefaultDisplay(); @@ -86,14 +94,77 @@ RenderWidget::RenderWidget( setAcceptDrops(true); } -QImage RenderWidget::capture() +void RenderLayer::draw(GLuint empty_vao, bool paths_display_active) +{ + QMutexLocker locker(&m_mutex); + + if (m_gl_tex->width() != m_image.width() || m_gl_tex->height() != m_image.height()) + { + if (m_gl_tex->isCreated()) + m_gl_tex->destroy(); + + m_gl_tex->create(); + m_gl_tex->setSize(m_image.width(), m_image.height()); + m_gl_tex->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Nearest); + } + + m_gl_tex->setData(m_image, QOpenGLTexture::MipMapGeneration::DontGenerateMipMaps); + + m_gl->glUseProgram(m_shader_program); + + GLfloat mult = paths_display_active ? 0.6 : 1.0; + m_gl->glUniform1f(m_mult_loc, mult); + + m_gl->glActiveTexture(GL_TEXTURE0); + m_gl_tex->bind(); + m_gl->glDisable(GL_DEPTH_TEST); + m_gl->glDepthMask(GL_FALSE); + m_gl->glBindVertexArray(empty_vao); + m_gl->glDrawArrays(GL_TRIANGLES, 0, 3); + m_gl->glDepthMask(GL_TRUE); +} + + +void RenderLayer::init_gl(QSurfaceFormat format) +{ + if (!m_gl) + { + RENDERER_LOG_ERROR("Attempted to initialize GL without first setting GL functions"); + return; + } + + auto vertex_shader = load_gl_shader("fullscreen_tri.vert"); + auto fragment_shader = load_gl_shader("final_render.frag"); + + create_shader_program( + m_gl, + m_shader_program, + &vertex_shader, + &fragment_shader); + + m_mult_loc = m_gl->glGetUniformLocation(m_shader_program, "u_mult"); + + m_gl_initialized = true; +} + +void RenderLayer::set_gl_functions(QOpenGLFunctions_4_1_Core* functions) +{ + m_gl = functions; +} + +QImage RenderLayer::capture() { QMutexLocker locker(&m_mutex); return m_image.copy(); } -void RenderWidget::resize( +void RenderLayer::darken() +{ + multiply(0.2f); +} + +void RenderLayer::resize( const size_t width, const size_t height) { @@ -111,7 +182,7 @@ void RenderWidget::resize( clear(); } -void RenderWidget::clear() +void RenderLayer::clear() { QMutexLocker locker(&m_mutex); @@ -136,14 +207,14 @@ namespace } } -void RenderWidget::start_render() +void RenderLayer::start_render() { // Clear the image storage. if (m_image_storage) m_image_storage->clear(Color4f(0.0f)); } -void RenderWidget::multiply(const float multiplier) +void RenderLayer::multiply(const float multiplier) { QMutexLocker locker(&m_mutex); @@ -199,7 +270,7 @@ namespace } } -void RenderWidget::highlight_tile( +void RenderLayer::highlight_tile( const Frame& frame, const size_t tile_x, const size_t tile_y) @@ -242,7 +313,7 @@ void RenderWidget::highlight_tile( sizeof(BracketColor)); } -void RenderWidget::blit_tile( +void RenderLayer::blit_tile( const Frame& frame, const size_t tile_x, const size_t tile_y) @@ -255,7 +326,7 @@ void RenderWidget::blit_tile( update_tile_no_lock(tile_x, tile_y); } -void RenderWidget::blit_frame(const Frame& frame) +void RenderLayer::blit_frame(const Frame& frame) { QMutexLocker locker(&m_mutex); @@ -273,7 +344,7 @@ void RenderWidget::blit_frame(const Frame& frame) } } -void RenderWidget::slot_display_transform_changed(const QString& transform) +void RenderLayer::slot_display_transform_changed(const QString& transform) { { QMutexLocker locker(&m_mutex); @@ -320,7 +391,7 @@ namespace } } -void RenderWidget::allocate_working_storage(const CanvasProperties& frame_props) +void RenderLayer::allocate_working_storage(const CanvasProperties& frame_props) { if (!m_image_storage || !is_compatible(*m_image_storage, frame_props)) { @@ -355,7 +426,7 @@ void RenderWidget::allocate_working_storage(const CanvasProperties& frame_props) } } -void RenderWidget::blit_tile_no_lock( +void RenderLayer::blit_tile_no_lock( const Frame& frame, const size_t tile_x, const size_t tile_y) @@ -368,7 +439,7 @@ void RenderWidget::blit_tile_no_lock( dst_tile.copy_from(src_tile); } -void RenderWidget::update_tile_no_lock(const size_t tile_x, const size_t tile_y) +void RenderLayer::update_tile_no_lock(const size_t tile_x, const size_t tile_y) { // Retrieve the source tile. const Tile& src_tile = m_image_storage->tile(tile_x, tile_y); @@ -418,40 +489,5 @@ void RenderWidget::update_tile_no_lock(const size_t tile_x, const size_t tile_y) NativeDrawing::blit(dest, dest_stride, uint8_rgb_tile); } -void RenderWidget::paintEvent(QPaintEvent* event) -{ - QMutexLocker locker(&m_mutex); - - m_painter.begin(this); - m_painter.drawImage(rect(), m_image); - m_painter.end(); -} - -void RenderWidget::dragEnterEvent(QDragEnterEvent* event) -{ - if (event->mimeData()->hasFormat("text/plain")) - event->acceptProposedAction(); -} - -void RenderWidget::dragMoveEvent(QDragMoveEvent* event) -{ - if (pos().x() <= event->pos().x() && pos().y() <= event->pos().y() - && event->pos().x() < pos().x() + width() && event->pos().y() < pos().y() + height()) - { - event->accept(); - } - else - event->ignore(); -} - -void RenderWidget::dropEvent(QDropEvent* event) -{ - emit signal_material_dropped( - Vector2d( - static_cast(event->pos().x()) / width(), - static_cast(event->pos().y()) / height()), - event->mimeData()->text()); -} - } // namespace studio } // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/renderwidget.h b/src/appleseed.studio/mainwindow/rendering/renderlayer.h similarity index 85% rename from src/appleseed.studio/mainwindow/rendering/renderwidget.h rename to src/appleseed.studio/mainwindow/rendering/renderlayer.h index b47f3d90ca..c6af7778f9 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderwidget.h +++ b/src/appleseed.studio/mainwindow/rendering/renderlayer.h @@ -44,6 +44,7 @@ namespace OCIO = OCIO_NAMESPACE; // Qt headers. #include #include +#include #include #include @@ -58,7 +59,10 @@ namespace renderer { class Frame; } class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; +class QOpenGLFunctions_4_1_Core; +class QOpenGLTexture; class QPaintEvent; +class QRect; namespace appleseed { namespace studio { @@ -67,7 +71,7 @@ namespace studio { // A render widget based on QImage. // -class RenderWidget +class RenderLayer : public QWidget , public ICapturableWidget { @@ -75,7 +79,7 @@ class RenderWidget public: // Constructor. - RenderWidget( + RenderLayer( const size_t width, const size_t height, OCIO::ConstConfigRcPtr ocio_config, @@ -84,6 +88,9 @@ class RenderWidget // Thread-safe. QImage capture() override; + // Thread-safe. + void darken(); + // Thread-safe. void resize( const size_t width, @@ -113,6 +120,11 @@ class RenderWidget // Thread-safe. void blit_frame(const renderer::Frame& frame); + void draw(GLuint empty_vao, bool paths_display_active); + void init_gl(QSurfaceFormat format); + void set_gl_functions( + QOpenGLFunctions_4_1_Core* functions); + // Direct access to internals for high-performance drawing. QMutex& mutex(); QImage& image(); @@ -128,11 +140,16 @@ class RenderWidget private: mutable QMutex m_mutex; QImage m_image; - QPainter m_painter; std::unique_ptr m_float_tile_storage; std::unique_ptr m_uint8_tile_storage; std::unique_ptr m_image_storage; + QOpenGLFunctions_4_1_Core* m_gl; + QOpenGLTexture* m_gl_tex; + GLuint m_shader_program; + GLint m_mult_loc; + bool m_gl_initialized; + OCIO::ConstConfigRcPtr m_ocio_config; OCIO::ConstProcessorRcPtr m_ocio_processor; @@ -146,24 +163,19 @@ class RenderWidget void update_tile_no_lock( const size_t tile_x, const size_t tile_y); - - void paintEvent(QPaintEvent* event) override; - void dragEnterEvent(QDragEnterEvent* event) override; - void dragMoveEvent(QDragMoveEvent* event) override; - void dropEvent(QDropEvent* event) override; }; // -// RenderWidget class implementation. +// RenderLayer class implementation. // -inline QMutex& RenderWidget::mutex() +inline QMutex& RenderLayer::mutex() { return m_mutex; } -inline QImage& RenderWidget::image() +inline QImage& RenderLayer::image() { return m_image; } diff --git a/src/appleseed.studio/mainwindow/rendering/renderregionhandler.cpp b/src/appleseed.studio/mainwindow/rendering/viewportregionselectionhandler.cpp similarity index 91% rename from src/appleseed.studio/mainwindow/rendering/renderregionhandler.cpp rename to src/appleseed.studio/mainwindow/rendering/viewportregionselectionhandler.cpp index ae8c263e8b..9ea9b53eec 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderregionhandler.cpp +++ b/src/appleseed.studio/mainwindow/rendering/viewportregionselectionhandler.cpp @@ -28,7 +28,7 @@ // // Interface header. -#include "renderregionhandler.h" +#include "viewportregionselectionhandler.h" // appleseed.studio headers. #include "utility/mousecoordinatestracker.h" @@ -53,7 +53,7 @@ using namespace foundation; namespace appleseed { namespace studio { -RenderRegionHandler::RenderRegionHandler( +ViewportRegionSelectionHandler::ViewportRegionSelectionHandler( QWidget* widget, const MouseCoordinatesTracker& mouse_tracker) : m_widget(widget) @@ -65,22 +65,22 @@ RenderRegionHandler::RenderRegionHandler( m_widget->installEventFilter(this); } -RenderRegionHandler::~RenderRegionHandler() +ViewportRegionSelectionHandler::~ViewportRegionSelectionHandler() { m_widget->removeEventFilter(this); } -void RenderRegionHandler::set_enabled(const bool enabled) +void ViewportRegionSelectionHandler::set_enabled(const bool enabled) { m_enabled = enabled; } -void RenderRegionHandler::set_mode(const Mode mode) +void ViewportRegionSelectionHandler::set_mode(const Mode mode) { m_mode = mode; } -bool RenderRegionHandler::eventFilter(QObject* object, QEvent* event) +bool ViewportRegionSelectionHandler::eventFilter(QObject* object, QEvent* event) { if (m_enabled) { diff --git a/src/appleseed.studio/mainwindow/rendering/renderregionhandler.h b/src/appleseed.studio/mainwindow/rendering/viewportregionselectionhandler.h similarity index 95% rename from src/appleseed.studio/mainwindow/rendering/renderregionhandler.h rename to src/appleseed.studio/mainwindow/rendering/viewportregionselectionhandler.h index 2ad7f77a1e..574a7517a9 100644 --- a/src/appleseed.studio/mainwindow/rendering/renderregionhandler.h +++ b/src/appleseed.studio/mainwindow/rendering/viewportregionselectionhandler.h @@ -43,18 +43,18 @@ class QWidget; namespace appleseed { namespace studio { -class RenderRegionHandler +class ViewportRegionSelectionHandler : public QObject { Q_OBJECT public: // Default mode is RectangleSelectionMode. - RenderRegionHandler( + ViewportRegionSelectionHandler( QWidget* widget, const MouseCoordinatesTracker& mouse_tracker); - ~RenderRegionHandler() override; + ~ViewportRegionSelectionHandler() override; void set_enabled(const bool enabled); diff --git a/src/appleseed.studio/mainwindow/rendering/rendertab.cpp b/src/appleseed.studio/mainwindow/rendering/viewporttab.cpp similarity index 66% rename from src/appleseed.studio/mainwindow/rendering/rendertab.cpp rename to src/appleseed.studio/mainwindow/rendering/viewporttab.cpp index d936f9ea37..8383819b90 100644 --- a/src/appleseed.studio/mainwindow/rendering/rendertab.cpp +++ b/src/appleseed.studio/mainwindow/rendering/viewporttab.cpp @@ -28,11 +28,12 @@ // // Interface header. -#include "rendertab.h" +#include "viewporttab.h" // appleseed.studio headers. #include "mainwindow/project/projectexplorer.h" -#include "mainwindow/rendering/renderwidget.h" +//#include "mainwindow/rendering/lightpathsviewportmanager.h" +#include "mainwindow/rendering/renderingmanager.h" #include "utility/miscellaneous.h" // appleseed.renderer headers. @@ -69,15 +70,17 @@ namespace appleseed { namespace studio { // -// RenderTab class implementation. +// ViewportTab class implementation. // -RenderTab::RenderTab( +ViewportTab::ViewportTab( ProjectExplorer& project_explorer, Project& project, RenderingManager& rendering_manager, - OCIO::ConstConfigRcPtr ocio_config) - : m_project_explorer(project_explorer) + OCIO::ConstConfigRcPtr ocio_config, + renderer::ParamArray application_settings) + : m_application_settings(application_settings) + , m_project_explorer(project_explorer) , m_project(project) , m_rendering_manager(rendering_manager) , m_ocio_config(ocio_config) @@ -87,77 +90,77 @@ RenderTab::RenderTab( layout()->setSpacing(0); layout()->setMargin(0); - create_render_widget(); + create_viewport_widget(); create_toolbar(); create_scrollarea(); + create_light_paths_manager(); layout()->addWidget(m_toolbar); + layout()->addWidget(m_light_paths_manager->toolbar()); layout()->addWidget(m_scroll_area); recreate_handlers(); } -RenderWidget* RenderTab::get_render_widget() const +ViewportWidget* ViewportTab::get_viewport_widget() const { - return m_render_widget; + return m_viewport_widget; } -CameraController* RenderTab::get_camera_controller() const +CameraController* ViewportTab::get_camera_controller() const { return m_camera_controller.get(); } -ScenePickingHandler* RenderTab::get_scene_picking_handler() const +ScenePickingHandler* ViewportTab::get_scene_picking_handler() const { return m_scene_picking_handler.get(); } -void RenderTab::set_clear_frame_button_enabled(const bool enabled) +void ViewportTab::set_clear_frame_button_enabled(const bool enabled) { m_clear_frame_button->setEnabled(enabled); } -void RenderTab::set_render_region_buttons_enabled(const bool enabled) +void ViewportTab::set_render_region_buttons_enabled(const bool enabled) { m_set_render_region_button->setEnabled(enabled); m_clear_render_region_button->setEnabled(enabled); } -void RenderTab::clear() +void ViewportTab::render_began() { - m_render_widget->clear(); - m_render_widget->repaint(); + m_viewport_widget->get_render_layer()->darken(); + m_viewport_widget->get_light_paths_layer()->update_render_camera_transform(); + m_light_paths_manager.get()->reset(&m_project); + set_light_paths_toggle_enabled(false); + update(); } -void RenderTab::darken() -{ - m_render_widget->multiply(0.2f); -} - -void RenderTab::reset_zoom() +void ViewportTab::reset_zoom() { m_zoom_handler->reset_zoom(); } -void RenderTab::update() +void ViewportTab::update() { - m_render_widget->update(); + m_viewport_widget->update(); } -void RenderTab::update_size() +void ViewportTab::update_size() { m_set_render_region_button->setChecked(false); const CanvasProperties& props = m_project.get_frame()->image().properties(); - m_render_widget->resize( + m_viewport_widget->resize( props.m_canvas_width, props.m_canvas_height); recreate_handlers(); } -RenderTab::State RenderTab::save_state() const +ViewportTab::State ViewportTab::save_state() const { State state; state.m_zoom_handler_state = m_zoom_handler->save_state(); @@ -165,65 +168,142 @@ RenderTab::State RenderTab::save_state() const return state; } -void RenderTab::load_state(const State& state) +void ViewportTab::load_state(const State& state) { // The order matters here. m_zoom_handler->load_state(state.m_zoom_handler_state); m_pan_handler->load_state(state.m_pan_handler_state); } -void RenderTab::slot_render_widget_context_menu(const QPoint& point) +void ViewportTab::slot_base_layer_changed(const ViewportWidget::BaseLayer base_layer) +{ + switch (base_layer) + { + case ViewportWidget::BaseLayer::FinalRender: + if (m_rendering_manager.is_rendering() + && m_rendering_manager.get_rendering_mode() == RenderingManager::RenderingMode::InteractiveRendering) + m_camera_controller.get()->set_enabled(true); + else + m_camera_controller.get()->set_enabled(false); + break; + + case ViewportWidget::BaseLayer::OpenGL: + m_camera_controller.get()->set_enabled(true); + break; + } +} + +void ViewportTab::slot_camera_changed() { - emit signal_render_widget_context_menu(m_render_widget->mapToGlobal(point)); + + m_viewport_widget->get_light_paths_layer()->set_transform(get_camera_controller()->get_transform()); + m_viewport_widget->get_gl_scene_layer()->set_transform(get_camera_controller()->get_transform()); + update(); } -void RenderTab::slot_toggle_render_region(const bool checked) +void ViewportTab::slot_toggle_render_region(const bool checked) { m_scene_picking_handler->set_enabled(!checked); - m_render_region_handler->set_mode( + m_viewport_selection_handler->set_mode( checked - ? RenderRegionHandler::RenderRegionMode - : RenderRegionHandler::RectangleSelectionMode); + ? ViewportRegionSelectionHandler::RenderRegionMode + : ViewportRegionSelectionHandler::RectangleSelectionMode); } -void RenderTab::slot_set_render_region(const QRect& rect) +void ViewportTab::slot_set_render_region(const QRect& rect) { m_set_render_region_button->setChecked(false); emit signal_set_render_region(rect); } -void RenderTab::slot_toggle_pixel_inspector(const bool checked) +void ViewportTab::slot_toggle_pixel_inspector(const bool checked) { m_pixel_inspector_handler->set_enabled(checked); m_pixel_inspector_handler->update_tooltip_visibility(); } -void RenderTab::create_render_widget() +void ViewportTab::slot_viewport_widget_context_menu(const QPoint& point) +{ + emit signal_viewport_widget_context_menu(m_viewport_widget->mapToGlobal(point)); +} + +void ViewportTab::create_viewport_widget() { const CanvasProperties& props = m_project.get_frame()->image().properties(); - m_render_widget = - new RenderWidget( + m_viewport_widget = + new ViewportWidget( + m_project, props.m_canvas_width, props.m_canvas_height, - m_ocio_config); + m_ocio_config, + this); - m_render_widget->setContextMenuPolicy(Qt::CustomContextMenu); + m_viewport_widget->setContextMenuPolicy(Qt::CustomContextMenu); connect( - m_render_widget, SIGNAL(customContextMenuRequested(const QPoint&)), - SLOT(slot_render_widget_context_menu(const QPoint&))); + m_viewport_widget, SIGNAL(customContextMenuRequested(const QPoint&)), + SLOT(slot_viewport_widget_context_menu(const QPoint&))); - m_render_widget->setMouseTracking(true); + m_viewport_widget->setMouseTracking(true); } -void RenderTab::create_toolbar() +void ViewportTab::create_light_paths_manager() +{ + m_light_paths_manager.reset(new LightPathsViewportManager( + this, + &m_project, + m_application_settings)); + + connect( + m_viewport_widget, SIGNAL(signal_base_layer_changed(const ViewportWidget::BaseLayer)), + m_light_paths_manager.get(), SLOT(slot_base_layer_changed(const ViewportWidget::BaseLayer)) + ); + connect( + m_light_paths_manager.get(), SIGNAL(signal_should_display(const bool)), + m_viewport_widget, SLOT(slot_light_paths_should_display(const bool)) + ); + connect( + m_light_paths_toggle_button, SIGNAL(toggled(bool)), + m_light_paths_manager.get(), SLOT(slot_light_paths_display_toggled(bool)) + ); +} + +void ViewportTab::create_toolbar() { // Create the render toolbar. m_toolbar = new QToolBar(); m_toolbar->setObjectName("render_toolbar"); m_toolbar->setIconSize(QSize(18, 18)); + // Create the label preceding the scene display combo box + QLabel* scene_layer_label = new QLabel("Scene Display Mode:"); + scene_layer_label->setObjectName("display_mode_label"); + m_toolbar->addWidget(scene_layer_label); + + // Create the scene base layer combo box + m_base_layer_combo = new QComboBox(); + m_base_layer_combo->setObjectName("base_layer_combo"); + for (int i = 0; i < ViewportWidget::BaseLayer::BASE_LAYER_MAX_VALUE; i++) { + m_base_layer_combo->addItem(ViewportWidget::base_layer_string(static_cast(i))); + } + m_toolbar->addWidget(m_base_layer_combo); + connect( + m_base_layer_combo, SIGNAL(activated(int)), + m_viewport_widget, SLOT(slot_base_layer_changed(int)) + ); + connect( + m_viewport_widget, SIGNAL(signal_base_layer_changed(const ViewportWidget::BaseLayer)), + SLOT(slot_base_layer_changed(const ViewportWidget::BaseLayer)) + ); + + m_light_paths_toggle_button = new QToolButton(); + m_light_paths_toggle_button->setText("Display Light Paths Overlay"); + m_light_paths_toggle_button->setCheckable(true); + m_toolbar->addWidget(m_light_paths_toggle_button); + + m_toolbar->addSeparator(); + // Save Frame and AOVs button. QToolButton* save_aovs_button = new QToolButton(); save_aovs_button->setIcon(load_icons("rendertab_save_all_aovs")); @@ -317,12 +397,12 @@ void RenderTab::create_toolbar() m_toolbar->addSeparator(); // Create the label preceding the display combobox. - QLabel* display_label = new QLabel("Display:"); + QLabel* display_label = new QLabel("Display Transform:"); display_label->setObjectName("display_label"); m_toolbar->addWidget(display_label); // Create the display combobox. - QComboBox* m_display_transform_combo = new QComboBox(); + m_display_transform_combo = new QComboBox(); m_display_transform_combo->setObjectName("display_combo"); { const char* display_name = m_ocio_config->getDefaultDisplay(); @@ -343,7 +423,7 @@ void RenderTab::create_toolbar() m_toolbar->addWidget(m_display_transform_combo); connect( m_display_transform_combo, SIGNAL(currentIndexChanged(QString)), - m_render_widget, SLOT(slot_display_transform_changed(QString))); + m_viewport_widget->get_render_layer(), SLOT(slot_display_transform_changed(QString))); // Add stretchy spacer. // This places interactive widgets on the left and info on the right. @@ -381,7 +461,7 @@ void RenderTab::create_toolbar() m_toolbar->addWidget(m_a_label); } -void RenderTab::create_scrollarea() +void ViewportTab::create_scrollarea() { // Encapsulate the render widget into another widget that adds a margin around it. QWidget* render_widget_wrapper = new QWidget(); @@ -389,7 +469,7 @@ void RenderTab::create_scrollarea() render_widget_wrapper->setLayout(new QGridLayout()); render_widget_wrapper->layout()->setSizeConstraint(QLayout::SetFixedSize); render_widget_wrapper->layout()->setContentsMargins(20, 20, 20, 20); - render_widget_wrapper->layout()->addWidget(m_render_widget); + render_widget_wrapper->layout()->addWidget(m_viewport_widget); // Wrap the render widget in a scroll area. m_scroll_area = new QScrollArea(); @@ -398,13 +478,13 @@ void RenderTab::create_scrollarea() m_scroll_area->setWidget(render_widget_wrapper); } -void RenderTab::recreate_handlers() +void ViewportTab::recreate_handlers() { // Handler for zooming the render widget in and out with the keyboard or the mouse wheel. m_zoom_handler.reset( new WidgetZoomHandler( m_scroll_area, - m_render_widget)); + m_viewport_widget)); // Handler for panning the render widget with the mouse. m_pan_handler.reset( @@ -414,13 +494,13 @@ void RenderTab::recreate_handlers() // Handler for tracking and displaying mouse coordinates. m_mouse_tracker.reset( new MouseCoordinatesTracker( - m_render_widget, + m_viewport_widget, m_info_label)); - // Handle for tracking and displaying the color of the pixel under the mouse cursor. + // Handler for tracking and displaying the color of the pixel under the mouse cursor. m_pixel_color_tracker.reset( new PixelColorTracker( - m_render_widget, + m_viewport_widget, m_r_label, m_g_label, m_b_label, @@ -431,14 +511,14 @@ void RenderTab::recreate_handlers() // Handler for pixel inspection in the render widget. m_pixel_inspector_handler.reset( new PixelInspectorHandler( - m_render_widget, + m_viewport_widget, *m_mouse_tracker.get(), m_project)); // Camera handler. m_camera_controller.reset( new CameraController( - m_render_widget, + m_viewport_widget, m_project)); connect( m_camera_controller.get(), SIGNAL(signal_camera_change_begin()), @@ -449,6 +529,9 @@ void RenderTab::recreate_handlers() connect( m_camera_controller.get(), SIGNAL(signal_camera_changed()), SIGNAL(signal_camera_changed())); + connect( + m_camera_controller.get(), SIGNAL(signal_camera_changed()), + SLOT(slot_camera_changed())); connect( &m_project_explorer, SIGNAL(signal_frame_modified()), m_camera_controller.get(), SLOT(slot_frame_modified())); @@ -456,7 +539,7 @@ void RenderTab::recreate_handlers() // Handler for picking scene entities in the render widget. m_scene_picking_handler.reset( new ScenePickingHandler( - m_render_widget, + m_viewport_widget, m_picking_mode_combo, *m_mouse_tracker.get(), m_project_explorer, @@ -467,21 +550,27 @@ void RenderTab::recreate_handlers() connect( m_scene_picking_handler.get(), SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)), m_camera_controller.get(), SLOT(slot_entity_picked(renderer::ScenePicker::PickingResult))); + connect( + m_scene_picking_handler.get(), SIGNAL(signal_entity_picked(renderer::ScenePicker::PickingResult)), + m_light_paths_manager.get(), SLOT(slot_entity_picked(renderer::ScenePicker::PickingResult))); // Handler for setting render regions with the mouse. - m_render_region_handler.reset( - new RenderRegionHandler( - m_render_widget, + m_viewport_selection_handler.reset( + new ViewportRegionSelectionHandler( + m_viewport_widget, *m_mouse_tracker.get())); connect( - m_render_region_handler.get(), SIGNAL(signal_rectangle_selection(const QRect&)), + m_viewport_selection_handler.get(), SIGNAL(signal_rectangle_selection(const QRect&)), SIGNAL(signal_rectangle_selection(const QRect&))); connect( - m_render_region_handler.get(), SIGNAL(signal_render_region(const QRect&)), + m_viewport_selection_handler.get(), SIGNAL(signal_rectangle_selection(const QRect&)), + m_light_paths_manager.get(), SLOT(slot_rectangle_selection(const QRect&))); + connect( + m_viewport_selection_handler.get(), SIGNAL(signal_render_region(const QRect&)), SLOT(slot_set_render_region(const QRect&))); // Clipboard handler. - m_clipboard_handler.reset(new RenderClipboardHandler(m_render_widget, m_render_widget)); + m_clipboard_handler.reset(new RenderClipboardHandler(m_viewport_widget, m_viewport_widget)); // Set initial state. m_pixel_inspector_handler->set_enabled(false); @@ -495,7 +584,7 @@ void RenderTab::recreate_handlers() m_rendering_manager)); connect( - m_render_widget, + m_viewport_widget, SIGNAL(signal_material_dropped( const foundation::Vector2d&, const QString&)), @@ -505,7 +594,15 @@ void RenderTab::recreate_handlers() const QString&))); } +void ViewportTab::set_light_paths_toggle_enabled(const bool enabled) +{ + if (!enabled) + m_light_paths_toggle_button->setChecked(false); + + m_light_paths_toggle_button->setDisabled(!enabled); +} + } // namespace studio } // namespace appleseed -#include "mainwindow/rendering/moc_cpp_rendertab.cxx" +#include "mainwindow/rendering/moc_cpp_viewporttab.cxx" diff --git a/src/appleseed.studio/mainwindow/rendering/rendertab.h b/src/appleseed.studio/mainwindow/rendering/viewporttab.h similarity index 57% rename from src/appleseed.studio/mainwindow/rendering/rendertab.h rename to src/appleseed.studio/mainwindow/rendering/viewporttab.h index 99c8a4ee2f..750f267e16 100644 --- a/src/appleseed.studio/mainwindow/rendering/rendertab.h +++ b/src/appleseed.studio/mainwindow/rendering/viewporttab.h @@ -31,12 +31,14 @@ // appleseed.studio headers. #include "mainwindow/rendering/cameracontroller.h" +#include "mainwindow/rendering/lightpathsviewportmanager.h" #include "mainwindow/rendering/materialdrophandler.h" #include "mainwindow/rendering/pixelcolortracker.h" #include "mainwindow/rendering/pixelinspectorhandler.h" #include "mainwindow/rendering/renderclipboardhandler.h" -#include "mainwindow/rendering/renderregionhandler.h" #include "mainwindow/rendering/scenepickinghandler.h" +#include "mainwindow/rendering/viewportregionselectionhandler.h" +#include "mainwindow/rendering/viewportwidget.h" #include "utility/mousecoordinatestracker.h" #include "utility/scrollareapanhandler.h" #include "utility/widgetzoomhandler.h" @@ -54,7 +56,6 @@ namespace OCIO = OCIO_NAMESPACE; // Forward declarations. namespace appleseed { namespace studio { class ProjectExplorer; } } -namespace appleseed { namespace studio { class RenderWidget; } } namespace renderer { class Entity; } namespace renderer { class Project; } namespace renderer { class RenderingManager; } @@ -73,27 +74,28 @@ namespace studio { // A tab wrapping a render widget and its toolbar. // -class RenderTab +class ViewportTab : public QWidget { Q_OBJECT public: - RenderTab( + ViewportTab( ProjectExplorer& project_explorer, renderer::Project& project, RenderingManager& rendering_manager, - OCIO::ConstConfigRcPtr ocio_config); + OCIO::ConstConfigRcPtr ocio_config, + renderer::ParamArray application_settings); - RenderWidget* get_render_widget() const; + ViewportWidget* get_viewport_widget() const; CameraController* get_camera_controller() const; ScenePickingHandler* get_scene_picking_handler() const; void set_clear_frame_button_enabled(const bool enabled); + void set_light_paths_toggle_enabled(const bool enabled); void set_render_region_buttons_enabled(const bool enabled); - void clear(); - void darken(); + void render_began(); void reset_zoom(); void update(); @@ -113,9 +115,9 @@ class RenderTab void signal_quicksave_frame_and_aovs(); void signal_set_render_region(const QRect& rect); void signal_clear_render_region(); - void signal_render_widget_context_menu(const QPoint& point); void signal_reset_zoom(); void signal_clear_frame(); + void signal_viewport_widget_context_menu(const QPoint& point); void signal_camera_change_begin(); void signal_camera_changed(); @@ -125,45 +127,54 @@ class RenderTab void signal_rectangle_selection(const QRect& rect); private slots: - void slot_render_widget_context_menu(const QPoint& point); - void slot_toggle_render_region(const bool checked); + void slot_camera_changed(); + void slot_base_layer_changed(const ViewportWidget::BaseLayer base_layer); void slot_set_render_region(const QRect& rect); void slot_toggle_pixel_inspector(const bool checked); + void slot_toggle_render_region(const bool checked); + void slot_viewport_widget_context_menu(const QPoint& point); private: - RenderWidget* m_render_widget; - QScrollArea* m_scroll_area; - QToolBar* m_toolbar; - QToolButton* m_set_render_region_button; - QToolButton* m_clear_render_region_button; - QToolButton* m_clear_frame_button; - QComboBox* m_picking_mode_combo; - QLabel* m_info_label; - QLabel* m_r_label; - QLabel* m_g_label; - QLabel* m_b_label; - QLabel* m_a_label; - - ProjectExplorer& m_project_explorer; - renderer::Project& m_project; - RenderingManager& m_rendering_manager; - - std::unique_ptr m_zoom_handler; - std::unique_ptr m_pan_handler; - std::unique_ptr m_material_drop_handler; - std::unique_ptr m_mouse_tracker; - std::unique_ptr m_pixel_color_tracker; - std::unique_ptr m_pixel_inspector_handler; - std::unique_ptr m_camera_controller; - std::unique_ptr m_scene_picking_handler; - std::unique_ptr m_render_region_handler; - std::unique_ptr m_clipboard_handler; - - OCIO::ConstConfigRcPtr m_ocio_config; - - void create_render_widget(); - void create_toolbar(); + ViewportWidget* m_viewport_widget; + + QScrollArea* m_scroll_area; + QToolBar* m_toolbar; + QToolButton* m_set_render_region_button; + QToolButton* m_clear_render_region_button; + QToolButton* m_clear_frame_button; + QToolButton* m_light_paths_toggle_button; + QComboBox* m_picking_mode_combo; + QComboBox* m_display_transform_combo; + QComboBox* m_base_layer_combo; + QLabel* m_info_label; + QLabel* m_r_label; + QLabel* m_g_label; + QLabel* m_b_label; + QLabel* m_a_label; + + ProjectExplorer& m_project_explorer; + renderer::Project& m_project; + RenderingManager& m_rendering_manager; + renderer::ParamArray m_application_settings; + + std::unique_ptr m_light_paths_manager; + std::unique_ptr m_zoom_handler; + std::unique_ptr m_pan_handler; + std::unique_ptr m_material_drop_handler; + std::unique_ptr m_mouse_tracker; + std::unique_ptr m_pixel_color_tracker; + std::unique_ptr m_pixel_inspector_handler; + std::unique_ptr m_camera_controller; + std::unique_ptr m_scene_picking_handler; + std::unique_ptr m_viewport_selection_handler; + std::unique_ptr m_clipboard_handler; + + OCIO::ConstConfigRcPtr m_ocio_config; + + void create_light_paths_manager(); void create_scrollarea(); + void create_toolbar(); + void create_viewport_widget(); void recreate_handlers(); }; diff --git a/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp b/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp new file mode 100644 index 0000000000..fdaa212428 --- /dev/null +++ b/src/appleseed.studio/mainwindow/rendering/viewportwidget.cpp @@ -0,0 +1,415 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "viewportwidget.h" + +// appleseed.studio headers. +#include "utility/gl.h" + +// appleseed.renderer headers. +#include "renderer/api/frame.h" + +// appleseed.foundation headers. +#include "foundation/image/canvasproperties.h" +#include "foundation/image/image.h" +#include "foundation/image/nativedrawing.h" +#include "foundation/image/tile.h" +#include "foundation/math/scalar.h" +#include "foundation/platform/types.h" + +// Qt headers. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Standard headers. +#include +#include + +using namespace foundation; +using namespace renderer; +using namespace std; + +namespace appleseed { +namespace studio { + +// +// ViewportWidget class implementation. +// + +ViewportWidget::ViewportWidget( + const renderer::Project& project, + const size_t width, + const size_t height, + OCIO::ConstConfigRcPtr ocio_config, + QWidget* parent) + : QOpenGLWidget(parent) + , m_project(project) + , m_draw_light_paths(false) + , m_active_base_layer(static_cast(0)) + , m_resolve_program(0) + , m_accum_loc(0) + , m_accum_tex(0) + , m_revealage_loc(0) + , m_revealage_tex(0) + , m_accum_revealage_fb(0) +{ + setFocusPolicy(Qt::StrongFocus); + setFixedWidth(static_cast(width)); + setFixedHeight(static_cast(height)); + setAutoFillBackground(false); + setAttribute(Qt::WA_OpaquePaintEvent, true); + + create_render_layer(ocio_config); + create_gl_scene_layer(); + create_light_paths_layer(); + + resize(width, height); + + setAcceptDrops(true); +} + +ViewportWidget::~ViewportWidget() +{ + m_gl->glDeleteProgram(m_resolve_program); + m_gl->glDeleteTextures(1, &m_accum_tex); + m_gl->glDeleteTextures(1, &m_revealage_tex); + m_gl->glDeleteTextures(1, &m_color_tex); + m_gl->glDeleteTextures(1, &m_depth_tex); + m_gl->glDeleteFramebuffers(1, &m_accum_revealage_fb); + m_gl->glDeleteFramebuffers(1, &m_main_fb); +} + +QString ViewportWidget::base_layer_string(BaseLayer layer) +{ + switch (layer) + { + case BaseLayer::FinalRender: return QString("Final Render"); + case BaseLayer::OpenGL: return QString("OpenGL"); + } + assert(false); + return QString("BaseLayer"); +} + +void ViewportWidget::create_render_layer(OCIO::ConstConfigRcPtr ocio_config) +{ + m_render_layer = + std::unique_ptr(new RenderLayer( + m_width, + m_height, + ocio_config)); +} + +void ViewportWidget::create_gl_scene_layer() +{ + m_gl_scene_layer = + std::unique_ptr(new GLSceneLayer( + m_project, + m_width, + m_height)); +} + +void ViewportWidget::create_light_paths_layer() +{ + m_light_paths_layer = + std::unique_ptr(new LightPathsLayer( + m_project, + m_width, + m_height)); +} + +RenderLayer* ViewportWidget::get_render_layer() +{ + return m_render_layer.get(); +} + +LightPathsLayer* ViewportWidget::get_light_paths_layer() +{ + return m_light_paths_layer.get(); +} + +GLSceneLayer* ViewportWidget::get_gl_scene_layer() +{ + return m_gl_scene_layer.get(); +} + +QImage ViewportWidget::capture() +{ + return grabFramebuffer(); +} + +void ViewportWidget::initializeGL() { + RENDERER_LOG_INFO("initializing opengl."); + + m_gl = QOpenGLContext::currentContext()->versionFunctions(); + + const auto qs_format = format(); + if (!m_gl->initializeOpenGLFunctions()) + { + const int major_version = qs_format.majorVersion(); + const int minor_version = qs_format.minorVersion(); + RENDERER_LOG_ERROR( + "opengl: could not load required gl functions. loaded version %d.%d, required version 3.3.", + major_version, + minor_version); + return; + } + + m_gl->glGenVertexArrays(1, &m_empty_vao); + m_gl->glBindVertexArray(m_empty_vao); + m_gl->glGenBuffers(1, &m_empty_vbo); + m_gl->glBindBuffer(GL_ARRAY_BUFFER, m_empty_vbo); + float vals[3]{ 0.0f, 0.0f, 0.0f }; + m_gl->glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3, static_cast(vals), GL_STATIC_DRAW); + m_gl->glVertexAttribPointer(0, sizeof(float), GL_FLOAT, GL_FALSE, sizeof(float), static_cast(0)); + m_gl->glEnableVertexAttribArray(0); + + auto vertex_shader = load_gl_shader("fullscreen_tri.vert"); + auto fragment_shader = load_gl_shader("oit_resolve.frag"); + + create_shader_program( + m_gl, + m_resolve_program, + &vertex_shader, + &fragment_shader); + + m_accum_loc = m_gl->glGetUniformLocation(m_resolve_program, "u_accum_tex"); + m_revealage_loc = m_gl->glGetUniformLocation(m_resolve_program, "u_revealage_tex"); + + GLuint temp_fbs[2]; + m_gl->glGenFramebuffers(2, temp_fbs); + + m_main_fb = temp_fbs[0]; + m_accum_revealage_fb = temp_fbs[1]; + + GLuint temp_texs[4]; + m_gl->glGenTextures(4, temp_texs); + + m_color_tex = temp_texs[0]; + m_depth_tex = temp_texs[1]; + m_accum_tex = temp_texs[2]; + m_revealage_tex = temp_texs[3]; + + resizeGL(width(), height()); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_main_fb); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_color_tex, 0); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_tex, 0); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_accum_revealage_fb); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_accum_tex, 0); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_revealage_tex, 0); + m_gl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_tex, 0); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, 0); + + m_gl->glUseProgram(m_resolve_program); + m_gl->glUniform1i(m_accum_loc, 0); + m_gl->glUniform1i(m_revealage_loc, 1); + + m_render_layer->set_gl_functions(m_gl); + m_render_layer->init_gl(qs_format); + m_gl_scene_layer->set_gl_functions(m_gl); + m_gl_scene_layer->init_gl(qs_format); + m_light_paths_layer->set_gl_functions(m_gl); + m_light_paths_layer->init_gl(qs_format); +} + +void ViewportWidget::resize( + const size_t width, + const size_t height) +{ + m_render_layer->resize(width, height); + m_light_paths_layer->resize(width, height); +} + +void ViewportWidget::resizeGL( + int width, + int height) +{ + m_light_paths_layer->resize(width, height); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_color_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_depth_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_accum_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_HALF_FLOAT, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, m_revealage_tex); + m_gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, width, height, 0, GL_RED, GL_HALF_FLOAT, NULL); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + m_gl->glBindTexture(GL_TEXTURE_2D, 0); +} + +void ViewportWidget::paintGL() +{ + + double dpr = static_cast(m_render_layer->image().devicePixelRatio()); + GLsizei w = static_cast(width() * dpr); + GLsizei h = static_cast(height() * dpr); + m_gl->glViewport(0, 0, w, h); + + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_main_fb); + // Clear the main framebuffers + GLfloat main_clear[]{ 0.0, 0.0, 0.0, 0.0 }; + m_gl->glClearBufferfv(GL_COLOR, 0, main_clear); + m_gl->glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0, 0); + + if (m_active_base_layer == BaseLayer::FinalRender) + { + m_render_layer->draw(m_empty_vao, m_draw_light_paths); + } + + QOpenGLContext *ctx = const_cast(QOpenGLContext::currentContext()); + + if (m_active_base_layer == BaseLayer::OpenGL || m_draw_light_paths) + m_gl_scene_layer->draw_depth_only(); + + if (m_active_base_layer == BaseLayer::OpenGL) + m_gl_scene_layer->draw(); + + if (m_draw_light_paths) + { + // Bind accum/revealage framebuffer + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_accum_revealage_fb); + + // Set both attachments as active draw buffers + const GLenum buffers[]{ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + m_gl->glDrawBuffers(2, buffers); + + // Clear the buffers + GLfloat accum_clear_col[]{ 0.0, 0.0, 0.0, 0.0 }; + m_gl->glClearBufferfv(GL_COLOR, 0, accum_clear_col); + GLfloat revealage_clear_col[]{ 1.0, 0.0, 0.0, 0.0 }; + m_gl->glClearBufferfv(GL_COLOR, 1, revealage_clear_col); + + // Enable proper blending for each + m_gl->glEnable(GL_BLEND); + m_gl->glBlendFunci(0, GL_ONE, GL_ONE); + m_gl->glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + m_gl->glEnable(GL_DEPTH_TEST); + m_gl->glDepthMask(GL_FALSE); + + if (m_active_base_layer == BaseLayer::FinalRender) + m_light_paths_layer->draw_render_camera(); + else + m_light_paths_layer->draw(); + + m_gl->glUseProgram(m_resolve_program); + + // Set default framebuffer object + m_gl->glBindFramebuffer(GL_FRAMEBUFFER, m_main_fb); + m_gl->glDrawBuffer(GL_COLOR_ATTACHMENT0); + + m_gl->glBindVertexArray(m_empty_vao); + + m_gl->glActiveTexture(GL_TEXTURE0); + m_gl->glBindTexture(GL_TEXTURE_2D, m_accum_tex); + m_gl->glActiveTexture(GL_TEXTURE1); + m_gl->glBindTexture(GL_TEXTURE_2D, m_revealage_tex); + + m_gl->glDisable(GL_DEPTH_TEST); + m_gl->glDepthMask(GL_FALSE); + m_gl->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + m_gl->glDrawArrays(GL_TRIANGLES, 0, 3); + + m_gl->glActiveTexture(GL_TEXTURE1); + m_gl->glBindTexture(GL_TEXTURE_2D, 0); + m_gl->glActiveTexture(GL_TEXTURE0); + m_gl->glBindTexture(GL_TEXTURE_2D, 0); + m_gl->glDepthMask(GL_TRUE); + } + + m_gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, m_main_fb); + m_gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->defaultFramebufferObject()); + m_gl->glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); +} + +void ViewportWidget::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat("text/plain")) + event->acceptProposedAction(); +} + +void ViewportWidget::dragMoveEvent(QDragMoveEvent* event) +{ + if (pos().x() <= event->pos().x() && pos().y() <= event->pos().y() + && event->pos().x() < pos().x() + width() && event->pos().y() < pos().y() + height()) + { + event->accept(); + } + else + event->ignore(); +} + +void ViewportWidget::dropEvent(QDropEvent* event) +{ + emit signal_material_dropped( + Vector2d( + static_cast(event->pos().x()) / width(), + static_cast(event->pos().y()) / height()), + event->mimeData()->text()); +} + +void ViewportWidget::slot_base_layer_changed(int index) +{ + assert(index < BaseLayer::BASE_LAYER_MAX_VALUE); + m_active_base_layer = static_cast(index); + update(); + + emit signal_base_layer_changed(m_active_base_layer); +} + +void ViewportWidget::slot_light_paths_should_display(const bool should_display) +{ + m_draw_light_paths = should_display; + update(); +} + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/mainwindow/rendering/viewportwidget.h b/src/appleseed.studio/mainwindow/rendering/viewportwidget.h new file mode 100644 index 0000000000..1a41c88595 --- /dev/null +++ b/src/appleseed.studio/mainwindow/rendering/viewportwidget.h @@ -0,0 +1,165 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// appleseed.studio headers. +#include "mainwindow/rendering/lightpathslayer.h" +#include "mainwindow/rendering/glscenelayer.h" +#include "mainwindow/rendering/renderclipboardhandler.h" +#include "mainwindow/rendering/renderlayer.h" + + +// appleseed.foundation headers. +#include "foundation/image/image.h" +#include "foundation/image/tile.h" +#include "foundation/math/vector.h" + +// Qt headers. +#include +#include +#include +#include +#include + +// OpenColorIO headers. +#include +namespace OCIO = OCIO_NAMESPACE; + +// Standard headers. +#include +#include +#include + +// Forward declarations. +namespace foundation { class CanvasProperties; } +namespace renderer { class Frame; } +class QDragEnterEvent; +class QDragMoveEvent; +class QDropEvent; +class QPaintEvent; +class QOpenGLFunctions_4_1_Core; + +namespace appleseed { +namespace studio { + +// +// A render widget based on QImage. +// + +class ViewportWidget + : public QOpenGLWidget + , public ICapturableWidget +{ + Q_OBJECT + + public: + // Constructor. + ViewportWidget( + const renderer::Project& project, + const size_t width, + const size_t height, + OCIO::ConstConfigRcPtr ocio_config, + QWidget* parent = nullptr); + + ~ViewportWidget(); + + enum BaseLayer { + FinalRender, + OpenGL, + BASE_LAYER_MAX_VALUE + }; + + static QString base_layer_string(BaseLayer layer); + + // Thread-safe. + QImage capture() override; + + // Thread-safe. + void resize( + const size_t width, + const size_t height); + + RenderLayer* get_render_layer(); + GLSceneLayer* get_gl_scene_layer(); + LightPathsLayer* get_light_paths_layer(); + + signals: + void signal_material_dropped( + const foundation::Vector2d& drop_pos, + const QString& material_name); + void signal_base_layer_changed(const ViewportWidget::BaseLayer layer); + + public slots: + void slot_light_paths_should_display(const bool should_display); + + private slots: + void slot_base_layer_changed(int index); + + private: + const renderer::Project& m_project; + int m_width; + int m_height; + + QOpenGLFunctions_4_1_Core* m_gl; + QPainter m_painter; + + GLuint m_depth_tex; + GLuint m_color_tex; + GLuint m_accum_tex; + GLuint m_revealage_tex; + GLuint m_main_fb; + GLuint m_accum_revealage_fb; + GLuint m_empty_vao; + GLuint m_empty_vbo; + + GLuint m_resolve_program; + GLint m_accum_loc; + GLint m_revealage_loc; + + std::unique_ptr m_render_layer; + std::unique_ptr m_gl_scene_layer; + std::unique_ptr m_light_paths_layer; + + bool m_draw_light_paths; + BaseLayer m_active_base_layer; + + void create_light_paths_layer(); + void create_gl_scene_layer(); + void create_render_layer(OCIO::ConstConfigRcPtr ocio_config); + + void initializeGL() override; + void resizeGL(int width, int height) override; + void paintGL() override; + void dragEnterEvent(QDragEnterEvent* event) override; + void dragMoveEvent(QDragMoveEvent* event) override; + void dropEvent(QDropEvent* event) override; +}; + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/resources/resources.qrc b/src/appleseed.studio/resources/resources.qrc index a55b80d91f..d4833e1d22 100644 --- a/src/appleseed.studio/resources/resources.qrc +++ b/src/appleseed.studio/resources/resources.qrc @@ -1,8 +1,11 @@ images/appleseed-logo-256.png + shaders/final_render.frag + shaders/fullscreen_tri.vert shaders/lightpaths.frag shaders/lightpaths.vert + shaders/oit_resolve.frag shaders/scene.frag shaders/scene.vert widgets/checkbox_checked_disabled.png diff --git a/src/appleseed.studio/resources/shaders/final_render.frag b/src/appleseed.studio/resources/shaders/final_render.frag new file mode 100644 index 0000000000..6f9eadf6d7 --- /dev/null +++ b/src/appleseed.studio/resources/shaders/final_render.frag @@ -0,0 +1,41 @@ +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#version 410 + +in vec2 f_uv; + +uniform sampler2D u_render_tex; +uniform float u_mult; + +out vec4 Target0; + +void main() { + vec3 col = texture(u_render_tex, f_uv, 0).rgb; + + Target0 = vec4(col * u_mult, 1.0); +} diff --git a/src/appleseed.studio/resources/shaders/fullscreen_tri.vert b/src/appleseed.studio/resources/shaders/fullscreen_tri.vert new file mode 100644 index 0000000000..7dbb4b2100 --- /dev/null +++ b/src/appleseed.studio/resources/shaders/fullscreen_tri.vert @@ -0,0 +1,48 @@ +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#version 410 + +const vec2 verts[3] = vec2[]( + vec2(3.0, 1.0), + vec2(-1.0, -3.0), + vec2(-1.0, 1.0) +); + +const vec2 uvs[3] = vec2[]( + vec2(2.0, 0.0), + vec2(0.0, 2.0), + vec2(0.0, 0.0) +); + +out vec2 f_uv; + +void main() +{ + f_uv = uvs[gl_VertexID]; + gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0); +} \ No newline at end of file diff --git a/src/appleseed.studio/resources/shaders/lightpaths.frag b/src/appleseed.studio/resources/shaders/lightpaths.frag index f67540108f..be1d2e26c3 100644 --- a/src/appleseed.studio/resources/shaders/lightpaths.frag +++ b/src/appleseed.studio/resources/shaders/lightpaths.frag @@ -25,14 +25,38 @@ // THE SOFTWARE. // -#version 330 +#version 410 #extension GL_ARB_separate_shader_objects : enable -layout(location = 0) in vec3 v_color; +flat in vec4 f_color; +in float f_aa_norm; +flat in float f_thickness; +flat in float f_total_thickness; +flat in float f_aspect_expansion_len; -out vec4 Target0; +uniform vec2 u_res; +layout(location = 0) out vec4 accum_target; +layout(location = 1) out float revealage_target; + +void write_pixel(vec4 premultiplied_col, vec3 transmit) { + premultiplied_col.a *= 1.0 - clamp((transmit.r + transmit.g + transmit.b) * (1.0 / 3.0), 0, 1); + + float a = min(1.0, premultiplied_col.a) * 8.0 + 0.01; + float b = -gl_FragCoord.z * 0.95 + 1.0; + + float w = clamp(a * a * a * 1e8 * b * b * b, 1e-2, 3e2); + + accum_target = premultiplied_col * w; + revealage_target = premultiplied_col.a; +} void main() { - Target0 = vec4(v_color, 1.0); + float dist = abs(f_aa_norm) * f_total_thickness - 0.5 / f_aspect_expansion_len; + float eps = fwidth(dist); + float a = 1.0 - smoothstep(f_thickness - eps, f_thickness + eps, dist); + a *= f_color.a; + + vec4 premult = vec4(f_color.rgb * a, a); + write_pixel(premult, vec3(0.0)); } diff --git a/src/appleseed.studio/resources/shaders/lightpaths.vert b/src/appleseed.studio/resources/shaders/lightpaths.vert index 6664383094..6c27dc7bab 100644 --- a/src/appleseed.studio/resources/shaders/lightpaths.vert +++ b/src/appleseed.studio/resources/shaders/lightpaths.vert @@ -25,19 +25,91 @@ // THE SOFTWARE. // -#version 330 -#extension GL_ARB_separate_shader_objects : enable +#version 410 -layout(location = 0) in vec3 a_pos; -layout(location = 1) in vec3 a_color; +const float AA_BUFFER_SIZE = 1.0; + +layout(location = 0) in vec3 v_previous; +layout(location = 1) in vec3 v_position; +layout(location = 2) in vec3 v_next; +layout(location = 3) in float v_luminance; +layout(location = 4) in int v_direction_start_end; +layout(location = 5) in vec3 v_color; +layout(location = 6) in vec3 v_surface_normal; -uniform mat4 u_view; uniform mat4 u_proj; +uniform mat4 u_view; +uniform vec2 u_res; +uniform float u_max_luminance; +uniform float u_max_thickness; +uniform float u_min_thickness; + +uniform int u_first_selected; +uniform int u_last_selected; + +flat out vec4 f_color; +out float f_aa_norm; +flat out float f_thickness; +flat out float f_total_thickness; +flat out float f_aspect_expansion_len; + +void main() { + float aspect = u_res.x / u_res.y; + vec2 aspect_vec = vec2(aspect, 1.0); + mat4 vp = u_proj * u_view; + vec4 prev_proj = vp * vec4(v_previous, 1.0); + vec4 curr_proj = vp * vec4(v_position, 1.0); + vec4 next_proj = vp * vec4(v_next, 1.0); + + //get 2D screen space with W divide and aspect correction + vec2 curr_screen = curr_proj.xy / curr_proj.w * aspect_vec; + vec2 prev_screen = prev_proj.xy / prev_proj.w * aspect_vec; + vec2 next_screen = next_proj.xy / next_proj.w * aspect_vec; + + float orientation = 1.0; + if ((v_direction_start_end & 1) == 1) + { + orientation = -1.0; + } + + vec2 dir = vec2(0.0); + if ((v_direction_start_end & 2) == 2) + { + //starting point uses (next - current) + dir = normalize(next_screen - curr_screen); + } + else if ((v_direction_start_end & 4) == 4) + { + //ending point uses (current - v_previous) + dir = normalize(curr_screen - prev_screen); + } + vec2 perp_dir = vec2(-dir.y, dir.x); + + vec4 normal_clip = vp * vec4(v_surface_normal, 0.0); + normal_clip.xy *= aspect_vec; + normal_clip = normalize(normal_clip); + vec2 tang_clip = vec2(-normal_clip.y, normal_clip.x); + + float tdp = dot(tang_clip, perp_dir); + vec2 expansion = perp_dir; + if (tdp > 0.05) + expansion = tang_clip / tdp; + + vec2 norm_exp = normalize(expansion); + vec2 res_exp_dir = vec2(norm_exp.x * u_res.x, norm_exp.y * u_res.y); + f_aspect_expansion_len = length(res_exp_dir); + + f_thickness = (max(max(min(v_luminance / u_max_luminance, 1.0), 0.0) * u_max_thickness, u_min_thickness) / 2.0) / f_aspect_expansion_len; + + f_total_thickness = f_thickness + AA_BUFFER_SIZE / f_aspect_expansion_len; + + expansion *= f_total_thickness; + expansion *= orientation; -layout(location = 0) out vec3 v_color; + gl_Position = vec4((curr_screen + expansion) / aspect_vec, curr_proj.z / curr_proj.w, 1.0); + f_aa_norm = orientation; -void main() -{ - v_color = a_color; - gl_Position = u_proj * u_view * vec4(a_pos, 1.0); + bool is_selected = gl_VertexID >= u_first_selected && gl_VertexID < u_last_selected; + float a = is_selected ? 1.0 : 0.2; + f_color = vec4(v_color, a); } diff --git a/src/appleseed.studio/resources/shaders/oit_resolve.frag b/src/appleseed.studio/resources/shaders/oit_resolve.frag new file mode 100644 index 0000000000..3a091797f8 --- /dev/null +++ b/src/appleseed.studio/resources/shaders/oit_resolve.frag @@ -0,0 +1,52 @@ +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#version 410 + +uniform sampler2D u_accum_tex; +uniform sampler2D u_revealage_tex; + +out vec4 Target0; + +float max4(vec4 v) { + return max(max(max(v.x, v.y), v.z), v.w); +} + +void main() { + ivec2 coord = ivec2(gl_FragCoord.xy); + + float revealage = texelFetch(u_revealage_tex, coord, 0).r; + + vec4 accum = texelFetch(u_accum_tex, coord, 0); + // Suppress overflow + if (isinf(max4(abs(accum)))) { + accum.rgb = vec3(accum.a); + } + vec3 averageColor = accum.rgb / max(accum.a, 0.00001); + + Target0 = vec4(averageColor, 1.0 - revealage); +} diff --git a/src/appleseed.studio/resources/shaders/scene.frag b/src/appleseed.studio/resources/shaders/scene.frag index 2dc350f5d2..d58363f361 100644 --- a/src/appleseed.studio/resources/shaders/scene.frag +++ b/src/appleseed.studio/resources/shaders/scene.frag @@ -25,7 +25,7 @@ // THE SOFTWARE. // -#version 330 +#version 410 #extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 v_world_pos; diff --git a/src/appleseed.studio/resources/shaders/scene.vert b/src/appleseed.studio/resources/shaders/scene.vert index 18f61388f8..a1bdde9adc 100644 --- a/src/appleseed.studio/resources/shaders/scene.vert +++ b/src/appleseed.studio/resources/shaders/scene.vert @@ -25,7 +25,7 @@ // THE SOFTWARE. // -#version 330 +#version 410 #extension GL_ARB_separate_shader_objects : enable layout(location = 0) in vec3 a_pos; diff --git a/src/appleseed.studio/utility/gl.cpp b/src/appleseed.studio/utility/gl.cpp new file mode 100644 index 0000000000..3ed09c6725 --- /dev/null +++ b/src/appleseed.studio/utility/gl.cpp @@ -0,0 +1,149 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "gl.h" + +// appleseed.studio headers. +#include "renderer/api/utility.h" +#include "utility/miscellaneous.h" + +// Qt headers. +#include +#include +#include + +using namespace std; +using namespace renderer; + +namespace appleseed { +namespace studio { + +const string shader_kind_to_string(const GLint shader_kind) +{ + switch (shader_kind) { + case GL_VERTEX_SHADER: + return "Vertex"; + case GL_FRAGMENT_SHADER: + return "Fragment"; + } + return "Unknown Kind"; +} + +void compile_shader( + QOpenGLFunctions_4_1_Core* f, + const GLuint shader, + const GLsizei count, + const GLchar** src_string, + const GLint* length) +{ + f->glShaderSource(shader, count, src_string, length); + f->glCompileShader(shader); + GLint success; + f->glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (!success) + { + char info_log[1024]; + f->glGetShaderInfoLog(shader, 1024, NULL, info_log); + + GLint shader_kind; + f->glGetShaderiv(shader, GL_SHADER_TYPE, &shader_kind); + string shader_kind_string = shader_kind_to_string(shader_kind); + + RENDERER_LOG_ERROR("opengl: %s shader compilation failed:\n%s", shader_kind_string.c_str(), info_log); + } +} + +void link_shader_program( + QOpenGLFunctions_4_1_Core* f, + const GLuint program, + const GLuint vert, + const GLuint frag) +{ + f->glAttachShader(program, vert); + + if (frag != 0) + f->glAttachShader(program, frag); + + f->glLinkProgram(program); + + GLint success; + f->glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (!success) + { + char info_log[1024]; + f->glGetProgramInfoLog(program, 1024, NULL, info_log); + RENDERER_LOG_ERROR("opengl: shader program linking failed:\n%s", info_log); + } +} + +void create_shader_program( + QOpenGLFunctions_4_1_Core* f, + GLuint& program, + const QByteArray* vert_source, + const QByteArray* frag_source) +{ + assert(vert_source != nullptr); + bool has_frag_shader = frag_source != nullptr; + + GLuint vert = f->glCreateShader(GL_VERTEX_SHADER); + GLuint frag = has_frag_shader ? f->glCreateShader(GL_FRAGMENT_SHADER) : 0; + + auto gl_vert_source = static_cast(vert_source->constData()); + auto gl_vert_source_length = static_cast(vert_source->size()); + + compile_shader(f, vert, 1, &gl_vert_source, &gl_vert_source_length); + + if (has_frag_shader) + { + auto gl_frag_source = static_cast(frag_source->constData()); + auto gl_frag_source_length = static_cast(frag_source->size()); + compile_shader(f, frag, 1, &gl_frag_source, &gl_frag_source_length); + } + + program = f->glCreateProgram(); + link_shader_program(f, program, vert, frag); + + f->glDeleteShader(vert); + if (has_frag_shader) + f->glDeleteShader(frag); +} + +QByteArray load_gl_shader(const QString& base_name) +{ + const QString resource_path(QString(":/shaders/%1").arg(base_name)); + + QFile file(resource_path); + file.open(QFile::ReadOnly); + + return file.readAll(); +} + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/utility/gl.h b/src/appleseed.studio/utility/gl.h new file mode 100644 index 0000000000..b63b4c583c --- /dev/null +++ b/src/appleseed.studio/utility/gl.h @@ -0,0 +1,75 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +// Qt headers. +#include +#include + +// standard headers. +#include + +// Forward declarations. +class QByteArray; +class QString; + +namespace appleseed { +namespace studio { + +// Get a string from an OpenGL shader kind value. +const std::string shader_kind_to_string(const GLint shader_kind); + +// Compile a GL shader. +void compile_shader( + QOpenGLFunctions_4_1_Core* f, + const GLuint shader, + const GLsizei count, + const GLchar** src_string, + const GLint* length); + +// Link a GL shader program. +void link_shader_program( + QOpenGLFunctions_4_1_Core* f, + const GLuint program, + const GLuint vert, + const GLuint frag); + +// Create a GL shader program with a vertex and optional fragment shader. +void create_shader_program( + QOpenGLFunctions_4_1_Core* f, + GLuint& program, + const QByteArray* vert_source, + const QByteArray* frag_source); + +// Load a GLSL shader from file into a QByteArray. +QByteArray load_gl_shader(const QString& base_name); + +} // namespace studio +} // namespace appleseed + diff --git a/src/appleseed.studio/utility/miscellaneous.cpp b/src/appleseed.studio/utility/miscellaneous.cpp index 32432076a9..ecece9828b 100644 --- a/src/appleseed.studio/utility/miscellaneous.cpp +++ b/src/appleseed.studio/utility/miscellaneous.cpp @@ -196,16 +196,6 @@ bool file_exists(const QString& path) return info.exists() && info.isFile(); } -QByteArray load_gl_shader(const QString& base_name) -{ - const QString resource_path(QString(":/shaders/%1").arg(base_name)); - - QFile file(resource_path); - file.open(QFile::ReadOnly); - - return file.readAll(); -} - QIcon load_icons(const QString& base_name) { const QString base_icon_filepath(make_app_path("icons/%1.png").arg(base_name)); diff --git a/src/appleseed.studio/utility/miscellaneous.h b/src/appleseed.studio/utility/miscellaneous.h index 2558cb9c13..361eadafef 100644 --- a/src/appleseed.studio/utility/miscellaneous.h +++ b/src/appleseed.studio/utility/miscellaneous.h @@ -80,9 +80,6 @@ QString combine_name_and_shortcut(const QString& name, const QKeySequence& short // Check whether a file exists. bool file_exists(const QString& path); -// Load a GLSL shader from file into a QByteArray. -QByteArray load_gl_shader(const QString& base_name); - // Load an icon and its variants (hover, disabled...) from the application's icons directory. QIcon load_icons(const QString& base_name); diff --git a/src/appleseed/renderer/kernel/lighting/directlightingintegrator.cpp b/src/appleseed/renderer/kernel/lighting/directlightingintegrator.cpp index eb0f454606..d0b0dbf115 100644 --- a/src/appleseed/renderer/kernel/lighting/directlightingintegrator.cpp +++ b/src/appleseed/renderer/kernel/lighting/directlightingintegrator.cpp @@ -467,6 +467,7 @@ void DirectLightingIntegrator::add_emitting_shape_sample_contribution( light_path_stream->sampled_emitting_shape( *sample.m_shape, sample.m_point, + sample.m_geometric_normal, material_value.m_beauty, edf_value); } @@ -542,6 +543,7 @@ void DirectLightingIntegrator::add_non_physical_light_sample_contribution( light_path_stream->sampled_non_physical_light( *light, emission_position, + m_material_sampler.get_shading_point().get_geometric_normal(), material_value.m_beauty, light_value); } diff --git a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp index 4655b51362..12480e596a 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp +++ b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp @@ -275,6 +275,10 @@ void LightPathRecorder::get_light_path_vertex( result.m_radiance[0] = source_vertex.m_radiance[0]; result.m_radiance[1] = source_vertex.m_radiance[1]; result.m_radiance[2] = source_vertex.m_radiance[2]; + + result.m_surface_normal[0] = source_vertex.m_surface_normal[0]; + result.m_surface_normal[1] = source_vertex.m_surface_normal[1]; + result.m_surface_normal[2] = source_vertex.m_surface_normal[2]; } bool LightPathRecorder::write(const char* filename) const diff --git a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h index cebcebc3d9..93e094c774 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h +++ b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h @@ -68,6 +68,7 @@ struct LightPathVertex const Entity* m_entity; float m_position[3]; float m_radiance[3]; + float m_surface_normal[3]; }; diff --git a/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp b/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp index 8b12da70cb..b075a29de4 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp +++ b/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp @@ -77,7 +77,8 @@ void LightPathStream::clear() void LightPathStream::begin_path( const PixelContext& pixel_context, const Camera* camera, - const Vector3d& camera_vertex_position) + const Vector3d& camera_vertex_position, + const Vector3d& camera_vertex_normal) { assert(m_events.empty()); assert(m_hit_reflector_data.empty()); @@ -90,6 +91,7 @@ void LightPathStream::begin_path( m_camera = camera; m_camera_vertex_position = Vector3f(camera_vertex_position); + m_camera_vertex_normal = Vector3f(camera_vertex_normal); m_pixel_coords = pixel_context.get_pixel_coords(); m_sample_position = Vector2f(pixel_context.get_sample_position()); } @@ -107,6 +109,7 @@ void LightPathStream::hit_reflector(const PathVertex& vertex) HitReflectorData data; data.m_object_instance = &vertex.m_shading_point->get_object_instance(); data.m_vertex_position = Vector3f(vertex.get_point()); + data.m_surface_normal = Vector3f(vertex.get_geometric_normal()); data.m_path_throughput = vertex.m_throughput.to_rgb(g_std_lighting_conditions); m_hit_reflector_data.push_back(data); } @@ -126,6 +129,7 @@ void LightPathStream::hit_emitter( HitEmitterData data; data.m_object_instance = &vertex.m_shading_point->get_object_instance(); data.m_vertex_position = Vector3f(vertex.get_point()); + data.m_surface_normal = Vector3f(vertex.get_geometric_normal()); data.m_path_throughput = vertex.m_throughput.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_hit_emitter_data.push_back(data); @@ -134,6 +138,7 @@ void LightPathStream::hit_emitter( void LightPathStream::sampled_emitting_shape( const EmittingShape& shape, const Vector3d& emission_position, + const Vector3d& emission_normal, const Spectrum& material_value, const Spectrum& emitted_radiance) { @@ -147,6 +152,7 @@ void LightPathStream::sampled_emitting_shape( shape.get_assembly_instance()->get_assembly().object_instances().get_by_index( shape.get_object_instance_index()); data.m_vertex_position = Vector3f(emission_position); + data.m_surface_normal = Vector3f(emission_normal); data.m_material_value = material_value.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_sampled_emitter_data.push_back(data); @@ -155,6 +161,7 @@ void LightPathStream::sampled_emitting_shape( void LightPathStream::sampled_non_physical_light( const Light& light, const Vector3d& emission_position, + const Vector3d& emission_normal, const Spectrum& material_value, const Spectrum& emitted_radiance) { @@ -166,6 +173,7 @@ void LightPathStream::sampled_non_physical_light( SampledEmitterData data; data.m_entity = &light; data.m_vertex_position = Vector3f(emission_position); + data.m_surface_normal = Vector3f(emission_normal); data.m_material_value = material_value.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_sampled_emitter_data.push_back(data); @@ -244,6 +252,7 @@ void LightPathStream::create_path_from_hit_emitter(const size_t emitter_event_in StoredPathVertex emitter_vertex; emitter_vertex.m_entity = hit_emitter_data.m_object_instance; emitter_vertex.m_position = hit_emitter_data.m_vertex_position; + emitter_vertex.m_surface_normal = hit_emitter_data.m_surface_normal; emitter_vertex.m_radiance = hit_emitter_data.m_emitted_radiance; m_vertices.push_back(emitter_vertex); @@ -264,6 +273,7 @@ void LightPathStream::create_path_from_hit_emitter(const size_t emitter_event_in StoredPathVertex reflector_vertex; reflector_vertex.m_entity = event_data.m_object_instance; reflector_vertex.m_position = event_data.m_vertex_position; + reflector_vertex.m_surface_normal = event_data.m_surface_normal; reflector_vertex.m_radiance = current_radiance; m_vertices.push_back(reflector_vertex); @@ -288,6 +298,7 @@ void LightPathStream::create_path_from_hit_emitter(const size_t emitter_event_in StoredPathVertex camera_vertex; camera_vertex.m_entity = m_camera; camera_vertex.m_position = m_camera_vertex_position; + camera_vertex.m_surface_normal = m_camera_vertex_normal; camera_vertex.m_radiance = current_radiance; m_vertices.push_back(camera_vertex); @@ -319,6 +330,7 @@ void LightPathStream::create_path_from_sampled_emitter(const size_t emitter_even StoredPathVertex emitter_vertex; emitter_vertex.m_entity = sampled_emitter_data.m_entity; emitter_vertex.m_position = sampled_emitter_data.m_vertex_position; + emitter_vertex.m_surface_normal = sampled_emitter_data.m_surface_normal; emitter_vertex.m_radiance = sampled_emitter_data.m_emitted_radiance; m_vertices.push_back(emitter_vertex); @@ -339,6 +351,7 @@ void LightPathStream::create_path_from_sampled_emitter(const size_t emitter_even StoredPathVertex reflector_vertex; reflector_vertex.m_entity = event_data.m_object_instance; reflector_vertex.m_position = event_data.m_vertex_position; + reflector_vertex.m_surface_normal = event_data.m_surface_normal; reflector_vertex.m_radiance = current_radiance; m_vertices.push_back(reflector_vertex); @@ -363,6 +376,7 @@ void LightPathStream::create_path_from_sampled_emitter(const size_t emitter_even StoredPathVertex camera_vertex; camera_vertex.m_entity = m_camera; camera_vertex.m_position = m_camera_vertex_position; + camera_vertex.m_surface_normal = m_camera_vertex_normal; camera_vertex.m_radiance = current_radiance; m_vertices.push_back(camera_vertex); @@ -394,6 +408,7 @@ void LightPathStream::create_path_from_sampled_environment(const size_t env_even StoredPathVertex emitter_vertex; emitter_vertex.m_entity = sampled_env_data.m_environment_edf; emitter_vertex.m_position = last_reflector_data.m_vertex_position + m_scene_diameter * sampled_env_data.m_emission_direction; + emitter_vertex.m_surface_normal = -sampled_env_data.m_emission_direction; emitter_vertex.m_radiance = sampled_env_data.m_emitted_radiance; m_vertices.push_back(emitter_vertex); @@ -414,6 +429,7 @@ void LightPathStream::create_path_from_sampled_environment(const size_t env_even StoredPathVertex reflector_vertex; reflector_vertex.m_entity = event_data.m_object_instance; reflector_vertex.m_position = event_data.m_vertex_position; + reflector_vertex.m_surface_normal = event_data.m_surface_normal; reflector_vertex.m_radiance = current_radiance; m_vertices.push_back(reflector_vertex); @@ -438,6 +454,7 @@ void LightPathStream::create_path_from_sampled_environment(const size_t env_even StoredPathVertex camera_vertex; camera_vertex.m_entity = m_camera; camera_vertex.m_position = m_camera_vertex_position; + camera_vertex.m_surface_normal = m_camera_vertex_normal; camera_vertex.m_radiance = current_radiance; m_vertices.push_back(camera_vertex); diff --git a/src/appleseed/renderer/kernel/lighting/lightpathstream.h b/src/appleseed/renderer/kernel/lighting/lightpathstream.h index 8c008e3b20..9f4c33ffb1 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathstream.h +++ b/src/appleseed/renderer/kernel/lighting/lightpathstream.h @@ -67,7 +67,8 @@ class LightPathStream void begin_path( const PixelContext& pixel_context, const Camera* camera, - const foundation::Vector3d& camera_vertex_position); + const foundation::Vector3d& camera_vertex_position, + const foundation::Vector3d& camera_vertex_normal); void hit_reflector( const PathVertex& vertex); @@ -79,12 +80,14 @@ class LightPathStream void sampled_emitting_shape( const EmittingShape& shape, const foundation::Vector3d& emission_position, + const foundation::Vector3d& emission_normal, const Spectrum& material_value, const Spectrum& emitted_radiance); void sampled_non_physical_light( const Light& light, const foundation::Vector3d& emission_position, + const foundation::Vector3d& emission_normal, const Spectrum& material_value, const Spectrum& emitted_radiance); @@ -117,6 +120,7 @@ class LightPathStream { const ObjectInstance* m_object_instance; // object instance that was hit foundation::Vector3f m_vertex_position; // world space position of the hit point on the reflector + foundation::Vector3f m_surface_normal; // world space normal of the surface of the reflector foundation::Color3f m_path_throughput; // cumulative path throughput up to but excluding this vertex, in reverse order (i.e. in the order from camera to light source) }; @@ -130,6 +134,7 @@ class LightPathStream { const Entity* m_entity; // object instance or non-physical light that was sampled foundation::Vector3f m_vertex_position; // world space position of the emitting point on the emitter + foundation::Vector3f m_surface_normal; // world space normal of the surface of the emitter foundation::Color3f m_material_value; // BSDF value at the previous vertex foundation::Color3f m_emitted_radiance; // emitted radiance in W.sr^-1.m^-2 }; @@ -157,6 +162,7 @@ class LightPathStream const Entity* m_entity; // object instance or non-physical light foundation::Vector3f m_position; // world space position of this vertex foundation::Color3f m_radiance; // radiance arriving at this vertex, in W.sr^-1.m^-2 + foundation::Vector3f m_surface_normal; }; // Scene. @@ -168,6 +174,7 @@ class LightPathStream foundation::Vector2i m_pixel_coords; foundation::Vector2f m_sample_position; foundation::Vector3f m_camera_vertex_position; + foundation::Vector3f m_camera_vertex_normal; // Scattering events (transient). std::vector m_events; diff --git a/src/appleseed/renderer/kernel/lighting/pt/ptlightingengine.cpp b/src/appleseed/renderer/kernel/lighting/pt/ptlightingengine.cpp index 18690cd788..bab0aaf4c8 100644 --- a/src/appleseed/renderer/kernel/lighting/pt/ptlightingengine.cpp +++ b/src/appleseed/renderer/kernel/lighting/pt/ptlightingengine.cpp @@ -168,7 +168,8 @@ namespace m_light_path_stream->begin_path( pixel_context, shading_point.get_scene().get_render_data().m_active_camera, - shading_point.get_ray().m_org); + shading_point.get_ray().m_org, + shading_point.get_ray().m_dir); } if (m_params.m_next_event_estimation) diff --git a/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp b/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp index 91b1803189..14f2cb3ff8 100644 --- a/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp +++ b/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp @@ -80,6 +80,15 @@ namespace const ParamArray& params) : Camera(name, params) { + // Extract the film dimensions from the camera parameters. + m_film_dimensions = extract_film_dimensions(); + + // Extract the abscissa of the near plane from the camera parameters. + m_near_z = extract_near_z(); + + // Precompute reciprocals of film dimensions. + m_rcp_film_width = 1.0 / m_film_dimensions[0]; + m_rcp_film_height = 1.0 / m_film_dimensions[1]; } void release() override @@ -125,19 +134,9 @@ namespace if (!Camera::on_render_begin(project, parent, recorder, abort_switch)) return false; - // Extract the film dimensions from the camera parameters. - m_film_dimensions = extract_film_dimensions(); - - // Extract the abscissa of the near plane from the camera parameters. - m_near_z = extract_near_z(); - // Retrieve the scene diameter that will be used to position the camera. m_safe_scene_diameter = project.get_scene()->get_render_data().m_safe_diameter; - // Precompute reciprocals of film dimensions. - m_rcp_film_width = 1.0 / m_film_dimensions[0]; - m_rcp_film_height = 1.0 / m_film_dimensions[1]; - // Precompute pixel area. const size_t pixel_count = project.get_frame()->image().properties().m_pixel_count; m_rcp_pixel_area = static_cast(pixel_count / (m_film_dimensions[0] * m_film_dimensions[1])); @@ -290,6 +289,9 @@ namespace 0.5 + point.x * m_rcp_film_width, 0.5 - point.y * m_rcp_film_height); } + + private: + bool m_extracted_params; }; } diff --git a/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp b/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp index 769065d018..fd04dec5c3 100644 --- a/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp +++ b/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp @@ -54,6 +54,18 @@ namespace renderer PerspectiveCamera::PerspectiveCamera(const char* name, const ParamArray& params) : Camera(name, params) { + // Extract the film dimensions from the camera parameters. + m_film_dimensions = extract_film_dimensions(); + + // Extract the focal length from the camera parameters. + m_focal_length = extract_focal_length(m_film_dimensions[0]); + // Extract the abscissa of the near plane from the camera parameters. + m_near_z = extract_near_z(); + // Extract the shift from the camera parameters. + m_shift = extract_shift(); + // Precompute reciprocals of film dimensions. + m_rcp_film_width = 1.0 / m_film_dimensions[0]; + m_rcp_film_height = 1.0 / m_film_dimensions[1]; } const foundation::Vector2d& PerspectiveCamera::get_film_dimensions() const @@ -80,22 +92,6 @@ bool PerspectiveCamera::on_render_begin( if (!Camera::on_render_begin(project, parent, recorder, abort_switch)) return false; - // Extract the film dimensions from the camera parameters. - m_film_dimensions = extract_film_dimensions(); - - // Extract the focal length from the camera parameters. - m_focal_length = extract_focal_length(m_film_dimensions[0]); - - // Extract the abscissa of the near plane from the camera parameters. - m_near_z = extract_near_z(); - - // Extract the shift from the camera parameters. - m_shift = extract_shift(); - - // Precompute reciprocals of film dimensions. - m_rcp_film_width = 1.0 / m_film_dimensions[0]; - m_rcp_film_height = 1.0 / m_film_dimensions[1]; - // Precompute pixel area. const size_t pixel_count = project.get_frame()->image().properties().m_pixel_count; m_pixel_area = m_film_dimensions[0] * m_film_dimensions[1] / pixel_count; From 2790eda34e39f2236d6c921fb2864f2c40723358 Mon Sep 17 00:00:00 2001 From: Kevin Masson Date: Sat, 21 Dec 2019 12:10:30 +0100 Subject: [PATCH 2/6] use c++11 for each loop --- src/appleseed.studio/mainwindow/mainwindow.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/appleseed.studio/mainwindow/mainwindow.cpp b/src/appleseed.studio/mainwindow/mainwindow.cpp index bb4997cb31..42b7202cb7 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.cpp +++ b/src/appleseed.studio/mainwindow/mainwindow.cpp @@ -69,7 +69,6 @@ #include "foundation/platform/python.h" #include "foundation/platform/system.h" #include "foundation/utility/containers/dictionary.h" -#include "foundation/utility/foreach.h" #include "foundation/utility/log/logmessage.h" // Qt headers. @@ -742,17 +741,12 @@ void MainWindow::update_workspace() m_ui->attribute_editor_scrollarea_contents->setEnabled(true); // Enable/disable light paths - if (m_project_manager.is_project_open() && - m_project_manager.get_project()->get_light_path_recorder().get_light_path_count() > 0) - { - for (const_each i = m_viewport_tabs; i; ++i) - i->second->set_light_paths_toggle_enabled(true); - } - else - { - for (const_each i = m_viewport_tabs; i; ++i) - i->second->set_light_paths_toggle_enabled(false); - } + const bool enable_light_paths_toogle = + m_project_manager.is_project_open() && + m_project_manager.get_project()->get_light_path_recorder().get_light_path_count() > 0; + + for (const std::pair& kvp : m_viewport_tabs) + kvp.second->set_light_paths_toggle_enabled(enable_light_paths_toogle); } void MainWindow::update_project_explorer() From d7d2ce96da71d96e24f83cbd04f469788bb7b765 Mon Sep 17 00:00:00 2001 From: Kevin Masson Date: Sat, 21 Dec 2019 12:45:07 +0100 Subject: [PATCH 3/6] Apply some review comments --- .../mainwindow/mainwindow.cpp | 7 +- .../mainwindow/rendering/glscenelayer.cpp | 2 +- .../mainwindow/rendering/glscenelayer.h | 67 ++++++++++--------- .../mainwindow/rendering/lightpathslayer.cpp | 42 ++++++------ .../mainwindow/rendering/lightpathslayer.h | 12 ++-- .../rendering/lightpathsviewportmanager.cpp | 2 - .../rendering/lightpathsviewportmanager.h | 2 +- .../modeling/camera/orthographiccamera.cpp | 3 - .../modeling/camera/perspectivecamera.cpp | 3 + 9 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/appleseed.studio/mainwindow/mainwindow.cpp b/src/appleseed.studio/mainwindow/mainwindow.cpp index 42b7202cb7..acddfd0e66 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.cpp +++ b/src/appleseed.studio/mainwindow/mainwindow.cpp @@ -2046,10 +2046,11 @@ void MainWindow::slot_clear_frame() frame->clear_main_and_aov_images(); // In the UI, clear all render widgets to black. - for (const_each i = m_viewport_tabs; i; ++i) + for (const std::pair& kvp : m_viewport_tabs) { - i->second->get_viewport_widget()->get_render_layer()->clear(); - i->second->get_viewport_widget()->repaint(); + const auto viewport_widget = kvp.second->get_viewport_widget(); + viewport_widget->get_render_layer()->clear(); + viewport_widget->repaint(); } } diff --git a/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp b/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp index a95e6f36d5..a80a447407 100644 --- a/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp +++ b/src/appleseed.studio/mainwindow/rendering/glscenelayer.cpp @@ -5,7 +5,7 @@ // // This software is released under the MIT license. // -// Copyright (c) 2018 Francois Beaune, The appleseedhq Organization +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/src/appleseed.studio/mainwindow/rendering/glscenelayer.h b/src/appleseed.studio/mainwindow/rendering/glscenelayer.h index 372208a261..9d34c65dda 100644 --- a/src/appleseed.studio/mainwindow/rendering/glscenelayer.h +++ b/src/appleseed.studio/mainwindow/rendering/glscenelayer.h @@ -5,7 +5,7 @@ // // This software is released under the MIT license. // -// Copyright (c) 2018 Gray Olson, The appleseedhq Organization +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -40,8 +40,8 @@ #include "foundation/math/vector.h" // Qt headers. -#include #include +#include // Standard headers. #include @@ -56,8 +56,8 @@ namespace renderer { class Camera; } namespace renderer { class Object; } namespace renderer { class ObjectInstance; } namespace renderer { class Project; } -class QKeyEvent; class QImage; +class QKeyEvent; class QOpenGLFunctions_4_1_Core; class QSurfaceFormat; @@ -76,8 +76,8 @@ class GLSceneLayer public: GLSceneLayer( const renderer::Project& project, - const size_t width, - const size_t height); + const std::size_t width, + const std::size_t height); ~GLSceneLayer(); @@ -98,33 +98,36 @@ class GLSceneLayer void slot_synchronize_camera(); private: - const renderer::Project& m_project; - renderer::Camera& m_camera; - foundation::Matrix4d m_camera_matrix; - foundation::Vector3f m_camera_position; - - bool m_backface_culling_enabled; - - QOpenGLFunctions_4_1_Core* m_gl; - - std::vector m_scene_object_data_vbos; - std::vector m_scene_object_data_index_counts; - std::vector m_scene_object_instance_vbos; - std::vector m_scene_object_instance_counts; - std::vector m_scene_object_current_instances; - std::vector m_scene_object_vaos; - std::unordered_map m_scene_object_index_map; - GLuint m_scene_shader_program; - GLint m_scene_view_mat_location; - GLint m_scene_proj_mat_location; - GLint m_scene_camera_pos_location; - GLuint m_depthonly_shader_program; - GLint m_depthonly_view_mat_location; - GLint m_depthonly_proj_mat_location; - GLint m_depthonly_camera_pos_location; - foundation::Matrix4f m_gl_view_matrix; - foundation::Matrix4f m_gl_proj_matrix; - bool m_initialized; + const renderer::Project& m_project; + renderer::Camera& m_camera; + foundation::Matrix4d m_camera_matrix; + foundation::Vector3f m_camera_position; + + bool m_backface_culling_enabled; + + QOpenGLFunctions_4_1_Core* m_gl; + + std::vector m_scene_object_data_vbos; + std::vector m_scene_object_data_index_counts; + std::vector m_scene_object_instance_vbos; + std::vector m_scene_object_instance_counts; + std::vector m_scene_object_current_instances; + std::vector m_scene_object_vaos; + std::unordered_map m_scene_object_index_map; + GLuint m_scene_shader_program; + GLint m_scene_view_mat_location; + GLint m_scene_proj_mat_location; + GLint m_scene_camera_pos_location; + + GLuint m_depthonly_shader_program; + GLint m_depthonly_view_mat_location; + GLint m_depthonly_proj_mat_location; + GLint m_depthonly_camera_pos_location; + + foundation::Matrix4f m_gl_view_matrix; + foundation::Matrix4f m_gl_proj_matrix; + + bool m_initialized; void render_scene(); void cleanup_gl_data(); diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp index 7a27c8e90a..2fcad1abf1 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp @@ -5,7 +5,7 @@ // // This software is released under the MIT license. // -// Copyright (c) 2018 Francois Beaune, The appleseedhq Organization +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -51,8 +51,8 @@ // Qt headers. #include #include -#include #include +#include // Standard headers. #include @@ -61,14 +61,14 @@ using namespace foundation; using namespace renderer; -using namespace std; namespace appleseed { namespace studio { -namespace { +namespace +{ // Byte stride of a vec3. - const size_t Vec3ByteStride = sizeof(float) * 3; + constexpr std::size_t Vec3ByteStride = sizeof(float) * 3; // Struct of an element of the "others" vertex buffer struct OtherAttributes { @@ -81,8 +81,8 @@ namespace { LightPathsLayer::LightPathsLayer( const Project& project, - const size_t width, - const size_t height) + const std::size_t width, + const std::size_t height) : m_project(project) , m_camera(*m_project.get_uncached_active_camera()) , m_selected_light_path_index(-1) @@ -93,12 +93,11 @@ LightPathsLayer::LightPathsLayer( , m_min_thickness(1.0f) , m_max_luminance(0.0f) { - const float time = m_camera.get_shutter_middle_time(); set_transform(m_camera.transform_sequence().evaluate(time)); } -void LightPathsLayer::resize(const size_t width, const size_t height) +void LightPathsLayer::resize(const std::size_t width, const std::size_t height) { m_width = width, m_height = height; @@ -130,7 +129,7 @@ void LightPathsLayer::set_light_paths(const LightPathArray& light_paths) { // Sort paths by descending radiance at the camera. const auto& light_path_recorder = m_project.get_light_path_recorder(); - sort( + std::sort( &m_light_paths[0], &m_light_paths[0] + m_light_paths.size(), [&light_path_recorder](const LightPath& lhs, const LightPath& rhs) @@ -184,7 +183,8 @@ void LightPathsLayer::slot_display_next_light_path() void LightPathsLayer::slot_synchronize_camera() { m_camera.transform_sequence().clear(); - m_camera.transform_sequence().set_transform(0.0f, + m_camera.transform_sequence().set_transform( + 0.0f, Transformd::from_local_to_parent(inverse(m_camera_matrix))); } @@ -192,6 +192,7 @@ void LightPathsLayer::load_light_paths_data() { m_path_terminator_vertex_indices.clear(); m_max_luminance = 0.0f; + if (!m_light_paths.empty()) { m_path_terminator_vertex_indices.push_back(0); @@ -205,14 +206,14 @@ void LightPathsLayer::load_light_paths_data() // Add two because we need extra at the front and back for the extra 'previous' and 'next' attributes const size_t total_gl_vertex_count = 2 * (light_path_recorder.get_vertex_count() + 2); - vector indices; - vector positions_buffer; + std::vector indices; + std::vector positions_buffer; // * 3 since we want 3 floats per position positions_buffer.reserve(total_gl_vertex_count * 3); indices.reserve(total_gl_vertex_count); // Add an empty vertex at the beginning to serve as first 'previous' - array empty_positions = + std::array empty_positions = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, @@ -222,9 +223,9 @@ void LightPathsLayer::load_light_paths_data() empty_positions.begin(), empty_positions.end()); - vector others_buffer; + std::vector others_buffer; others_buffer.reserve(total_gl_vertex_count); - array others; + std::array others; for (size_t light_path_idx = 0; light_path_idx < m_light_paths.size(); light_path_idx++) { @@ -243,9 +244,8 @@ void LightPathsLayer::load_light_paths_data() auto piece_luminance = luminance(piece_radiance); m_max_luminance = max(m_max_luminance, piece_luminance); piece_radiance /= piece_luminance; - //piece_radiance /= piece_radiance + Color3f(1.0); - array positions_temp_store = { + std::array positions_temp_store = { prev.m_position[0], prev.m_position[1], prev.m_position[2], prev.m_position[0], prev.m_position[1], prev.m_position[2], vert.m_position[0], vert.m_position[1], vert.m_position[2], @@ -292,7 +292,7 @@ void LightPathsLayer::load_light_paths_data() others.begin(), others.end()); - array indices_scratch = { + std::array indices_scratch = { start_index, start_index + 1, start_index + 2, start_index + 1, start_index + 2, start_index + 3, }; @@ -590,12 +590,12 @@ void LightPathsLayer::dump_selected_light_path() const const auto& light_path_recorder = m_project.get_light_path_recorder(); const auto& path = m_light_paths[m_selected_light_path_index]; - for (size_t i = path.m_vertex_begin_index; i < path.m_vertex_end_index; ++i) + for (std::size_t i = path.m_vertex_begin_index; i < path.m_vertex_end_index; ++i) { LightPathVertex v; light_path_recorder.get_light_path_vertex(i, v); - const string entity_name = + const std::string entity_name = v.m_entity != nullptr ? foundation::format("\"{0}\"", v.m_entity->get_path().c_str()) : "n/a"; diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h index 41246d5691..f0bd15182f 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.h @@ -5,7 +5,7 @@ // // This software is released under the MIT license. // -// Copyright (c) 2018 Francois Beaune, The appleseedhq Organization +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -74,10 +74,10 @@ class LightPathsLayer: public QObject public: LightPathsLayer( const renderer::Project& project, - const size_t width, - const size_t height); + const std::size_t width, + const std::size_t height); - void resize(const size_t width, const size_t height); + void resize(const std::size_t width, const std::size_t height); void update_render_camera_transform(); @@ -121,8 +121,8 @@ class LightPathsLayer: public QObject float m_max_luminance; float m_max_thickness; float m_min_thickness; - size_t m_width; - size_t m_height; + std::size_t m_width; + std::size_t m_height; QOpenGLFunctions_4_1_Core* m_gl; diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp index 76fbc596a2..4390ea3259 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp @@ -325,5 +325,3 @@ void LightPathsViewportManager::recreate_handlers() } // namespace studio } // namespace appleseed - -//#include "mainwindow/rendering/moc_cpp_lightpathsviewportmanager.cxx" diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h index 5894a83c97..12e73bfc23 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h @@ -62,7 +62,7 @@ namespace appleseed { namespace studio { // -// Manager for the light paths display overlay into the viewport +// Manages the state of the light paths overlay. // class LightPathsViewportManager diff --git a/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp b/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp index 14f2cb3ff8..a6a45bf535 100644 --- a/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp +++ b/src/appleseed/renderer/modeling/camera/orthographiccamera.cpp @@ -289,9 +289,6 @@ namespace 0.5 + point.x * m_rcp_film_width, 0.5 - point.y * m_rcp_film_height); } - - private: - bool m_extracted_params; }; } diff --git a/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp b/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp index fd04dec5c3..ce96322154 100644 --- a/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp +++ b/src/appleseed/renderer/modeling/camera/perspectivecamera.cpp @@ -59,10 +59,13 @@ PerspectiveCamera::PerspectiveCamera(const char* name, const ParamArray& params) // Extract the focal length from the camera parameters. m_focal_length = extract_focal_length(m_film_dimensions[0]); + // Extract the abscissa of the near plane from the camera parameters. m_near_z = extract_near_z(); + // Extract the shift from the camera parameters. m_shift = extract_shift(); + // Precompute reciprocals of film dimensions. m_rcp_film_width = 1.0 / m_film_dimensions[0]; m_rcp_film_height = 1.0 / m_film_dimensions[1]; From 3e69f4edbe91e13907b66335859e06b5b671d050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaune?= Date: Sat, 21 Dec 2019 12:50:06 +0100 Subject: [PATCH 4/6] Update lightpathslayer.cpp --- src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp index 2fcad1abf1..b8c2445daf 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathslayer.cpp @@ -5,7 +5,7 @@ // // This software is released under the MIT license. // -// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal From 060a11180fa6e1d95baf30c18b68d6302a14a27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beaune?= Date: Sat, 21 Dec 2019 12:50:47 +0100 Subject: [PATCH 5/6] Update CMakeLists.txt --- src/appleseed.studio/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/appleseed.studio/CMakeLists.txt b/src/appleseed.studio/CMakeLists.txt index 93b65560f0..eef36ca02d 100644 --- a/src/appleseed.studio/CMakeLists.txt +++ b/src/appleseed.studio/CMakeLists.txt @@ -248,8 +248,8 @@ source_group ("mainwindow\\pythonconsole" FILES set (mainwindow_rendering_sources mainwindow/rendering/cameracontroller.cpp mainwindow/rendering/cameracontroller.h - mainwindow/rendering/glscenelayer.h mainwindow/rendering/glscenelayer.cpp + mainwindow/rendering/glscenelayer.h mainwindow/rendering/lightpathslayer.cpp mainwindow/rendering/lightpathslayer.h mainwindow/rendering/lightpathspickinghandler.cpp From 37114a816f52f116e18c86bb16664c17bd433758 Mon Sep 17 00:00:00 2001 From: Gray Olson Date: Tue, 20 Aug 2019 18:58:53 -0700 Subject: [PATCH 6/6] implement light path filtering by light path expressions # Conflicts: # src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp # src/appleseed/renderer/kernel/lighting/lightpathstream.h --- src/appleseed.studio/CMakeLists.txt | 2 + .../rendering/lightpathspickinghandler.cpp | 100 ++++++++++--- .../rendering/lightpathspickinghandler.h | 10 +- .../rendering/lightpathsviewportmanager.cpp | 50 ++++++- .../rendering/lightpathsviewportmanager.h | 7 + src/appleseed.studio/utility/lpevalidator.cpp | 76 ++++++++++ src/appleseed.studio/utility/lpevalidator.h | 74 ++++++++++ src/appleseed/CMakeLists.txt | 2 + .../kernel/lighting/lightpathrecorder.cpp | 133 ++++++++++++++++-- .../kernel/lighting/lightpathrecorder.h | 4 +- .../kernel/lighting/lightpathstream.cpp | 35 +++++ .../kernel/lighting/lightpathstream.h | 6 + .../renderer/kernel/lighting/scatteringmode.h | 5 + src/appleseed/renderer/utility/lpsortaov.cpp | 55 ++++++++ src/appleseed/renderer/utility/lpsortaov.h | 62 ++++++++ 15 files changed, 585 insertions(+), 36 deletions(-) create mode 100644 src/appleseed.studio/utility/lpevalidator.cpp create mode 100644 src/appleseed.studio/utility/lpevalidator.h create mode 100644 src/appleseed/renderer/utility/lpsortaov.cpp create mode 100644 src/appleseed/renderer/utility/lpsortaov.h diff --git a/src/appleseed.studio/CMakeLists.txt b/src/appleseed.studio/CMakeLists.txt index eef36ca02d..c06ca77f1a 100644 --- a/src/appleseed.studio/CMakeLists.txt +++ b/src/appleseed.studio/CMakeLists.txt @@ -369,6 +369,8 @@ set (utility_sources utility/inputwidgetproxies.cpp utility/inputwidgetproxies.h utility/interop.h + utility/lpevalidator.cpp + utility/lpevalidator.h utility/miscellaneous.cpp utility/miscellaneous.h utility/mousecoordinatestracker.cpp diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp index 3f8e1cf8bd..07290a3055 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.cpp @@ -72,7 +72,7 @@ void LightPathsPickingHandler::set_enabled(const bool enabled) m_enabled = enabled; } -void LightPathsPickingHandler::pick(const Vector2i& pixel) const +void LightPathsPickingHandler::pick(const Vector2i& pixel, QString* lpe) { const Image& image = m_project.get_frame()->image(); const CanvasProperties& props = image.properties(); @@ -82,32 +82,46 @@ void LightPathsPickingHandler::pick(const Vector2i& pixel) const pixel.x < static_cast(props.m_canvas_width) && pixel.y < static_cast(props.m_canvas_height)) { + m_prev_query_x_min = static_cast(pixel.x); + m_prev_query_y_min = static_cast(pixel.y); + m_prev_query_x_max = static_cast(pixel.x); + m_prev_query_y_max = static_cast(pixel.y); + m_prev_query_valid = true; LightPathArray light_paths; m_project.get_light_path_recorder().query( - static_cast(pixel.x), - static_cast(pixel.y), - static_cast(pixel.x), - static_cast(pixel.y), + m_prev_query_x_min, + m_prev_query_y_min, + m_prev_query_x_max, + m_prev_query_y_max, + lpe == NULL ? NULL : lpe->toUtf8().data(), light_paths); if (light_paths.empty()) - RENDERER_LOG_INFO("no light path found at pixel (%d, %d).", pixel.x, pixel.y); + RENDERER_LOG_INFO("no light path found at pixel (%d, %d) with light path expression `%s`.", + pixel.x, + pixel.y, + lpe ? lpe->toUtf8().data() : ""); else { RENDERER_LOG_INFO( - "%s light path%s found at pixel (%d, %d).", + "%s light path%s found at pixel (%d, %d) with light path expression `%s`.", pretty_uint(light_paths.size()).c_str(), light_paths.size() > 1 ? "s" : "", pixel.x, - pixel.y); + pixel.y, + lpe ? lpe->toUtf8().data() : ""); } m_viewport_widget->get_light_paths_layer()->set_light_paths(light_paths); m_viewport_widget->update(); } + else + { + m_prev_query_valid = false; + } } -void LightPathsPickingHandler::pick(const AABB2i& rect) const +void LightPathsPickingHandler::pick(const AABB2i& rect, QString* lpe) { const Image& image = m_project.get_frame()->image(); const CanvasProperties& props = image.properties(); @@ -122,32 +136,84 @@ void LightPathsPickingHandler::pick(const AABB2i& rect) const if (final_rect.is_valid()) { + m_prev_query_x_min = static_cast(final_rect.min.x); + m_prev_query_y_min = static_cast(final_rect.min.y); + m_prev_query_x_max = static_cast(final_rect.max.x); + m_prev_query_y_max = static_cast(final_rect.max.y); + m_prev_query_valid = true; LightPathArray light_paths; m_project.get_light_path_recorder().query( - static_cast(final_rect.min.x), - static_cast(final_rect.min.y), - static_cast(final_rect.max.x), - static_cast(final_rect.max.y), + m_prev_query_x_min, + m_prev_query_y_min, + m_prev_query_x_max, + m_prev_query_y_max, + lpe == NULL ? NULL : lpe->toUtf8().data(), light_paths); if (light_paths.empty()) { - RENDERER_LOG_INFO("no light path found in rectangle (%d, %d)-(%d, %d).", + RENDERER_LOG_INFO("no light path found in rectangle (%d, %d)-(%d, %d) with light path expression `%s`.", final_rect.min.x, final_rect.min.y, final_rect.max.x, - final_rect.max.y); + final_rect.max.y, + lpe ? lpe->toUtf8().data() : ""); } else { RENDERER_LOG_INFO( - "%s light path%s found in rectangle (%d, %d)-(%d, %d).", + "%s light path%s found in rectangle (%d, %d)-(%d, %d) with light path expression `%s`.", pretty_uint(light_paths.size()).c_str(), light_paths.size() > 1 ? "s" : "", final_rect.min.x, final_rect.min.y, final_rect.max.x, - final_rect.max.y); + final_rect.max.y, + lpe ? lpe->toUtf8().data() : ""); + } + + m_viewport_widget->get_light_paths_layer()->set_light_paths(light_paths); + m_viewport_widget->update(); + } + else + { + m_prev_query_valid = false; + } +} + +void LightPathsPickingHandler::pick(QString* lpe) +{ + if (m_prev_query_valid) + { + LightPathArray light_paths; + m_project.get_light_path_recorder().query( + m_prev_query_x_min, + m_prev_query_y_min, + m_prev_query_x_max, + m_prev_query_y_max, + lpe == NULL ? NULL : lpe->toUtf8().data(), + light_paths); + + if (light_paths.empty()) + { + RENDERER_LOG_INFO("no light path found in rectangle (%d, %d)-(%d, %d) with light path expression `%s`.", + m_prev_query_x_min, + m_prev_query_y_min, + m_prev_query_x_max, + m_prev_query_y_max, + lpe ? lpe->toUtf8().data() : ""); + } + else + { + RENDERER_LOG_INFO( + "%s light path%s found in rectangle (%d, %d)-(%d, %d) with light path expression `%s`.", + pretty_uint(light_paths.size()).c_str(), + light_paths.size() > 1 ? "s" : "", + m_prev_query_x_min, + m_prev_query_y_min, + m_prev_query_x_max, + m_prev_query_y_max, + lpe ? lpe->toUtf8().data() : ""); } m_viewport_widget->get_light_paths_layer()->set_light_paths(light_paths); diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h index 736c7eac29..4d22d60536 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathspickinghandler.h @@ -58,13 +58,19 @@ class LightPathsPickingHandler void set_enabled(const bool enabled); - void pick(const foundation::Vector2i& pixel) const; - void pick(const foundation::AABB2i& rect) const; + void pick(const foundation::Vector2i& pixel, QString* lpe); + void pick(const foundation::AABB2i& rect, QString* lpe); + void pick(QString* lpe); private: ViewportWidget* m_viewport_widget; const renderer::Project& m_project; bool m_enabled; + size_t m_prev_query_x_min; + size_t m_prev_query_y_min; + size_t m_prev_query_x_max; + size_t m_prev_query_y_max; + bool m_prev_query_valid; }; } // namespace studio diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp index 4390ea3259..8669b991c3 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.cpp @@ -47,12 +47,16 @@ #include "foundation/math/aabb.h" #include "foundation/math/vector.h" +// OSL headers. +#include "OSL/accum.h" + // Qt headers. #include #include #include #include #include +#include #include #include #include @@ -65,6 +69,7 @@ #include using namespace foundation; +using namespace OSL; using namespace renderer; namespace appleseed { @@ -90,6 +95,8 @@ LightPathsViewportManager::LightPathsViewportManager( light_paths_layer, SIGNAL(signal_light_path_selection_changed(const int, const int)), SLOT(slot_light_path_selection_changed(const int, const int))); + m_lpe_validator = new LpeValidator(); + create_toolbar(); recreate_handlers(); @@ -152,11 +159,17 @@ void LightPathsViewportManager::slot_entity_picked(const ScenePicker::PickingRes set_display_enabled(true); + QString* lpe = NULL; + if (m_lpe_input->hasAcceptableInput() && m_lpe_input->text().size() > 0) + { + lpe = &m_lpe_input->text(); + } const CanvasProperties& props = m_project->get_frame()->image().properties(); m_screen_space_paths_picking_handler->pick( Vector2i( result.m_ndc[0] * static_cast(props.m_canvas_width), - result.m_ndc[1] * static_cast(props.m_canvas_height))); + result.m_ndc[1] * static_cast(props.m_canvas_height)), + lpe); } void LightPathsViewportManager::slot_light_paths_display_toggled(const bool active) @@ -168,11 +181,17 @@ void LightPathsViewportManager::slot_rectangle_selection(const QRect& rect) { if (!m_picking_enabled || !m_enabled) return; + QString* lpe = NULL; + if (m_lpe_input->hasAcceptableInput() && m_lpe_input->text().size() > 0) + { + lpe = &m_lpe_input->text(); + } set_display_enabled(true); m_screen_space_paths_picking_handler->pick( AABB2i( Vector2i(rect.x(), rect.y()), - Vector2i(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1))); + Vector2i(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1)), + lpe); } void LightPathsViewportManager::slot_light_path_selection_changed( @@ -220,6 +239,19 @@ void LightPathsViewportManager::slot_camera_changed() if (!m_enabled) return; } +void LightPathsViewportManager::slot_lpe_input_editing_finished() +{ + if (!m_enabled) return; + + QString* lpe = NULL; + if (m_lpe_input->hasAcceptableInput() && m_lpe_input->text().size() > 0) + { + lpe = &m_lpe_input->text(); + } + set_display_enabled(true); + m_screen_space_paths_picking_handler->pick(lpe); +} + void LightPathsViewportManager::create_toolbar() { LightPathsLayer* light_paths_layer = m_viewport_tab->get_viewport_widget()->get_light_paths_layer(); @@ -297,6 +329,20 @@ void LightPathsViewportManager::create_toolbar() m_info_label->setObjectName("info_label"); m_toolbar->addWidget(m_info_label); + // Create a label and text input for light path expression filtering + m_lpe_label = new QLabel("Filter by Light Path Expression:"); + m_lpe_label->setObjectName("lpe_label"); + m_toolbar->addWidget(m_lpe_label); + + m_lpe_input = new QLineEdit(); + m_lpe_input->setObjectName("lpe_input"); + m_lpe_input->setValidator(m_lpe_validator); + connect( + m_lpe_input, SIGNAL(editingFinished()), + this, SLOT(slot_lpe_input_editing_finished()) + ); + m_toolbar->addWidget(m_lpe_input); + m_toolbar->setDisabled(true); m_toolbar->hide(); } diff --git a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h index 12e73bfc23..3b96313c60 100644 --- a/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h +++ b/src/appleseed.studio/mainwindow/rendering/lightpathsviewportmanager.h @@ -33,6 +33,7 @@ #include "mainwindow/rendering/lightpathspickinghandler.h" #include "mainwindow/rendering/renderclipboardhandler.h" #include "mainwindow/rendering/viewportwidget.h" +#include "utility/lpevalidator.h" #include "utility/mousecoordinatestracker.h" #include "utility/scrollareapanhandler.h" #include "utility/widgetzoomhandler.h" @@ -52,6 +53,7 @@ namespace appleseed { namespace studio { class ViewportTab; } } namespace renderer { class ParamArray; } namespace renderer { class Project; } class QLabel; +class QLineEdit; class QPoint; class QRect; class QScrollArea; @@ -99,6 +101,7 @@ class LightPathsViewportManager const int total_light_paths); void slot_save_light_paths(); void slot_camera_changed(); + void slot_lpe_input_editing_finished(); private: bool m_enabled; @@ -112,6 +115,10 @@ class LightPathsViewportManager QToolButton* m_prev_path_button; QToolButton* m_next_path_button; QLabel* m_info_label; + QLabel* m_lpe_label; + QLineEdit* m_lpe_input; + + LpeValidator* m_lpe_validator; std::unique_ptr m_pan_handler; std::unique_ptr m_mouse_tracker; diff --git a/src/appleseed.studio/utility/lpevalidator.cpp b/src/appleseed.studio/utility/lpevalidator.cpp new file mode 100644 index 0000000000..2f458b1a4e --- /dev/null +++ b/src/appleseed.studio/utility/lpevalidator.cpp @@ -0,0 +1,76 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "lpevalidator.h" + +using namespace OSL; + +namespace appleseed { +namespace studio { + +// LpeValidator implementation + +LpeValidator::LpeValidator() +{} + +QValidator::State LpeValidator::validate(QString& input, int& pos) const +{ + AccumAutomata automata; + + if (automata.addRule(input.toUtf8().data(), 0, false) != NULL) + { + return QValidator::State::Acceptable; + } + else + { + return QValidator::State::Intermediate; + } +} + +// LPSortAov implementation + +void LPSortAov::write( + void* flush_data, + Color3& color, + float alpha, + bool has_color, + bool has_alpha) +{ + size_t lp_idx = (size_t)flush_data; + if (has_color && color.x > 0) + // only add if there is a positive color in x + m_received.push_back(lp_idx); +} + +std::vector LPSortAov::get_received() const +{ + return m_received; +} + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed.studio/utility/lpevalidator.h b/src/appleseed.studio/utility/lpevalidator.h new file mode 100644 index 0000000000..8f02ae13b7 --- /dev/null +++ b/src/appleseed.studio/utility/lpevalidator.h @@ -0,0 +1,74 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// OSL headers +#include "OSL/accum.h" +#include "OSL/oslclosure.h" + +// Qt headers. +#include + +// std headers. +#include +#include + +using namespace OSL; + +namespace appleseed { +namespace studio { + +class LpeValidator + : public QValidator +{ + public: + LpeValidator(); + + QValidator::State validate(QString& input, int& pos) const override; +}; + +class LPSortAov : public Aov +{ + public: + LPSortAov() {} + + virtual ~LPSortAov() {} + + virtual void write(void* flush_data, + Color3& color, + float alpha, + bool has_color, + bool has_alpha) override; + + std::vector get_received() const; + + protected: + std::vector m_received; +}; + +} // namespace studio +} // namespace appleseed diff --git a/src/appleseed/CMakeLists.txt b/src/appleseed/CMakeLists.txt index ff67971d71..49e4a7fa7f 100644 --- a/src/appleseed/CMakeLists.txt +++ b/src/appleseed/CMakeLists.txt @@ -2145,6 +2145,8 @@ set (renderer_utility_sources renderer/utility/filesystem.cpp renderer/utility/filesystem.h renderer/utility/iostreamop.h + renderer/utility/lpsortaov.cpp + renderer/utility/lpsortaov.h renderer/utility/messagecontext.cpp renderer/utility/messagecontext.h renderer/utility/oiiomaketexture.cpp diff --git a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp index 12480e596a..6f45cf6b46 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp +++ b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.cpp @@ -35,6 +35,7 @@ #include "renderer/kernel/lighting/pathvertex.h" #include "renderer/modeling/color/colorspace.h" #include "renderer/modeling/entity/entity.h" +#include "renderer/utility/lpsortaov.h" // appleseed.foundation headers. #include "foundation/core/exceptions/exceptionioerror.h" @@ -46,6 +47,9 @@ #include "foundation/utility/stopwatch.h" #include "foundation/utility/string.h" +// OSL headers. +#include "OSL/accum.h" + // Standard headers. #include #include @@ -57,6 +61,7 @@ #include using namespace foundation; +using namespace OSL; namespace renderer { @@ -222,36 +227,136 @@ void LightPathRecorder::finalize( } } +namespace { + + struct LpePath { + std::vector> lpe_events; + size_t idx; + }; + +} + void LightPathRecorder::query( const size_t x0, const size_t y0, const size_t x1, const size_t y1, + const char* lpe, LightPathArray& result) const { assert(impl->m_streams.size() == 1); const LightPathStream* stream = impl->m_streams[0].get(); - for (size_t y = y0; y <= y1; ++y) + if (lpe == NULL) { - for (size_t x = x0; x <= x1; ++x) + for (size_t y = y0; y <= y1; ++y) { - const auto& index_entry = impl->m_index[y * impl->m_render_width + x]; - - for (size_t p = index_entry.m_begin_path; p < index_entry.m_end_path; ++p) + for (size_t x = x0; x <= x1; ++x) { - const auto& source_path = stream->m_paths[p]; + const auto& index_entry = impl->m_index[y * impl->m_render_width + x]; + + for (size_t p = index_entry.m_begin_path; p < index_entry.m_end_path; ++p) + { + const auto& source_path = stream->m_paths[p]; + + LightPath path; + path.m_pixel_coords[0] = source_path.m_pixel_coords[0]; + path.m_pixel_coords[1] = source_path.m_pixel_coords[1]; + path.m_sample_position[0] = source_path.m_sample_position[0]; + path.m_sample_position[1] = source_path.m_sample_position[1]; + path.m_vertex_begin_index = source_path.m_vertex_begin_index; + path.m_vertex_end_index = source_path.m_vertex_end_index; + + result.push_back(path); + } + } + } + } + else + { + std::vector possible_paths; - LightPath path; - path.m_pixel_coords[0] = source_path.m_pixel_coords[0]; - path.m_pixel_coords[1] = source_path.m_pixel_coords[1]; - path.m_sample_position[0] = source_path.m_sample_position[0]; - path.m_sample_position[1] = source_path.m_sample_position[1]; - path.m_vertex_begin_index = source_path.m_vertex_begin_index; - path.m_vertex_end_index = source_path.m_vertex_end_index; + assert(impl->m_streams.size() == 1); + const LightPathStream* stream = impl->m_streams[0].get(); - result.push_back(path); + for (size_t y = y0; y <= y1; ++y) + { + for (size_t x = x0; x <= x1; ++x) + { + const auto& index_entry = impl->m_index[y * impl->m_render_width + x]; + + for (size_t p = index_entry.m_begin_path; p < index_entry.m_end_path; ++p) + { + const auto& source_path = stream->m_paths[p]; + + std::vector> lpe_events; + for (size_t vertex_idx = source_path.m_vertex_begin_index; vertex_idx < source_path.m_vertex_end_index; vertex_idx++) + { + assert(vertex_idx < stream->m_vertices.size()); + const auto& source_vertex = stream->m_vertices[vertex_idx]; + std::array tag = { + ustring(&source_vertex.m_lpe_tag[0], 1), + ustring(&source_vertex.m_lpe_tag[1], 1) + }; + lpe_events.insert(lpe_events.begin(), tag); + } + + LpePath path; + path.idx = p; + path.lpe_events = lpe_events; + + possible_paths.push_back(path); + } + } + } + + AccumAutomata automata; + if (!automata.addRule(lpe, 0)) + { + RENDERER_LOG_INFO("LPE rule compilation failed"); + } + automata.compile(); + + LPSortAov* lp_sort_aov = new LPSortAov(); + + Accumulator accum(&automata); + accum.setAov(0, lp_sort_aov, false, false); + + for (size_t i = 0; i < possible_paths.size(); i++) { + accum.begin(); + accum.pushState(); + LpePath path = possible_paths[i]; + // for each ray stop in the path + for (size_t j = 0; j < path.lpe_events.size(); j++) { + // for each label in this hit + std::array tag = path.lpe_events[j]; + accum.move(tag[0]); + accum.move(tag[1]); + // always finish the hit with a stop label + accum.move(Labels::STOP); } + // Here is were we have reached a light, accumulate color + accum.accum(Color3(1, 1, 1)); + // Restore state and flush path index to store in our sorting Aov + accum.popState(); + accum.end((void *)(size_t)path.idx); + } + + const auto path_indices = lp_sort_aov->get_received(); + for (size_t i = 0; i < path_indices.size(); i++) + { + size_t p = path_indices[i]; + const auto& source_path = stream->m_paths[p]; + + LightPath path; + path.m_pixel_coords[0] = source_path.m_pixel_coords[0]; + path.m_pixel_coords[1] = source_path.m_pixel_coords[1]; + path.m_sample_position[0] = source_path.m_sample_position[0]; + path.m_sample_position[1] = source_path.m_sample_position[1]; + path.m_vertex_begin_index = source_path.m_vertex_begin_index; + path.m_vertex_end_index = source_path.m_vertex_end_index; + + result.push_back(path); } } } diff --git a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h index 93e094c774..0670fffca6 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h +++ b/src/appleseed/renderer/kernel/lighting/lightpathrecorder.h @@ -42,6 +42,7 @@ namespace renderer { class Entity; } namespace renderer { class LightPathStream; } namespace renderer { class Project; } +class QString; namespace renderer { @@ -115,13 +116,14 @@ class APPLESEED_DLLSYMBOL LightPathRecorder // Return the total number of stored vertices in all light paths. `finalize()` must have been called. size_t get_vertex_count() const; - // Retrieve all light paths falling into a region of the render. + // Retrieve all light paths falling into a region of the render and which optionally satisfy a given Light Path Expression. // All bounds are inclusive. `finalize()` must have been called. void query( const size_t x0, const size_t y0, const size_t x1, const size_t y1, + const char* lpe, LightPathArray& result) const; // Retrieve a given light path vertex. `finalize()` must have been called. diff --git a/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp b/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp index b075a29de4..fa55289634 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp +++ b/src/appleseed/renderer/kernel/lighting/lightpathstream.cpp @@ -57,6 +57,25 @@ using namespace foundation; namespace renderer { +namespace { + char get_lpe_scattering_mode(int mode) { + char scatter_mode; + if (ScatteringMode::has_diffuse(mode)) { + scatter_mode = 'D'; + } + else if (ScatteringMode::has_glossy(mode)) { + scatter_mode = 'G'; + } + else if (ScatteringMode::has_specular(mode)) { + scatter_mode = 'S'; + } + else { + scatter_mode = '-'; + } + return scatter_mode; + } +} + LightPathStream::LightPathStream(const Project& project) : m_scene(*project.get_scene()) // at this time the scene's render data are not available { @@ -111,6 +130,8 @@ void LightPathStream::hit_reflector(const PathVertex& vertex) data.m_vertex_position = Vector3f(vertex.get_point()); data.m_surface_normal = Vector3f(vertex.get_geometric_normal()); data.m_path_throughput = vertex.m_throughput.to_rgb(g_std_lighting_conditions); + char scatter_mode = get_lpe_scattering_mode(vertex.m_scattering_modes); + data.m_lpe_tag = { { 'R', scatter_mode } }; m_hit_reflector_data.push_back(data); } @@ -130,6 +151,8 @@ void LightPathStream::hit_emitter( data.m_object_instance = &vertex.m_shading_point->get_object_instance(); data.m_vertex_position = Vector3f(vertex.get_point()); data.m_surface_normal = Vector3f(vertex.get_geometric_normal()); + char scatter_mode = get_lpe_scattering_mode(vertex.m_scattering_modes); + data.m_lpe_tag = { { 'O', scatter_mode } }; data.m_path_throughput = vertex.m_throughput.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_hit_emitter_data.push_back(data); @@ -153,6 +176,7 @@ void LightPathStream::sampled_emitting_shape( shape.get_object_instance_index()); data.m_vertex_position = Vector3f(emission_position); data.m_surface_normal = Vector3f(emission_normal); + data.m_lpe_tag = { { 'O', '_' } }; data.m_material_value = material_value.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_sampled_emitter_data.push_back(data); @@ -174,6 +198,7 @@ void LightPathStream::sampled_non_physical_light( data.m_entity = &light; data.m_vertex_position = Vector3f(emission_position); data.m_surface_normal = Vector3f(emission_normal); + data.m_lpe_tag = { { 'L', '_' } }; data.m_material_value = material_value.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_sampled_emitter_data.push_back(data); @@ -193,6 +218,7 @@ void LightPathStream::sampled_environment( SampledEnvData data; data.m_environment_edf = &environment_edf; data.m_emission_direction = emission_direction; + data.m_lpe_tag = { { 'B', '_' } }; data.m_material_value = material_value.to_rgb(g_std_lighting_conditions); data.m_emitted_radiance = emitted_radiance.to_rgb(g_std_lighting_conditions); m_sampled_env_data.push_back(data); @@ -254,6 +280,7 @@ void LightPathStream::create_path_from_hit_emitter(const size_t emitter_event_in emitter_vertex.m_position = hit_emitter_data.m_vertex_position; emitter_vertex.m_surface_normal = hit_emitter_data.m_surface_normal; emitter_vertex.m_radiance = hit_emitter_data.m_emitted_radiance; + emitter_vertex.m_lpe_tag = hit_emitter_data.m_lpe_tag; m_vertices.push_back(emitter_vertex); Color3f current_radiance = hit_emitter_data.m_emitted_radiance; @@ -274,6 +301,7 @@ void LightPathStream::create_path_from_hit_emitter(const size_t emitter_event_in reflector_vertex.m_entity = event_data.m_object_instance; reflector_vertex.m_position = event_data.m_vertex_position; reflector_vertex.m_surface_normal = event_data.m_surface_normal; + reflector_vertex.m_lpe_tag = hit_emitter_data.m_lpe_tag; reflector_vertex.m_radiance = current_radiance; m_vertices.push_back(reflector_vertex); @@ -299,6 +327,7 @@ void LightPathStream::create_path_from_hit_emitter(const size_t emitter_event_in camera_vertex.m_entity = m_camera; camera_vertex.m_position = m_camera_vertex_position; camera_vertex.m_surface_normal = m_camera_vertex_normal; + camera_vertex.m_lpe_tag = { 'C', '_' }; camera_vertex.m_radiance = current_radiance; m_vertices.push_back(camera_vertex); @@ -331,6 +360,7 @@ void LightPathStream::create_path_from_sampled_emitter(const size_t emitter_even emitter_vertex.m_entity = sampled_emitter_data.m_entity; emitter_vertex.m_position = sampled_emitter_data.m_vertex_position; emitter_vertex.m_surface_normal = sampled_emitter_data.m_surface_normal; + emitter_vertex.m_lpe_tag = sampled_emitter_data.m_lpe_tag; emitter_vertex.m_radiance = sampled_emitter_data.m_emitted_radiance; m_vertices.push_back(emitter_vertex); @@ -352,6 +382,7 @@ void LightPathStream::create_path_from_sampled_emitter(const size_t emitter_even reflector_vertex.m_entity = event_data.m_object_instance; reflector_vertex.m_position = event_data.m_vertex_position; reflector_vertex.m_surface_normal = event_data.m_surface_normal; + reflector_vertex.m_lpe_tag = event_data.m_lpe_tag; reflector_vertex.m_radiance = current_radiance; m_vertices.push_back(reflector_vertex); @@ -378,6 +409,7 @@ void LightPathStream::create_path_from_sampled_emitter(const size_t emitter_even camera_vertex.m_position = m_camera_vertex_position; camera_vertex.m_surface_normal = m_camera_vertex_normal; camera_vertex.m_radiance = current_radiance; + camera_vertex.m_lpe_tag = { 'C', '_' }; m_vertices.push_back(camera_vertex); // Store path. @@ -409,6 +441,7 @@ void LightPathStream::create_path_from_sampled_environment(const size_t env_even emitter_vertex.m_entity = sampled_env_data.m_environment_edf; emitter_vertex.m_position = last_reflector_data.m_vertex_position + m_scene_diameter * sampled_env_data.m_emission_direction; emitter_vertex.m_surface_normal = -sampled_env_data.m_emission_direction; + emitter_vertex.m_lpe_tag = sampled_env_data.m_lpe_tag; emitter_vertex.m_radiance = sampled_env_data.m_emitted_radiance; m_vertices.push_back(emitter_vertex); @@ -430,6 +463,7 @@ void LightPathStream::create_path_from_sampled_environment(const size_t env_even reflector_vertex.m_entity = event_data.m_object_instance; reflector_vertex.m_position = event_data.m_vertex_position; reflector_vertex.m_surface_normal = event_data.m_surface_normal; + reflector_vertex.m_lpe_tag = event_data.m_lpe_tag; reflector_vertex.m_radiance = current_radiance; m_vertices.push_back(reflector_vertex); @@ -455,6 +489,7 @@ void LightPathStream::create_path_from_sampled_environment(const size_t env_even camera_vertex.m_entity = m_camera; camera_vertex.m_position = m_camera_vertex_position; camera_vertex.m_surface_normal = m_camera_vertex_normal; + camera_vertex.m_lpe_tag = { 'C', '_' }; camera_vertex.m_radiance = current_radiance; m_vertices.push_back(camera_vertex); diff --git a/src/appleseed/renderer/kernel/lighting/lightpathstream.h b/src/appleseed/renderer/kernel/lighting/lightpathstream.h index 9f4c33ffb1..b2de1fae30 100644 --- a/src/appleseed/renderer/kernel/lighting/lightpathstream.h +++ b/src/appleseed/renderer/kernel/lighting/lightpathstream.h @@ -30,6 +30,7 @@ // appleseed.renderer headers. #include "renderer/global/globaltypes.h" +#include "renderer/kernel/lighting/scatteringmode.h" // appleseed.foundation headers. #include "foundation/image/color.h" @@ -122,6 +123,7 @@ class LightPathStream foundation::Vector3f m_vertex_position; // world space position of the hit point on the reflector foundation::Vector3f m_surface_normal; // world space normal of the surface of the reflector foundation::Color3f m_path_throughput; // cumulative path throughput up to but excluding this vertex, in reverse order (i.e. in the order from camera to light source) + lpe_event_tag m_lpe_tag; // the light path expression tag for this event }; struct HitEmitterData @@ -137,6 +139,7 @@ class LightPathStream foundation::Vector3f m_surface_normal; // world space normal of the surface of the emitter foundation::Color3f m_material_value; // BSDF value at the previous vertex foundation::Color3f m_emitted_radiance; // emitted radiance in W.sr^-1.m^-2 + lpe_event_tag m_lpe_tag; // the light path expression tag for this event }; struct SampledEnvData @@ -145,6 +148,7 @@ class LightPathStream foundation::Vector3f m_emission_direction; // world space emission direction pointing toward the environment foundation::Color3f m_material_value; // BSDF value at the previous vertex foundation::Color3f m_emitted_radiance; // emitted radiance in W.sr^-1.m^-2 + lpe_event_tag m_lpe_tag; // the light path expression tag for this event }; typedef foundation::Vector Vector2u16; @@ -155,6 +159,7 @@ class LightPathStream foundation::Vector2f m_sample_position; std::uint32_t m_vertex_begin_index; // index of the first vertex in m_vertices std::uint32_t m_vertex_end_index; // index of one vertex past the last one in m_vertices + foundation::Vector3f m_surface_normal; }; struct StoredPathVertex @@ -163,6 +168,7 @@ class LightPathStream foundation::Vector3f m_position; // world space position of this vertex foundation::Color3f m_radiance; // radiance arriving at this vertex, in W.sr^-1.m^-2 foundation::Vector3f m_surface_normal; + lpe_event_tag m_lpe_tag; // the light path expression tag for this event }; // Scene. diff --git a/src/appleseed/renderer/kernel/lighting/scatteringmode.h b/src/appleseed/renderer/kernel/lighting/scatteringmode.h index 2456c02b05..b02b7d0b74 100644 --- a/src/appleseed/renderer/kernel/lighting/scatteringmode.h +++ b/src/appleseed/renderer/kernel/lighting/scatteringmode.h @@ -32,6 +32,7 @@ #include "renderer/modeling/scene/visibilityflags.h" // Standard headers. +#include #include namespace renderer @@ -140,4 +141,8 @@ inline VisibilityFlags::Type ScatteringMode::get_vis_flags(const Mode mode) } } +// A tag for a light path expression event which contains two characters, corresponding +// to the event type and scattering mode. +typedef std::array lpe_event_tag; + } // namespace renderer diff --git a/src/appleseed/renderer/utility/lpsortaov.cpp b/src/appleseed/renderer/utility/lpsortaov.cpp new file mode 100644 index 0000000000..54e5da67e2 --- /dev/null +++ b/src/appleseed/renderer/utility/lpsortaov.cpp @@ -0,0 +1,55 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "lpsortaov.h" + +using namespace OSL; + +namespace renderer { + +// LPSortAov implementation + +void LPSortAov::write( + void* flush_data, + Color3& color, + float alpha, + bool has_color, + bool has_alpha) +{ + size_t lp_idx = (size_t)flush_data; + if (has_color && color.x > 0) + // only add if there is a positive color in x + m_received.push_back(lp_idx); +} + +std::vector LPSortAov::get_received() const +{ + return m_received; +} + +} // namespace renderer diff --git a/src/appleseed/renderer/utility/lpsortaov.h b/src/appleseed/renderer/utility/lpsortaov.h new file mode 100644 index 0000000000..30f7631e1a --- /dev/null +++ b/src/appleseed/renderer/utility/lpsortaov.h @@ -0,0 +1,62 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2019 Gray Olson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// OSL headers +#include "OSL/accum.h" +#include "OSL/oslclosure.h" + +// Qt headers. + +// std headers. +#include +#include + +using namespace OSL; + +namespace renderer { + +class LPSortAov : public Aov +{ + public: + LPSortAov() {} + + virtual ~LPSortAov() {} + + virtual void write(void* flush_data, + Color3& color, + float alpha, + bool has_color, + bool has_alpha) override; + + std::vector get_received() const; + + protected: + std::vector m_received; +}; + +} // namespace renderer