Vulkan-Hpp
CameraManipulator.cpp
Go to the documentation of this file.
1 // Copyright(c) 2019, NVIDIA CORPORATION. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 // ignore warning 4127: conditional expression is constant
17 #if defined( _MSC_VER )
18 # pragma warning( disable : 4127 )
19 #elif defined( __clang__ )
20 # if ( 10 <= __clang_major__ )
21 # pragma clang diagnostic ignored "-Wdeprecated-volatile" // to keep glm/detail/type_half.inl compiling
22 # endif
23 #elif defined( __GNUC__ )
24 // don't know how to switch off that warning here
25 #else
26 // unknow compiler... just ignore the warnings for yourselves ;)
27 #endif
28 
29 #include "CameraManipulator.hpp"
30 
31 #include <glm/glm.hpp>
32 #include <glm/gtx/rotate_vector.hpp>
33 
34 namespace vk
35 {
36  namespace su
37  {
38  const float trackballSize = 0.8f;
39 
40  //-----------------------------------------------------------------------------
41  // MATH functions
42  //
43  template <typename T>
44  bool isZero( const T & _a )
45  {
46  return fabs( _a ) < std::numeric_limits<T>::epsilon();
47  }
48  template <typename T>
49  bool isOne( const T & _a )
50  {
51  return areEqual( _a, (T)1 );
52  }
53  inline float sign( float s )
54  {
55  return ( s < 0.f ) ? -1.f : 1.f;
56  }
57 
59  {
60  update();
61  }
62 
63  glm::vec3 const & CameraManipulator::getCameraPosition() const
64  {
65  return m_cameraPosition;
66  }
67 
68  glm::vec3 const & CameraManipulator::getCenterPosition() const
69  {
70  return m_centerPosition;
71  }
72 
73  glm::mat4 const & CameraManipulator::getMatrix() const
74  {
75  return m_matrix;
76  }
77 
79  {
80  return m_mode;
81  }
82 
83  glm::ivec2 const & CameraManipulator::getMousePosition() const
84  {
85  return m_mousePosition;
86  }
87 
89  {
90  return m_roll;
91  }
92 
94  {
95  return m_speed;
96  }
97 
98  glm::vec3 const & CameraManipulator::getUpVector() const
99  {
100  return m_upVector;
101  }
102 
103  glm::u32vec2 const & CameraManipulator::getWindowSize() const
104  {
105  return m_windowSize;
106  }
107 
108  CameraManipulator::Action CameraManipulator::mouseMove( glm::ivec2 const & position, MouseButton mouseButton, ModifierFlags & modifiers )
109  {
110  Action curAction = Action::None;
111  switch ( mouseButton )
112  {
113  case MouseButton::Left:
114  if ( ( ( modifiers & ModifierFlagBits::Ctrl ) && ( modifiers & ModifierFlagBits::Shift ) ) || ( modifiers & ModifierFlagBits::Alt ) )
115  {
116  curAction = m_mode == Mode::Examine ? Action::LookAround : Action::Orbit;
117  }
118  else if ( modifiers & ModifierFlagBits::Shift )
119  {
120  curAction = Action::Dolly;
121  }
122  else if ( modifiers & ModifierFlagBits::Ctrl )
123  {
124  curAction = Action::Pan;
125  }
126  else
127  {
128  curAction = m_mode == Mode::Examine ? Action::Orbit : Action::LookAround;
129  }
130  break;
131  case MouseButton::Middle: curAction = Action::Pan; break;
132  case MouseButton::Right: curAction = Action::Dolly; break;
133  default: assert( false );
134  }
135  assert( curAction != Action::None );
136  motion( position, curAction );
137 
138  return curAction;
139  }
140 
141  void CameraManipulator::setLookat( const glm::vec3 & cameraPosition, const glm::vec3 & centerPosition, const glm::vec3 & upVector )
142  {
143  m_cameraPosition = cameraPosition;
144  m_centerPosition = centerPosition;
145  m_upVector = upVector;
146  update();
147  }
148 
150  {
151  m_mode = mode;
152  }
153 
154  void CameraManipulator::setMousePosition( glm::ivec2 const & position )
155  {
156  m_mousePosition = position;
157  }
158 
159  void CameraManipulator::setRoll( float roll )
160  {
161  m_roll = roll;
162  update();
163  }
164 
165  void CameraManipulator::setSpeed( float speed )
166  {
167  m_speed = speed;
168  }
169 
170  void CameraManipulator::setWindowSize( glm::ivec2 const & size )
171  {
172  m_windowSize = size;
173  }
174 
175  void CameraManipulator::wheel( int value )
176  {
177  float fValue = static_cast<float>( value );
178  float dx = ( fValue * std::abs( fValue ) ) / static_cast<float>( m_windowSize[0] );
179 
180  glm::vec3 z = m_cameraPosition - m_centerPosition;
181  float length = z.length() * 0.1f;
182  length = length < 0.001f ? 0.001f : length;
183 
184  dx *= m_speed;
185  dolly( glm::vec2( dx, dx ) );
186  update();
187  }
188 
189  void CameraManipulator::dolly( glm::vec2 const & delta )
190  {
191  glm::vec3 z = m_centerPosition - m_cameraPosition;
192  float length = glm::length( z );
193 
194  // We are at the point of interest, and don't know any direction, so do nothing!
195  if ( isZero( length ) )
196  {
197  return;
198  }
199 
200  // Use the larger movement.
201  float dd;
202  if ( m_mode != Mode::Examine )
203  {
204  dd = -delta[1];
205  }
206  else
207  {
208  dd = fabs( delta[0] ) > fabs( delta[1] ) ? delta[0] : -delta[1];
209  }
210 
211  float factor = m_speed * dd / length;
212 
213  // Adjust speed based on distance.
214  length /= 10;
215  length = length < 0.001f ? 0.001f : length;
216  factor *= length;
217 
218  // Don't move to or through the point of interest.
219  if ( 1.0f <= factor )
220  {
221  return;
222  }
223 
224  z *= factor;
225 
226  // Not going up
227  if ( m_mode == Mode::Walk )
228  {
229  if ( m_upVector.y > m_upVector.z )
230  {
231  z.y = 0;
232  }
233  else
234  {
235  z.z = 0;
236  }
237  }
238 
239  m_cameraPosition += z;
240 
241  // In fly mode, the interest moves with us.
242  if ( m_mode != Mode::Examine )
243  {
244  m_centerPosition += z;
245  }
246  }
247 
248  void CameraManipulator::motion( glm::ivec2 const & position, Action action )
249  {
250  glm::vec2 delta( float( position[0] - m_mousePosition[0] ) / float( m_windowSize[0] ),
251  float( position[1] - m_mousePosition[1] ) / float( m_windowSize[1] ) );
252 
253  switch ( action )
254  {
255  case Action::Orbit:
256  if ( m_mode == Mode::Trackball )
257  {
258  orbit( delta, true ); // trackball(position);
259  }
260  else
261  {
262  orbit( delta, false );
263  }
264  break;
265  case Action::Dolly: dolly( delta ); break;
266  case Action::Pan: pan( delta ); break;
267  case Action::LookAround:
268  if ( m_mode == Mode::Trackball )
269  {
270  trackball( position );
271  }
272  else
273  {
274  orbit( glm::vec2( delta[0], -delta[1] ), true );
275  }
276  break;
277  default: break;
278  }
279 
280  update();
281 
282  m_mousePosition = position;
283  }
284 
285  void CameraManipulator::orbit( glm::vec2 const & delta, bool invert )
286  {
287  if ( isZero( delta[0] ) && isZero( delta[1] ) )
288  {
289  return;
290  }
291 
292  // Full width will do a full turn
293  float dx = delta[0] * float( glm::two_pi<float>() );
294  float dy = delta[1] * float( glm::two_pi<float>() );
295 
296  // Get the camera
297  glm::vec3 origin( invert ? m_cameraPosition : m_centerPosition );
298  glm::vec3 position( invert ? m_centerPosition : m_cameraPosition );
299 
300  // Get the length of sight
301  glm::vec3 centerToEye( position - origin );
302  float radius = glm::length( centerToEye );
303  centerToEye = glm::normalize( centerToEye );
304 
305  // Find the rotation around the UP axis (Y)
306  glm::vec3 zAxis( centerToEye );
307  glm::mat4 yRotation = glm::rotate( -dx, m_upVector );
308 
309  // Apply the (Y) rotation to the eye-center vector
310  glm::vec4 tmpVector = yRotation * glm::vec4( centerToEye.x, centerToEye.y, centerToEye.z, 0.0f );
311  centerToEye = glm::vec3( tmpVector.x, tmpVector.y, tmpVector.z );
312 
313  // Find the rotation around the X vector: cross between eye-center and up (X)
314  glm::vec3 xAxis = glm::cross( m_upVector, zAxis );
315  xAxis = glm::normalize( xAxis );
316  glm::mat4 xRotation = glm::rotate( -dy, xAxis );
317 
318  // Apply the (X) rotation to the eye-center vector
319  tmpVector = xRotation * glm::vec4( centerToEye.x, centerToEye.y, centerToEye.z, 0 );
320  glm::vec3 rotatedVector( tmpVector.x, tmpVector.y, tmpVector.z );
321  if ( sign( rotatedVector.x ) == sign( centerToEye.x ) )
322  {
323  centerToEye = rotatedVector;
324  }
325 
326  // Make the vector as long as it was originally
327  centerToEye *= radius;
328 
329  // Finding the new position
330  glm::vec3 newPosition = centerToEye + origin;
331 
332  if ( !invert )
333  {
334  m_cameraPosition = newPosition; // Normal: change the position of the camera
335  }
336  else
337  {
338  m_centerPosition = newPosition; // Inverted: change the interest point
339  }
340  }
341 
342  void CameraManipulator::pan( glm::vec2 const & delta )
343  {
344  glm::vec3 z( m_cameraPosition - m_centerPosition );
345  float length = static_cast<float>( glm::length( z ) ) / 0.785f; // 45 degrees
346  z = glm::normalize( z );
347  glm::vec3 x = glm::normalize( glm::cross( m_upVector, z ) );
348  glm::vec3 y = glm::normalize( glm::cross( z, x ) );
349  x *= -delta[0] * length;
350  y *= delta[1] * length;
351 
352  if ( m_mode == Mode::Fly )
353  {
354  x = -x;
355  y = -y;
356  }
357 
358  m_cameraPosition += x + y;
359  m_centerPosition += x + y;
360  }
361 
362  double CameraManipulator::projectOntoTBSphere( const glm::vec2 & p )
363  {
364  double z;
365  double d = length( p );
366  if ( d < trackballSize * 0.70710678118654752440 )
367  {
368  // inside sphere
369  z = sqrt( trackballSize * trackballSize - d * d );
370  }
371  else
372  {
373  // on hyperbola
374  double t = trackballSize / 1.41421356237309504880;
375  z = t * t / d;
376  }
377 
378  return z;
379  }
380 
381  void CameraManipulator::trackball( glm::ivec2 const & position )
382  {
383  glm::vec2 p0( 2 * ( m_mousePosition[0] - m_windowSize[0] / 2 ) / double( m_windowSize[0] ),
384  2 * ( m_windowSize[1] / 2 - m_mousePosition[1] ) / double( m_windowSize[1] ) );
385  glm::vec2 p1( 2 * ( position[0] - m_windowSize[0] / 2 ) / double( m_windowSize[0] ),
386  2 * ( m_windowSize[1] / 2 - position[1] ) / double( m_windowSize[1] ) );
387 
388  // determine the z coordinate on the sphere
389  glm::vec3 pTB0( p0[0], p0[1], projectOntoTBSphere( p0 ) );
390  glm::vec3 pTB1( p1[0], p1[1], projectOntoTBSphere( p1 ) );
391 
392  // calculate the rotation axis via cross product between p0 and p1
393  glm::vec3 axis = glm::cross( pTB0, pTB1 );
394  axis = glm::normalize( axis );
395 
396  // calculate the angle
397  float t = glm::length( pTB0 - pTB1 ) / ( 2.f * trackballSize );
398 
399  // clamp between -1 and 1
400  if ( t > 1.0f )
401  {
402  t = 1.0f;
403  }
404  else if ( t < -1.0f )
405  {
406  t = -1.0f;
407  }
408 
409  float rad = 2.0f * asin( t );
410 
411  {
412  glm::vec4 rot_axis = m_matrix * glm::vec4( axis, 0 );
413  glm::mat4 rot_mat = glm::rotate( rad, glm::vec3( rot_axis.x, rot_axis.y, rot_axis.z ) );
414 
415  glm::vec3 pnt = m_cameraPosition - m_centerPosition;
416  glm::vec4 pnt2 = rot_mat * glm::vec4( pnt.x, pnt.y, pnt.z, 1 );
417  m_cameraPosition = m_centerPosition + glm::vec3( pnt2.x, pnt2.y, pnt2.z );
418  glm::vec4 up2 = rot_mat * glm::vec4( m_upVector.x, m_upVector.y, m_upVector.z, 0 );
419  m_upVector = glm::vec3( up2.x, up2.y, up2.z );
420  }
421  }
422 
423  void CameraManipulator::update()
424  {
425  m_matrix = glm::lookAt( m_cameraPosition, m_centerPosition, m_upVector );
426 
427  if ( !isZero( m_roll ) )
428  {
429  glm::mat4 rot = glm::rotate( m_roll, glm::vec3( 0, 0, 1 ) );
430  m_matrix = m_matrix * rot;
431  }
432  }
433 
434  } // namespace su
435 } // namespace vk
Action mouseMove(glm::ivec2 const &position, MouseButton mouseButton, ModifierFlags &modifiers)
void setMousePosition(glm::ivec2 const &position)
glm::mat4 const & getMatrix() const
glm::vec3 const & getCameraPosition() const
void setLookat(const glm::vec3 &cameraPosition, const glm::vec3 &centerPosition, const glm::vec3 &upVector)
glm::vec3 const & getUpVector() const
void setWindowSize(glm::ivec2 const &size)
glm::u32vec2 const & getWindowSize() const
glm::ivec2 const & getMousePosition() const
glm::vec3 const & getCenterPosition() const
float sign(float s)
bool isOne(const T &_a)
const float trackballSize
bool isZero(const T &_a)
Definition: vulkan.cppm:23