Radium Engine  1.5.20
Loading...
Searching...
No Matches
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>
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
20namespace Ra {
21
22using Core::Math::Pi;
23using namespace Ra::Core::Utils;
24
25namespace Gui {
26
27#define KMA_VALUE( XX ) KeyMappingManager::KeyMappingAction TrackballCameraManipulator::XX;
28KeyMappingCamera
29#undef KMA_VALUE
30
31void 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
47void 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() } {
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
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
120
121 if ( m_light != nullptr ) {
124 }
125}
126
132
136
137Core::Transform::ConstTranslationPart TrackballCameraManipulator::getTrackballCenter() const {
138 return m_referenceFrame.translation();
139}
140
142 return KeyMapping::getContext();
143}
144
145void 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
151void 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
159void 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
167void 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
181void 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,
252 return m_keyMappingCallbackManager.triggerEventCallback( action, event );
253}
254
255void 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
264
265 if ( m_light != nullptr ) {
268 }
269}
270
271void 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();
282
283 if ( m_light != nullptr ) { m_light->setDirection( m_camera->getDirection() ); }
284}
285
286void 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
307
308 if ( m_light != nullptr ) {
311 }
312}
313
314void TrackballCameraManipulator::handleCameraRotate( Scalar dx, Scalar dy ) {
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
355void TrackballCameraManipulator::handleCameraPan( Scalar dx, Scalar dy ) {
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
370void TrackballCameraManipulator::handleCameraMoveForward( Scalar dx, Scalar dy ) {
371 handleCameraMoveForward( Ra::Core::Math::sign( dy ) * Ra::Core::Vector2 { dx, dy }.norm() );
372}
373
374void TrackballCameraManipulator::handleCameraMoveForward( Scalar z ) {
375
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
386void TrackballCameraManipulator::handleCameraZoom( Scalar dx, Scalar dy ) {
387 handleCameraZoom( Ra::Core::Math::sign( dy ) * Ra::Core::Vector2 { dx, dy }.norm() );
388}
389
390void 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
417void 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
425bool 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
T acos(T... args)
T atan2(T... args)
ProjType
Define the projection type.
Definition Camera.hpp:19
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
void setType(const ProjType &projectionType)
Set the projection type to projectionType.
Definition Camera.hpp:327
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
ProjType getType() const
Return the projection type.
Definition Camera.hpp:323
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:67
virtual void setPosition(const Eigen::Matrix< Scalar, 3, 1 > &)
Definition Light.hpp:73
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)
void addEventCallback(KeyMappingAction action, Callback callback)
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.
T cos(T... args)
T isfinite(T... args)
T right(T... args)
T max(T... args)
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
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T sin(T... args)