Radium Engine  1.5.0
Viewer.cpp
1 #include <glbinding-aux/ContextInfo.h>
2 #include <glbinding-aux/debug.h>
3 #include <glbinding-aux/types_to_string.h>
4 #include <glbinding/Binding.h>
5 #include <glbinding/glbinding.h>
6 
7 #include <glbinding/Version.h>
8 // Do not import namespace to prevent glbinding/QTOpenGL collision
9 #include <glbinding/gl/gl.h>
10 
11 #include <globjects/globjects.h>
12 
13 // include radium engine here to prevent glbinding incompatibility with gl.h
14 #include <Gui/Viewer/Viewer.hpp>
15 
16 #include <iostream>
17 
18 #include <QOpenGLContext>
19 
20 #include <QApplication>
21 #include <QMouseEvent>
22 #include <QPainter>
23 #include <QTimer>
24 
25 #define STB_IMAGE_WRITE_IMPLEMENTATION
26 #include <stb/stb_image_write.h>
27 
28 #include <Core/Asset/Camera.hpp>
29 #include <Core/Asset/FileData.hpp>
30 #include <Core/Containers/MakeShared.hpp>
31 #include <Core/Math/Math.hpp>
32 #include <Core/Utils/Color.hpp>
33 #include <Core/Utils/Log.hpp>
34 #include <Core/Utils/StringUtils.hpp>
35 #include <Engine/Data/ShaderProgramManager.hpp>
36 #include <Engine/Data/ViewingParameters.hpp>
37 #include <Engine/Rendering/ForwardRenderer.hpp>
38 #include <Engine/Rendering/Renderer.hpp>
39 #include <Engine/Scene/CameraComponent.hpp>
40 #include <Engine/Scene/CameraManager.hpp>
41 #include <Engine/Scene/Component.hpp>
42 #include <Engine/Scene/DirLight.hpp>
43 #include <Engine/Scene/EntityManager.hpp>
44 #include <Engine/Scene/SystemDisplay.hpp>
45 #include <Gui/AboutDialog/RadiumHelpDialog.hpp>
46 #include <Gui/Utils/KeyMappingManager.hpp>
47 #include <Gui/Utils/Keyboard.hpp>
48 #include <Gui/Utils/PickingManager.hpp>
49 #include <Gui/Viewer/FlightCameraManipulator.hpp>
50 #include <Gui/Viewer/Gizmo/GizmoManager.hpp>
51 #include <Gui/Viewer/RotateAroundCameraManipulator.hpp>
52 #include <Gui/Viewer/TrackballCameraManipulator.hpp>
53 #include <Gui/Viewer/Viewer.hpp>
54 
55 #include <QApplication>
56 #include <QMouseEvent>
57 #include <QPainter>
58 #include <QTimer>
59 
60 namespace Ra {
61 namespace Gui {
62 
63 using namespace Core::Utils; // log
64 using namespace glbinding;
65 
66 using ViewerMapping = KeyMappingManageable<Viewer>;
67 
68 #define KMA_VALUE( x ) KeyMappingManager::KeyMappingAction Viewer::x;
69 KeyMappingViewer
70 #undef KMA_VALUE
71 
72 // Register all keymapings related to the viewer and its managed functionalities (Trackball
73 // camera, Gizmo, ..)
75  auto keyMappingManager = KeyMappingManager::getInstance();
76 
77  // Add default manipulator listener
78  keyMappingManager->addListener( TrackballCameraManipulator::configureKeyMapping );
79  keyMappingManager->addListener( FlightCameraManipulator::configureKeyMapping );
80  keyMappingManager->addListener(
82  // add viewer related listener
83  keyMappingManager->addListener( GizmoManager::configureKeyMapping );
84  keyMappingManager->addListener( configureKeyMapping );
85 
86  m_keyMappingCallbackManager = KeyMappingCallbackManager { ViewerMapping::getContext() };
87  m_keyMappingCallbackManager.addEventCallback( VIEWER_TOGGLE_WIREFRAME, [this]( QEvent* event ) {
88  if ( event->type() == QEvent::KeyPress ) m_currentRenderer->toggleWireframe();
89  } );
90  m_keyMappingCallbackManager.addEventCallback( VIEWER_RELOAD_SHADERS, [this]( QEvent* event ) {
91  if ( event->type() == QEvent::KeyPress ) reloadShaders();
92  } );
93  m_keyMappingCallbackManager.addEventCallback(
94  VIEWER_PICKING_MULTI_CIRCLE, [this]( QEvent* event ) {
95  if ( event->type() == QEvent::KeyPress ) {
96  m_isBrushPickingEnabled = !m_isBrushPickingEnabled;
97  m_currentRenderer->setBrushRadius( m_isBrushPickingEnabled ? m_brushRadius : 0 );
98  emit toggleBrushPicking( m_isBrushPickingEnabled );
99  }
100  } );
101  m_keyMappingCallbackManager.addEventCallback( VIEWER_SWITCH_CAMERA, []( QEvent* event ) {
102  if ( event->type() == QEvent::KeyPress ) {
103  auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
104  Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
105  static int idx = 0;
106  if ( cameraManager->count() > 0 ) {
107  idx %= cameraManager->count();
108  cameraManager->activate( idx );
109  }
110  idx++;
111  }
112  } );
113 
114  m_keyMappingCallbackManager.addEventCallback( VIEWER_CAMERA_FIT_SCENE, [this]( QEvent* event ) {
115  if ( event->type() == QEvent::KeyPress ) fitCamera();
116  } );
117  m_keyMappingCallbackManager.addEventCallback( VIEWER_HELP, [this]( QEvent* event ) {
118  if ( event->type() == QEvent::KeyPress ) {
119  displayHelpDialog();
120  requestActivate();
121  }
122  } );
123 }
124 
125 void Viewer::configureKeyMapping_impl() {
126  auto keyMappingManager = KeyMappingManager::getInstance();
127  ViewerMapping::setContext( keyMappingManager->getContext( "ViewerContext" ) );
128  if ( ViewerMapping::getContext().isInvalid() ) {
129  LOG( logINFO )
130  << "ViewerContext not defined (maybe the configuration file do not contains it)";
131  LOG( Ra::Core::Utils::logERROR ) << "ViewerContext all keymapping invalide !";
132  return;
133  }
134 
135 #define KMA_VALUE( XX ) XX = keyMappingManager->getAction( ViewerMapping::getContext(), #XX );
136  KeyMappingViewer
137 #undef KMA_VALUE
138 }
139 
140 Viewer::Viewer( QScreen* screen ) :
141  WindowQt( screen ),
142  m_currentRenderer( nullptr ),
143  m_pickingManager( new PickingManager() ),
144  m_isBrushPickingEnabled( false ),
145  m_brushRadius( 10 ),
146  m_camera( nullptr ),
147  m_gizmoManager( nullptr ),
148  m_keyMappingCallbackManager { ViewerMapping::getContext() } {}
149 
151  if ( m_glInitialized.load() ) {
152  makeCurrent();
153  m_renderers.clear();
154 
155  delete m_gizmoManager;
156  doneCurrent();
157  }
158 }
159 
161  m_camera.reset( ci );
162 }
163 
165  return m_camera.get();
166 }
167 
168 void Viewer::resetToDefaultCamera() {
169 
170  auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
171  Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
172  cameraManager->resetActiveCamera();
173 }
174 
176  return m_gizmoManager;
177 }
178 
180  return m_currentRenderer;
181 }
182 
184  return m_currentRenderer;
185 }
186 
187 int Viewer::addRenderer( const std::shared_ptr<Engine::Rendering::Renderer>& e ) {
188  m_renderers.push_back( e );
189 
190  // initialize the renderer (deferred if GL is not ready yet)
191  if ( m_glInitialized.load() ) {
192  makeCurrent();
193  initializeRenderer( e.get() );
194  LOG( logINFO ) << "[Viewer] New Renderer (" << e->getRendererName() << ") added.";
195  doneCurrent();
196  }
197  else {
198  LOG( logINFO ) << "[Viewer] New Renderer (" << e->getRendererName()
199  << ") added with deferred init.";
200  m_pendingRenderers.push_back( e );
201  }
202 
203  return m_renderers.size() - 1;
204 }
205 
206 PickingManager* Viewer::getPickingManager() {
207  return m_pickingManager;
208 }
209 
210 void Viewer::update( const Scalar dt ) {
211  CORE_UNUSED( dt );
212  if ( m_gizmoManager != nullptr ) { m_gizmoManager->updateValues(); }
213 }
214 
215 // Asynchronous rendering implementation
216 
217 void Viewer::startRendering( const Scalar dt ) {
218 
219  CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized before rendering." );
220 
221  CORE_ASSERT( m_currentRenderer != nullptr, "No renderer found." );
222 
223  m_pickingManager->clear();
224  makeCurrent();
225 
226  // update znear/zfar to fit the scene ...
227  auto entityManager = Engine::RadiumEngine::getInstance()->getEntityManager();
228  if ( entityManager ) {
229 
230  // to fit scene only
231  // auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
232  // to fit also debug and system entity aabb
233  Core::Aabb aabb {};
234  for ( const auto& entity : entityManager->getEntities() ) {
235  // entity aabb is in world space
236  aabb.extend( entity->computeAabb() );
237  }
238 
239  if ( !aabb.isEmpty() ) { m_camera->getCamera()->fitZRange( aabb ); }
240  else {
241  auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
242  Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
243 
244  // scene is empty, reset to defaults bounds
245  m_camera->setCameraZNear( cameraManager->defaultCamera.getZNear() );
246  m_camera->setCameraZFar( cameraManager->defaultCamera.getZFar() );
247  }
248  }
249 
252  if ( !m_currentRenderer->hasLight() ) {
253  if ( m_camera->hasLightAttached() )
254  m_currentRenderer->addLight( m_camera->getLight() );
255  else
256  LOG( logDEBUG ) << "Unable to attach the head light!";
257  }
259  m_camera->getCamera()->getViewMatrix(), m_camera->getCamera()->getProjMatrix(), dt };
260  m_currentRenderer->render( data );
261 }
262 
264  if ( isExposed() ) { m_context->swapBuffers( this ); }
265  doneCurrent();
266 }
267 
269  CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized before rendering." );
270 
271  CORE_ASSERT( m_currentRenderer != nullptr, "No renderer found." );
272 
273  CORE_ASSERT( m_currentRenderer->getPickingQueries().size() ==
274  m_currentRenderer->getPickingResults().size(),
275  "There should be one result per query." );
276 
277  for ( uint i = 0; i < m_currentRenderer->getPickingQueries().size(); ++i ) {
279  m_currentRenderer->getPickingQueries()[i];
280 
281  if ( query.m_purpose == Engine::Rendering::Renderer::PickingPurpose::MANIPULATION ) {
282  const auto& result = m_currentRenderer->getPickingResults()[i];
283  m_pickingManager->setCurrent( result );
284  emit rightClickPicking( result );
285  }
286  }
287 }
288 
289 void Viewer::fitCameraToScene( const Core::Aabb& aabb ) {
290  if ( !aabb.isEmpty() ) {
291  CORE_ASSERT( m_camera != nullptr, "No camera found." );
292  m_camera->fitScene( aabb );
293 
294  // uncomment to display scene aabb
295  // RA_DISPLAY_AABB( aabb, Color::Blue() );
296  emit needUpdate();
297  }
298  else { LOG( logINFO ) << "Unable to fit the camera to the scene : empty Bbox."; }
299 }
300 
301 void Viewer::fitCamera() {
302  auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
303  if ( aabb.isEmpty() ) { getCameraManipulator()->resetCamera(); }
304  else { fitCameraToScene( aabb ); }
305 }
306 
307 std::vector<std::string> Viewer::getRenderersName() const {
308  std::vector<std::string> ret;
309 
310  for ( const auto& renderer : m_renderers ) {
311  if ( renderer ) { ret.push_back( renderer->getRendererName() ); }
312  }
313 
314  return ret;
315 }
316 
317 void Viewer::grabFrame( const std::string& filename ) {
318  makeCurrent();
319 
320  size_t w, h;
321  auto writtenPixels = m_currentRenderer->grabFrame( w, h );
322 
323  std::string ext = Core::Utils::getFileExt( filename );
324 
325  if ( ext == "bmp" ) { stbi_write_bmp( filename.c_str(), w, h, 4, writtenPixels.get() ); }
326  else if ( ext == "png" ) {
327  stbi_write_png( filename.c_str(), w, h, 4, writtenPixels.get(), w * 4 * sizeof( uchar ) );
328  }
329  else {
330  LOG( logWARNING ) << "Cannot write frame to " << filename << " : unsupported extension";
331  }
332 
333  doneCurrent();
334 }
335 
336 void Viewer::enableDebug() {
337  glbinding::setCallbackMask( glbinding::CallbackMask::After |
338  glbinding::CallbackMask::ParametersAndReturnValue );
339  glbinding::setAfterCallback( []( const glbinding::FunctionCall& call ) {
340  std::cerr << call.function->name() << "(";
341  for ( unsigned i = 0; i < call.parameters.size(); ++i ) {
342  std::cerr << call.parameters[i].get();
343  if ( i < call.parameters.size() - 1 ) { std::cerr << ", "; }
344  }
345  std::cerr << ")";
346  if ( call.returnValue ) { std::cerr << " -> " << call.returnValue.get(); }
347  std::cerr << std::endl;
348  } );
349 }
350 
352  CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized reload shaders." );
353 
354  makeCurrent();
355  // FIXME : check thread-saefty of this.
356  m_currentRenderer->lockRendering();
357  m_currentRenderer->reloadShaders();
358  m_currentRenderer->unlockRendering();
359  doneCurrent();
360 
361  emit needUpdate();
362 }
363 
364 void Viewer::displayTexture( const QString& tex ) {
365  CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized to display textures." );
366 
367  makeCurrent();
368  m_currentRenderer->lockRendering();
369  m_currentRenderer->displayTexture( tex.toStdString() );
370  m_currentRenderer->unlockRendering();
371  doneCurrent();
372 
373  emit needUpdate();
374 }
375 
376 bool Viewer::changeRenderer( int index ) {
377  if ( m_glInitialized.load() && m_renderers[index] ) {
378  makeCurrent();
379 
380  if ( m_currentRenderer != nullptr ) { m_currentRenderer->lockRendering(); }
381 
382  m_currentRenderer = m_renderers[index].get();
383  // renderers in m_renderers are supposed to be locked
384  auto deviceSize = toDevice( { width(), height() } );
385  m_currentRenderer->resize( deviceSize.x(), deviceSize.y() );
386  // Configure the renderObjects for this renderer
387  m_currentRenderer->buildAllRenderTechniques();
388  m_currentRenderer->unlockRendering();
389 
390  LOG( logINFO ) << "[Viewer] Set active renderer: " << m_currentRenderer->getRendererName();
391 
392  // resize camera viewport since the one in show event might have 0x0
393  if ( m_camera ) { m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() ); }
394 
395  doneCurrent();
396  emit rendererReady();
397 
398  emit needUpdate();
399  return true;
400  }
401  return false;
402 }
403 
404 void Viewer::enablePostProcess( int enabled ) {
405  m_currentRenderer->enablePostProcess( enabled );
406 }
407 
408 void Viewer::enableDebugDraw( int enabled ) {
409  m_currentRenderer->enableDebugDraw( enabled );
410 }
411 
412 void Viewer::setBackgroundColor( const Core::Utils::Color& background ) {
413  m_backgroundColor = background;
414  for ( const auto& renderer : m_renderers )
415  renderer->setBackgroundColor( m_backgroundColor );
416 
417  emit needUpdate();
418 }
419 
420 void Viewer::onAboutToCompose() {
421  // This slot function is called from the main thread as part of the event loop
422  // when the GUI is about to update. We have to wait for the rendering to finish.
423  m_currentRenderer->lockRendering();
424 }
425 
426 void Viewer::onAboutToResize() {
427  // Like swap buffers, resizing is a blocking operation and we have to wait for the rendering
428  // to finish before resizing.
429  m_currentRenderer->lockRendering();
430 }
431 
432 void Viewer::onResized() {
433  m_currentRenderer->unlockRendering();
434  emit needUpdate();
435 }
436 
437 void Viewer::onFrameSwapped() {
438  // This slot is called from the main thread as part of the event loop when the
439  // GUI has finished displaying the rendered image, so we unlock the renderer.
440  m_currentRenderer->unlockRendering();
441 }
442 
444  if ( m_gizmoManager == nullptr ) { m_gizmoManager = new GizmoManager( this ); }
445 }
446 
448  auto deviceSize = toDevice( { width(), height() } );
449 
450  // see issue #261 Qt Event order and default viewport management (Viewer.cpp)
451  // https://github.com/STORM-IRIT/Radium-Engine/issues/261
452  gl::glViewport( 0, 0, deviceSize.x(), deviceSize.y() );
453 
454  renderer->initialize( deviceSize.x(), deviceSize.y() );
455  renderer->setBackgroundColor( m_backgroundColor );
456  renderer->lockRendering();
457 }
458 
460  globjects::init( getProcAddress );
461  // mark openGL as initialized
462  m_glInitialized = true;
463 
464  LOG( logINFO ) << "*** Radium Engine OpenGL context ***";
465  LOG( logINFO ) << "Renderer (glbinding) : " << glbinding::aux::ContextInfo::renderer();
466  LOG( logINFO ) << "Vendor (glbinding) : " << glbinding::aux::ContextInfo::vendor();
467  LOG( logINFO ) << "OpenGL (glbinding) : "
468  << glbinding::aux::ContextInfo::version().toString();
469  LOG( logINFO ) << "GLSL : "
470  << gl::glGetString( gl::GLenum( GL_SHADING_LANGUAGE_VERSION ) );
471 
472  LOG( logINFO ) << "*** Radium Engine Viewer ***";
473 
474  // emit the signal so that the client will initialize the OpenGL part of the Engine
475  // and custom OpenGL properties
477 
478  // Configure the viewer services
479  auto deviceSize = toDevice( { width(), height() } );
480  // create default camera interface : trackball
481  m_camera = std::make_unique<TrackballCameraManipulator>();
482  m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() );
483  auto headlight = new Engine::Scene::DirectionalLight(
484  Ra::Engine::Scene::SystemEntity::getInstance(), "headlight" );
485  headlight->setColor( Ra::Core::Utils::Color::Grey( 1.0_ra ) );
486 
487  m_camera->attachLight( headlight );
488 
489  // Register to the camera manager active camera changes
490  auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
491  Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
492  cameraManager->activeCameraObservers().attach(
493  [this, size = deviceSize]( Core::Utils::Index /*idx*/ ) {
494  m_camera->updateCamera();
495  m_camera->getCamera()->setViewport( size.x(), size.y() );
496  } );
497 
498  // Initialize renderers added to the viewer before initializeGL
499  for ( auto& rptr : m_pendingRenderers ) {
500  initializeRenderer( rptr.get() );
501  }
502  m_pendingRenderers.clear();
503 
504  // create the gizmo manager (Ui)
506 
507  // Signal that OpenGL is initialized
508  emit glInitialized();
509 
510  // If no renderer was added before that (either by slots on requestEngineOpenGLInitialization
511  // or on glInitialized), add default forward renderer
512  if ( m_renderers.empty() ) {
513  LOG( logINFO ) << "[Viewer] No renderer added, adding default (Forward Renderer)";
514  std::shared_ptr<Ra::Engine::Rendering::Renderer> e(
516  addRenderer( e );
517  }
518 
519  if ( m_currentRenderer == nullptr ) { changeRenderer( 0 ); }
520 
521  return m_glInitialized;
522 }
523 
524 void Viewer::resizeGL( QResizeEvent* event ) {
526 
527  auto deviceSize = toDevice( { event->size().width(), event->size().height() } );
528  gl::glViewport( 0, 0, deviceSize.x(), deviceSize.y() );
529  m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() );
530  m_currentRenderer->resize( deviceSize.x(), deviceSize.y() );
531 
532  emit needUpdate();
533 }
534 
536 Viewer::getPickingMode( const KeyMappingManager::KeyMappingAction& action ) const {
537  if ( action == VIEWER_PICKING_VERTEX ) {
538  return m_isBrushPickingEnabled ? Engine::Rendering::Renderer::C_VERTEX
540  }
541  if ( action == VIEWER_PICKING_EDGE ) {
542  return m_isBrushPickingEnabled ? Engine::Rendering::Renderer::C_EDGE
544  }
545  if ( action == VIEWER_PICKING_TRIANGLE ) {
546  return m_isBrushPickingEnabled ? Engine::Rendering::Renderer::C_TRIANGLE
548  }
549  if ( action == VIEWER_PICKING ) { return Engine::Rendering::Renderer::RO; }
551 }
552 
553 void Viewer::propagateEventToParent( QEvent* event ) {
554  event->ignore();
555  if ( !isTopLevel() ) { QApplication::sendEvent( parent(), event ); }
556 }
557 
558 void Viewer::keyPressEvent( QKeyEvent* event ) {
559  keyPressed( event->key() );
560  if ( !m_glInitialized.load() || event->isAutoRepeat() || !handleKeyPressEvent( event ) )
561  propagateEventToParent( event );
562  else
563  emit needUpdate();
564 }
565 
566 void Viewer::keyReleaseEvent( QKeyEvent* event ) {
567  if ( !m_glInitialized.load() || !handleKeyReleaseEvent( event ) )
568  propagateEventToParent( event );
569  else
570  emit needUpdate();
571  keyReleased( event->key() );
572 }
573 
574 void Viewer::mousePressEvent( QMouseEvent* event ) {
575  if ( !m_glInitialized.load() ) {
576  propagateEventToParent( event );
577  return;
578  }
579 
580  m_currentRenderer->setMousePosition( toDevice( { event->pos().x(), event->pos().y() } ) );
581 
582  // get what's under the mouse
583  auto result = pickAtPosition( toDevice( { event->pos().x(), height() - event->pos().y() } ) );
584  m_depthUnderMouse = result.getDepth();
585 
586  handleMousePressEvent( event, result );
587  emit onMousePress( event );
588  emit needUpdate();
589 }
590 
591 void Viewer::mouseReleaseEvent( QMouseEvent* event ) {
592  handleMouseReleaseEvent( event );
593  emit onMouseRelease( event );
594  emit needUpdate();
595 }
596 
597 void Viewer::mouseMoveEvent( QMouseEvent* event ) {
598  if ( !m_glInitialized.load() ) {
599  event->ignore();
600  return;
601  }
602 
603  m_currentRenderer->setMousePosition( toDevice( { event->pos().x(), event->pos().y() } ) );
604 
605  auto result = pickAtPosition( toDevice( { event->pos().x(), height() - event->pos().y() } ) );
606  m_depthUnderMouse = result.getDepth();
607 
608  handleMouseMoveEvent( event, result );
609  emit onMouseMove( event );
610  emit needUpdate();
611 }
612 
613 void Viewer::wheelEvent( QWheelEvent* event ) {
614 
615  if ( !m_glInitialized.load() ) {
616  event->ignore();
617  return;
618  }
619 
620  handleWheelEvent( event );
621 
622  emit needUpdate();
623 }
624 
625 void Viewer::showEvent( QShowEvent* ev ) {
626  WindowQt::showEvent( ev );
627  auto deviceSize = toDevice( { width(), height() } );
628  m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() );
629 
630  emit needUpdate();
631 }
632 
633 void Viewer::focusOutEvent( QFocusEvent* ) {
634  releaseAllKeys();
635 }
636 
640 Viewer::getComponentActions( const Qt::MouseButtons& buttons,
641  const Qt::KeyboardModifiers& modifiers,
642  int key,
643  bool wheel ) {
644  auto keyMap = KeyMappingManager::getInstance();
645 
646  auto actionCamera =
647  keyMap->getAction( keyMap->getContext( "CameraContext" ), buttons, modifiers, key, wheel );
648  auto actionGizmo =
649  keyMap->getAction( keyMap->getContext( "GizmoContext" ), buttons, modifiers, key, wheel );
650  auto actionViewer =
651  keyMap->getAction( keyMap->getContext( "ViewerContext" ), buttons, modifiers, key, wheel );
652  return { actionCamera, actionGizmo, actionViewer };
653 }
654 
655 bool Viewer::handleKeyPressEvent( QKeyEvent* event ) {
656  bool eventCatched = false;
657 
658  auto [actionCamera, actionGizmo, actionViewer] =
659  getComponentActions( Qt::NoButton, event->modifiers(), activeKey(), false );
660 
661  // Is keymapping something of the viewer only ?
662  // or should be dispatched to all receivers ?
663 
664  if ( actionCamera.isValid() ) {
665  eventCatched = m_camera->handleKeyPressEvent( event, actionCamera );
666  }
667  else if ( actionGizmo.isValid() ) {
668  // \todo add gizmo manager handleKeyPressEvent
669  // m_gizmoManager->handleKeyPressEvent( event, action );
670  // eventCatched = true;
671  }
672  else if ( actionViewer.isValid() ) {
673  eventCatched = m_keyMappingCallbackManager.triggerEventCallback( actionViewer, event );
674  }
675  return eventCatched;
676 }
677 
678 bool Viewer::handleKeyReleaseEvent( QKeyEvent* event ) {
679  bool eventCatched = false;
680 
681  // Is keymapping something of the viewer only ?
682  // or should be dispatched to all receivers ?
683  auto [actionCamera, actionGizmo, actionViewer] =
684  getComponentActions( Qt::NoButton, event->modifiers(), activeKey(), false );
685 
686  if ( actionCamera.isValid() ) {
687  eventCatched = m_camera->handleKeyReleaseEvent( event, actionCamera );
688  }
689  else if ( actionViewer.isValid() ) {
690  eventCatched = m_keyMappingCallbackManager.triggerEventCallback( actionViewer, event );
691  }
692  return eventCatched;
693 }
694 
695 void Viewer::handleMousePressEvent( QMouseEvent* event,
697 
705 
706  // for now just handle one active context
707  m_activeContext.setInvalid();
708 
709  auto keyMap = KeyMappingManager::getInstance();
711  auto buttons = event->buttons();
712  auto modifiers = event->modifiers();
713  auto key = activeKey();
714 
715  // nothing under mouse ? juste move the camera ...
716  if ( result.getRoIdx().isInvalid() ) {
717  if ( m_camera->handleMousePressEvent( event, buttons, modifiers, key ) ) {
718  m_activeContext = m_camera->mappingContext();
719  }
720  else {
721  // should not pass here, since viewerContext is only for valid picking ...
722  m_activeContext = KeyMappingManageable::getContext();
723  }
724  }
726  else {
727  // something under the mouse, let's check if it's a gizmo ro
728  getGizmoManager()->handlePickingResult( result.getRoIdx() );
730  event, buttons, modifiers, key, *m_camera->getCamera() ) ) {
731  m_activeContext = GizmoManager::getContext();
732  } // if not, try to do camera stuff
733  else if ( m_camera->handleMousePressEvent( event, buttons, modifiers, key ) ) {
734  m_activeContext = m_camera->mappingContext();
735  }
736  else {
737  m_activeContext = KeyMappingManageable::getContext();
738  auto action = keyMap->getAction( m_activeContext, buttons, modifiers, key );
739  auto pickingMode = getPickingMode( action );
740 
741  if ( pickingMode != Ra::Engine::Rendering::Renderer::NONE ) {
742  // Push query, we may also do it here ...
744  toDevice( { event->x(), height() - event->y() } ),
745  Engine::Rendering::Renderer::PickingPurpose::MANIPULATION,
746  pickingMode };
747  m_currentRenderer->addPickingRequest( query );
748  }
749  }
750  }
751 }
752 
753 void Viewer::handleMouseReleaseEvent( QMouseEvent* event ) {
754  if ( m_activeContext == m_camera->mappingContext() ) {
755  m_camera->handleMouseReleaseEvent( event );
756  }
757  if ( m_activeContext == GizmoManager::getContext() ) {
758  m_gizmoManager->handleMouseReleaseEvent( event );
759  }
760  m_activeContext.setInvalid();
761 }
762 
763 void Viewer::handleMouseMoveEvent( QMouseEvent* event,
765 
766  auto keyMap = KeyMappingManager::getInstance();
767  auto buttons = event->buttons();
768  auto modifiers = event->modifiers();
769  auto key = activeKey();
770  // if needed can use
771  // auto action = keyMap->getAction( m_activeContext, buttons, modifiers, key );
772 
773  if ( m_activeContext == m_camera->mappingContext() ) {
774  m_camera->handleMouseMoveEvent( event, buttons, modifiers, key );
775  }
776  else if ( m_activeContext == GizmoManager::getContext() ) {
778  event, buttons, modifiers, key, *m_camera->getCamera() );
779  }
780  else if ( m_activeContext == KeyMappingManageable::getContext() ) {
781  auto action = keyMap->getAction( m_activeContext, buttons, modifiers, key );
782  auto pickingMode = getPickingMode( action );
783  if ( pickingMode != Ra::Engine::Rendering::Renderer::NONE ) {
784  Engine::Rendering::Renderer::PickingQuery query = {
785  toDevice( { event->x(), height() - event->y() } ),
786  Engine::Rendering::Renderer::PickingPurpose::MANIPULATION,
787  pickingMode };
788  m_currentRenderer->addPickingRequest( query );
789  }
790  }
791  else { getGizmoManager()->handlePickingResult( result.getRoIdx() ); }
792 }
793 
794 void Viewer::handleWheelEvent( QWheelEvent* event ) {
795 
796  auto keyMap = KeyMappingManager::getInstance();
797  auto buttons = event->buttons();
798  auto modifiers = event->modifiers();
799  auto key = activeKey();
800  auto action =
801  keyMap->getAction( KeyMappingManageable::getContext(), buttons, modifiers, key, true );
802 
803  if ( action == VIEWER_SCALE_BRUSH && m_isBrushPickingEnabled ) {
804  m_brushRadius +=
805  ( event->angleDelta().y() * 0.01 + event->angleDelta().x() * 0.01 ) > 0 ? 5 : -5;
806  m_brushRadius = std::max( m_brushRadius, Scalar( 5 ) );
807  m_currentRenderer->setBrushRadius( m_brushRadius );
808  }
809  else { m_camera->handleWheelEvent( event, buttons, modifiers, key ); }
810 }
811 
812 Ra::Engine::Rendering::Renderer::PickingResult Viewer::pickAtPosition( Core::Vector2 position ) {
813  makeCurrent();
814  auto result = m_currentRenderer->doPickingNow(
815  { position,
816  Engine::Rendering::Renderer::PickingPurpose::SELECTION,
818  { m_camera->getCamera()->getViewMatrix(), m_camera->getCamera()->getProjMatrix(), 0. } );
819 
820  doneCurrent();
821  return result;
822 }
823 
825  auto renderer = getRenderer();
826  if ( renderer ) {
827  makeCurrent();
829  auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
830  if ( aabb.isEmpty() ) { getCameraManipulator()->resetCamera(); }
831  else { fitCameraToScene( aabb ); }
832  doneCurrent();
833  return true;
834  }
835  return false;
836 }
837 
838 void Viewer::displayHelpDialog() {
839  if ( !m_helpDialog ) { m_helpDialog.reset( new RadiumHelpDialog() ); }
840  m_helpDialog->show();
841  m_helpDialog->raise();
842  m_helpDialog->activateWindow();
843 }
844 
846 Viewer::addCustomAction( const std::string& actionName,
847  const KeyMappingManager::EventBinding& binding,
848  std::function<void( QEvent* )> callback ) {
849 
850  return m_keyMappingCallbackManager.addActionAndCallback( actionName, binding, callback );
851 }
852 
853 } // namespace Gui
854 } // namespace Ra
int attach(Observer observer)
Definition: Observable.hpp:54
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
void addPickingRequest(const PickingQuery &query)
Definition: Renderer.hpp:591
virtual std::string getRendererName() const =0
Get the name of the renderer, e.g to be displayed in the UI.
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
void enableDebugDraw(bool enabled)
Definition: Renderer.hpp:583
void enablePostProcess(bool enabled)
Definition: Renderer.hpp:587
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
void setBackgroundColor(const Core::Utils::Color &color)
Update the background color (does not trigger a redraw)
Definition: Renderer.hpp:612
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
@ C_TRIANGLE
Picks all triangles of a mesh within a screen space circle.
Definition: Renderer.hpp:72
@ C_EDGE
Picks all edges of a mesh within a screen space circle.
Definition: Renderer.hpp:71
@ TRIANGLE
Pick a triangle of a mesh.
Definition: Renderer.hpp:69
@ EDGE
Pick an edge of a mesh.
Definition: Renderer.hpp:68
@ VERTEX
Pick a vertex of a mesh.
Definition: Renderer.hpp:67
const std::vector< PickingResult > & getPickingResults() const
Definition: Renderer.hpp:595
const std::vector< PickingQuery > & getPickingQueries() const
Definition: Renderer.hpp:599
void resetActiveCamera()
reset the active camera data to default camera
Core::Utils::Observable< Core::Utils::Index > & activeCameraObservers()
get a ref to active camera observers to add/remove an observer
The CameraManipulator class is the generic class for camera manipulators.
virtual void resetCamera()=0
Reset the Camera settings to default values.
void updateValues() override
Retrieve the transform from the editable and update the gizmos.
virtual bool handleMouseMoveEvent(QMouseEvent *event, const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &modifiers, int key, const Core::Asset::Camera &cam)
void handlePickingResult(int drawableId)
Callback when a drawable is picked.
This class manage a collection of binding/callback associated with a context.
bool triggerEventCallback(QEvent *event, int key, bool wheel=false)
void addEventCallback(KeyMappingAction action, Callback callback)
KeyMappingManager::KeyMappingAction addActionAndCallback(const std::string &actionName, const KeyMappingManager::EventBinding &binding, Callback callback)
Inner class to store event binding.
Ra::Core::Utils::Index KeyMappingAction
handle to an action
virtual bool prepareDisplay()
Definition: Viewer.cpp:824
void resizeGL(QResizeEvent *event) override
Resize the view port and the camera. Called by the resize event.
Definition: Viewer.cpp:524
std::unique_ptr< CameraManipulator > m_camera
Owning pointer to the camera.
Definition: Viewer.hpp:311
void startRendering(const Scalar dt)
Start rendering (potentially asynchronously in a separate thread)
Definition: Viewer.cpp:217
bool initializeGL() override
Definition: Viewer.cpp:459
void initializeRenderer(Engine::Rendering::Renderer *renderer)
Initialize renderer internal state + configure lights.
Definition: Viewer.cpp:447
void enableDebugDraw(int enabled)
Toggle the debug drawing.
Definition: Viewer.cpp:408
void swapBuffers()
Blocks until rendering is finished.
Definition: Viewer.cpp:263
PickingManager * m_pickingManager
Owning Pointer to the feature picking manager.
Definition: Viewer.hpp:305
void rendererReady()
Emitted when GL context is ready. We except call to addRenderer here.
void requestEngineOpenGLInitialization()
void enablePostProcess(int enabled)
Toggle the post-process effetcs.
Definition: Viewer.cpp:404
void onMousePress(QMouseEvent *event)
Event sent after a mouse press event has been processed, but before emitting needUpdate()
void mouseReleaseEvent(QMouseEvent *event) override
Definition: Viewer.cpp:591
virtual void update(const Scalar dt)
Update the internal viewer state to the (application) time dt.
Definition: Viewer.cpp:210
int addRenderer(const std::shared_ptr< Engine::Rendering::Renderer > &e)
Definition: Viewer.cpp:187
PickingManager * getPickingManager()
Access to the feature picking manager.
Definition: Viewer.cpp:206
std::vector< std::shared_ptr< Engine::Rendering::Renderer > > m_renderers
Definition: Viewer.hpp:300
Core::Vector2 toDevice(const Core::Vector2 &logicCoordinates)
Definition: Viewer.hpp:165
void mouseMoveEvent(QMouseEvent *event) override
Definition: Viewer.cpp:597
void reloadShaders()
Tell the renderer to reload all shaders.
Definition: Viewer.cpp:351
std::vector< std::string > getRenderersName() const
Returns the names of the different registred renderers.
Definition: Viewer.cpp:307
void mousePressEvent(QMouseEvent *event) override
Definition: Viewer.cpp:574
KeyMappingManager::KeyMappingAction addCustomAction(const std::string &actionName, const KeyMappingManager::EventBinding &binding, std::function< void(QEvent *)> callback)
Add a custom event callback.
Definition: Viewer.cpp:846
Viewer(QScreen *screen=nullptr)
Constructor.
Definition: Viewer.cpp:140
bool changeRenderer(int index)
Set the renderer.
Definition: Viewer.cpp:376
void displayTexture(const QString &tex)
Set the final display texture.
Definition: Viewer.cpp:364
void setCameraManipulator(CameraManipulator *ci)
Set the current camera interface.
Definition: Viewer.cpp:160
const Engine::Rendering::Renderer * getRenderer() const
Read-only access to renderer.
Definition: Viewer.cpp:179
void onMouseRelease(QMouseEvent *event)
Event sent after a mouse release event has been processed, but before emitting needUpdate()
virtual void setupKeyMappingCallbacks()
add observers to keyMappingManager for gizmo, camera and viewer.
Definition: Viewer.cpp:74
void grabFrame(const std::string &filename)
Write the current frame as an image. Supports either BMP or PNG file names.
Definition: Viewer.cpp:317
void createGizmoManager()
create gizmos
Definition: Viewer.cpp:443
void fitCameraToScene(const Core::Aabb &sceneAabb)
Moves the camera so that the whole scene is visible.
Definition: Viewer.cpp:289
~Viewer() override
Destructor.
Definition: Viewer.cpp:150
GizmoManager * m_gizmoManager
Owning (QObject child) pointer to gizmo manager.
Definition: Viewer.hpp:314
void rightClickPicking(const Ra::Engine::Rendering::Renderer::PickingResult &result)
Emitted when the rendered is correctly initialized.
GizmoManager * getGizmoManager()
Access to gizmo manager.
Definition: Viewer.cpp:175
void wheelEvent(QWheelEvent *event) override
Definition: Viewer.cpp:613
CameraManipulator * getCameraManipulator()
Access to camera interface.
Definition: Viewer.cpp:164
void onMouseMove(QMouseEvent *event)
Event sent after a mouse move event has been processed, but before emitting needUpdate()
void focusOutEvent(QFocusEvent *event) override
reset key pressed, in case a key is pressed when focus lost
Definition: Viewer.cpp:633
void processPicking()
Emits signals corresponding to picking requests.
Definition: Viewer.cpp:268
virtual void handleMousePressEvent(QMouseEvent *event, Ra::Engine::Rendering::Renderer::PickingResult &result)
Definition: Viewer.cpp:695
Base class for OpenGL widgets, compatble with Qt and globjects/glbindings.
Definition: WindowQt.hpp:28
ScopedGLContext activateScopedContext()
Definition: WindowQt.hpp:154
void makeCurrent()
Definition: WindowQt.cpp:80
void doneCurrent()
Definition: WindowQt.cpp:89
QOpenGLContext * context()
Definition: WindowQt.cpp:76
Definition: Cage.cpp:3
the set of viewing parameters extracted from the camera and given to the renderer