Radium Engine  1.5.0
Renderer.cpp
1 #include <Engine/Rendering/Renderer.hpp>
2 
3 #include <Core/Asset/FileData.hpp>
4 #include <Core/Geometry/MeshPrimitives.hpp>
5 #include <Core/Utils/Log.hpp>
6 #include <Engine/Data/Material.hpp>
7 #include <Engine/Data/Mesh.hpp>
8 #include <Engine/Data/ShaderConfigFactory.hpp>
9 #include <Engine/Data/ShaderProgram.hpp>
10 #include <Engine/Data/ShaderProgramManager.hpp>
11 #include <Engine/Data/Texture.hpp>
12 #include <Engine/Data/TextureManager.hpp>
13 #include <Engine/Data/ViewingParameters.hpp>
14 #include <Engine/OpenGL.hpp>
15 #include <Engine/RadiumEngine.hpp>
16 #include <Engine/Rendering/RenderObject.hpp>
17 #include <Engine/Rendering/RenderObjectManager.hpp>
18 #include <Engine/Scene/LightManager.hpp>
19 
20 // temporary fix for issue #837
21 #include <Engine/Scene/GeometryComponent.hpp>
22 
23 #include <globjects/Framebuffer.h>
24 
25 #include <globjects/Texture.h>
26 
27 #include <algorithm>
28 #include <iostream>
29 
30 namespace Ra {
31 namespace Engine {
32 namespace Rendering {
33 
34 using namespace Core::Utils; // log
35 
36 namespace {
37 const GLenum buffers[] = { GL_COLOR_ATTACHMENT0,
38  GL_COLOR_ATTACHMENT1,
39  GL_COLOR_ATTACHMENT2,
40  GL_COLOR_ATTACHMENT3,
41  GL_COLOR_ATTACHMENT4,
42  GL_COLOR_ATTACHMENT5,
43  GL_COLOR_ATTACHMENT6,
44  GL_COLOR_ATTACHMENT7 };
45 }
46 
48  m_quadMesh { nullptr },
49  m_depthTexture { nullptr },
50  m_fancyTexture { nullptr },
51  m_pickingFbo { nullptr },
52  m_pickingTexture { nullptr } {}
53 
54 Renderer::~Renderer() = default;
55 
56 void Renderer::initialize( uint width, uint height ) {
58  auto resourcesRootDir { RadiumEngine::getInstance()->getResourcesDir() };
59 
60  // Get aliases on frequently used managers from the Engine
61  // Not that ownership is never transfered when using raw pointers
62  // See :
63  // https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ri-raw
64  // https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-ptr
65  m_renderObjectManager = RadiumEngine::getInstance()->getRenderObjectManager();
66  m_shaderProgramManager = RadiumEngine::getInstance()->getShaderProgramManager();
67 
68  m_width = width;
69  m_height = height;
70 
72  { { "DrawScreen" },
73  resourcesRootDir + "Shaders/2DShaders/Basic2D.vert.glsl",
74  resourcesRootDir + "Shaders/2DShaders/DrawScreen.frag.glsl" } );
76  { { "DrawScreenI" },
77  resourcesRootDir + "Shaders/2DShaders/Basic2D.vert.glsl",
78  resourcesRootDir + "Shaders/2DShaders/DrawScreenI.frag.glsl" } );
80  { { "CircleBrush" },
81  resourcesRootDir + "Shaders/2DShaders/Basic2D.vert.glsl",
82  resourcesRootDir + "Shaders/2DShaders/CircleBrush.frag.glsl" } );
84  { { "DisplayDepthBuffer" },
85  resourcesRootDir + "Shaders/2DShaders/Basic2D.vert.glsl",
86  resourcesRootDir + "Shaders/2DShaders//DepthDisplay.frag.glsl" } );
87 
88  Data::ShaderConfiguration pickingPointsConfig( "PickingPoints" );
89  pickingPointsConfig.addShader( Data::ShaderType_VERTEX,
90  resourcesRootDir + "Shaders/Picking/Picking.vert.glsl" );
91  pickingPointsConfig.addShader( Data::ShaderType_GEOMETRY,
92  resourcesRootDir + "Shaders/Picking/PickingPoints.geom.glsl" );
93  pickingPointsConfig.addShader( Data::ShaderType_FRAGMENT,
94  resourcesRootDir + "Shaders/Picking/Picking.frag.glsl" );
96  {
97  auto pickingShader = m_shaderProgramManager->addShaderProgram( pickingPointsConfig );
98  CORE_ASSERT( pickingShader, "Picking Shader is required for points" );
99  m_pickingShaders[Data::Displayable::PKM_POINTS] = *pickingShader;
100  }
101 
102  Data::ShaderConfiguration pickingLinesConfig( "PickingLines" );
103  pickingLinesConfig.addShader( Data::ShaderType_VERTEX,
104  resourcesRootDir + "Shaders/Picking/Picking.vert.glsl" );
105  pickingLinesConfig.addShader( Data::ShaderType_GEOMETRY,
106  resourcesRootDir + "Shaders/Picking/PickingLines.geom.glsl" );
107  pickingLinesConfig.addShader( Data::ShaderType_FRAGMENT,
108  resourcesRootDir + "Shaders/Picking/Picking.frag.glsl" );
110  {
111  auto pickingShader = m_shaderProgramManager->addShaderProgram( pickingLinesConfig );
112  CORE_ASSERT( pickingShader, "Picking Shader is required for lines" );
113  m_pickingShaders[Data::Displayable::PKM_LINES] = *pickingShader;
114  }
115 
116  Data::ShaderConfiguration pickingLinesAdjacencyConfig( "PickingLinesAdjacency" );
117  pickingLinesAdjacencyConfig.addShader( Data::ShaderType_VERTEX,
118  resourcesRootDir + "Shaders/Picking/Picking.vert.glsl" );
119  pickingLinesAdjacencyConfig.addShader( Data::ShaderType_GEOMETRY,
120  resourcesRootDir +
121  "Shaders/Picking/PickingLinesAdjacency.geom.glsl" );
122  pickingLinesAdjacencyConfig.addShader( Data::ShaderType_FRAGMENT,
123  resourcesRootDir + "Shaders/Picking/Picking.frag.glsl" );
124  Data::ShaderConfigurationFactory::addConfiguration( pickingLinesAdjacencyConfig );
125  {
126  auto pickingShader =
127  m_shaderProgramManager->addShaderProgram( pickingLinesAdjacencyConfig );
128  CORE_ASSERT( pickingShader, "Picking Shader is required for lines adjacency" );
129  m_pickingShaders[Data::Displayable::PKM_LINE_ADJ] = *pickingShader;
130  }
131 
132  Data::ShaderConfiguration pickingTrianglesConfig( "PickingTriangles" );
133  pickingTrianglesConfig.addShader( Data::ShaderType_VERTEX,
134  resourcesRootDir + "Shaders/Picking/Picking.vert.glsl" );
135  pickingTrianglesConfig.addShader( Data::ShaderType_GEOMETRY,
136  resourcesRootDir +
137  "Shaders/Picking/PickingTriangles.geom.glsl" );
138  pickingTrianglesConfig.addShader( Data::ShaderType_FRAGMENT,
139  resourcesRootDir + "Shaders/Picking/Picking.frag.glsl" );
140  Data::ShaderConfigurationFactory::addConfiguration( pickingTrianglesConfig );
141  {
142  auto pickingShader = m_shaderProgramManager->addShaderProgram( pickingTrianglesConfig );
143  CORE_ASSERT( pickingShader, "Picking Shader is required for triangles" );
144  m_pickingShaders[Data::Displayable::PKM_TRI] = *pickingShader;
145  }
146 
147  Data::TextureParameters texparams;
148  texparams.width = m_width;
149  texparams.height = m_height;
150  texparams.target = GL_TEXTURE_2D;
151  texparams.minFilter = GL_NEAREST;
152  texparams.magFilter = GL_NEAREST;
153 
154  texparams.name = "Depth";
155  texparams.internalFormat = GL_DEPTH_COMPONENT24;
156  texparams.format = GL_DEPTH_COMPONENT;
157  texparams.type = GL_UNSIGNED_INT;
158  m_depthTexture = std::make_unique<Data::Texture>( texparams );
159 
160  m_pickingFbo = std::make_unique<globjects::Framebuffer>();
161  texparams.name = "Picking";
162  texparams.internalFormat = GL_RGBA32I;
163  texparams.format = GL_RGBA_INTEGER;
164  texparams.type = GL_INT;
165  m_pickingTexture = std::make_unique<Data::Texture>( texparams );
166 
167  // Final texture
168  texparams.name = "Final image";
169  texparams.internalFormat = GL_RGBA32F;
170  texparams.format = GL_RGBA;
171  texparams.type = GL_SCALAR;
172  m_fancyTexture = std::make_unique<Data::Texture>( texparams );
173 
175  m_secondaryTextures["Picking Texture"] = m_pickingTexture.get();
176 
177  // Quad mesh
178  Core::Geometry::TriangleMesh mesh =
179  Core::Geometry::makeZNormalQuad( Core::Vector2( -1.f, 1.f ) );
180 
181  auto qm = std::make_unique<Data::Mesh>( "quad" );
182  qm->loadGeometry( std::move( mesh ) );
183  m_quadMesh = std::move( qm ); // we need to move, as loadGeometry is not a member of Displayable
184  m_quadMesh->updateGL();
185 
187 
188  resize( m_width, m_height );
189 }
190 
192  const Data::ViewingParameters& renderData ) {
193  CORE_ASSERT( RadiumEngine::getInstance() != nullptr, "Engine is not initialized." );
194  // skip query if out of window (can occur when picking while moving outside)
195  if ( query.m_screenCoords.x() < 0 || query.m_screenCoords.x() > m_width - 1 ||
196  query.m_screenCoords.y() < 0 || query.m_screenCoords.y() > m_height - 1 ) {
197  // return empty result
198  return {};
199  }
200 
201  PickingResult result;
202 
203  std::lock_guard<std::mutex> renderLock( m_renderMutex );
204  CORE_UNUSED( renderLock );
205 
206  // 0. Save eventual already bound FBO (e.g. QtOpenGLWidget) and viewport
207  saveExternalFBOInternal();
208 
209  // 1. Gather render objects if needed
210  feedRenderQueuesInternal( renderData );
211  updateRenderObjectsInternal( renderData );
212  // 3. Do picking if needed
213 
214  m_pickingFbo->bind();
217  // preparePicking( renderData );
218  // here is preparePicking code + save depth
219  GL_ASSERT( glDepthMask( GL_TRUE ) );
220  GL_ASSERT( glColorMask( 1, 1, 1, 1 ) );
221  GL_ASSERT( glDrawBuffers( 1, buffers ) );
222 
223  float clearDepth = 1.0;
224  int clearColor[] = { -1, -1, -1, -1 };
225 
226  GL_ASSERT( glClearBufferiv( GL_COLOR, 0, clearColor ) );
227  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
228 
229  splitRenderQueuesForPicking( renderData );
230 
231  // First draw Geometry Objects
232  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
233  GL_ASSERT( glDepthFunc( GL_LESS ) );
234 
235  renderForPicking( renderData, m_pickingShaders, m_fancyRenderObjectsPicking );
237  float depth;
238  m_pickingFbo->readPixels( { { static_cast<int>( query.m_screenCoords.x() ),
239  static_cast<int>( query.m_screenCoords.y() ),
240  1,
241  1 } },
242  GL_DEPTH_COMPONENT,
243  GL_FLOAT,
244  &depth );
245  result.setDepth( depth );
246 
248 
249  if ( m_drawDebug ) {
250  // Then draw debug objects
251  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
252  renderForPicking( renderData, m_pickingShaders, m_debugRenderObjectsPicking );
253  // Then draw xrayed objects on top of normal objects
254  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
255  renderForPicking( renderData, m_pickingShaders, m_xrayRenderObjectsPicking );
256  }
257 
258  // Finally draw ui stuff on top of everything
259  // these have a different way to compute the transform matrices
260  // FIXME (florian): find a way to use renderForPicking()!
261  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
262  for ( uint i = 0; i < m_pickingShaders.size(); ++i ) {
263  m_pickingShaders[i]->bind();
264  m_pickingShaders[i]->setUniform( "transform.proj", renderData.projMatrix );
265  m_pickingShaders[i]->setUniform( "transform.view", renderData.viewMatrix );
266 
267  for ( const auto& ro : m_uiRenderObjectsPicking[i] ) {
268  if ( ro->isVisible() && ro->isPickable() ) {
269  m_pickingShaders[i]->setUniform( "objectId", ro->getIndex().getValue() );
270 
271  Core::Matrix4 M = ro->getTransformAsMatrix();
272  Core::Matrix4 MV = renderData.viewMatrix * M;
273  Scalar d = MV.block<3, 1>( 0, 3 ).norm();
274 
275  Core::Matrix4 S = Core::Matrix4::Identity();
276  S( 0, 0 ) = S( 1, 1 ) = S( 2, 2 ) = d;
277 
278  M = M * S;
279  Core::Matrix4 N = M.inverse().transpose();
280 
281  m_pickingShaders[i]->setUniform( "transform.model", M );
282  m_pickingShaders[i]->setUniform( "transform.worldNormal", N );
283 
284  // render
285  ro->getMesh()->render( m_pickingShaders[i] );
286  }
287  }
288  }
289 
291 
292  int pick[4];
293 
294  // Now read the Picking Texture to address the Picking Requests.
295  m_pickingFbo->readPixels( GL_COLOR_ATTACHMENT0,
296  { { static_cast<int>( query.m_screenCoords.x() ),
297  static_cast<int>( query.m_screenCoords.y() ),
298  1,
299  1 } },
300  GL_RGBA_INTEGER,
301  GL_INT,
302  pick );
303  result.setRoIdx( pick[0] ); // RO idx
304  result.addIndex( { pick[2], pick[1], pick[3] } );
305  result.setMode( query.m_mode );
306  m_pickingFbo->unbind();
307 
309 
310  restoreExternalFBOInternal();
311 
312  return result;
313 } // namespace Engine
314 
316  if ( !m_initialized ) return;
317 
318  CORE_ASSERT( RadiumEngine::getInstance() != nullptr, "Engine is not initialized." );
319 
320  std::lock_guard<std::mutex> renderLock( m_renderMutex );
321  CORE_UNUSED( renderLock );
322 
323  m_timerData.renderStart = Core::Utils::Clock::now();
324 
325  // 0. Save eventual already bound FBO (e.g. QtOpenGLWidget) and viewport
326  saveExternalFBOInternal();
327 
328  // 1. Gather render objects if needed
329  // TODO : make this only once and only update modified objects at each frame
330  // Actually, this correspond to 3 loops over all ROs. Could be done without loops, just by
331  // using observers
332  feedRenderQueuesInternal( data );
333 
334  m_timerData.feedRenderQueuesEnd = Core::Utils::Clock::now();
335 
336  // 2. Update them (from an opengl point of view)
337  // TODO : This naively updates the OpenGL State of objects at each frame.
338  // Do it only for modified objects (With an observer ?)
339  updateRenderObjectsInternal( data );
340  m_timerData.updateEnd = Core::Utils::Clock::now();
341 
342  // 3. Do picking if needed
343  // TODO : Make picking much more effient.
344  // Do not need to loop twice on objects to implement picking.
345  m_pickingResults.clear();
346  if ( !m_pickingQueries.empty() ) { doPicking( data ); }
347  m_lastFramePickingQueries = m_pickingQueries;
348  m_pickingQueries.clear();
349 
350  updateStepInternal( data );
351 
352  // 4. Do the rendering.
353  renderInternal( data );
354  m_timerData.mainRenderEnd = Core::Utils::Clock::now();
355 
356  // 5. Post processing
357  postProcessInternal( data );
358  m_timerData.postProcessEnd = Core::Utils::Clock::now();
359 
360  // 6. Debug
361  debugInternal( data );
362 
363  // 7. Draw UI
364  uiInternal( data );
365 
366  // 8. Write image to Qt framebuffer.
367  drawScreenInternal();
368  m_timerData.renderEnd = Core::Utils::Clock::now();
369 
370  // 9. Tell renderobjects they have been drawn (to decreaase the counter)
371  // TODO : this must be done when rendering the object, not after.
372  // doing this here make looping on Ros twice (at least) and even much more due to
373  // implementations of indirectly called methods.
374  notifyRenderObjectsRenderingInternal();
375 }
376 
377 void Renderer::saveExternalFBOInternal() {
378  RadiumEngine::getInstance()->pushFboAndViewport();
379  // Set the internal rendering viewport
380  glViewport( 0, 0, int( m_width ), int( m_height ) );
381 }
382 
383 void Renderer::updateRenderObjectsInternal( const Data::ViewingParameters& /*renderData*/ ) {
385  for ( auto& ro : m_fancyRenderObjects ) {
386  ro->updateGL();
387  }
388  for ( auto& ro : m_xrayRenderObjects ) {
389  ro->updateGL();
390  }
391  for ( auto& ro : m_debugRenderObjects ) {
392  ro->updateGL();
393  }
394  for ( auto& ro : m_uiRenderObjects ) {
395  ro->updateGL();
396  }
397 }
398 
399 void Renderer::feedRenderQueuesInternal( const Data::ViewingParameters& /*renderData*/ ) {
400  m_fancyRenderObjects.clear();
401  m_debugRenderObjects.clear();
402  m_uiRenderObjects.clear();
403  m_xrayRenderObjects.clear();
404 
405  m_renderObjectManager->getRenderObjectsByType( m_fancyRenderObjects,
406  RenderObjectType::Geometry );
407  m_renderObjectManager->getRenderObjectsByType( m_debugRenderObjects, RenderObjectType::Debug );
408  m_renderObjectManager->getRenderObjectsByType( m_uiRenderObjects, RenderObjectType::UI );
409 
410  for ( auto it = m_fancyRenderObjects.begin(); it != m_fancyRenderObjects.end(); ) {
411  if ( ( *it )->isXRay() ) {
412  m_xrayRenderObjects.push_back( *it );
413  it = m_fancyRenderObjects.erase( it );
414  }
415  else { ++it; }
416  }
417 
418  for ( auto it = m_debugRenderObjects.begin(); it != m_debugRenderObjects.end(); ) {
419  if ( ( *it )->isXRay() ) {
420  m_xrayRenderObjects.push_back( *it );
421  it = m_debugRenderObjects.erase( it );
422  }
423  else { ++it; }
424  }
425 
426  for ( auto it = m_uiRenderObjects.begin(); it != m_uiRenderObjects.end(); ) {
427  if ( ( *it )->isXRay() ) {
428  m_xrayRenderObjects.push_back( *it );
429  it = m_uiRenderObjects.erase( it );
430  }
431  else { ++it; }
432  }
433 }
434 
435 // subroutine to Renderer::splitRenderQueuesForPicking()
436 void Renderer::splitRQ( const std::vector<RenderObjectPtr>& renderQueue,
437  std::array<std::vector<RenderObjectPtr>, 4>& renderQueuePicking ) {
438  // clean renderQueuePicking
439  for ( auto& q : renderQueuePicking ) {
440  q.clear();
441  }
442 
443  // fill renderQueuePicking from renderQueue
444  for ( auto& roPtr : renderQueue ) {
445  auto mode = roPtr->getMesh()->pickingRenderMode();
446  if ( mode != Data::Displayable::NO_PICKING )
447  renderQueuePicking[size_t( mode )].push_back( roPtr );
448  }
449 }
450 
451 void Renderer::splitRenderQueuesForPicking( const Data::ViewingParameters& /*renderData*/ ) {
452  splitRQ( m_fancyRenderObjects, m_fancyRenderObjectsPicking );
453  splitRQ( m_debugRenderObjects, m_debugRenderObjectsPicking );
454  splitRQ( m_uiRenderObjects, m_uiRenderObjectsPicking );
455  splitRQ( m_xrayRenderObjects, m_xrayRenderObjectsPicking );
456 }
457 
458 // subroutine to Renderer::doPicking()
459 void Renderer::renderForPicking(
460  const Data::ViewingParameters& renderData,
461  const std::array<const Data::ShaderProgram*, 4>& pickingShaders,
462  const std::array<std::vector<RenderObjectPtr>, 4>& renderQueuePicking ) {
463  for ( uint i = 0; i < pickingShaders.size(); ++i ) {
464  pickingShaders[i]->bind();
465  pickingShaders[i]->setUniform( "transform.proj", renderData.projMatrix );
466  pickingShaders[i]->setUniform( "transform.view", renderData.viewMatrix );
467  for ( const auto& ro : renderQueuePicking[i] ) {
468  if ( ro->isVisible() && ro->isPickable() ) {
469  pickingShaders[i]->setUniform( "objectId", ro->getIndex().getValue() );
470  Core::Matrix4 M = ro->getTransformAsMatrix();
471  Core::Matrix4 N = M.inverse().transpose();
472  pickingShaders[i]->setUniform( "transform.model", M );
473  pickingShaders[i]->setUniform( "transform.worldNormal", N );
474  // hack to pick point cloud (issue ##837)
475  auto pointCloud = dynamic_cast<Scene::PointCloudComponent*>( ro->getComponent() );
476  if ( pointCloud ) {
477  pickingShaders[i]->setUniform( "pointCloudSplatRadius",
478  pointCloud->getSplatSize() / 2.f );
479  }
480  // render
481  ro->getMesh()->render( pickingShaders[i] );
482  }
483  }
484  }
485 }
486 
487 void Renderer::doPicking( const Data::ViewingParameters& renderData ) {
488  m_pickingResults.reserve( m_pickingQueries.size() );
489 
490  m_pickingFbo->bind();
491  preparePicking( renderData );
492 
493  // Now read the Picking Texture to address the Picking Requests.
494  GL_ASSERT( glReadBuffer( GL_COLOR_ATTACHMENT0 ) );
495 
496  int pick[4];
497  for ( const PickingQuery& query : m_pickingQueries ) {
498  PickingResult result;
499  // fill picking result according to picking mode
500  if ( query.m_mode < C_VERTEX ) {
501  // skip query if out of window (can occur when picking while moving outside)
502  if ( query.m_screenCoords.x() < 0 || query.m_screenCoords.x() > m_width - 1 ||
503  query.m_screenCoords.y() < 0 || query.m_screenCoords.y() > m_height - 1 ) {
504  // this qurey has an empty result
505  m_pickingResults.push_back( {} );
506  continue;
507  }
508  GL_ASSERT( glReadPixels( query.m_screenCoords.x(),
509  query.m_screenCoords.y(),
510  1,
511  1,
512  GL_RGBA_INTEGER,
513  GL_INT,
514  pick ) );
515  result.setRoIdx( pick[0] ); // RO idx
516  result.addIndex( { pick[2], pick[1], pick[3] } );
517  }
518  else {
519  // select the results for the RO with the most representatives
520  // (or first to come if same amount)
521  std::map<int, PickingResult> resultPerRO;
522  for ( auto i = -m_brushRadius; i <= m_brushRadius; i += 3 ) {
523  auto h = std::round( std::sqrt( m_brushRadius * m_brushRadius - i * i ) );
524  for ( auto j = -h; j <= +h; j += 3 ) {
525  const int x = query.m_screenCoords.x() + i;
526  const int y = query.m_screenCoords.y() - j;
527  // skip query if out of window (can occur when picking while moving outside)
528  if ( x < 0 || x > int( m_width ) - 1 || y < 0 || y > int( m_height ) - 1 ) {
529  continue;
530  }
531  GL_ASSERT( glReadPixels( x, y, 1, 1, GL_RGBA_INTEGER, GL_INT, pick ) );
532  resultPerRO[pick[0]].setRoIdx( pick[0] );
533  resultPerRO[pick[0]].addIndex( { pick[2], pick[1], pick[3] } );
534  }
535  }
536 
537  auto itr = std::max_element(
538  resultPerRO.begin(),
539  resultPerRO.end(),
540  []( const std::map<int, PickingResult>::value_type& a,
541  const std::map<int, PickingResult>::value_type& b ) -> bool {
542  return a.second.getIndices().size() < b.second.getIndices().size();
543  } );
544  result = itr->second;
545  }
546  result.setMode( query.m_mode );
547  m_pickingResults.push_back( result );
548  }
549 
550  m_pickingFbo->unbind();
551 }
552 void Renderer::preparePicking( const Data::ViewingParameters& renderData ) {
553 
554  GL_ASSERT( glDepthMask( GL_TRUE ) );
555  GL_ASSERT( glColorMask( 1, 1, 1, 1 ) );
556  GL_ASSERT( glDrawBuffers( 1, buffers ) );
557 
558  float clearDepth = 1.0;
559  int clearColor[] = { -1, -1, -1, -1 };
560 
561  GL_ASSERT( glClearBufferiv( GL_COLOR, 0, clearColor ) );
562  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
563 
564  splitRenderQueuesForPicking( renderData );
565 
566  // First draw Geometry Objects
567  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
568  GL_ASSERT( glDepthFunc( GL_LESS ) );
569 
570  renderForPicking( renderData, m_pickingShaders, m_fancyRenderObjectsPicking );
571 
572  if ( m_drawDebug ) {
573  // Then draw debug objects
574  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
575  renderForPicking( renderData, m_pickingShaders, m_debugRenderObjectsPicking );
576 
577  // Then draw xrayed objects on top of normal objects
578  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
579  renderForPicking( renderData, m_pickingShaders, m_xrayRenderObjectsPicking );
580  }
581 
582  // Finally draw ui stuff on top of everything
583  // these have a different way to compute the transform matrices
584  // FIXME (florian): find a way to use renderForPicking()!
585  GL_ASSERT( glClearBufferfv( GL_DEPTH, 0, &clearDepth ) );
586  for ( uint i = 0; i < m_pickingShaders.size(); ++i ) {
587  m_pickingShaders[i]->bind();
588  m_pickingShaders[i]->setUniform( "transform.proj", renderData.projMatrix );
589  m_pickingShaders[i]->setUniform( "transform.view", renderData.viewMatrix );
590 
591  for ( const auto& ro : m_uiRenderObjectsPicking[i] ) {
592  if ( ro->isVisible() && ro->isPickable() ) {
593  m_pickingShaders[i]->setUniform( "objectId", ro->getIndex().getValue() );
594 
595  Core::Matrix4 M = ro->getTransformAsMatrix();
596  Core::Matrix4 MV = renderData.viewMatrix * M;
597  Scalar d = MV.block<3, 1>( 0, 3 ).norm();
598 
599  Core::Matrix4 S = Core::Matrix4::Identity();
600  S( 0, 0 ) = S( 1, 1 ) = S( 2, 2 ) = d;
601 
602  M = M * S;
603  Core::Matrix4 N = M.inverse().transpose();
604 
605  m_pickingShaders[i]->setUniform( "transform.model", M );
606  m_pickingShaders[i]->setUniform( "transform.worldNormal", N );
607 
608  // render
609  ro->getMesh()->render( m_pickingShaders[i] );
610  }
611  }
612  }
613 }
614 
615 void Renderer::restoreExternalFBOInternal() {
616  RadiumEngine::getInstance()->popFboAndViewport();
617 }
618 
619 void Renderer::drawScreenInternal() {
620 
621  restoreExternalFBOInternal();
622  // Display the final screen
623  {
624  GL_ASSERT( glDepthFunc( GL_ALWAYS ) );
625 
626  auto shader = ( m_displayedTexture->getParameters().type == GL_INT ||
627  m_displayedTexture->getParameters().type == GL_UNSIGNED_INT )
628  ? ( m_displayedTexture->getParameters().format == GL_DEPTH_COMPONENT
629  ? m_shaderProgramManager->getShaderProgram( "DisplayDepthBuffer" )
630  : m_shaderProgramManager->getShaderProgram( "DrawScreenI" ) )
631  : m_shaderProgramManager->getShaderProgram( "DrawScreen" );
632  shader->bind();
633  shader->setUniform( "screenTexture", m_displayedTexture, 0 );
634  m_quadMesh->render( shader );
635 
636  GL_ASSERT( glDepthFunc( GL_LESS ) );
637  }
638  // draw brush circle if enabled
639  if ( m_brushRadius > 0 ) {
640  GL_ASSERT( glDisable( GL_BLEND ) );
641  GL_ASSERT( glDisable( GL_DEPTH_TEST ) );
642  auto shader = m_shaderProgramManager->getShaderProgram( "CircleBrush" );
643  shader->bind();
644  shader->setUniform( "mousePosition", m_mousePosition );
645  shader->setUniform( "brushRadius", m_brushRadius );
646  shader->setUniform( "dim", Core::Vector2( m_width, m_height ) );
647  m_quadMesh->render( shader );
648  GL_ASSERT( glEnable( GL_DEPTH_TEST ) );
649  GL_ASSERT( glEnable( GL_BLEND ) );
650  }
651 }
652 
653 void Renderer::notifyRenderObjectsRenderingInternal() {
654  for ( auto& ro : m_fancyRenderObjects ) {
655  ro->hasBeenRenderedOnce();
656  }
657 
658  for ( auto& ro : m_debugRenderObjects ) {
659  ro->hasBeenRenderedOnce();
660  }
661 
662  for ( auto& ro : m_xrayRenderObjects ) {
663  ro->hasBeenRenderedOnce();
664  }
665 
666  for ( auto& ro : m_uiRenderObjects ) {
667  ro->hasBeenRenderedOnce();
668  }
669 }
670 
671 void Renderer::resize( uint w, uint h ) {
672  // never init zero sized texture/fbo since fbo do not consider them as complete
673  // not initilialized ? init texture
674  // then only init if size is different than current
675  if ( ( w != 0 && h != 0 ) && ( !m_initialized || ( w != m_width || h != m_height ) ) ) {
676  m_width = w;
677  m_height = h;
678  m_depthTexture->resize( m_width, m_height );
679  m_pickingTexture->resize( m_width, m_height );
680  m_fancyTexture->resize( m_width, m_height );
681 
682  m_pickingFbo->bind();
683  m_pickingFbo->attachTexture( GL_DEPTH_ATTACHMENT, m_depthTexture->texture() );
684  m_pickingFbo->attachTexture( GL_COLOR_ATTACHMENT0, m_pickingTexture->texture() );
685  if ( m_pickingFbo->checkStatus() != GL_FRAMEBUFFER_COMPLETE ) {
686  LOG( logERROR ) << "File " << __FILE__ << "(" << __LINE__ << ") Picking FBO Error "
687  << m_pickingFbo->checkStatus();
688  }
689  m_pickingFbo->unbind();
690  resizeInternal();
691  GL_CHECK_ERROR;
692  m_initialized = true;
693  }
694 }
695 
696 void Renderer::displayTexture( const std::string& texName ) {
697  if ( m_secondaryTextures.find( texName ) != m_secondaryTextures.end() ) {
699  }
700  else { m_displayedTexture = m_fancyTexture.get(); }
701 }
702 
703 std::vector<std::string> Renderer::getAvailableTextures() const {
704  std::vector<std::string> ret;
705  ret.emplace_back( "Final image" );
706  std::transform( m_secondaryTextures.begin(),
707  m_secondaryTextures.end(),
708  std::back_inserter( ret ),
709  []( const std::pair<std::string, Data::Texture*> tex ) { return tex.first; } );
710  return ret;
711 }
712 
715 }
716 
717 std::unique_ptr<uchar[]> Renderer::grabFrame( size_t& w, size_t& h ) const {
719  tex->bind();
720 
721  // Get a buffer to store the pixels of the OpenGL texture (in float format)
722  auto pixels = std::unique_ptr<float[]>( new float[tex->width() * tex->height() * 4] );
723 
724  // Grab the texture data
725  GL_ASSERT( glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_SCALAR, pixels.get() ) );
726 
727  // Now we must convert the floats to RGB while flipping the image updisde down.
728  auto writtenPixels = std::unique_ptr<uchar[]>( new uchar[tex->width() * tex->height() * 4] );
729  for ( uint j = 0; j < tex->height(); ++j ) {
730  for ( uint i = 0; i < tex->width(); ++i ) {
731  auto in = 4 * ( j * tex->width() + i ); // Index in the texture buffer
732  auto ou = 4 * ( ( tex->height() - 1 - j ) * tex->width() +
733  i ); // Index in the final image (note the j flipping).
734 
735  writtenPixels[ou + 0] =
736  (uchar)std::clamp( float( pixels[in + 0] * 255.f ), float( 0 ), float( 255 ) );
737  writtenPixels[ou + 1] =
738  (uchar)std::clamp( float( pixels[in + 1] * 255.f ), float( 0 ), float( 255 ) );
739  writtenPixels[ou + 2] =
740  (uchar)std::clamp( float( pixels[in + 2] * 255.f ), float( 0 ), float( 255 ) );
741  writtenPixels[ou + 3] =
742  (uchar)std::clamp( float( pixels[in + 3] * 255.f ), float( 0 ), float( 255 ) );
743  }
744  }
745  w = tex->width();
746  h = tex->height();
747  return writtenPixels;
748 }
749 
750 void Renderer::addLight( const Scene::Light* light ) {
751  for ( auto m : m_lightmanagers )
752  m->addLight( light );
753 }
754 
755 bool Renderer::hasLight() const {
756  int n = 0;
757  for ( auto m : m_lightmanagers )
758  n += m->count();
759  return n != 0;
760 }
761 
763  std::vector<RenderObjectPtr> renderObjects;
764  m_renderObjectManager->getRenderObjectsByType( renderObjects, RenderObjectType::Geometry );
765  if ( renderObjects.size() > 0 ) {
766  for ( auto& ro : renderObjects ) {
767  buildRenderTechnique( ro.get() );
768  }
769  }
770  return 0;
771 }
772 
773 } // namespace Rendering
774 } // namespace Engine
775 } // namespace Ra
void addShader(ShaderType type, const std::string &name)
Core::Utils::optional< const Data::ShaderProgram * > addShaderProgram(const Data::ShaderConfiguration &config)
const Data::ShaderProgram * getShaderProgram(const std::string &id)
void bind(int unit=-1)
Bind the texture to enable its use in a shader.
Definition: Texture.cpp:88
size_t height() const
Definition: Texture.hpp:193
size_t width() const
Definition: Texture.hpp:189
const TextureParameters & getParameters() const
get read access to texture parameters
Definition: Texture.hpp:221
void addIndex(const std::tuple< int, int, int > &idx)
Add new ids to the result.
Definition: Renderer.hpp:512
Data::ShaderProgramManager * m_shaderProgramManager
Definition: Renderer.hpp:436
void render(const Data::ViewingParameters &renderData)
Tell the renderer it needs to render. This method does the following steps :
Definition: Renderer.cpp:315
void initialize(uint width, uint height)
Initialize renderer.
Definition: Renderer.cpp:56
PickingResult doPickingNow(const PickingQuery &query, const Data::ViewingParameters &renderData)
Definition: Renderer.cpp:191
virtual void displayTexture(const std::string &texName)
Change the texture that is displayed on screen. Set m_displayedIsDepth to true if depth linearization...
Definition: Renderer.cpp:696
virtual void debugInternal(const Data::ViewingParameters &renderData)=0
Add the debug layer with useful informations.
void resize(uint width, uint height)
Resize the viewport and all the screen textures, fbos. This function must be overrided as soon as som...
Definition: Renderer.cpp:671
bool hasLight() const
Tell if the renderer has an usable light.
Definition: Renderer.cpp:755
virtual void addLight(const Scene::Light *light)
Definition: Renderer.cpp:750
std::vector< Ra::Engine::Scene::LightManager * > m_lightmanagers
Definition: Renderer.hpp:455
virtual void postProcessInternal(const Data::ViewingParameters &renderData)=0
Do all post processing stuff. If you override this method, be careful to fill.
Data::Texture * getDisplayTexture() const
Definition: Renderer.hpp:559
virtual bool buildRenderTechnique(RenderObject *ro) const =0
std::unique_ptr< Data::Texture > m_fancyTexture
Final color texture : might be attached to the main framebuffer.
Definition: Renderer.hpp:475
Data::Texture * m_displayedTexture
The texture that will be displayed on screen. If no call to.
Definition: Renderer.hpp:451
virtual std::vector< std::string > getAvailableTextures() const
Return the names of renderer available textures.
Definition: Renderer.cpp:703
virtual std::unique_ptr< uchar[]> grabFrame(size_t &w, size_t &h) const
Definition: Renderer.cpp:717
@ C_VERTEX
Picks all vertices of a mesh within a screen space circle.
Definition: Renderer.hpp:70
RenderObjectManager * m_renderObjectManager
Definition: Renderer.hpp:442
virtual void initializeInternal()=0
initializeInternal Initialize the renderer dependant resources.
virtual void uiInternal(const Data::ViewingParameters &renderData)=0
Draw the UI data.
std::map< std::string, Data::Texture * > m_secondaryTextures
Textures exposed in the texture section box to be displayed.
Definition: Renderer.hpp:477
std::unique_ptr< Data::Texture > m_depthTexture
Depth texture : might be attached to the main framebuffer.
Definition: Renderer.hpp:473
virtual void updateStepInternal(const Data::ViewingParameters &renderData)=0
virtual void renderInternal(const Data::ViewingParameters &renderData)=0
All the scene rendering magics basically happens here.
void addConfiguration(const ShaderConfiguration &config)
Definition: Cage.cpp:3
GLenum magFilter
OpenGL magnification filter ( GL_LINEAR or GL_NEAREST )
Definition: Texture.hpp:68
size_t height
height of the texture (t dimension)
Definition: Texture.hpp:50
GLenum minFilter
OpenGL minification filter ( GL_LINEAR or GL_NEAREST or GL_XXX_MIPMAP_YYY )
Definition: Texture.hpp:66
GLenum format
Format of the external data.
Definition: Texture.hpp:54
size_t width
width of the texture (s dimension)
Definition: Texture.hpp:48
GLenum target
OpenGL target.
Definition: Texture.hpp:46
GLenum type
Type of the components in external data.
Definition: Texture.hpp:58
GLenum internalFormat
OpenGL internal format (WARNING, for Integer textures, must be GL_XXX_INTEGER)
Definition: Texture.hpp:56
std::string name
Name of the texture.
Definition: Texture.hpp:44
the set of viewing parameters extracted from the camera and given to the renderer