Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.6.3
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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>
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
60namespace Ra {
61namespace Gui {
62
63using namespace Core::Utils; // log
64using namespace glbinding;
65
66using ViewerMapping = KeyMappingManageable<Viewer>;
67
68#define KMA_VALUE( x ) KeyMappingManager::KeyMappingAction Viewer::x;
69KeyMappingViewer
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
125void 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
140Viewer::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 delete m_pickingManager;
159}
160
162 m_camera.reset( ci );
163}
164
168
169void Viewer::resetToDefaultCamera() {
170
171 auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
172 Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
173 cameraManager->resetActiveCamera();
174}
175
179
181 return m_currentRenderer;
182}
183
185 return m_currentRenderer;
186}
187
189 m_renderers.push_back( e );
190
191 // initialize the renderer (deferred if GL is not ready yet)
192 if ( m_glInitialized.load() ) {
193 makeCurrent();
194 initializeRenderer( e.get() );
195 LOG( logINFO ) << "[Viewer] New Renderer (" << e->getRendererName() << ") added.";
196 doneCurrent();
197 }
198 else {
199 LOG( logINFO ) << "[Viewer] New Renderer (" << e->getRendererName()
200 << ") added with deferred init.";
201 m_pendingRenderers.push_back( e );
202 }
203
204 return m_renderers.size() - 1;
205}
206
207PickingManager* Viewer::getPickingManager() {
208 return m_pickingManager;
209}
210
211void Viewer::update( const Scalar dt ) {
212 CORE_UNUSED( dt );
213 if ( m_gizmoManager != nullptr ) { m_gizmoManager->updateValues(); }
214}
215
216// Asynchronous rendering implementation
217
218void Viewer::startRendering( const Scalar dt ) {
219
220 CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized before rendering." );
221
222 CORE_ASSERT( m_currentRenderer != nullptr, "No renderer found." );
223
224 m_pickingManager->clear();
225 makeCurrent();
226
227 // update znear/zfar to fit the scene ...
228 auto entityManager = Engine::RadiumEngine::getInstance()->getEntityManager();
229 if ( entityManager ) {
230
231 // to fit scene only
232 // auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
233 // to fit also debug and system entity aabb
234 Core::Aabb aabb {};
235 for ( const auto& entity : entityManager->getEntities() ) {
236 // entity aabb is in world space
237 aabb.extend( entity->computeAabb() );
238 }
239
240 if ( !aabb.isEmpty() ) { m_camera->getCamera()->fitZRange( aabb ); }
241 else {
242 auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
243 Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
244
245 // scene is empty, reset to defaults bounds
246 m_camera->setCameraZNear( cameraManager->defaultCamera.getZNear() );
247 m_camera->setCameraZFar( cameraManager->defaultCamera.getZFar() );
248 }
249 }
250
253 if ( !m_currentRenderer->hasLight() ) {
254 if ( m_camera->hasLightAttached() )
255 m_currentRenderer->addLight( m_camera->getLight() );
256 else
257 LOG( logDEBUG ) << "Unable to attach the head light!";
258 }
260 m_camera->getCamera()->getViewMatrix(), m_camera->getCamera()->getProjMatrix(), dt };
261 m_currentRenderer->render( data );
262}
263
265 if ( isExposed() ) { m_context->swapBuffers( this ); }
266 doneCurrent();
267}
268
270 CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized before rendering." );
271
272 CORE_ASSERT( m_currentRenderer != nullptr, "No renderer found." );
273
274 CORE_ASSERT( m_currentRenderer->getPickingQueries().size() ==
275 m_currentRenderer->getPickingResults().size(),
276 "There should be one result per query." );
277
278 for ( uint i = 0; i < m_currentRenderer->getPickingQueries().size(); ++i ) {
280 m_currentRenderer->getPickingQueries()[i];
281
282 if ( query.m_purpose == Engine::Rendering::Renderer::PickingPurpose::MANIPULATION ) {
283 const auto& result = m_currentRenderer->getPickingResults()[i];
284 m_pickingManager->setCurrent( result );
285 emit rightClickPicking( result );
286 }
287 }
288}
289
290void Viewer::fitCameraToScene( const Core::Aabb& aabb ) {
291 if ( !aabb.isEmpty() ) {
292 CORE_ASSERT( m_camera != nullptr, "No camera found." );
293 m_camera->fitScene( aabb );
294
295 // uncomment to display scene aabb
296 // RA_DISPLAY_AABB( aabb, Color::Blue() );
297 emit needUpdate();
298 }
299 else { LOG( logINFO ) << "Unable to fit the camera to the scene : empty Bbox."; }
300}
301
302void Viewer::fitCamera() {
303 auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
304 if ( aabb.isEmpty() ) { getCameraManipulator()->resetCamera(); }
305 else { fitCameraToScene( aabb ); }
306}
307
310
311 for ( const auto& renderer : m_renderers ) {
312 if ( renderer ) { ret.push_back( renderer->getRendererName() ); }
313 }
314
315 return ret;
316}
317
318void Viewer::grabFrame( const std::string& filename ) {
319 makeCurrent();
320
321 size_t w, h;
322 auto writtenPixels = m_currentRenderer->grabFrame( w, h );
323
324 std::string ext = Core::Utils::getFileExt( filename );
325
326 if ( ext == "bmp" ) { stbi_write_bmp( filename.c_str(), w, h, 4, writtenPixels.get() ); }
327 else if ( ext == "png" ) {
328 stbi_write_png( filename.c_str(), w, h, 4, writtenPixels.get(), w * 4 * sizeof( uchar ) );
329 }
330 else {
331 LOG( logWARNING ) << "Cannot write frame to " << filename << " : unsupported extension";
332 }
333
334 doneCurrent();
335}
336
337void Viewer::enableDebug() {
338 glbinding::setCallbackMask( glbinding::CallbackMask::After |
339 glbinding::CallbackMask::ParametersAndReturnValue );
340 glbinding::setAfterCallback( []( const glbinding::FunctionCall& call ) {
341 std::cerr << call.function->name() << "(";
342 for ( unsigned i = 0; i < call.parameters.size(); ++i ) {
343 std::cerr << call.parameters[i].get();
344 if ( i < call.parameters.size() - 1 ) { std::cerr << ", "; }
345 }
346 std::cerr << ")";
347 if ( call.returnValue ) { std::cerr << " -> " << call.returnValue.get(); }
349 } );
350}
351
353 CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized reload shaders." );
354
355 makeCurrent();
356 // FIXME : check thread-saefty of this.
357 m_currentRenderer->lockRendering();
358 m_currentRenderer->reloadShaders();
359 m_currentRenderer->unlockRendering();
360 doneCurrent();
361
362 emit needUpdate();
363}
364
365void Viewer::displayTexture( const QString& tex ) {
366 CORE_ASSERT( m_glInitialized.load(), "OpenGL needs to be initialized to display textures." );
367
368 makeCurrent();
369 m_currentRenderer->lockRendering();
370 m_currentRenderer->displayTexture( tex.toStdString() );
371 m_currentRenderer->unlockRendering();
372 doneCurrent();
373
374 emit needUpdate();
375}
376
377bool Viewer::changeRenderer( int index ) {
378 if ( m_glInitialized.load() && m_renderers[index] ) {
379 makeCurrent();
380
381 if ( m_currentRenderer != nullptr ) { m_currentRenderer->lockRendering(); }
382
383 m_currentRenderer = m_renderers[index].get();
384 // renderers in m_renderers are supposed to be locked
385 auto deviceSize = toDevice( { width(), height() } );
386 m_currentRenderer->resize( deviceSize.x(), deviceSize.y() );
387 // Configure the renderObjects for this renderer
388 m_currentRenderer->buildAllRenderTechniques();
389 m_currentRenderer->unlockRendering();
390
391 LOG( logINFO ) << "[Viewer] Set active renderer: " << m_currentRenderer->getRendererName();
392
393 // resize camera viewport since the one in show event might have 0x0
394 if ( m_camera ) { m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() ); }
395
396 doneCurrent();
397 emit rendererReady();
398
399 emit needUpdate();
400 return true;
401 }
402 return false;
403}
404
405void Viewer::enablePostProcess( int enabled ) {
406 m_currentRenderer->enablePostProcess( enabled );
407}
408
409void Viewer::enableDebugDraw( int enabled ) {
410 m_currentRenderer->enableDebugDraw( enabled );
411}
412
413void Viewer::setBackgroundColor( const Core::Utils::Color& background ) {
414 m_backgroundColor = background;
415 for ( const auto& renderer : m_renderers )
416 renderer->setBackgroundColor( m_backgroundColor );
417
418 emit needUpdate();
419}
420
421void Viewer::onAboutToCompose() {
422 // This slot function is called from the main thread as part of the event loop
423 // when the GUI is about to update. We have to wait for the rendering to finish.
424 m_currentRenderer->lockRendering();
425}
426
427void Viewer::onAboutToResize() {
428 // Like swap buffers, resizing is a blocking operation and we have to wait for the rendering
429 // to finish before resizing.
430 m_currentRenderer->lockRendering();
431}
432
433void Viewer::onResized() {
434 m_currentRenderer->unlockRendering();
435 emit needUpdate();
436}
437
438void Viewer::onFrameSwapped() {
439 // This slot is called from the main thread as part of the event loop when the
440 // GUI has finished displaying the rendered image, so we unlock the renderer.
441 m_currentRenderer->unlockRendering();
442}
443
445 if ( m_gizmoManager == nullptr ) { m_gizmoManager = new GizmoManager( this ); }
446}
447
449 auto deviceSize = toDevice( { width(), height() } );
450
451 // see issue #261 Qt Event order and default viewport management (Viewer.cpp)
452 // https://github.com/STORM-IRIT/Radium-Engine/issues/261
453 gl::glViewport( 0, 0, deviceSize.x(), deviceSize.y() );
454
455 renderer->initialize( deviceSize.x(), deviceSize.y() );
456 renderer->setBackgroundColor( m_backgroundColor );
457 renderer->lockRendering();
458}
459
461 globjects::init( getProcAddress );
462 // mark openGL as initialized
463 m_glInitialized = true;
464
465 LOG( logINFO ) << "*** Radium Engine OpenGL context ***";
466 LOG( logINFO ) << "Renderer (glbinding) : " << glbinding::aux::ContextInfo::renderer();
467 LOG( logINFO ) << "Vendor (glbinding) : " << glbinding::aux::ContextInfo::vendor();
468 LOG( logINFO ) << "OpenGL (glbinding) : "
469 << glbinding::aux::ContextInfo::version().toString();
470 LOG( logINFO ) << "GLSL : "
471 << gl::glGetString( gl::GLenum( GL_SHADING_LANGUAGE_VERSION ) );
472
473 LOG( logINFO ) << "*** Radium Engine Viewer ***";
474
475 // emit the signal so that the client will initialize the OpenGL part of the Engine
476 // and custom OpenGL properties
478
479 // Configure the viewer services
480 auto deviceSize = toDevice( { width(), height() } );
481 // create default camera interface : trackball
482 m_camera = std::make_unique<TrackballCameraManipulator>();
483 m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() );
484
486 auto headlight = new Engine::Scene::DirectionalLight(
487 Ra::Engine::Scene::SystemEntity::getInstance(), "headlight" );
488 headlight->setColor( Ra::Core::Utils::Color::Grey( 1.0_ra ) );
489
490 m_camera->attachLight( headlight );
491
492 // Register to the camera manager active camera changes
493 auto cameraManager = static_cast<Ra::Engine::Scene::CameraManager*>(
494 Engine::RadiumEngine::getInstance()->getSystem( "DefaultCameraManager" ) );
495
496 cameraManager->activeCameraObservers().attach( [this]( Core::Utils::Index /*idx*/ ) {
497 m_camera->updateCamera();
498 // not sure still needed on mac, if yes, use up to date size.
499 // auto size = toDevice( { width(), height() } );
500 // m_camera->getCamera()->setViewport( size.x(), size.y() );
501 } );
502
503 // Initialize renderers added to the viewer before initializeGL
504 for ( auto& rptr : m_pendingRenderers ) {
505 initializeRenderer( rptr.get() );
506 }
507 m_pendingRenderers.clear();
508
509 // create the gizmo manager (Ui)
511
512 // Signal that OpenGL is initialized
513 emit glInitialized();
514
515 // If no renderer was added before that (either by slots on requestEngineOpenGLInitialization
516 // or on glInitialized), add default forward renderer
517 if ( m_renderers.empty() ) {
518 LOG( logINFO ) << "[Viewer] No renderer added, adding default (Forward Renderer)";
521 addRenderer( e );
522 }
523
524 if ( m_currentRenderer == nullptr ) { changeRenderer( 0 ); }
525
526 return m_glInitialized;
527}
528
529void Viewer::resizeGL( QResizeEvent* event ) {
531
532 auto deviceSize = toDevice( { event->size().width(), event->size().height() } );
533 gl::glViewport( 0, 0, deviceSize.x(), deviceSize.y() );
534 m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() );
535 m_currentRenderer->resize( deviceSize.x(), deviceSize.y() );
536
537 emit needUpdate();
538}
539
541Viewer::getPickingMode( const KeyMappingManager::KeyMappingAction& action ) const {
542 if ( action == VIEWER_PICKING_VERTEX ) {
543 return m_isBrushPickingEnabled ? Engine::Rendering::Renderer::C_VERTEX
545 }
546 if ( action == VIEWER_PICKING_EDGE ) {
547 return m_isBrushPickingEnabled ? Engine::Rendering::Renderer::C_EDGE
549 }
550 if ( action == VIEWER_PICKING_TRIANGLE ) {
551 return m_isBrushPickingEnabled ? Engine::Rendering::Renderer::C_TRIANGLE
553 }
554 if ( action == VIEWER_PICKING ) { return Engine::Rendering::Renderer::RO; }
556}
557
558void Viewer::propagateEventToParent( QEvent* event ) {
559 event->ignore();
560 if ( !isTopLevel() ) { QApplication::sendEvent( parent(), event ); }
561}
562
563void Viewer::keyPressEvent( QKeyEvent* event ) {
564 keyPressed( event->key() );
565 if ( !m_glInitialized.load() || event->isAutoRepeat() || !handleKeyPressEvent( event ) )
566 propagateEventToParent( event );
567 else
568 emit needUpdate();
569}
570
571void Viewer::keyReleaseEvent( QKeyEvent* event ) {
572 if ( !m_glInitialized.load() || !handleKeyReleaseEvent( event ) )
573 propagateEventToParent( event );
574 else
575 emit needUpdate();
576 keyReleased( event->key() );
577}
578
579void Viewer::mousePressEvent( QMouseEvent* event ) {
580 if ( !m_glInitialized.load() ) {
581 propagateEventToParent( event );
582 return;
583 }
584
585 m_currentRenderer->setMousePosition( toDevice( { event->pos().x(), event->pos().y() } ) );
586
587 // get what's under the mouse
588 auto result = pickAtPosition( toDevice( { event->pos().x(), height() - event->pos().y() } ) );
589 m_depthUnderMouse = result.getDepth();
590
591 handleMousePressEvent( event, result );
592 emit onMousePress( event );
593 emit needUpdate();
594}
595
596void Viewer::mouseReleaseEvent( QMouseEvent* event ) {
597 handleMouseReleaseEvent( event );
598 emit onMouseRelease( event );
599 emit needUpdate();
600}
601
602void Viewer::mouseMoveEvent( QMouseEvent* event ) {
603 if ( !m_glInitialized.load() ) {
604 event->ignore();
605 return;
606 }
607
608 m_currentRenderer->setMousePosition( toDevice( { event->pos().x(), event->pos().y() } ) );
609
610 auto result = pickAtPosition( toDevice( { event->pos().x(), height() - event->pos().y() } ) );
611 m_depthUnderMouse = result.getDepth();
612
613 handleMouseMoveEvent( event, result );
614 emit onMouseMove( event );
615 emit needUpdate();
616}
617
618void Viewer::wheelEvent( QWheelEvent* event ) {
619
620 if ( !m_glInitialized.load() ) {
621 event->ignore();
622 return;
623 }
624
625 handleWheelEvent( event );
626
627 emit needUpdate();
628}
629
630void Viewer::showEvent( QShowEvent* ev ) {
631 WindowQt::showEvent( ev );
632 auto deviceSize = toDevice( { width(), height() } );
633 m_camera->getCamera()->setViewport( deviceSize.x(), deviceSize.y() );
634
635 emit needUpdate();
636}
637
638void Viewer::focusOutEvent( QFocusEvent* ) {
639 releaseAllKeys();
640}
641
645Viewer::getComponentActions( const Qt::MouseButtons& buttons,
646 const Qt::KeyboardModifiers& modifiers,
647 int key,
648 bool wheel ) {
649 auto keyMap = KeyMappingManager::getInstance();
650
651 auto actionCamera =
652 keyMap->getAction( keyMap->getContext( "CameraContext" ), buttons, modifiers, key, wheel );
653 auto actionGizmo =
654 keyMap->getAction( keyMap->getContext( "GizmoContext" ), buttons, modifiers, key, wheel );
655 auto actionViewer =
656 keyMap->getAction( keyMap->getContext( "ViewerContext" ), buttons, modifiers, key, wheel );
657 return { actionCamera, actionGizmo, actionViewer };
658}
659
660bool Viewer::handleKeyPressEvent( QKeyEvent* event ) {
661 bool eventCatched = false;
662
663 auto [actionCamera, actionGizmo, actionViewer] =
664 getComponentActions( Qt::NoButton, event->modifiers(), activeKey(), false );
665
666 // Is keymapping something of the viewer only ?
667 // or should be dispatched to all receivers ?
668
669 if ( actionCamera.isValid() ) {
670 eventCatched = m_camera->handleKeyPressEvent( event, actionCamera );
671 }
672 else if ( actionGizmo.isValid() ) {
673 // \todo add gizmo manager handleKeyPressEvent
674 // m_gizmoManager->handleKeyPressEvent( event, action );
675 // eventCatched = true;
676 }
677 else if ( actionViewer.isValid() ) {
678 eventCatched = m_keyMappingCallbackManager.triggerEventCallback( actionViewer, event );
679 }
680 return eventCatched;
681}
682
683bool Viewer::handleKeyReleaseEvent( QKeyEvent* event ) {
684 bool eventCatched = false;
685
686 // Is keymapping something of the viewer only ?
687 // or should be dispatched to all receivers ?
688 auto [actionCamera, actionGizmo, actionViewer] =
689 getComponentActions( Qt::NoButton, event->modifiers(), activeKey(), false );
690
691 if ( actionCamera.isValid() ) {
692 eventCatched = m_camera->handleKeyReleaseEvent( event, actionCamera );
693 }
694 else if ( actionViewer.isValid() ) {
695 eventCatched = m_keyMappingCallbackManager.triggerEventCallback( actionViewer, event );
696 }
697 return eventCatched;
698}
699
700void Viewer::handleMousePressEvent( QMouseEvent* event,
702
710
711 // for now just handle one active context
712 m_activeContext.setInvalid();
713
714 auto keyMap = KeyMappingManager::getInstance();
716 auto buttons = event->buttons();
717 auto modifiers = event->modifiers();
718 auto key = activeKey();
719
720 // nothing under mouse ? juste move the camera ...
721 if ( result.getRoIdx().isInvalid() ) {
722 if ( m_camera->handleMousePressEvent( event, buttons, modifiers, key ) ) {
723 m_activeContext = m_camera->mappingContext();
724 }
725 else {
726 // should not pass here, since viewerContext is only for valid picking ...
727 m_activeContext = KeyMappingManageable::getContext();
728 }
729 }
731 else {
732 // something under the mouse, let's check if it's a gizmo ro
733 getGizmoManager()->handlePickingResult( result.getRoIdx() );
735 event, buttons, modifiers, key, *m_camera->getCamera() ) ) {
736 m_activeContext = GizmoManager::getContext();
737 } // if not, try to do camera stuff
738 else if ( m_camera->handleMousePressEvent( event, buttons, modifiers, key ) ) {
739 m_activeContext = m_camera->mappingContext();
740 }
741 else {
742 m_activeContext = KeyMappingManageable::getContext();
743 auto action = keyMap->getAction( m_activeContext, buttons, modifiers, key );
744 auto pickingMode = getPickingMode( action );
745
746 if ( pickingMode != Ra::Engine::Rendering::Renderer::NONE ) {
747 // Push query, we may also do it here ...
749 toDevice( { event->x(), height() - event->y() } ),
750 Engine::Rendering::Renderer::PickingPurpose::MANIPULATION,
751 pickingMode };
752 m_currentRenderer->addPickingRequest( query );
753 }
754 }
755 }
756}
757
758void Viewer::handleMouseReleaseEvent( QMouseEvent* event ) {
759 if ( m_activeContext == m_camera->mappingContext() ) {
760 m_camera->handleMouseReleaseEvent( event );
761 }
762 if ( m_activeContext == GizmoManager::getContext() ) {
763 m_gizmoManager->handleMouseReleaseEvent( event );
764 }
765 m_activeContext.setInvalid();
766}
767
768void Viewer::handleMouseMoveEvent( QMouseEvent* event,
770
771 auto keyMap = KeyMappingManager::getInstance();
772 auto buttons = event->buttons();
773 auto modifiers = event->modifiers();
774 auto key = activeKey();
775 // if needed can use
776 // auto action = keyMap->getAction( m_activeContext, buttons, modifiers, key );
777
778 if ( m_activeContext == m_camera->mappingContext() ) {
779 m_camera->handleMouseMoveEvent( event, buttons, modifiers, key );
780 }
781 else if ( m_activeContext == GizmoManager::getContext() ) {
783 event, buttons, modifiers, key, *m_camera->getCamera() );
784 }
785 else if ( m_activeContext == KeyMappingManageable::getContext() ) {
786 auto action = keyMap->getAction( m_activeContext, buttons, modifiers, key );
787 auto pickingMode = getPickingMode( action );
788 if ( pickingMode != Ra::Engine::Rendering::Renderer::NONE ) {
789 Engine::Rendering::Renderer::PickingQuery query = {
790 toDevice( { event->x(), height() - event->y() } ),
791 Engine::Rendering::Renderer::PickingPurpose::MANIPULATION,
792 pickingMode };
793 m_currentRenderer->addPickingRequest( query );
794 }
795 }
796 else { getGizmoManager()->handlePickingResult( result.getRoIdx() ); }
797}
798
799void Viewer::handleWheelEvent( QWheelEvent* event ) {
800
801 auto keyMap = KeyMappingManager::getInstance();
802 auto buttons = event->buttons();
803 auto modifiers = event->modifiers();
804 auto key = activeKey();
805 auto action =
806 keyMap->getAction( KeyMappingManageable::getContext(), buttons, modifiers, key, true );
807
808 if ( action == VIEWER_SCALE_BRUSH && m_isBrushPickingEnabled ) {
809 m_brushRadius +=
810 ( event->angleDelta().y() * 0.01 + event->angleDelta().x() * 0.01 ) > 0 ? 5 : -5;
811 m_brushRadius = std::max( m_brushRadius, Scalar( 5 ) );
812 m_currentRenderer->setBrushRadius( m_brushRadius );
813 }
814 else { m_camera->handleWheelEvent( event, buttons, modifiers, key ); }
815}
816
817Ra::Engine::Rendering::Renderer::PickingResult Viewer::pickAtPosition( Core::Vector2 position ) {
818 makeCurrent();
819 auto result = m_currentRenderer->doPickingNow(
820 { position,
821 Engine::Rendering::Renderer::PickingPurpose::SELECTION,
823 { m_camera->getCamera()->getViewMatrix(), m_camera->getCamera()->getProjMatrix(), 0. } );
824
825 doneCurrent();
826 return result;
827}
828
830 auto renderer = getRenderer();
831 if ( renderer ) {
832 makeCurrent();
834 auto aabb = Ra::Engine::RadiumEngine::getInstance()->computeSceneAabb();
835 if ( aabb.isEmpty() ) { getCameraManipulator()->resetCamera(); }
836 else { fitCameraToScene( aabb ); }
837 doneCurrent();
838 return true;
839 }
840 return false;
841}
842
843void Viewer::displayHelpDialog() {
844 if ( !m_helpDialog ) { m_helpDialog.reset( new RadiumHelpDialog() ); }
845 m_helpDialog->show();
846 m_helpDialog->raise();
847 m_helpDialog->activateWindow();
848}
849
852 const KeyMappingManager::EventBinding& binding,
853 std::function<void( QEvent* )> callback ) {
854
855 return m_keyMappingCallbackManager.addActionAndCallback( actionName, binding, callback );
856}
857
858} // namespace Gui
859} // namespace Ra
T c_str(T... args)
int attach(Observer observer)
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:756
virtual void addLight(const Scene::Light *light)
Definition Renderer.cpp:751
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:829
void resizeGL(QResizeEvent *event) override
Resize the view port and the camera. Called by the resize event.
Definition Viewer.cpp:529
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:218
bool initializeGL() override
Definition Viewer.cpp:460
void initializeRenderer(Engine::Rendering::Renderer *renderer)
Initialize renderer internal state + configure lights.
Definition Viewer.cpp:448
void enableDebugDraw(int enabled)
Toggle the debug drawing.
Definition Viewer.cpp:409
void swapBuffers()
Blocks until rendering is finished.
Definition Viewer.cpp:264
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:405
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:596
virtual void update(const Scalar dt)
Update the internal viewer state to the (application) time dt.
Definition Viewer.cpp:211
void toggleBrushPicking(bool on)
Emitted when the corresponding key is released (see keyReleaseEvent)
int addRenderer(const std::shared_ptr< Engine::Rendering::Renderer > &e)
Definition Viewer.cpp:188
PickingManager * getPickingManager()
Access to the feature picking manager.
Definition Viewer.cpp:207
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:602
void reloadShaders()
Tell the renderer to reload all shaders.
Definition Viewer.cpp:352
std::vector< std::string > getRenderersName() const
Returns the names of the different registred renderers.
Definition Viewer.cpp:308
void mousePressEvent(QMouseEvent *event) override
Definition Viewer.cpp:579
KeyMappingManager::KeyMappingAction addCustomAction(const std::string &actionName, const KeyMappingManager::EventBinding &binding, std::function< void(QEvent *)> callback)
Add a custom event callback.
Definition Viewer.cpp:851
Viewer(QScreen *screen=nullptr)
Constructor.
Definition Viewer.cpp:140
bool changeRenderer(int index)
Set the renderer.
Definition Viewer.cpp:377
void displayTexture(const QString &tex)
Set the final display texture.
Definition Viewer.cpp:365
void setCameraManipulator(CameraManipulator *ci)
Set the current camera interface.
Definition Viewer.cpp:161
const Engine::Rendering::Renderer * getRenderer() const
Read-only access to renderer.
Definition Viewer.cpp:180
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:318
void createGizmoManager()
create gizmos
Definition Viewer.cpp:444
void fitCameraToScene(const Core::Aabb &sceneAabb)
Moves the camera so that the whole scene is visible.
Definition Viewer.cpp:290
~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:176
void wheelEvent(QWheelEvent *event) override
Definition Viewer.cpp:618
CameraManipulator * getCameraManipulator()
Access to camera interface.
Definition Viewer.cpp:165
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:638
void processPicking()
Emits signals corresponding to picking requests.
Definition Viewer.cpp:269
virtual void handleMousePressEvent(QMouseEvent *event, Ra::Engine::Rendering::Renderer::PickingResult &result)
Definition Viewer.cpp:700
Base class for OpenGL widgets, compatble with Qt and globjects/glbindings.
Definition WindowQt.hpp:32
ScopedGLContext activateScopedContext()
Definition WindowQt.hpp:158
QOpenGLContext * context()
Definition WindowQt.cpp:77
T endl(T... args)
T max(T... args)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:4
T push_back(T... args)
T reset(T... args)
the set of viewing parameters extracted from the camera and given to the renderer