diff --git a/src/appleseed.python/bindpostprocessingstage.cpp b/src/appleseed.python/bindpostprocessingstage.cpp index c1a41239ba..d0b8be246c 100644 --- a/src/appleseed.python/bindpostprocessingstage.cpp +++ b/src/appleseed.python/bindpostprocessingstage.cpp @@ -82,6 +82,7 @@ void bind_post_processing_stage() .def("__init__", bpy::make_constructor(create_post_processing_stage)) .def("get_model", &PostProcessingStage::get_model) .def("get_order", &PostProcessingStage::get_order); + // @Note: this might need to be updated if a "real_time_preview" flag is added. bind_typed_entity_vector("PostProcessingStageContainer"); diff --git a/src/appleseed.studio/mainwindow/mainwindow.cpp b/src/appleseed.studio/mainwindow/mainwindow.cpp index 42ce097d59..54091a0ada 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.cpp +++ b/src/appleseed.studio/mainwindow/mainwindow.cpp @@ -777,6 +777,10 @@ void MainWindow::update_project_explorer() m_project_explorer, SIGNAL(signal_project_modified()), SLOT(slot_project_modified())); + connect( + m_project_explorer, SIGNAL(signal_post_processing_stage_modified(const std::uint64_t)), + SLOT(slot_post_processing_stage_modified(const std::uint64_t))); + connect( m_project_explorer, SIGNAL(signal_frame_modified()), SLOT(slot_frame_modified())); @@ -1264,6 +1268,25 @@ void MainWindow::start_rendering(const RenderingMode rendering_mode) m_render_tabs["RGB"]); } +namespace +{ + auto_release_ptr make_temporary_frame_copy(const Frame& frame) + { + // todo: creating a frame with denoising enabled is very expensive, see benchmark_frame.cpp. + ParamArray params_copy(frame.get_parameters()); + params_copy.remove_path("denoiser"); + + // Make a temporary copy of the frame. + // Render info, AOVs and other data are not copied. + auto_release_ptr working_frame = + FrameFactory::create((std::string(frame.get_name()) + "_copy").c_str(), params_copy); + + working_frame->image().copy_from(frame.image()); + + return working_frame; + } +} + void MainWindow::apply_false_colors_settings() { Project* project = m_project_manager.get_project(); @@ -1277,15 +1300,7 @@ void MainWindow::apply_false_colors_settings() if (false_colors_enabled) { - // Make a temporary copy of the frame. - // Render info, AOVs and other data are not copied. - // todo: creating a frame with denoising enabled is very expensive, see benchmark_frame.cpp. - auto_release_ptr working_frame = - FrameFactory::create( - (std::string(frame->get_name()) + "_copy").c_str(), - frame->get_parameters() - .remove_path("denoiser")); - working_frame->image().copy_from(frame->image()); + auto_release_ptr working_frame = make_temporary_frame_copy(*frame); // Create post-processing stage. auto_release_ptr stage( @@ -1597,6 +1612,28 @@ void MainWindow::slot_project_modified() update_window_title(); } +void MainWindow::slot_post_processing_stage_modified(const std::uint64_t stage_uid) +{ + Project* project = m_project_manager.get_project(); + assert(project != nullptr); + + Frame* frame = project->get_frame(); + assert(frame != nullptr); + + auto_release_ptr working_frame = make_temporary_frame_copy(*frame); + + // Preview the post-processing stage that was modified. + for (PostProcessingStage& stage : frame->post_processing_stages()) + { + if (stage.get_uid() == stage_uid) { + apply_post_processing_stage(stage, working_frame.ref()); + return; + } + } + + assert(false); +} + void MainWindow::slot_toggle_project_file_monitoring(const bool checked) { if (checked) diff --git a/src/appleseed.studio/mainwindow/mainwindow.h b/src/appleseed.studio/mainwindow/mainwindow.h index ba6b8c20c5..7167cdc9d4 100644 --- a/src/appleseed.studio/mainwindow/mainwindow.h +++ b/src/appleseed.studio/mainwindow/mainwindow.h @@ -256,6 +256,7 @@ class MainWindow void slot_pack_project_as(); void slot_close_project(); void slot_project_modified(); + void slot_post_processing_stage_modified(const std::uint64_t stage_uid); // Project file monitoring. void slot_toggle_project_file_monitoring(const bool checked); diff --git a/src/appleseed.studio/mainwindow/project/projectbuilder.h b/src/appleseed.studio/mainwindow/project/projectbuilder.h index 54e6ffc7d9..63e89f982a 100644 --- a/src/appleseed.studio/mainwindow/project/projectbuilder.h +++ b/src/appleseed.studio/mainwindow/project/projectbuilder.h @@ -39,6 +39,7 @@ #include "renderer/api/environment.h" #include "renderer/api/environmentedf.h" #include "renderer/api/light.h" +#include "renderer/api/postprocessing.h" #include "renderer/api/project.h" #include "renderer/api/scene.h" #include "renderer/api/shadergroup.h" @@ -96,12 +97,20 @@ class ProjectBuilder ParentEntity& parent, const foundation::Dictionary& values) const; + // Simulate partial specialization of edit_entity() for Entity = renderer::PostProcessingStage. + template + renderer::PostProcessingStage* edit_entity( + renderer::PostProcessingStage* old_entity, + ParentEntity& parent, + const foundation::Dictionary& values) const; + renderer::Frame* edit_frame( const foundation::Dictionary& values) const; signals: void signal_project_modified() const; void signal_frame_modified() const; + void signal_post_processing_stage_modified(const std::uint64_t stage_uid) const; public slots: void slot_notify_project_modification() const; @@ -300,6 +309,52 @@ inline renderer::Light* ProjectBuilder::edit_entity( return new_entity_ptr; } +template +inline renderer::PostProcessingStage* ProjectBuilder::edit_entity( + renderer::PostProcessingStage* old_entity, + ParentEntity& parent, + const foundation::Dictionary& values) const +{ + foundation::auto_release_ptr new_entity( + create_entity(values)); + renderer::PostProcessingStage* new_entity_ptr = new_entity.get(); + + renderer::EntityTraits::remove_entity(old_entity, parent); + renderer::EntityTraits::insert_entity(new_entity, parent); + + slot_notify_project_modification(); + + // @Note: we need to know whether or not to emit the signal. For this, + // we can either: + // * change the `PostProcessingStage` class to have a `bool` flag (not good) + // * pass this flag in its `params`, so that we can retrieve it here (maybe?) + // * pass the flag through `values` instead (not sure if it'd be better or not) + // + // @Fixme: if the flag was true and is now false, the effect needs to be "unapplied"! + +#if 0 + // @Note: using this to be able to inspect Dictionary values in Visual Studio.. :( + bool preview_enabled = false; + for (const auto& str : values.strings()) + { + const auto key = std::string(str.key()); + const auto value = std::string(str.value()); + if (key == "real_time_preview") + preview_enabled = true; + if (value == "real_time_preview") + preview_enabled = true; + } +#else + const bool preview_enabled = new_entity_ptr->get_parameters().get_required("real_time_preview"); +#endif + + // Signal the modified stage, so that it can be previewed. + if (preview_enabled) + emit signal_post_processing_stage_modified(new_entity_ptr->get_uid()); + + return new_entity_ptr; +} + template foundation::auto_release_ptr ProjectBuilder::create_entity( const foundation::Dictionary& values) const diff --git a/src/appleseed.studio/mainwindow/project/projectexplorer.cpp b/src/appleseed.studio/mainwindow/project/projectexplorer.cpp index 7f6f9306da..18e0ff6c47 100644 --- a/src/appleseed.studio/mainwindow/project/projectexplorer.cpp +++ b/src/appleseed.studio/mainwindow/project/projectexplorer.cpp @@ -118,6 +118,10 @@ ProjectExplorer::ProjectExplorer( connect( &m_project_builder, SIGNAL(signal_frame_modified()), SIGNAL(signal_frame_modified())); + + connect( + &m_project_builder, SIGNAL(signal_post_processing_stage_modified(const std::uint64_t)), + SIGNAL(signal_post_processing_stage_modified(const std::uint64_t))); } ProjectExplorer::~ProjectExplorer() diff --git a/src/appleseed.studio/mainwindow/project/projectexplorer.h b/src/appleseed.studio/mainwindow/project/projectexplorer.h index 67730da9e5..6e8eda2e7f 100644 --- a/src/appleseed.studio/mainwindow/project/projectexplorer.h +++ b/src/appleseed.studio/mainwindow/project/projectexplorer.h @@ -87,6 +87,7 @@ class ProjectExplorer signals: void signal_project_modified() const; void signal_frame_modified() const; + void signal_post_processing_stage_modified(const std::uint64_t stage_uid) const; private: QTreeWidget* m_tree_widget; diff --git a/src/appleseed.studio/mainwindow/project/tools.h b/src/appleseed.studio/mainwindow/project/tools.h index 512ef04ac9..d24584cccd 100644 --- a/src/appleseed.studio/mainwindow/project/tools.h +++ b/src/appleseed.studio/mainwindow/project/tools.h @@ -129,7 +129,7 @@ class LineEditSliderAdaptor // -// Binds QLineEdit and qtcommon::DoubleSlider controls together such that updading +// Binds QLineEdit and qtcommon::DoubleSlider controls together such that updating // the value in one control updates the value in the other. // diff --git a/src/appleseed/renderer/modeling/postprocessingstage/ipostprocessingstagefactory.cpp b/src/appleseed/renderer/modeling/postprocessingstage/ipostprocessingstagefactory.cpp index d90655ee12..5d888eecc3 100644 --- a/src/appleseed/renderer/modeling/postprocessingstage/ipostprocessingstagefactory.cpp +++ b/src/appleseed/renderer/modeling/postprocessingstage/ipostprocessingstagefactory.cpp @@ -55,6 +55,14 @@ void IPostProcessingStageFactory::add_common_input_metadata(DictionaryArray& met .insert("type", "soft")) .insert("use", "required") .insert("default", "0")); + + metadata.push_back( + Dictionary() + .insert("name", "real_time_preview") + .insert("label", "Real-time Preview") + .insert("type", "boolean") + .insert("use", "required") + .insert("default", "false")); } } // namespace renderer