From 42fc258a539ee44e61608ebdd5e7a80f69759916 Mon Sep 17 00:00:00 2001
From: Mathias Paulin <mathias.paulin@irit.fr>
Date: Thu, 25 Mar 2021 08:26:20 +0100
Subject: [PATCH] Fix several problems and missing functionality for the
 Visualization Renderer controller

---
 src/DemoApp/main.cpp                          | 132 +++---------------
 src/Mara/RadiumPlayer.cpp                     |  14 +-
 src/libRender/CMakeLists.txt                  |   8 +-
 .../RadiumNBR/Gui/FullFeaturedRendererGui.cpp |  24 ++--
 .../RadiumNBR/Gui/FullFeaturedRendererGui.hpp |   2 +-
 src/libRender/RadiumNBR/Gui/RendererPanel.cpp |  62 ++++++--
 src/libRender/RadiumNBR/Gui/RendererPanel.hpp |  20 ++-
 .../RadiumNBR/Gui/VisualizationGui.cpp        |  71 ++++++++++
 .../RadiumNBR/Gui/VisualizationGui.hpp        |  11 ++
 src/libRender/RadiumNBR/NodeBasedRenderer.cpp |   3 +-
 src/libRender/RadiumNBR/NodeBasedRenderer.hpp |   6 +-
 .../Passes/CustomAttribToColorPass.cpp        |  13 +-
 .../Passes/CustomAttribToColorPass.hpp        |   2 +
 .../{ => Renderer}/FullFeatureRenderer.cpp    |   2 +-
 .../{ => Renderer}/FullFeatureRenderer.hpp    |   0
 .../RadiumNBR/Renderer/Visualization.cpp      | 120 ++++++++++++++++
 .../RadiumNBR/Renderer/Visualization.hpp      |  53 +++++++
 17 files changed, 392 insertions(+), 151 deletions(-)
 create mode 100644 src/libRender/RadiumNBR/Gui/VisualizationGui.cpp
 create mode 100644 src/libRender/RadiumNBR/Gui/VisualizationGui.hpp
 rename src/libRender/RadiumNBR/{ => Renderer}/FullFeatureRenderer.cpp (99%)
 rename src/libRender/RadiumNBR/{ => Renderer}/FullFeatureRenderer.hpp (100%)
 create mode 100644 src/libRender/RadiumNBR/Renderer/Visualization.cpp
 create mode 100644 src/libRender/RadiumNBR/Renderer/Visualization.hpp

diff --git a/src/DemoApp/main.cpp b/src/DemoApp/main.cpp
index bdddcfb..47090cd 100644
--- a/src/DemoApp/main.cpp
+++ b/src/DemoApp/main.cpp
@@ -10,15 +10,12 @@
 
 // Include the customizable renderer system (only the used part here)
 #include <RadiumNBR/NodeBasedRenderer.hpp>
-#include <RadiumNBR/Passes/ClearPass.hpp>
-#include <RadiumNBR/Passes/CustomAttribToColorPass.hpp>
-#include <RadiumNBR/Passes/GeomPrepass.hpp>
+#include <RadiumNBR/Renderer/Visualization.hpp>
 
 // To add attribs to loaded objects
 #include <Engine/Data/Mesh.hpp>
 #include <Engine/Rendering/RenderObject.hpp>
 #include <Engine/Rendering/RenderObjectManager.hpp>
-#include <Engine/Scene/EntityManager.hpp>
 #include <Engine/Scene/SystemDisplay.hpp>
 
 #include <Core/Utils/Log.hpp>
@@ -26,91 +23,24 @@ using namespace Ra::Core::Utils; // log
 
 /**
  * This class parameterize the renderer just after the OpenGL system was initialized.
- * when a method of this controler is called, the OpenGL context of the drawing window is activated.
+ * when a method of this controller is called, the OpenGL context of the drawing window is
+ * activated.
  */
-class RendererController : public RadiumNBR::RenderControlFunctor
+class RendererController : public RadiumNBR::VisualizationController
 {
   public:
-    /*
-     * Called once : configure the renderer by adding passes and allocating controler resources
-     */
     void configure( RadiumNBR::NodeBasedRenderer* renderer, int w, int h ) override {
-        LOG( Ra::Core::Utils::logINFO )
-            << "Customizing the renderer " << renderer->getRendererName();
-
-        //! [Caching some helpers and data from the Engine and the renderer]
-        auto resourcesCheck = Ra::Core::Resources::getResourcesPath(
-            reinterpret_cast<void*>( &RadiumNBR::NodeBasedRendererMagic ),
-            {"Resources/RadiumNBR"} );
-        if ( !resourcesCheck )
-        {
-            LOG( Ra::Core::Utils::logERROR ) << "Unable to find resources for NodeBasedRenderer!";
-            return;
-        }
-        else
-        {
-            LOG( Ra::Core::Utils::logINFO )
-                << "NodeBasedRenderer Resources are at " << *resourcesCheck;
-        }
-        auto resourcesPath{*resourcesCheck};
-        auto shaderManager = Ra::Engine::RadiumEngine::getInstance()->getShaderProgramManager();
-        auto colortexture  = renderer->sharedTextures().find( "Linear RGB (RadiumNBR)" );
-        auto depthtexture  = renderer->sharedTextures().find( "Depth (RadiumNBR)" );
-        //! [Caching some helpers and data from the Engine and the renderer]
-
-        //! [Adding a clear-screen pass]
-        {
-            // pass that draw no object and is positioned at rank 0
-            auto pass = std::make_shared<RadiumNBR::ClearPass>( nullptr, 0 );
-            // set the output of the pass : clear the renderer Linear RGB output texture
-            pass->setOutput( *colortexture );
-            // set the clear color (pass internal state)
-            pass->setBackground( renderer->getBackgroundColor() );
-            pass->initializePass( w, h, shaderManager );
-            // add the pass to the renderer and activate it
-            renderer->addPass( pass, pass->index() );
-            pass->activate();
-        }
-        //! [Adding a clear-screen pass]
-
-        //! [Adding a Z-only pass]
-        {
-            // the z-only pass takes all the objects and draw them only on the shared depth buffer.
-            auto pass = std::make_shared<RadiumNBR::GeomPrePass>( renderer->allRenderObjects(), 1 );
-            // set the output to the shared depth texture
-            pass->setOutput( *depthtexture );
-            // configure access to shader/resources files and initialize the pass
-            pass->setResourcesDir( resourcesPath );
-            pass->initializePass( w, h, shaderManager );
-            // add the pass to the renderer and activate it
-            renderer->addPass( pass, pass->index() );
-            pass->activate();
-        }
-        //! [Adding a Z-only pass]
-
-        //! [Adding a CustomAttribToColorPass pass]
-        {
-            // this pass draw all the objects using the custom color function,
-            // Rendering is done against the shared z-buffer for hidden line removal and
-            // into the shared color buffer.
-            m_customPass = std::make_shared<RadiumNBR::CustomAttribToColorPass>(
-                renderer->allRenderObjects(), 2 );
-            // set the input/output textures
-            m_customPass->setInputs( *depthtexture );
-            m_customPass->setOutput( *colortexture );
-            m_customPass->setLightManager( renderer->getLightManager() );
-            m_customPass->setAttribToColorFunc( m_vertexFunction, m_fragmentFunction );
-            // configure access to shader/resources files and initialize the pass
-            m_customPass->setResourcesDir( resourcesPath );
-            m_customPass->initializePass( w, h, shaderManager );
-            // add the pass to the renderer and activate it
-            renderer->addPass( m_customPass, m_customPass->index() );
-            m_customPass->activate();
-        }
-        //! [Adding a CustomAttribToColorPass pass]
+        /*
+         * Called once : configure the renderer by adding passes and allocating controller resources
+         */
+        RadiumNBR::VisualizationController::configure( renderer, w, h );
+        /* add here your specific configure step :
+         *      -- add more passes, allocate resources, ...
+         */
     }
 
-    void update( const Ra::Engine::Data::ViewingParameters& ) override{
+    void update( const Ra::Engine::Data::ViewingParameters& params ) override {
+        RadiumNBR::VisualizationController::update( params );
         /* This function is called once before each frame.
          * You can use it to modify the scene, or modify the computation function of the renderer
          * For this, just call
@@ -119,29 +49,12 @@ class RendererController : public RadiumNBR::RenderControlFunctor
          */
     };
 
-    void resize( int w, int h ) override{
+    void resize( int w, int h ) override {
+        RadiumNBR::VisualizationController::resize( w, h );
         /* if you have resizeable resources in your controller, this method is called each time the
          * viewer window is resized so that you can dimension your resources accordingly
          */
     };
-
-    /// Set the custom glsl function for attrib management (vertex) and colorcomputation (fragment)
-    void setAttribToColorFunc( const std::string& vertex_source,
-                               const std::string& fragment_source ) {
-        m_vertexFunction   = vertex_source;
-        m_fragmentFunction = fragment_source;
-        if ( m_customPass )
-        { m_customPass->setAttribToColorFunc( m_vertexFunction, m_fragmentFunction ); }
-    }
-
-  private:
-    /// The custom pass if needed for modification
-    std::shared_ptr<RadiumNBR::CustomAttribToColorPass> m_customPass;
-
-    ///
-    std::string m_vertexFunction{"void outputCustomAttribs() {}"};
-    std::string m_fragmentFunction{"vec4 computeCustomColor(Material mat, vec3 lightDir, vec3 "
-                                   "viewDir) {return vec4(1, 0, 0, 1);}"};
 };
 
 /**
@@ -197,22 +110,19 @@ void AddCustomAttributeToMeshes() {
                     {
                         addAttrib( "myCustomAttrib",
                                    Ra::Core::Vector4Array{displayMesh->getNumVertices(),
-                                                          /*Ra::Core::Utils::Color::White()*/ {
-                                                              0.7_ra, 0.8_ra, 0.8_ra, 1_ra}} );
+                                                          {0.6_ra, 0.6_ra, 0.7_ra, 1_ra}} );
                     }
                     else if ( meshTypeHash == typeid( Ra::Engine::Data::PolyMesh ).hash_code() )
                     {
                         addAttrib( "myCustomAttrib",
-                                   Ra::Core::Vector4Array{
-                                       displayMesh->getNumVertices(),
-                                       Ra::Core::Utils::Color{0.8_ra, 0.7_ra, 0.8_ra}} );
+                                   Ra::Core::Vector4Array{displayMesh->getNumVertices(),
+                                                          {0.6_ra, 0.7_ra, 0.6_ra, 1_ra}} );
                     }
                     else if ( meshTypeHash == typeid( Ra::Engine::Data::PointCloud ).hash_code() )
                     {
                         addAttrib( "myCustomAttrib",
-                                   Ra::Core::Vector4Array{
-                                       displayMesh->getNumVertices(),
-                                       Ra::Core::Utils::Color{0.3_ra, 0.3_ra, 0.8_ra}} );
+                                   Ra::Core::Vector4Array{displayMesh->getNumVertices(),
+                                                          {0.7_ra, 0.6_ra, 0.6_ra, 1_ra}} );
                     }
                     else
                     {
@@ -311,7 +221,7 @@ int main( int argc, char* argv[] ) {
     //! [Instatiating the renderer giving a customization functor]
     RendererController renderControl;
     auto renderer = std::make_shared<RadiumNBR::NodeBasedRenderer>( renderControl );
-
+    renderControl.setAttribToColorFunc( customVertexAttrib, customFragmentColor );
     //! [Instatiating the renderer giving a customization functor]
 
     //! [Instatiating the application]
diff --git a/src/Mara/RadiumPlayer.cpp b/src/Mara/RadiumPlayer.cpp
index 7206426..6577cde 100644
--- a/src/Mara/RadiumPlayer.cpp
+++ b/src/Mara/RadiumPlayer.cpp
@@ -14,9 +14,11 @@ using namespace Ra::Core::Utils; // for LOG( logLEVEL )
 #include <QCommandLineParser>
 #include <QFileDialog>
 
-#include <RadiumNBR/FullFeatureRenderer.hpp>
 #include <RadiumNBR/Gui/FullFeaturedRendererGui.hpp>
+#include <RadiumNBR/Renderer/FullFeatureRenderer.hpp>
 
+#include <RadiumNBR/Gui/VisualizationGui.hpp>
+#include <RadiumNBR/Renderer/Visualization.hpp>
 #ifdef WITH_H3D_SUPPORT
 #    include <RadiumH3D/h3DLoader.hpp>
 #endif
@@ -141,6 +143,16 @@ void RadiumPlayer::addRenderers() {
             RadiumNBR::buildRadiumNBRGui( myRenderer.get(), [this]() { this->askForUpdate(); } );
         mainWindow->addRenderer( rendererName, myRenderer, controlPanel );
     }
+    // add visualization renderer
+    {
+        // TODO, this is a memory leak ... Who's owning the controller
+        auto renderControl       = new RadiumNBR::VisualizationController;
+        auto myRenderer          = std::make_shared<RadiumNBR::NodeBasedRenderer>( *renderControl );
+        std::string rendererName = myRenderer->getRendererName();
+        auto controlPanel =
+            RadiumNBR::buildControllerGui( myRenderer.get(), [this]() { this->askForUpdate(); } );
+        mainWindow->addRenderer( rendererName, myRenderer, controlPanel );
+    }
 }
 
 void RadiumPlayer::addApplicationExtension() {
diff --git a/src/libRender/CMakeLists.txt b/src/libRender/CMakeLists.txt
index 7853fff..7e0f3e8 100644
--- a/src/libRender/CMakeLists.txt
+++ b/src/libRender/CMakeLists.txt
@@ -31,7 +31,8 @@ set(markdowns
 set(sources
     RadiumNBR/NodeBasedRenderer.cpp
     RadiumNBR/EnvMap.cpp
-    RadiumNBR/FullFeatureRenderer.cpp
+    RadiumNBR/Renderer/FullFeatureRenderer.cpp
+    RadiumNBR/Renderer/Visualization.cpp
     RadiumNBR/SphereSampler.cpp
     RadiumNBR/Passes/ClearPass.cpp
     RadiumNBR/Passes/CustomAttribToColorPass.cpp
@@ -50,7 +51,8 @@ set(public_headers
     RadiumNBR/NodeBasedRendererMacro.hpp
     RadiumNBR/NodeBasedRenderer.hpp
     RadiumNBR/EnvMap.hpp
-    RadiumNBR/FullFeatureRenderer.hpp
+    RadiumNBR/Renderer/FullFeatureRenderer.hpp
+    RadiumNBR/Renderer/Visualization.hpp
     RadiumNBR/RenderPass.hpp
     RadiumNBR/SphereSampler.hpp
     RadiumNBR/Passes/ClearPass.hpp
@@ -131,11 +133,13 @@ set(gui_markdowns
 
 set(gui_sources
     RadiumNBR/Gui/FullFeaturedRendererGui.cpp
+    RadiumNBR/Gui/VisualizationGui.cpp
     RadiumNBR/Gui/RendererPanel.cpp
     )
 
 set(gui_public_headers
     RadiumNBR/Gui/FullFeaturedRendererGui.hpp
+    RadiumNBR/Gui/VisualizationGui.hpp
     RadiumNBR/Gui/RendererPanel.hpp
     )
 
diff --git a/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp
index 49fd5b0..c6b5f63 100644
--- a/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp
+++ b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.cpp
@@ -1,5 +1,5 @@
-#include <RadiumNBR/FullFeatureRenderer.hpp>
 #include <RadiumNBR/Gui/FullFeaturedRendererGui.hpp>
+#include <RadiumNBR/Renderer/FullFeatureRenderer.hpp>
 
 namespace RadiumNBR {
 using namespace Gui;
@@ -10,22 +10,21 @@ RadiumNBR::Gui::RendererPanel* buildRadiumNBRGui( FullFeatureRenderer* renderer,
 
     auto defColor = Ra::Core::Utils::Color::linearRGBTosRGB( renderer->getBackgroundColor() );
     auto clrClbck = [renderer, appUpdateCallback]( const Ra::Core::Utils::Color& clr ) {
-      // set the background color for all passes that need it
-      renderer->setBackgroundColor( clr );
-      appUpdateCallback();
+        // set the background color for all passes that need it
+        renderer->setBackgroundColor( clr );
+        appUpdateCallback();
     };
     controlPanel->addColorInput( "Background Color", clrClbck, defColor );
 
     controlPanel->addSeparator();
 
-    controlPanel->beginLayout(QBoxLayout::LeftToRight);
+    controlPanel->beginLayout( QBoxLayout::LeftToRight );
 
     controlPanel->addOption( " Wireframe rendering", [renderer, appUpdateCallback]( bool b ) {
-      renderer->wireframeMode( b );
-      appUpdateCallback();
+        renderer->wireframeMode( b );
+        appUpdateCallback();
     } );
 
-
     controlPanel->addOption(
         " Show Debug ",
         [renderer, appUpdateCallback]( bool b ) {
@@ -43,17 +42,16 @@ RadiumNBR::Gui::RendererPanel* buildRadiumNBRGui( FullFeatureRenderer* renderer,
         false );
     controlPanel->endLayout( true );
 
-
     auto envmpClbck = [renderer, appUpdateCallback]( const std::string& files ) {
         renderer->setEnvMap( files );
         appUpdateCallback();
     };
 
-    controlPanel->beginLayout(QBoxLayout::LeftToRight);
+    controlPanel->beginLayout( QBoxLayout::LeftToRight );
     controlPanel->addFileInput(
         "Environment map", envmpClbck, "../", "Images (*.png *.jpg *.pfm *.exr *hdr)" );
 
-    controlPanel->beginLayout(QBoxLayout::TopToBottom );
+    controlPanel->beginLayout( QBoxLayout::TopToBottom );
     controlPanel->addOption(
         " Show envMap ",
         [renderer, appUpdateCallback]( bool b ) {
@@ -70,10 +68,10 @@ RadiumNBR::Gui::RendererPanel* buildRadiumNBRGui( FullFeatureRenderer* renderer,
         },
         100 );
     controlPanel->endLayout();
-    controlPanel->endLayout(true);
+    controlPanel->endLayout( true );
 
     // AO control
-    controlPanel->beginLayout(QBoxLayout::LeftToRight);
+    controlPanel->beginLayout( QBoxLayout::LeftToRight );
     controlPanel->addScalarInput(
         "AO radius",
         [renderer, appUpdateCallback]( double r ) {
diff --git a/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp
index ccfd7c5..479b39a 100644
--- a/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp
+++ b/src/libRender/RadiumNBR/Gui/FullFeaturedRendererGui.hpp
@@ -1,7 +1,7 @@
 #pragma once
-#include <RadiumNBR/Gui/RendererPanel.hpp>
 #include <RadiumNBR/NodeBasedRendererMacro.hpp>
 
+#include <RadiumNBR/Gui/RendererPanel.hpp>
 namespace RadiumNBR {
 class FullFeatureRenderer;
 
diff --git a/src/libRender/RadiumNBR/Gui/RendererPanel.cpp b/src/libRender/RadiumNBR/Gui/RendererPanel.cpp
index 4aa1a7f..518fabb 100644
--- a/src/libRender/RadiumNBR/Gui/RendererPanel.cpp
+++ b/src/libRender/RadiumNBR/Gui/RendererPanel.cpp
@@ -4,8 +4,11 @@
 #include <QVBoxLayout>
 
 #include <QColorDialog>
+#include <QDialog>
+#include <QDialogButtonBox>
 #include <QDoubleSpinBox>
 #include <QFileDialog>
+#include <QPlainTextEdit>
 #include <QPushButton>
 #include <QRadioButton>
 #include <QSlider>
@@ -21,10 +24,9 @@ RendererPanel::RendererPanel( const std::string& name, QWidget* parent ) : QFram
     auto panelName = new QLabel( this );
     panelName->setFrameStyle( QFrame::HLine );
     m_currentLayout->addWidget( panelName );
-    //addSeparator();
+    // addSeparator();
     setVisible( false );
     m_layouts.push( m_currentLayout );
-
 }
 
 // Method to populate the panel
@@ -132,37 +134,73 @@ void RendererPanel::addFileInput( const std::string& name,
     m_currentLayout->addWidget( button );
 }
 
-void RendererPanel::beginLayout(QBoxLayout::Direction dir) {
-    m_layouts.push(m_currentLayout);
+void RendererPanel::beginLayout( QBoxLayout::Direction dir ) {
+    m_layouts.push( m_currentLayout );
     m_currentLayout = new QBoxLayout( dir );
 }
 
 void RendererPanel::endLayout( bool separator ) {
     m_layouts.top()->addLayout( m_currentLayout );
     m_currentLayout = m_layouts.top();
-    if ( separator ) {
-        addSeparator();
-    }
+    if ( separator ) { addSeparator(); }
     m_layouts.pop();
 }
 
 void RendererPanel::addSeparator() {
     QFrame* line = new QFrame();
-    switch ( m_currentLayout->direction() ) {
+    switch ( m_currentLayout->direction() )
+    {
     case QBoxLayout::LeftToRight:
     case QBoxLayout::RightToLeft:
-        line->setFrameShape(QFrame::VLine);
+        line->setFrameShape( QFrame::VLine );
         break;
     case QBoxLayout::TopToBottom:
     case QBoxLayout::BottomToTop:
-        line->setFrameShape(QFrame::HLine);
+        line->setFrameShape( QFrame::HLine );
         break;
     default:
-        line->setFrameShape(QFrame::HLine);
+        line->setFrameShape( QFrame::HLine );
     }
-    line->setFrameShadow(QFrame::Sunken);
+    line->setFrameShadow( QFrame::Sunken );
     m_currentLayout->addWidget( line );
 }
 
+void RendererPanel::addCodeEditor( const std::string& name,
+                                   std::function<void( std::string )> callback,
+                                   const std::string& initialText ) {
+
+    auto button     = new QPushButton( name.c_str(), this );
+    QDialog* dialog = nullptr;
+    auto editDialog = [this, callback, initialText, dialog]() mutable {
+        if ( !dialog )
+        {
+            dialog = new QDialog( this );
+
+            auto buttonBox = new QDialogButtonBox(
+                QDialogButtonBox::Save | QDialogButtonBox::Cancel | QDialogButtonBox::Ok );
+            auto textEditor = new QPlainTextEdit();
+            textEditor->setPlainText( QString::fromStdString( initialText ) );
+
+            connect( buttonBox, &QDialogButtonBox::accepted, [callback, textEditor]() {
+                callback( textEditor->toPlainText().toStdString() );
+            } );
+
+            connect( buttonBox, &QDialogButtonBox::rejected, [callback, textEditor, initialText]() {
+                textEditor->setPlainText( QString::fromStdString( initialText ) );
+            } );
+
+            QVBoxLayout* mainLayout = new QVBoxLayout;
+            mainLayout->addWidget( textEditor );
+            mainLayout->addWidget( buttonBox );
+            dialog->setLayout( mainLayout );
+        }
+        dialog->show();
+        dialog->raise();
+        dialog->activateWindow();
+    };
+    connect( button, &QPushButton::clicked, editDialog );
+    m_currentLayout->addWidget( button );
+}
+
 } // namespace Gui
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Gui/RendererPanel.hpp b/src/libRender/RadiumNBR/Gui/RendererPanel.hpp
index 6242972..f5c465c 100644
--- a/src/libRender/RadiumNBR/Gui/RendererPanel.hpp
+++ b/src/libRender/RadiumNBR/Gui/RendererPanel.hpp
@@ -5,8 +5,8 @@
 #include <QVBoxLayout>
 
 #include <functional>
-#include <string>
 #include <stack>
+#include <string>
 
 #include <Core/Utils/Color.hpp>
 
@@ -48,7 +48,7 @@ class NodeBasedRenderer_LIBRARY_API RendererPanel : public QFrame
      *      - QBoxLayout::TopToBottom
      *      - QBoxLayout::BottomToTop
      */
-    void beginLayout(QBoxLayout::Direction dir = QBoxLayout::LeftToRight);
+    void beginLayout( QBoxLayout::Direction dir = QBoxLayout::LeftToRight );
 
     /**
      * Close the current layout.
@@ -59,7 +59,7 @@ class NodeBasedRenderer_LIBRARY_API RendererPanel : public QFrame
     /**
      * Add a separator
      */
-     void addSeparator();
+    void addSeparator();
 
     /** Add an option to the panel
      *  An option is an on/off checkbox to activate a state of the renderer.
@@ -123,6 +123,16 @@ class NodeBasedRenderer_LIBRARY_API RendererPanel : public QFrame
                        std::function<void( std::string )> callback,
                        const std::string& rootDirectory,
                        const std::string& filters );
+
+    /**
+     *
+     * @param name
+     * @param callback
+     * @param initialText
+     */
+    void addCodeEditor( const std::string& name,
+                        std::function<void( std::string )> callback,
+                        const std::string& initialText );
     /**@}*/
 
   private:
@@ -130,10 +140,10 @@ class NodeBasedRenderer_LIBRARY_API RendererPanel : public QFrame
     QVBoxLayout* m_contentLayout;
 
     /// The current layout where UI element will be added
-    QBoxLayout *m_currentLayout;
+    QBoxLayout* m_currentLayout;
 
     /// The stack of layouts
-    std::stack<QBoxLayout *> m_layouts;
+    std::stack<QBoxLayout*> m_layouts;
 };
 } // namespace Gui
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp b/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp
new file mode 100644
index 0000000..47a694a
--- /dev/null
+++ b/src/libRender/RadiumNBR/Gui/VisualizationGui.cpp
@@ -0,0 +1,71 @@
+#include <RadiumNBR/Gui/VisualizationGui.hpp>
+#include <RadiumNBR/NodeBasedRenderer.hpp>
+#include <RadiumNBR/Renderer/Visualization.hpp>
+
+namespace RadiumNBR {
+using namespace Gui;
+
+RadiumNBR::Gui::RendererPanel*
+buildControllerGui( NodeBasedRenderer* renderer, const std::function<void()>& appUpdateCallback ) {
+    auto& controller = dynamic_cast<VisualizationController&>( renderer->getController() );
+
+    auto controlPanel = new RendererPanel( renderer->getRendererName() );
+
+    auto defColor = Ra::Core::Utils::Color::linearRGBTosRGB( renderer->getBackgroundColor() );
+    auto clrClbck = [renderer, appUpdateCallback]( const Ra::Core::Utils::Color& clr ) {
+        // set the background color for all passes that need it
+        renderer->setBackgroundColor( clr );
+        appUpdateCallback();
+    };
+    controlPanel->addColorInput( "Background Color", clrClbck, defColor );
+
+    controlPanel->addSeparator();
+
+    controlPanel->beginLayout( QBoxLayout::LeftToRight );
+
+    controlPanel->addOption(
+        " Post processing",
+        [&controller, appUpdateCallback]( bool b ) {
+            controller.enablePostProcess( b );
+            appUpdateCallback();
+        },
+        true );
+
+    controlPanel->addOption(
+        " Show Debug ",
+        [renderer, appUpdateCallback]( bool b ) {
+            renderer->showDebug( b );
+            appUpdateCallback();
+        },
+        false );
+
+    controlPanel->addOption(
+        " Show UI ",
+        [renderer, appUpdateCallback]( bool b ) {
+            renderer->showUI( b );
+            appUpdateCallback();
+        },
+        false );
+    controlPanel->endLayout( true );
+
+    auto visualizationFunction = controller.getAttribToColorFunc();
+    controlPanel->addCodeEditor(
+        "Attribute function",
+        [&controller, visualizationFunction, appUpdateCallback]( const std::string& s ) {
+            controller.setAttribToColorFunc( s, visualizationFunction.second );
+            appUpdateCallback();
+        },
+        visualizationFunction.first );
+
+    controlPanel->addCodeEditor(
+        "Color function",
+        [&controller, visualizationFunction, appUpdateCallback]( const std::string& s ) {
+            controller.setAttribToColorFunc( visualizationFunction.first, s );
+            appUpdateCallback();
+        },
+        visualizationFunction.second );
+
+    return controlPanel;
+}
+
+} // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Gui/VisualizationGui.hpp b/src/libRender/RadiumNBR/Gui/VisualizationGui.hpp
new file mode 100644
index 0000000..e633c55
--- /dev/null
+++ b/src/libRender/RadiumNBR/Gui/VisualizationGui.hpp
@@ -0,0 +1,11 @@
+#pragma once
+#include <RadiumNBR/NodeBasedRendererMacro.hpp>
+
+#include <RadiumNBR/Gui/RendererPanel.hpp>
+namespace RadiumNBR {
+class NodeBasedRenderer;
+
+NodeBasedRenderer_LIBRARY_API RadiumNBR::Gui::RendererPanel*
+buildControllerGui( NodeBasedRenderer* renderer, const std::function<void()>& appUpdateCallback );
+
+} // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/NodeBasedRenderer.cpp b/src/libRender/RadiumNBR/NodeBasedRenderer.cpp
index c8bd461..e1cbe28 100644
--- a/src/libRender/RadiumNBR/NodeBasedRenderer.cpp
+++ b/src/libRender/RadiumNBR/NodeBasedRenderer.cpp
@@ -289,7 +289,8 @@ void NodeBasedRenderer::postProcessInternal( const ViewingParameters& /* renderD
     GL_ASSERT( glDisable( GL_DEPTH_TEST ) );
     GL_ASSERT( glDepthMask( GL_FALSE ) );
 
-    auto shader = m_shaderProgramManager->getShaderProgram( "Hdr2Ldr" );
+    auto shader = m_postProcessEnabled ? m_shaderProgramManager->getShaderProgram( "Hdr2Ldr" )
+                                       : m_shaderProgramManager->getShaderProgram( "DrawScreen" );
     shader->bind();
     shader->setUniform( "screenTexture", m_sharedTextures["Linear RGB (RadiumNBR)"].get(), 0 );
     m_quadMesh->render( shader );
diff --git a/src/libRender/RadiumNBR/NodeBasedRenderer.hpp b/src/libRender/RadiumNBR/NodeBasedRenderer.hpp
index 0c7950d..ab3691b 100644
--- a/src/libRender/RadiumNBR/NodeBasedRenderer.hpp
+++ b/src/libRender/RadiumNBR/NodeBasedRenderer.hpp
@@ -57,8 +57,7 @@ class NodeBasedRenderer_LIBRARY_API NodeBasedRenderer : public Ra::Engine::Rende
   public:
     //
     NodeBasedRenderer();
-    // TODO : instead of an std::function, restrict to a ref to a functor (class with () operator
-    // Such a functor could then be used also during the update step.
+
     explicit NodeBasedRenderer( RenderControlFunctor& controller );
 
     ~NodeBasedRenderer() override;
@@ -91,6 +90,9 @@ class NodeBasedRenderer_LIBRARY_API NodeBasedRenderer : public Ra::Engine::Rende
     /// Access the default light manager
     Ra::Engine::Scene::LightManager* getLightManager() { return m_lightmanagers[0]; }
 
+    /// Access the controler
+    RenderControlFunctor& getController() { return m_controller; }
+
   protected:
     void initializeInternal() override;
     void resizeInternal() override;
diff --git a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp
index b5c9150..d85a293 100644
--- a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp
+++ b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.cpp
@@ -124,7 +124,6 @@ bool CustomAttribToColorPass::buildRenderTechnique(
 
     if ( m_needConfigRebuild )
     {
-        m_needConfigRebuild = false;
         Ra::Engine::Data::ShaderConfigurationFactory::removeConfiguration(
             {"CustomAtt2ClrPass::CustomColorProgram" + mat->getMaterialName()} );
     }
@@ -233,10 +232,20 @@ void CustomAttribToColorPass::setAttribToColorFunc( const std::string& vertex_so
                                                     const std::string& fragment_source ) {
     m_customVertexAttrib  = vertex_source;
     m_customFragmentColor = fragment_source;
-    m_needConfigRebuild   = true;
+    rebuildShaders();
 }
 
 void CustomAttribToColorPass::setLightManager( const Ra::Engine::Scene::LightManager* lm ) {
     m_lightmanager = lm;
 }
+
+void CustomAttribToColorPass::rebuildShaders() {
+    m_needConfigRebuild = true;
+    for ( const auto& ro : *m_objectsToRender )
+    {
+        auto rt = ro->getRenderTechnique();
+        buildRenderTechnique( ro.get(), *rt );
+    }
+    m_needConfigRebuild = false;
+}
 } // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp
index 493d7a8..37bdfe4 100644
--- a/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp
+++ b/src/libRender/RadiumNBR/Passes/CustomAttribToColorPass.hpp
@@ -45,6 +45,8 @@ class CustomAttribToColorPass : public RenderPass
     void setAttribToColorFunc( const std::string& vertex_source,
                                const std::string& fragment_source );
 
+    void rebuildShaders();
+
   private:
     /// The framebuffer used to render this pass
     std::unique_ptr<globjects::Framebuffer> m_fbo{nullptr};
diff --git a/src/libRender/RadiumNBR/FullFeatureRenderer.cpp b/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.cpp
similarity index 99%
rename from src/libRender/RadiumNBR/FullFeatureRenderer.cpp
rename to src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.cpp
index 03a0c24..6a78926 100644
--- a/src/libRender/RadiumNBR/FullFeatureRenderer.cpp
+++ b/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.cpp
@@ -1,4 +1,4 @@
-#include <RadiumNBR/FullFeatureRenderer.hpp>
+#include <RadiumNBR/Renderer/FullFeatureRenderer.hpp>
 
 #include <Core/Containers/MakeShared.hpp>
 #include <Core/Resources/Resources.hpp>
diff --git a/src/libRender/RadiumNBR/FullFeatureRenderer.hpp b/src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.hpp
similarity index 100%
rename from src/libRender/RadiumNBR/FullFeatureRenderer.hpp
rename to src/libRender/RadiumNBR/Renderer/FullFeatureRenderer.hpp
diff --git a/src/libRender/RadiumNBR/Renderer/Visualization.cpp b/src/libRender/RadiumNBR/Renderer/Visualization.cpp
new file mode 100644
index 0000000..d504965
--- /dev/null
+++ b/src/libRender/RadiumNBR/Renderer/Visualization.cpp
@@ -0,0 +1,120 @@
+#include <Engine/Data/ShaderProgramManager.hpp>
+#include <Engine/RadiumEngine.hpp>
+#include <RadiumNBR/Passes/ClearPass.hpp>
+#include <RadiumNBR/Passes/CustomAttribToColorPass.hpp>
+#include <RadiumNBR/Passes/GeomPrepass.hpp>
+#include <RadiumNBR/Renderer/Visualization.hpp>
+
+#include <Core/Utils/Log.hpp>
+using namespace Ra::Core::Utils; // log
+
+namespace RadiumNBR {
+
+void VisualizationController::configure( RadiumNBR::NodeBasedRenderer* renderer, int w, int h ) {
+    m_renderer = renderer;
+
+    //! [Caching some helpers and data from the Engine and the renderer]
+    auto resourcesCheck = Ra::Core::Resources::getResourcesPath(
+        reinterpret_cast<void*>( &RadiumNBR::NodeBasedRendererMagic ), {"Resources/RadiumNBR"} );
+    if ( !resourcesCheck )
+    {
+        LOG( Ra::Core::Utils::logERROR ) << "Unable to find resources for NodeBasedRenderer!";
+        return;
+    }
+    else
+    { LOG( Ra::Core::Utils::logINFO ) << "NodeBasedRenderer Resources are at " << *resourcesCheck; }
+    auto resourcesPath{*resourcesCheck};
+    auto shaderManager = Ra::Engine::RadiumEngine::getInstance()->getShaderProgramManager();
+    auto colortexture  = renderer->sharedTextures().find( "Linear RGB (RadiumNBR)" );
+    auto depthtexture  = renderer->sharedTextures().find( "Depth (RadiumNBR)" );
+    //! [Caching some helpers and data from the Engine and the renderer]
+
+    //! [Adding a clear-screen pass]
+    {
+        // pass that draw no object and is positioned at rank 0
+        m_clearPass = std::make_shared<RadiumNBR::ClearPass>( nullptr, 0 );
+        // set the output of the pass : clear the renderer Linear RGB output texture
+        m_clearPass->setOutput( *colortexture );
+        // set the clear color (pass internal state)
+        m_clearPass->setBackground( {0.5, 0.1, 0.1} /*renderer->getBackgroundColor()*/ );
+        m_clearPass->initializePass( w, h, shaderManager );
+        // add the pass to the renderer and activate it
+        renderer->addPass( m_clearPass, m_clearPass->index() );
+        m_clearPass->activate();
+    }
+    //! [Adding a clear-screen pass]
+
+    //! [Adding a Z-only pass]
+    {
+        // the z-only pass takes all the objects and draw them only on the shared depth buffer.
+        auto pass = std::make_shared<RadiumNBR::GeomPrePass>( renderer->allRenderObjects(), 1 );
+        // set the output to the shared depth texture
+        pass->setOutput( *depthtexture );
+        // configure access to shader/resources files and initialize the pass
+        pass->setResourcesDir( resourcesPath );
+        pass->initializePass( w, h, shaderManager );
+        // add the pass to the renderer and activate it
+        renderer->addPass( pass, pass->index() );
+        pass->activate();
+    }
+    //! [Adding a Z-only pass]
+
+    //! [Adding a CustomAttribToColorPass pass]
+    {
+        // this pass draw all the objects using the custom color function,
+        // Rendering is done against the shared z-buffer for hidden line removal and
+        // into the shared color buffer.
+        m_customPass =
+            std::make_shared<RadiumNBR::CustomAttribToColorPass>( renderer->allRenderObjects(), 2 );
+        // set the input/output textures
+        m_customPass->setInputs( *depthtexture );
+        m_customPass->setOutput( *colortexture );
+        m_customPass->setLightManager( renderer->getLightManager() );
+        m_customPass->setAttribToColorFunc( m_vertexFunction, m_fragmentFunction );
+        // configure access to shader/resources files and initialize the pass
+        m_customPass->setResourcesDir( resourcesPath );
+        m_customPass->initializePass( w, h, shaderManager );
+        // add the pass to the renderer and activate it
+        renderer->addPass( m_customPass, m_customPass->index() );
+        m_customPass->activate();
+    }
+    //! [Adding a CustomAttribToColorPass pass]
+}
+
+void VisualizationController::update( const Ra::Engine::Data::ViewingParameters& ) {
+    /* This function is called once before each frame.
+     * You can use it to modify the scene, or modify the computation function of the renderer
+     * For this, just call
+     * m_customPass->setAttribToColorFunc( customVertexAttrib, customFragmentColor );
+     * with the wanted shader glsl source code.
+     */
+    if ( m_postProcess ) { m_clearPass->setBackground( m_renderer->getBackgroundColor() ); }
+    else
+    {
+        auto defColor = Ra::Core::Utils::Color::linearRGBTosRGB( m_renderer->getBackgroundColor() );
+        m_clearPass->setBackground( defColor );
+    }
+};
+
+void VisualizationController::resize( int w, int h ){
+    /* if you have resizeable resources in your controller, this method is called each time the
+     * viewer window is resized so that you can dimension your resources accordingly
+     */
+};
+
+/// Set the custom glsl function for attrib management (vertex) and colorcomputation (fragment)
+void VisualizationController::setAttribToColorFunc( const std::string& vertex_source,
+                                                    const std::string& fragment_source ) {
+    m_vertexFunction   = vertex_source;
+    m_fragmentFunction = fragment_source;
+    if ( m_customPass )
+    {
+        m_customPass->setAttribToColorFunc( m_vertexFunction, m_fragmentFunction );
+        m_customPass->rebuildShaders();
+    }
+}
+
+std::pair<std::string&, std::string&> VisualizationController::getAttribToColorFunc() {
+    return {m_vertexFunction, m_fragmentFunction};
+}
+} // namespace RadiumNBR
diff --git a/src/libRender/RadiumNBR/Renderer/Visualization.hpp b/src/libRender/RadiumNBR/Renderer/Visualization.hpp
new file mode 100644
index 0000000..ecf18ae
--- /dev/null
+++ b/src/libRender/RadiumNBR/Renderer/Visualization.hpp
@@ -0,0 +1,53 @@
+#include <RadiumNBR/NodeBasedRenderer.hpp>
+
+namespace RadiumNBR {
+class CustomAttribToColorPass;
+class ClearPass;
+
+/**
+ * This class parameterize the renderer just after the OpenGL system was initialized.
+ * when a method of this controler is called, the OpenGL context of the drawing window is activated.
+ */
+class NodeBasedRenderer_LIBRARY_API VisualizationController : public RadiumNBR::RenderControlFunctor
+{
+  public:
+    /*
+     * Called once : configure the renderer by adding passes and allocating controler resources
+     */
+    void configure( RadiumNBR::NodeBasedRenderer* renderer, int w, int h ) override;
+
+    void update( const Ra::Engine::Data::ViewingParameters& ) override;
+
+    void resize( int w, int h ) override;
+
+    /// Set the custom glsl function for attrib management (vertex) and colorcomputation (fragment)
+    void setAttribToColorFunc( const std::string& vertex_source,
+                               const std::string& fragment_source );
+
+    /// Get the glsl functions
+    std::pair<std::string&, std::string&> getAttribToColorFunc();
+
+    /// activate or deactivate the gamma-correction on the computed color
+    inline void enablePostProcess( bool b ) {
+        m_postProcess = b;
+        m_renderer->enablePostProcess( b );
+    }
+
+  private:
+    /// The custom pass if needed for modification
+    std::shared_ptr<RadiumNBR::CustomAttribToColorPass> m_customPass;
+
+    /// The clear pass for the background
+    std::shared_ptr<RadiumNBR::ClearPass> m_clearPass;
+    ///
+    std::string m_vertexFunction{"void outputCustomAttribs() {\n}\n"};
+    std::string m_fragmentFunction{
+        "vec4 computeCustomColor(Material mat, vec3 lightDir, "
+        "vec3 viewDir, vec3 normal_world) {\n return vec4(normal_world*0.5+0.5, 1);\n}\n"};
+
+    RadiumNBR::NodeBasedRenderer* m_renderer;
+
+    bool m_postProcess{true};
+};
+
+} // namespace RadiumNBR
-- 
GitLab