Radium Engine  1.5.20
Loading...
Searching...
No Matches
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
30namespace Ra {
31namespace Engine {
32namespace Rendering {
33
34using namespace Core::Utils; // log
35
36namespace {
37const 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
54Renderer::~Renderer() = default;
55
56void 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" );
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.image.width = m_width;
149 texparams.image.height = m_height;
150 texparams.image.target = GL_TEXTURE_2D;
151 texparams.sampler.minFilter = GL_NEAREST;
152 texparams.sampler.magFilter = GL_NEAREST;
153
154 texparams.name = "Depth";
155 texparams.image.internalFormat = GL_DEPTH_COMPONENT24;
156 texparams.image.format = GL_DEPTH_COMPONENT;
157 texparams.image.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.image.internalFormat = GL_RGBA32I;
163 texparams.image.format = GL_RGBA_INTEGER;
164 texparams.image.type = GL_INT;
165 m_pickingTexture = std::make_unique<Data::Texture>( texparams );
166
167 // Final texture
168 texparams.name = "Final image";
169 texparams.image.internalFormat = GL_RGBA32F;
170 texparams.image.format = GL_RGBA;
171 texparams.image.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
377void Renderer::saveExternalFBOInternal() {
378 RadiumEngine::getInstance()->pushFboAndViewport();
379 // Set the internal rendering viewport
380 glViewport( 0, 0, int( m_width ), int( m_height ) );
381}
382
383void 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
399void 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,
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()
436void 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
451void 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()
459void 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
487void 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)
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(),
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}
552void 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
615void Renderer::restoreExternalFBOInternal() {
616 RadiumEngine::getInstance()->popFboAndViewport();
617}
618
619void Renderer::drawScreenInternal() {
620
621 restoreExternalFBOInternal();
622 // Display the final screen
623 {
624 GL_ASSERT( glDepthFunc( GL_ALWAYS ) );
625
626 auto shader = ( m_displayedTexture->getParameters().image.type == GL_INT ||
627 m_displayedTexture->getParameters().image.type == GL_UNSIGNED_INT )
628 ? ( m_displayedTexture->getParameters().image.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
653void 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
671void 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->getGpuTexture() );
684 m_pickingFbo->attachTexture( GL_COLOR_ATTACHMENT0, m_pickingTexture->getGpuTexture() );
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();
691 GL_CHECK_ERROR;
692 m_initialized = true;
693 }
694}
695
696void 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
705 ret.emplace_back( "Final image" );
708 std::back_inserter( ret ),
709 []( const std::pair<std::string, Data::Texture*> tex ) { return tex.first; } );
710 return ret;
711}
712
716
717std::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->getWidth() * tex->getHeight() * 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 =
729 std::unique_ptr<uchar[]>( new uchar[tex->getWidth() * tex->getHeight() * 4] );
730 for ( uint j = 0; j < tex->getHeight(); ++j ) {
731 for ( uint i = 0; i < tex->getWidth(); ++i ) {
732 auto in = 4 * ( j * tex->getWidth() + i ); // Index in the texture buffer
733 auto ou = 4 * ( ( tex->getHeight() - 1 - j ) * tex->getWidth() +
734 i ); // Index in the final image (note the j flipping).
735
736 writtenPixels[ou + 0] =
737 (uchar)std::clamp( float( pixels[in + 0] * 255.f ), float( 0 ), float( 255 ) );
738 writtenPixels[ou + 1] =
739 (uchar)std::clamp( float( pixels[in + 1] * 255.f ), float( 0 ), float( 255 ) );
740 writtenPixels[ou + 2] =
741 (uchar)std::clamp( float( pixels[in + 2] * 255.f ), float( 0 ), float( 255 ) );
742 writtenPixels[ou + 3] =
743 (uchar)std::clamp( float( pixels[in + 3] * 255.f ), float( 0 ), float( 255 ) );
744 }
745 }
746 w = tex->getWidth();
747 h = tex->getHeight();
748 return writtenPixels;
749}
750
751void Renderer::addLight( const Scene::Light* light ) {
752 for ( auto m : m_lightmanagers )
753 m->addLight( light );
754}
755
756bool Renderer::hasLight() const {
757 int n = 0;
758 for ( auto m : m_lightmanagers )
759 n += m->count();
760 return n != 0;
761}
762
764 std::vector<RenderObjectPtr> renderObjects;
765 m_renderObjectManager->getRenderObjectsByType( renderObjects, RenderObjectType::Geometry );
766 if ( renderObjects.size() > 0 ) {
767 for ( auto& ro : renderObjects ) {
768 buildRenderTechnique( ro.get() );
769 }
770 }
771 return 0;
772}
773
774} // namespace Rendering
775} // namespace Engine
776} // namespace Ra
T back_inserter(T... args)
T begin(T... args)
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)
Represent a Texture of the engine.
Definition Texture.hpp:120
void bind(int unit=-1)
Bind the texture to GPU texture unit to enable its use in a shader. Need active OpenGL context.
Definition Texture.cpp:129
size_t getWidth() const
Definition Texture.hpp:179
const TextureParameters & getParameters() const
get read access to texture parameters
Definition Texture.hpp:200
size_t getHeight() const
Definition Texture.hpp:182
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:756
virtual void addLight(const Scene::Light *light)
Definition Renderer.cpp:751
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.
T emplace_back(T... args)
T end(T... args)
T max_element(T... args)
T move(T... args)
void addConfiguration(const ShaderConfiguration &config)
@ UI
"Ui" render objects are drawn on top of everything else and view independant
@ Debug
"Debug" render objects are drawn like any other object (not lit though)
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T round(T... args)
T size(T... args)
T sqrt(T... args)
GLenum internalFormat
OpenGL internal format (WARNING, for Integer textures, must be GL_XXX_INTEGER)
Definition Texture.hpp:77
GLenum minFilter
OpenGL minification filter ( GL_LINEAR or GL_NEAREST or GL_XXX_MIPMAP_YYY )
Definition Texture.hpp:30
GLenum magFilter
OpenGL magnification filter ( GL_LINEAR or GL_NEAREST )
Definition Texture.hpp:32
Describes the sampler and image of a texture.
Definition Texture.hpp:99
the set of viewing parameters extracted from the camera and given to the renderer
T transform(T... args)