Radium Engine  1.5.20
Loading...
Searching...
No Matches
RotateAroundCameraManipulator.cpp
1#include <Core/Types.hpp>
2#include <Core/Utils/Color.hpp>
3#include <Engine/RadiumEngine.hpp>
4#include <Engine/Rendering/RenderObject.hpp>
5#include <Engine/Rendering/RenderObjectManager.hpp>
6#include <Engine/Rendering/Renderer.hpp>
7#include <Engine/Scene/Light.hpp>
9#include <Gui/Utils/KeyMappingManager.hpp>
10#include <Gui/Viewer/RotateAroundCameraManipulator.hpp>
11#include <Gui/Viewer/Viewer.hpp>
12
13namespace Ra {
14namespace Gui {
15
16using namespace Ra::Core::Utils;
17
18#define KMA_VALUE( XX ) KeyMappingManager::KeyMappingAction RotateAroundCameraManipulator::XX;
19KeyMappingRotateAroundCamera
20#undef KMA_VALUE
21
22void RotateAroundCameraManipulator::configureKeyMapping_impl() {
23 auto keyMappingManager = Gui::KeyMappingManager::getInstance();
24
25 KeyMapping::setContext( KeyMappingManager::getInstance()->getContext( "CameraContext" ) );
26 if ( KeyMapping::getContext().isInvalid() ) {
27 LOG( logINFO )
28 << "CameraContext not defined (maybe the configuration file do not contains it)";
29 LOG( logERROR ) << "CameraContext all keymapping invalid !";
30 return;
31 }
32
33#define KMA_VALUE( XX ) XX = keyMappingManager->getAction( KeyMapping::getContext(), #XX );
34 KeyMappingRotateAroundCamera
35#undef KMA_VALUE
36}
37
38void RotateAroundCameraManipulator::setupKeyMappingCallbacks() {
39
40 m_keyMappingCallbackManager.addEventCallback(
41 TRACKBALLCAMERA_ROTATE, [=]( QEvent* event ) { rotateCallback( event ); } );
42 m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_PAN,
43 [=]( QEvent* event ) { panCallback( event ); } );
44 m_keyMappingCallbackManager.addEventCallback( TRACKBALLCAMERA_ZOOM,
45 [=]( QEvent* event ) { zoomCallback( event ); } );
46 m_keyMappingCallbackManager.addEventCallback(
47 TRACKBALLCAMERA_MOVE_FORWARD, [=]( QEvent* event ) { moveForwardCallback( event ); } );
48 m_keyMappingCallbackManager.addEventCallback(
49 ROTATEAROUND_ALIGN_WITH_CLOSEST_AXIS,
50 [=]( QEvent* event ) { alignWithClosestAxisCallback( event ); } );
51 m_keyMappingCallbackManager.addEventCallback(
52 ROTATEAROUND_SET_PIVOT, [=]( QEvent* event ) { setPivotCallback( event ); } );
53}
54
55void RotateAroundCameraManipulator::alignWithClosestAxisCallback( QEvent* event ) {
56 if ( event->type() == QEvent::KeyPress ) { alignOnClosestAxis(); }
57}
58
59void RotateAroundCameraManipulator::moveForwardCallback( QEvent* event ) {
60 if ( event->type() == QEvent::MouseMove ) {
61 auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
62 auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
63 handleCameraMoveForward( dx, dy );
64 }
65}
66void RotateAroundCameraManipulator::panCallback( QEvent* event ) {
67 if ( event->type() == QEvent::MouseMove ) {
68 auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
69 auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
70 handleCameraPan( dx, dy );
71 }
72}
73void RotateAroundCameraManipulator::rotateCallback( QEvent* event ) {
74 if ( event->type() == QEvent::MouseMove ) {
75 auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
76 handleCameraRotate( mouseEvent->pos().x(), mouseEvent->pos().y() );
77 }
78}
79void RotateAroundCameraManipulator::setPivotCallback( QEvent* event ) {
80 if ( event->type() == QEvent::KeyPress ) { setPivotFromPixel( m_lastMouseX, m_lastMouseY ); }
81}
82void RotateAroundCameraManipulator::zoomCallback( QEvent* event ) {
83 if ( event->type() == QEvent::MouseMove ) {
84 auto mouseEvent = reinterpret_cast<QMouseEvent*>( event );
85 auto [dx, dy] = computeDeltaMouseMove( mouseEvent );
86 handleCameraZoom( dx, dy );
87 }
88}
89
90void rotateAroundPoint( Ra::Core::Asset::Camera* cam,
91 Ra::Core::Vector3& target,
92 Ra::Core::Quaternion& rotation,
93 const Ra::Core::Vector3& point ) {
94 Ra::Core::Vector3 t = cam->getPosition();
95 Scalar l = ( target - t ).norm();
96 Ra::Core::Transform inverseCamRotateAround;
97 Ra::Core::AngleAxis aa0 { rotation };
98 Ra::Core::AngleAxis aa1 { aa0.angle(), ( cam->getFrame().linear() * aa0.axis() ).normalized() };
99 inverseCamRotateAround.setIdentity();
100 inverseCamRotateAround.rotate( aa1 );
101 cam->applyTransform( inverseCamRotateAround );
102
103 Ra::Core::Vector3 trans = point + inverseCamRotateAround * ( t - point );
104 cam->setPosition( trans );
105 target = cam->getPosition() + l * cam->getDirection();
106}
107
108RotateAroundCameraManipulator::RotateAroundCameraManipulator( Ra::Gui::Viewer* viewer ) :
110 m_keyMappingCallbackManager { KeyMapping::getContext() },
111 m_viewer( viewer ) {
112 setupKeyMappingCallbacks();
113 m_cameraSensitivity = 0.5_ra;
114}
115
116RotateAroundCameraManipulator::RotateAroundCameraManipulator( const CameraManipulator& cm,
117 Ra::Gui::Viewer* viewer ) :
119 m_keyMappingCallbackManager { KeyMapping::getContext() },
120 m_viewer( viewer ) {
121 setupKeyMappingCallbacks();
122 m_cameraSensitivity = 0.5_ra;
123}
124
125bool RotateAroundCameraManipulator::handleMouseMoveEvent(
126 QMouseEvent* event,
127 const Qt::MouseButtons& /*buttons*/,
128 const Qt::KeyboardModifiers& /* modifiers*/,
129 int key ) {
130
131 bool handled = m_keyMappingCallbackManager.triggerEventCallback( event, key );
132
133 m_lastMouseX = event->pos().x();
134 m_lastMouseY = event->pos().y();
135
136 if ( m_light != nullptr ) {
137 m_light->setPosition( m_camera->getPosition() );
138 m_light->setDirection( m_camera->getDirection() );
139 }
140
141 return handled;
142}
143
144bool RotateAroundCameraManipulator::handleKeyPressEvent(
145 QKeyEvent* event,
147 return m_keyMappingCallbackManager.triggerEventCallback( action, event );
148}
149
150void RotateAroundCameraManipulator::setPivot( Ra::Core::Vector3 pivot ) {
151 m_pivot = pivot;
152}
153
154void RotateAroundCameraManipulator::setPivotFromPixel( Scalar x, Scalar y ) {
155 using Ra::Core::Vector3;
157
158 auto dc = m_viewer->toDevice( { x, y } );
159
160 m_viewer->makeCurrent();
161 Scalar z = m_viewer->getDepthUnderMouse();
162 m_viewer->doneCurrent();
163
164 auto pivotPoint = m_camera->unProjectFromScreen( Vector3( dc[0], dc[1], z ) );
165
166 setPivot( pivotPoint );
167
168 // draw a 5 logical pixel sphere, shift lc by 5 pixels
169 auto dcs = m_viewer->toDevice( { x + 5, y } );
170 auto pivotShifted = m_camera->unProjectFromScreen( Vector3( dcs[0], dcs[1], z ) );
171 auto radius = ( pivotPoint - pivotShifted ).norm();
172
173 auto id = RA_DISPLAY_SPHERE( pivotPoint, radius, Color::Yellow() );
174 auto ro = Engine::RadiumEngine::getInstance()->getRenderObjectManager()->getRenderObject( id );
175 ro->setLifetime( 100 );
176}
177
178void RotateAroundCameraManipulator::alignOnClosestAxis() {
179 Ra::Core::Vector3 x( 1_ra, 0_ra, 0_ra );
180 Ra::Core::Vector3 y( 0_ra, 1_ra, 0_ra );
181 Ra::Core::Vector3 z( 0_ra, 0_ra, 1_ra );
182 Ra::Core::Vector3 oldDirection, newDirection = m_camera->getDirection();
183 Ra::Core::Vector3 newUpVector = m_camera->getUpVector();
184 // getFrame().inverse() is a transform we can apply on Vector3
185 Ra::Core::Vector3 pivotInCamSpace = m_camera->getFrame().inverse() * m_pivot;
186
187 auto updateMaxAndAxis =
188 []( Ra::Core::Vector3 ref, Ra::Core::Vector3 axis, Ra::Core::Vector3& out, Scalar& max ) {
189 Scalar d = ref.dot( axis );
190 if ( d > max ) {
191 max = d;
192 out = axis;
193 }
194 };
195
196 Scalar max = 0_ra;
197 Ra::Core::Vector3 ref = m_camera->getDirection();
198 for ( auto axis : std::vector<Ra::Core::Vector3> { -x, x, -y, y, -z, z } ) {
199 updateMaxAndAxis( ref, axis, newDirection, max );
200 }
201 m_camera->setDirection( newDirection );
202
203 max = 0_ra;
204 ref = m_camera->getUpVector();
205 for ( auto axis : std::vector<Ra::Core::Vector3> { -x, x, -y, y, -z, z } ) {
206 updateMaxAndAxis( ref, axis, newUpVector, max );
207 }
208 m_camera->setUpVector( newUpVector );
209
210 Ra::Core::Vector3 newPivot = m_camera->getFrame() * pivotInCamSpace;
211 Ra::Core::Vector3 trans = m_pivot - newPivot;
212 m_camera->setPosition( m_camera->getPosition() + trans );
213
214 if ( m_light != nullptr ) {
215 m_light->setPosition( m_camera->getPosition() );
216 m_light->setDirection( m_camera->getDirection() );
217 }
218}
219
220KeyMappingManager::Context RotateAroundCameraManipulator::mappingContext() {
221 return KeyMapping::getContext();
222}
223
224void RotateAroundCameraManipulator::fitScene( const Core::Aabb& aabb ) {
225 TrackballCameraManipulator::fitScene( aabb );
226 setPivot( aabb.center() );
227}
228
229void RotateAroundCameraManipulator::handleCameraRotate( Scalar x, Scalar y ) {
230 Ra::Core::Vector3 trans = m_camera->projectToScreen( m_pivot );
231 Ra::Core::Quaternion rot = deformedBallQuaternion( x, y, trans[0], trans[1] );
232 Ra::Core::Vector3 pivot = m_pivot;
233 rotateAroundPoint( m_camera, m_target, rot, pivot );
234}
235
236Scalar RotateAroundCameraManipulator::projectOnBall( Scalar x, Scalar y ) {
237 const Scalar size = 1.0;
238 const Scalar size2 = size * size;
239 const Scalar size_limit = size2 * 0.5;
240
241 const Scalar d = x * x + y * y;
242 return d < size_limit ? std::sqrt( size2 - d ) : size_limit / std::sqrt( d );
243}
244
245Ra::Core::Quaternion
246RotateAroundCameraManipulator::deformedBallQuaternion( Scalar x, Scalar y, Scalar cx, Scalar cy ) {
247 // Points on the deformed ball
248 Scalar px = m_cameraSensitivity * ( m_lastMouseX - cx ) / m_camera->getWidth();
249 Scalar py = m_cameraSensitivity * ( cy - m_lastMouseY ) / m_camera->getHeight();
250 Scalar dx = m_cameraSensitivity * ( x - cx ) / m_camera->getWidth();
251 Scalar dy = m_cameraSensitivity * ( cy - y ) / m_camera->getHeight();
252
253 const Ra::Core::Vector3 p1( px, py, projectOnBall( px, py ) );
254 const Ra::Core::Vector3 p2( dx, dy, projectOnBall( dx, dy ) );
255 // Approximation of rotation angle
256 // Should be divided by the projectOnBall size, but it is 1.0
257 Ra::Core::Vector3 axis = p2.cross( p1 );
258
259 if ( axis.norm() > 10_ra * Ra::Core::Math::machineEps ) {
260 const Scalar angle =
261 5.0_ra *
262 std::asin( std::sqrt( axis.squaredNorm() / p1.squaredNorm() / p2.squaredNorm() ) );
263 return Ra::Core::Quaternion( Ra::Core::AngleAxis( angle, axis.normalized() ) );
264 }
265 return Ra::Core::Quaternion {
266 Ra::Core::AngleAxis( 0_ra, Ra::Core::Vector3( 0_ra, 0_ra, 1_ra ) ) };
267}
268
269void RotateAroundCameraManipulator::handleCameraForward( Scalar z ) {
270 Ra::Core::Vector3 trans = m_camera->getDirection() * z;
271 Ra::Core::Transform transfo( Ra::Core::Transform::Identity() );
272 transfo.translate( trans );
273
274 m_camera->applyTransform( transfo );
275}
276
277void RotateAroundCameraManipulator::handleCameraPan( Scalar dx, Scalar dy ) {
278 Scalar x = dx * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
279 Scalar y = dy * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
280 // Move camera and trackball center, keep the distance to the center
281 Ra::Core::Vector3 R = -m_camera->getRightVector();
282 Ra::Core::Vector3 U = m_camera->getUpVector();
283
284 Ra::Core::Transform T( Ra::Core::Transform::Identity() );
285 Ra::Core::Vector3 t = x * R + y * U;
286 T.translate( t );
287 m_target += t;
288
289 m_camera->applyTransform( T );
290}
291
292} // namespace Gui
293} // namespace Ra
T asin(T... args)
Camera class storing the Camera frame and the projection properties The view direction is -z in camer...
Definition Camera.hpp:16
void setPosition(const Core::Vector3 &position)
Set the position of the camera to position.
Definition Camera.hpp:248
Core::Vector3 getPosition() const
Return the position.
Definition Camera.hpp:244
Core::Transform getFrame() const
Definition Camera.hpp:236
Core::Vector3 getDirection() const
Return the direction the camera is looking at.
Definition Camera.hpp:254
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_lastMouseY
y-position of the mouse on the screen at the manipulation start.
void addEventCallback(KeyMappingAction action, Callback callback)
Ra::Core::Utils::Index KeyMappingAction
handle to an action
Ra::Core::Utils::Index Context
handle to a Context
A Trackball manipulator for Cameras.
T max(T... args)
Scalar angle(const Vector_ &v1, const Vector_ &v2)
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
STL namespace.
T ref(T... args)
T sqrt(T... args)