← Back to team overview

kicad-developers team mailing list archive

PATCH: Raytracing - a more pleasing way sequencing blocks to render ?

 

Hi,
I often use the excellent raytracing mode to get a nice 3D view of the board.

The rendering of the individual blocks is done in a Morton sequence,
possibly to minimize cache-miss penalties.

Since this render sequence starts in one corner, it usually takes a
while until the "meat" of the render hits the interesting parts in the
middle of the board.

So I implemented an alternative sequence: from the center out, in a
growing circle. To ease the time long renders take, it is actually two
complementing checkerboard patterns in consecutive passes (hard to
explain, you have to patch and look for yourself). I find this much
more pleasing to watch and also it helps to early see for the user if
they want to adjust the view as the important parts are rendered
first.

Of course, this will be slightly slower (possibly due to cache
issues), but in measurements on my machine this is typically in the
imperceptible noise (9.0 seconds vs. 9.1 seconds for instance).

Given that there might be a performance penalty I prepared two
patches: one that just does the rendering from the center, and one
which provides a setting in the menu-bar to give full choice.
(it might be good for comparison the two types of renderings, but
given the complexity and the added end-user confusion and the
essentially imperceptible performance penalty, I'd tend towards just
using the simple implementation without menu-bar settings).

Anyway, attached you find two patches:
raytrace-from-center-always.patch just does as described above,
without setting.
The raytrace-from-center-with-menubar-setting.patch goes all the way
of having a menu-bar choice and storing the setting in a configuration
(more complex, less preferred).

These patches are against the 5.1 branch, but it also applies cleanly to master.

Cheers,
  Henner.
diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp
index b059cfeaf..72274ac4b 100644
--- a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp
+++ b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <thread>
 #include <chrono>
+#include <algorithm>
 
 #include "c3d_render_raytracing.h"
 #include "mortoncodes.h"
@@ -2074,6 +2075,13 @@ bool C3D_RENDER_RAYTRACING::initializeOpenGL()
 }
 
 
+static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
+{
+    const float dx = (float)a.x - (float)b.x;
+    const float dy = (float)a.y - (float)b.y;
+    return hypotf(dx, dy);
+}
+
 void C3D_RENDER_RAYTRACING::initialize_block_positions()
 {
 
@@ -2123,26 +2131,32 @@ void C3D_RENDER_RAYTRACING::initialize_block_positions()
     m_postshader_ssao.UpdateSize( m_realBufferSize );
 
 
-    // Calc block positions
+    // Calc block positions for regular rendering. Choose an 'inside out'
+    // style of rendering
     // /////////////////////////////////////////////////////////////////////
     m_blockPositions.clear();
-    m_blockPositions.reserve( (m_realBufferSize.x / RAYPACKET_DIM) *
-                              (m_realBufferSize.y / RAYPACKET_DIM) );
-
-    i = 0;
-
-    while(1)
-    {
-        SFVEC2UI blockPos( DecodeMorton2X(i) * RAYPACKET_DIM,
-                           DecodeMorton2Y(i) * RAYPACKET_DIM );
-        i++;
-
-        if( (blockPos.x >= m_realBufferSize.x) && (blockPos.y >= m_realBufferSize.y) )
-            break;
-
-        if( (blockPos.x < m_realBufferSize.x) && (blockPos.y < m_realBufferSize.y) )
-            m_blockPositions.push_back( blockPos );
-    }
+    const int blocks_x = m_realBufferSize.x / RAYPACKET_DIM;
+    const int blocks_y = m_realBufferSize.y / RAYPACKET_DIM;
+    m_blockPositions.reserve( blocks_x * blocks_y );
+
+    for (int x = 0; x < blocks_x; ++x)
+        for (int y = 0; y < blocks_y; ++y)
+            m_blockPositions.push_back(SFVEC2UI(x * RAYPACKET_DIM,
+                                                y * RAYPACKET_DIM));
+
+    const SFVEC2UI center(m_realBufferSize.x/2, m_realBufferSize.y/2);
+    std::sort(m_blockPositions.begin(), m_blockPositions.end(),
+              [&](const SFVEC2UI &a, const SFVEC2UI &b)
+                  {
+                      // Sort order:
+                      // 1) One type of checkerboard pattern first
+                      // 2) then inside out
+                      const bool half_a = ((a.x / RAYPACKET_DIM) % 2) ^ ((a.y / RAYPACKET_DIM) % 2);
+                      const bool half_b = ((b.x / RAYPACKET_DIM) % 2) ^ ((b.y / RAYPACKET_DIM) % 2);
+                      if (half_a == half_b)
+                          return distance(a, center) < distance(b, center);
+                      return half_a < half_b;
+                  });
 
     // Create m_shader buffer
     delete[] m_shaderBuffer;
diff --git a/3d-viewer/3d_enums.h b/3d-viewer/3d_enums.h
index 7ff5d1403..2c56c3528 100644
--- a/3d-viewer/3d_enums.h
+++ b/3d-viewer/3d_enums.h
@@ -56,6 +56,7 @@ enum DISPLAY3D_FLG {
     FL_RENDER_RAYTRACING_POST_PROCESSING,
     FL_RENDER_RAYTRACING_ANTI_ALIASING,
     FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES,
+    FL_RENDER_RAYTRACING_RENDER_FROM_CENTER,
     FL_LAST
 };
 
diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp
index b059cfeaf..19b92c03d 100644
--- a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp
+++ b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <thread>
 #include <chrono>
+#include <algorithm>
 
 #include "c3d_render_raytracing.h"
 #include "mortoncodes.h"
@@ -59,6 +60,7 @@ C3D_RENDER_RAYTRACING::C3D_RENDER_RAYTRACING( CINFO3D_VISU &aSettings ) :
     m_stats_converted_roundsegment2d_to_roundsegment = 0;
     m_oldWindowsSize.x = 0;
     m_oldWindowsSize.y = 0;
+    m_oldCenterRender = m_settings.GetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER );
     m_outlineBoard2dObjects = NULL;
     m_firstHitinfo = NULL;
     m_shaderBuffer = NULL;
@@ -192,6 +194,12 @@ bool C3D_RENDER_RAYTRACING::Redraw( bool aIsMoving, REPORTER *aStatusTextReporte
         initialize_block_positions();
     }
 
+    // Recalculate buffer if the center draw flag changed
+    if ( m_settings.GetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER ) != m_oldCenterRender )
+    {
+        m_oldCenterRender = m_settings.GetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER );
+        initialize_block_positions();
+    }
 
     // Clear buffers
     // /////////////////////////////////////////////////////////////////////////
@@ -2073,6 +2081,12 @@ bool C3D_RENDER_RAYTRACING::initializeOpenGL()
     return true;
 }
 
+static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
+{
+    const float dx = (float)a.x - (float)b.x;
+    const float dy = (float)a.y - (float)b.y;
+    return hypotf(dx, dy);
+}
 
 void C3D_RENDER_RAYTRACING::initialize_block_positions()
 {
@@ -2123,25 +2137,52 @@ void C3D_RENDER_RAYTRACING::initialize_block_positions()
     m_postshader_ssao.UpdateSize( m_realBufferSize );
 
 
-    // Calc block positions
+    // Calc block positions. This allows a cache optimized version and
+    // a more pleasing 'inside out-rendering' mode (which in practice is
+    // typically not distinguishable in speed).
     // /////////////////////////////////////////////////////////////////////
     m_blockPositions.clear();
-    m_blockPositions.reserve( (m_realBufferSize.x / RAYPACKET_DIM) *
-                              (m_realBufferSize.y / RAYPACKET_DIM) );
-
-    i = 0;
+    const int blocks_x = m_realBufferSize.x / RAYPACKET_DIM;
+    const int blocks_y = m_realBufferSize.y / RAYPACKET_DIM;
+    m_blockPositions.reserve( blocks_x * blocks_y );
 
-    while(1)
+    if ( m_settings.GetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER ) )
     {
-        SFVEC2UI blockPos( DecodeMorton2X(i) * RAYPACKET_DIM,
-                           DecodeMorton2Y(i) * RAYPACKET_DIM );
-        i++;
+        for (int x = 0; x < blocks_x; ++x)
+            for (int y = 0; y < blocks_y; ++y)
+                m_blockPositions.push_back(SFVEC2UI(x * RAYPACKET_DIM,
+                                                    y * RAYPACKET_DIM));
+
+        const SFVEC2UI center(m_realBufferSize.x/2, m_realBufferSize.y/2);
+        std::sort(m_blockPositions.begin(), m_blockPositions.end(),
+                  [&](const SFVEC2UI &a, const SFVEC2UI &b)
+                      {
+                          // Sort order:
+                          // 1) One type of checkerboard pattern first
+                          // 2) then inside out
+                          const bool half_a = ((a.x / RAYPACKET_DIM) % 2) ^ ((a.y / RAYPACKET_DIM) % 2);
+                          const bool half_b = ((b.x / RAYPACKET_DIM) % 2) ^ ((b.y / RAYPACKET_DIM) % 2);
+                          if (half_a == half_b)
+                              return distance(a, center) < distance(b, center);
+                          return half_a < half_b;
+                      });
+    }
+    else
+    {
+        i = 0;
 
-        if( (blockPos.x >= m_realBufferSize.x) && (blockPos.y >= m_realBufferSize.y) )
-            break;
+        while(1)
+        {
+            SFVEC2UI blockPos( DecodeMorton2X(i) * RAYPACKET_DIM,
+                               DecodeMorton2Y(i) * RAYPACKET_DIM );
+            i++;
+
+            if( (blockPos.x >= m_realBufferSize.x) && (blockPos.y >= m_realBufferSize.y) )
+                break;
 
-        if( (blockPos.x < m_realBufferSize.x) && (blockPos.y < m_realBufferSize.y) )
-            m_blockPositions.push_back( blockPos );
+            if( (blockPos.x < m_realBufferSize.x) && (blockPos.y < m_realBufferSize.y) )
+                m_blockPositions.push_back( blockPos );
+        }
     }
 
     // Create m_shader buffer
diff --git a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h
index 4f5b41775..ac9527d66 100644
--- a/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h
+++ b/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.h
@@ -158,8 +158,9 @@ private:
 
     // Morton codes
 
-    /// used to see if the windows size changed
+    /// used to see if settings changed that require size re-calculations.
     wxSize m_oldWindowsSize;
+    bool m_oldCenterRender;
 
     /// this encodes the Morton code positions
     std::vector< SFVEC2UI > m_blockPositions;
diff --git a/3d-viewer/3d_viewer/3d_menubar.cpp b/3d-viewer/3d_viewer/3d_menubar.cpp
index 740622a8c..832adf652 100644
--- a/3d-viewer/3d_viewer/3d_menubar.cpp
+++ b/3d-viewer/3d_viewer/3d_menubar.cpp
@@ -221,6 +221,11 @@ void EDA_3D_VIEWER::CreateMenuBar()
                  _( "Apply Screen Space Ambient Occlusion and Global Illumination reflections on final render (slow)"),
                  KiBitmap( green_xpm ), wxITEM_CHECK );
 
+    AddMenuItem( renderOptionsMenu_RAYTRACING, ID_MENU3D_FL_RAYTRACING_RENDER_FROM_CENTER,
+                 _( "Center first" ),
+                 _( "Start rendering blocks from the inside out."),
+                 KiBitmap( green_xpm ), wxITEM_CHECK );
+
     prefsMenu->AppendSeparator();
 
 
diff --git a/3d-viewer/3d_viewer/eda_3d_viewer.cpp b/3d-viewer/3d_viewer/eda_3d_viewer.cpp
index 1c470cd3d..f7a5b5abc 100644
--- a/3d-viewer/3d_viewer/eda_3d_viewer.cpp
+++ b/3d-viewer/3d_viewer/eda_3d_viewer.cpp
@@ -103,6 +103,7 @@ static const wxChar keyRenderRAY_Reflections[]  = wxT( "Render_RAY_Reflections"
 static const wxChar keyRenderRAY_PostProcess[]  = wxT( "Render_RAY_PostProcess" );
 static const wxChar keyRenderRAY_AAliasing[]    = wxT( "Render_RAY_AntiAliasing" );
 static const wxChar keyRenderRAY_ProceduralT[]  = wxT( "Render_RAY_ProceduralTextures" );
+static const wxChar keyRenderRAY_CenterRender[]  = wxT( "Render_RAY_RenderFromCenter" );
 
 static const wxChar keyShowAxis[]               = wxT( "ShowAxis" );
 static const wxChar keyShowGrid[]               = wxT( "ShowGrid3D" );
@@ -147,7 +148,7 @@ BEGIN_EVENT_TABLE( EDA_3D_VIEWER, EDA_BASE_FRAME )
                          ID_MENU3D_FL_OPENGL_RENDER_SHOW_MODEL_BBOX,
                          EDA_3D_VIEWER::OnUpdateUIOpenGL )
     EVT_UPDATE_UI_RANGE( ID_MENU3D_FL_RAYTRACING_RENDER_SHADOWS,
-                         ID_MENU3D_FL_RAYTRACING_PROCEDURAL_TEXTURES,
+                         ID_MENU3D_FL_RAYTRACING_RENDER_FROM_CENTER,
                          EDA_3D_VIEWER::OnUpdateUIRayTracing )
 
     EVT_UPDATE_UI( ID_RENDER_CURRENT_VIEW, EDA_3D_VIEWER::OnUpdateUIEngine )
@@ -477,6 +478,11 @@ void EDA_3D_VIEWER::Process_Special_Functions( wxCommandEvent &event )
         NewDisplay( true );
         return;
 
+    case ID_MENU3D_FL_RAYTRACING_RENDER_FROM_CENTER:
+        m_settings.SetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER, isChecked );
+        NewDisplay( true );
+        return;
+
     case ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING:
         m_settings.SetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING, isChecked );
         m_canvas->Request_refresh();
@@ -777,6 +783,9 @@ void EDA_3D_VIEWER::LoadSettings( wxConfigBase *aCfg )
     aCfg->Read( keyRenderRAY_ProceduralT, &tmp, true );
     m_settings.SetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES, tmp );
 
+    aCfg->Read( keyRenderRAY_CenterRender, &tmp, true );
+    m_settings.SetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER, tmp );
+
     aCfg->Read( keyShowAxis, &tmp, true );
     m_settings.SetFlag( FL_AXIS, tmp );
 
@@ -883,6 +892,7 @@ void EDA_3D_VIEWER::SaveSettings( wxConfigBase *aCfg )
     aCfg->Write( keyRenderRAY_PostProcess,  m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) );
     aCfg->Write( keyRenderRAY_AAliasing,    m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) );
     aCfg->Write( keyRenderRAY_ProceduralT,  m_settings.GetFlag( FL_RENDER_RAYTRACING_PROCEDURAL_TEXTURES ) );
+    aCfg->Write( keyRenderRAY_CenterRender,  m_settings.GetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER ) );
 
     aCfg->Write( keyShowAxis,               m_settings.GetFlag( FL_AXIS ) );
     aCfg->Write( keyShowGrid,               (int)m_settings.GridGet() );
@@ -1256,6 +1266,10 @@ void EDA_3D_VIEWER::OnUpdateUIRayTracing( wxUpdateUIEvent& aEvent )
         aEvent.Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) );
         break;
 
+    case ID_MENU3D_FL_RAYTRACING_RENDER_FROM_CENTER:
+        aEvent.Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_RENDER_FROM_CENTER ) );
+        break;
+
     case ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING:
         aEvent.Check( m_settings.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) );
         break;
diff --git a/3d-viewer/3d_viewer_id.h b/3d-viewer/3d_viewer_id.h
index 7b193359d..11c042f7d 100644
--- a/3d-viewer/3d_viewer_id.h
+++ b/3d-viewer/3d_viewer_id.h
@@ -77,6 +77,7 @@ enum id_3dview_frm
     ID_MENU3D_FL_RAYTRACING_POST_PROCESSING,
     ID_MENU3D_FL_RAYTRACING_ANTI_ALIASING,
     ID_MENU3D_FL_RAYTRACING_PROCEDURAL_TEXTURES,
+    ID_MENU3D_FL_RAYTRACING_RENDER_FROM_CENTER,
 
     ID_MENU_SCREENCOPY_PNG,
     ID_MENU_SCREENCOPY_JPEG,

Follow ups