Radium Engine  1.5.0
TrackballCameraManipulator.cpp
1 #include <Gui/Viewer/TrackballCameraManipulator.hpp>
2 
3 #include <Core/Asset/Camera.hpp>
4 #include <Core/Math/Math.hpp>
5 #include <Core/Utils/Log.hpp>
6 #include <Engine/RadiumEngine.hpp>
7 #include <Engine/Rendering/RenderObject.hpp>
8 #include <Engine/Rendering/RenderObjectManager.hpp>
9 #include <Engine/Scene/Light.hpp>
10 #include <Engine/Scene/SystemDisplay.hpp>
11 #include <Gui/Utils/KeyMappingManager.hpp>
12 #include <Gui/Utils/Keyboard.hpp>
13 
14 #include <Engine/Scene/CameraComponent.hpp>
15 #include <QApplication>
16 #include <QMessageBox>
17 #include <algorithm>
18 #include <iostream>
19 
20 namespace Ra {
21 
22 using Core::Math::Pi;
23 using namespace Ra::Core::Utils;
24 
25 namespace Gui {
26 
27 #define KMA_VALUE( XX ) KeyMappingManager::KeyMappingAction TrackballCameraManipulator::XX;
28 KeyMappingCamera
29 #undef KMA_VALUE
30 
31 void TrackballCameraManipulator::configureKeyMapping_impl() {
32 
33  KeyMapping::setContext( KeyMappingManager::getInstance()->getContext( "CameraContext" ) );
34  if ( KeyMapping::getContext().isInvalid() ) {
35  LOG( logINFO )
36  << "CameraContext not defined (maybe the configuration file do not contains it)";
37  LOG( logERROR ) << "CameraContext all keymapping invalide !";
38  return;
39  }
40 
41 #define KMA_VALUE( XX ) \
42  XX = KeyMappingManager::getInstance()->getAction( KeyMapping::getContext(), #XX );
43  KeyMappingCamera
44 #undef KMA_VALUE
45 }
46 
47 void TrackballCameraManipulator::setupKeyMappingCallbacks() {
48 
49  m_keyMappingCallbackManager.addEventCallback(
50  TRACKBALLCAMERA_ROTATE, [=]( QEvent* event ) { rotateCallback( event ); } );
51  m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_PAN,
52  [=]( QEvent* event ) { panCallback( event ); } );
53  m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_ZOOM,
54  [=]( QEvent* event ) { zoomCallback( event ); } );
55  m_keyMappingCallbackManager.addEventCallback(
56  TRACKBALLCAMERA_MOVE_FORWARD, [=]( QEvent* event ) { moveForwardCallback( event ); } );
57 
58  m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_PROJ_MODE, [=]( QEvent* ) {
59  using ProjType = Ra::Core::Asset::Camera::ProjType;
60  m_camera->setType( m_camera->getType() == ProjType::ORTHOGRAPHIC ? ProjType::PERSPECTIVE
61  : ProjType::ORTHOGRAPHIC );
62  } );
63 
64  m_keyMappingCallbackManager.addEventCallback( CAMERA_TOGGLE_QUICK, [=]( QEvent* ) {
65  static bool quick = false;
66  quick = !quick;
67  if ( quick ) { m_quickCameraModifier = 10.0_ra; }
68  else { m_quickCameraModifier = 1.0_ra; }
69  } );
70 }
71 
73  CameraManipulator(), m_keyMappingCallbackManager { KeyMapping::getContext() } {
74  resetCamera();
75  setupKeyMappingCallbacks();
76  m_cameraSensitivity = 1.5_ra;
77 }
78 
80  CameraManipulator( other ), m_keyMappingCallbackManager { KeyMapping::getContext() } {
81 
83  m_referenceFrame.translation() =
85  m_distFromCenter = ( m_referenceFrame.translation() - m_camera->getPosition() ).norm();
87 
88  setupKeyMappingCallbacks();
89  m_cameraSensitivity = 1.5_ra;
90 }
91 
93 
95  m_camera->setFrame( Core::Transform::Identity() );
96  m_camera->setPosition( Core::Vector3( 0_ra, 0_ra, 2_ra ) );
97  m_camera->setDirection( Core::Vector3( 0_ra, 0_ra, -1_ra ) );
98  m_distFromCenter = 2.0_ra;
99  m_referenceFrame = Core::Transform::Identity();
100  m_referenceFrame.translation() = Core::Vector3::Zero();
101 
102  updatePhiTheta();
103 
105  if ( m_light != nullptr ) {
108  }
109 }
110 
112  // try to keep target near the previous camera's one, take it at the same distance from
113  // camera, but in the new direction.
114  m_distFromCenter = ( m_referenceFrame.translation() - m_camera->getPosition() ).norm();
116  m_referenceFrame.translation() =
118 
119  updatePhiTheta();
120 
121  if ( m_light != nullptr ) {
124  }
125 }
126 
128  m_distFromCenter = rad;
129  m_referenceFrame.translation() =
131 }
132 
134  return m_distFromCenter;
135 }
136 
137 Core::Transform::ConstTranslationPart TrackballCameraManipulator::getTrackballCenter() const {
138  return m_referenceFrame.translation();
139 }
140 
142  return KeyMapping::getContext();
143 }
144 
145 void TrackballCameraManipulator::mousePressSaveData( const QMouseEvent* mouseEvent ) {
146  m_lastMouseX = mouseEvent->pos().x();
147  m_lastMouseY = mouseEvent->pos().y();
148  m_phiDir = -Core::Math::signNZ( m_theta );
149 }
150 
151 void TrackballCameraManipulator::rotateCallback( QEvent* event ) {
152  if ( event->type() == QEvent::MouseMove ) {
153  auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
154  auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
155  handleCameraRotate( dx, dy );
156  }
157 }
158 
159 void TrackballCameraManipulator::panCallback( QEvent* event ) {
160  if ( event->type() == QEvent::MouseMove ) {
161  auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
162  auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
163  handleCameraPan( dx, dy );
164  }
165 }
166 
167 void TrackballCameraManipulator::zoomCallback( QEvent* event ) {
168  if ( event->type() == QEvent::MouseMove ) {
169  auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
170  auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
171  handleCameraZoom( dx, dy );
172  }
173  else if ( event->type() == QEvent::Wheel ) {
174  auto wheelEvent = reinterpret_cast<QWheelEvent*>( event );
175  handleCameraZoom(
176  ( wheelEvent->angleDelta().y() * 0.01_ra + wheelEvent->angleDelta().x() * 0.01_ra ) *
178  }
179 }
180 
181 void TrackballCameraManipulator::moveForwardCallback( QEvent* event ) {
182 
183  if ( event->type() == QEvent::MouseMove ) {
184  auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
185  auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
186  handleCameraMoveForward( dx, dy );
187  }
188  else if ( event->type() == QEvent::Wheel ) {
189  auto wheelEvent = reinterpret_cast<QWheelEvent*>( event );
190 
191  handleCameraMoveForward(
192  ( wheelEvent->angleDelta().y() * 0.01_ra + wheelEvent->angleDelta().x() * 0.01_ra ) *
194  }
195 }
196 
198  const Qt::MouseButtons&,
199  const Qt::KeyboardModifiers&,
200  int key ) {
201 
202  m_lastMouseX = event->pos().x();
203  m_lastMouseY = event->pos().y();
204  m_phiDir = -Core::Math::signNZ( m_theta );
205 
206  bool handled = m_keyMappingCallbackManager.triggerEventCallback( event, key );
207 
208  return handled;
209 }
210 
212  const Qt::MouseButtons&,
213  const Qt::KeyboardModifiers&,
214  int key ) {
215  bool handled = m_keyMappingCallbackManager.triggerEventCallback( event, key );
216 
217  m_lastMouseX = event->pos().x();
218  m_lastMouseY = event->pos().y();
219 
220  if ( m_light != nullptr ) {
223  }
224 
225  return handled;
226 }
227 
229 
230  return false;
231 }
232 
234  const Qt::MouseButtons&,
235  const Qt::KeyboardModifiers&,
236  int key
237 
238 ) {
239  bool handled = m_keyMappingCallbackManager.triggerEventCallback( event, key, true );
240 
241  if ( m_light != nullptr ) {
244  }
245 
246  return handled;
247 }
248 
250  QKeyEvent* event,
251  const KeyMappingManager::KeyMappingAction& action ) {
252  return m_keyMappingCallbackManager.triggerEventCallback( action, event );
253 }
254 
255 void TrackballCameraManipulator::setCameraPosition( const Core::Vector3& position ) {
256  if ( position == m_referenceFrame.translation() ) {
257  QMessageBox::warning( nullptr, "Error", "Position cannot be set to target point" );
258  return;
259  }
260  m_camera->setPosition( position );
261  m_referenceFrame.translation() = position + m_distFromCenter * m_camera->getDirection();
262 
263  updatePhiTheta();
264 
265  if ( m_light != nullptr ) {
268  }
269 }
270 
271 void TrackballCameraManipulator::setCameraTarget( const Core::Vector3& target ) {
272  if ( m_camera->getPosition() == m_referenceFrame.translation() ) {
273  QMessageBox::warning( nullptr, "Error", "Target cannot be set to current camera position" );
274  return;
275  }
276 
277  m_referenceFrame.translation() = target;
278 
279  m_camera->setDirection( ( target - m_camera->getPosition() ).normalized() );
280  m_distFromCenter = ( target - m_camera->getPosition() ).norm();
281  updatePhiTheta();
282 
283  if ( m_light != nullptr ) { m_light->setDirection( m_camera->getDirection() ); }
284 }
285 
286 void TrackballCameraManipulator::fitScene( const Core::Aabb& aabb ) {
287  Scalar f = m_camera->getFOV();
288  Scalar a = m_camera->getAspect();
289 
290  const Scalar r = ( aabb.max() - aabb.min() ).norm() / 2_ra;
291  const Scalar x = r / std::sin( f / 2_ra );
292  const Scalar y = r / std::sin( f * a / 2_ra );
293  Scalar d = std::max( std::max( x, y ), 0.001_ra );
294 
295  m_camera->setFrame( Core::Transform::Identity() );
296  Core::Vector3 camPos { aabb.center().x(), aabb.center().y(), aabb.center().z() + d };
297  m_camera->setPosition( camPos );
298  Core::Vector3 camDir { aabb.center() - camPos };
299  m_distFromCenter = camDir.norm();
301 
302  // no ref camera here, use wolrd frame to align with
303  m_referenceFrame.setIdentity();
304  m_referenceFrame.translation() = aabb.center();
305 
306  updatePhiTheta();
307 
308  if ( m_light != nullptr ) {
311  }
312 }
313 
314 void TrackballCameraManipulator::handleCameraRotate( Scalar dx, Scalar dy ) {
315  Scalar dphi = m_phiDir * dx * m_cameraSensitivity * m_quickCameraModifier;
316  Scalar dtheta = -dy * m_cameraSensitivity * m_quickCameraModifier;
317 
318  Scalar phi = m_phi + dphi;
319  Scalar theta = m_theta + dtheta;
320  Core::Vector3 dir { std::sin( phi ) * std::sin( theta ),
321  std::cos( theta ),
322  std::cos( phi ) * std::sin( theta ) };
323 
324  Core::Vector3 right { -dir[2], 0, dir[0] };
325  right.normalize();
326  if ( ( m_referenceFrame.linear().inverse() * m_camera->getRightVector() ).dot( right ) < 0 )
327  right = -right;
328 
329  Core::Vector3 up = dir.cross( right ).normalized();
330 
331  dir = m_referenceFrame.linear() * dir;
332  right = m_referenceFrame.linear() * right;
333  up = m_referenceFrame.linear() * up;
334 
335  Core::Matrix3 m;
336  // clang-format off
337  m << right[0], up[0], dir[0], //
338  right[1], up[1], dir[1], //
339  right[2], up[2], dir[2]; //
340  // clang-format on
341  Core::Transform t;
342 
343  t.setIdentity();
344  t.linear() = m;
345  Core::Vector3 pos = m_referenceFrame.translation() + m_distFromCenter * dir;
346  t.translation() = pos;
347  m_camera->setFrame( t );
348 
349  m_phi = phi;
350  m_theta = theta;
351 
352  clampThetaPhi();
353 }
354 
355 void TrackballCameraManipulator::handleCameraPan( Scalar dx, Scalar dy ) {
356  Scalar x = dx * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
357  Scalar y = dy * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
358  // Move camera and trackball center, keep the distance to the center
359  Core::Vector3 R = -m_camera->getRightVector();
360  Core::Vector3 U = m_camera->getUpVector();
361 
362  Core::Transform T( Core::Transform::Identity() );
363  Core::Vector3 t = x * R + y * U;
364  T.translate( t );
365 
366  m_camera->applyTransform( T );
367  m_referenceFrame.translation() += t;
368 }
369 
370 void TrackballCameraManipulator::handleCameraMoveForward( Scalar dx, Scalar dy ) {
371  handleCameraMoveForward( Ra::Core::Math::sign( dy ) * Ra::Core::Vector2 { dx, dy }.norm() );
372 }
373 
374 void TrackballCameraManipulator::handleCameraMoveForward( Scalar z ) {
375 
376  Scalar moveFactor = z * m_distFromCenter * m_cameraSensitivity * m_quickCameraModifier;
377 
378  Core::Transform T( Core::Transform::Identity() );
379  T.translate( moveFactor * m_camera->getDirection() );
380 
381  m_camera->applyTransform( T );
382 
383  m_distFromCenter = ( m_referenceFrame.translation() - m_camera->getPosition() ).norm();
384 }
385 
386 void TrackballCameraManipulator::handleCameraZoom( Scalar dx, Scalar dy ) {
387  handleCameraZoom( Ra::Core::Math::sign( dy ) * Ra::Core::Vector2 { dx, dy }.norm() );
388 }
389 
390 void TrackballCameraManipulator::handleCameraZoom( Scalar z ) {
392  m_camera->setZoomFactor( zoom );
393 }
394 
397  const Core::Vector3 R = m_referenceFrame.linear().inverse() * ( -m_camera->getDirection() );
398 
399  m_theta = std::acos( R.y() );
400 
401  // unlikely to have z and x to 0, unless direction is perfectly aligned with
402  // m_referenceFrame.z() in this case phi is given by the relative orientation of right/up in
403  // the z/x plane of m_reference frame.
404  if ( UNLIKELY( areApproxEqual( R.z(), 0_ra ) && areApproxEqual( R.x(), 0_ra ) ) ) {
405  Scalar fx = m_referenceFrame.matrix().block<3, 1>( 0, 2 ).dot( m_camera->getRightVector() );
406  Scalar fy = m_referenceFrame.matrix().block<3, 1>( 0, 2 ).dot( m_camera->getUpVector() );
407  m_phi = std::atan2( fx, fy );
408  }
409  else { m_phi = std::atan2( R.x(), R.z() ); }
410 
411  // no need to clamp, atan2 is by def \in [-pi,pi]
412  // acos in [0, pi]
413  // clampThetaPhi();
414  CORE_ASSERT( std::isfinite( m_theta ) && std::isfinite( m_phi ), "Error in trackball camera" );
415 }
416 
417 void TrackballCameraManipulator::clampThetaPhi() {
418  // Keep phi between 0 and 2pi
419  if ( m_phi < 0_ra ) { m_phi += 2_ra * Pi; }
420  // Keep theta in [-pi, pi] (instead of [0,pi]) to allows scene flip
421  if ( m_theta < -Pi ) { m_theta += 2_ra * Pi; }
422  if ( m_theta > Pi ) { m_theta -= 2_ra * Pi; }
423 }
424 
425 bool TrackballCameraManipulator::checkIntegrity( const std::string& mess ) const {
426  Core::Vector3 c = m_camera->getPosition() + m_distFromCenter * m_camera->getDirection();
427  Scalar d = ( m_referenceFrame.translation() - c ).norm();
428  if ( d > 0.001_ra ) {
429  LOG( logWARNING ) << "TrackballCameraManipulator Integrity problem : " << mess;
430  LOG( logWARNING ) << "\t Position " << m_camera->getPosition().transpose();
431  LOG( logWARNING ) << "\t Ref "
432  << ( m_referenceFrame.translation() +
434  .transpose();
435  LOG( logWARNING ) << "\t Direction " << m_camera->getDirection().transpose();
436  LOG( logWARNING ) << "\t Center " << c.transpose();
437  LOG( logWARNING ) << "\t Distance " << d;
438  LOG( logWARNING ) << "\t angles " << m_phi << " " << m_theta;
439  }
440  return d < 0.001_ra;
441 }
442 
443 } // namespace Gui
444 } // namespace Ra
ProjType
Define the projection type.
Definition: Camera.hpp:21
void setPosition(const Core::Vector3 &position)
Set the position of the camera to position.
Definition: Camera.hpp:248
void setDirection(const Core::Vector3 &direction)
Definition: Camera.cpp:32
Core::Vector3 getPosition() const
Return the position.
Definition: Camera.hpp:244
void setZoomFactor(const Scalar &zoomFactor)
Set the zoom factor to zoomFactor.
Definition: Camera.hpp:306
Core::Vector3 getUpVector() const
Return the up vector.
Definition: Camera.hpp:258
Scalar getZoomFactor() const
Return the zoom factor.
Definition: Camera.hpp:302
void setFrame(const Core::Transform &frame)
Set the frame of the camera to frame.
Definition: Camera.hpp:240
Scalar getFOV() const
Definition: Camera.hpp:272
Scalar getAspect() const
Return the aspect ratio of the viewport.
Definition: Camera.hpp:319
Core::Transform getFrame() const
Definition: Camera.hpp:236
Core::Vector3 getDirection() const
Return the direction the camera is looking at.
Definition: Camera.hpp:254
virtual void setDirection(const Eigen::Matrix< Scalar, 3, 1 > &)
Definition: Light.hpp:69
virtual void setPosition(const Eigen::Matrix< Scalar, 3, 1 > &)
Definition: Light.hpp:75
The CameraManipulator class is the generic class for camera manipulators.
Scalar m_lastMouseX
x-position of the mouse on the screen at the manipulation start.
Scalar m_quickCameraModifier
Additional factor for camera sensitivity.
Core::Asset::Camera * m_camera
The Camera.
Scalar m_lastMouseY
y-position of the mouse on the screen at the manipulation start.
Scalar m_wheelSpeedModifier
Speed modifier on mouse wheel events.
Engine::Scene::Light * m_light
The light attached to the Camera.
Scalar m_cameraSensitivity
the Camera sensitivity to manipulation.
bool triggerEventCallback(QEvent *event, int key, bool wheel=false)
Ra::Core::Utils::Index KeyMappingAction
handle to an action
Ra::Core::Utils::Index Context
handle to a Context
const Core::Transform::ConstTranslationPart getTrackballCenter() const
KeyMappingManager::Context mappingContext() override
bool handleWheelEvent(QWheelEvent *event, const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &modifiers, int key) override
Scalar getTrackballRadius() const
Return the distance from the camera to the target point.
bool handleKeyPressEvent(QKeyEvent *event, const KeyMappingManager::KeyMappingAction &action) override
bool handleMouseReleaseEvent(QMouseEvent *event) override
Scalar m_phiDir
sign of m_theta at mousePressEvent, to guide the phi rotation direction.
bool handleMousePressEvent(QMouseEvent *event, const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &modifiers, int key) override
Core::Transform m_referenceFrame
initial frame of the camera, centered on target, to compute angles.
bool handleMouseMoveEvent(QMouseEvent *event, const Qt::MouseButtons &buttons, const Qt::KeyboardModifiers &modifiers, int key) override
void updatePhiTheta()
Update the polar coordinates of the Camera w.r.t. the trackball center.
Scalar m_distFromCenter
The distance from the camera to the trackball center.
std::enable_if<!std::numeric_limits< T >::is_integer, bool >::type areApproxEqual(T x, T y, T espilonBoostFactor=T(10))
Compare two numbers such that |x-y| < espilon*epsilonBoostFactor.
Definition: Math.hpp:42
constexpr T signNZ(const T &val)
Definition: Math.hpp:114
constexpr int sign(const T &val)
Returns the sign of any numeric type as { -1, 0, 1}.
Definition: Math.hpp:106
Definition: Cage.cpp:3