KiCad PCB EDA Suite
camera.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2016 Mario Luzeiro <[email protected]>
5  * Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
29 #include "camera.h"
30 #include <wx/log.h>
31 
32 
33 // A helper function to normalize aAngle between -2PI and +2PI
34 inline void normalise2PI( float& aAngle )
35 {
36  while( aAngle > 0.0 )
37  aAngle -= static_cast<float>( M_PI * 2.0f );
38 
39  while( aAngle < 0.0 )
40  aAngle += static_cast<float>( M_PI * 2.0f );
41 }
42 
43 
47 const wxChar *CAMERA::m_logTrace = wxT( "KI_TRACE_CAMERA" );
48 
49 
50 #define DEFAULT_MIN_ZOOM 0.10f
51 #define DEFAULT_MAX_ZOOM 1.20f
52 
53 
54 CAMERA::CAMERA( float aInitialDistance )
55 {
56  wxLogTrace( m_logTrace, wxT( "CAMERA::CAMERA" ) );
57 
58  m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -aInitialDistance );
60  m_windowSize = SFVEC2I( 0, 0 );
63 
66 
67  Reset();
68 }
69 
70 
72 {
73  m_parametersChanged = true;
74  m_projectionMatrix = glm::mat4( 1.0f );
75  m_projectionMatrixInv = glm::mat4( 1.0f );
76  m_rotationMatrix = glm::mat4( 1.0f );
77  m_rotationMatrixAux = glm::mat4( 1.0f );
78  m_lastPosition = wxPoint( 0, 0 );
79 
80  m_zoom = 1.0f;
81  m_zoom_t0 = 1.0f;
82  m_zoom_t1 = 1.0f;
89 
90  m_rotate_aux = SFVEC3F( 0.0f );
91  m_rotate_aux_t0 = SFVEC3F( 0.0f );
92  m_rotate_aux_t1 = SFVEC3F( 0.0f );
93 
96  m_viewMatrixInverse = glm::inverse( m_viewMatrix );
97  m_scr_nX.clear();
98  m_scr_nY.clear();
100 }
101 
102 
104 {
106  m_zoom_t1 = 1.0f;
107  m_rotate_aux_t1 = SFVEC3F( 0.0f );
109 
110  // Since 0 = 2pi, we want to reset the angle to be the closest
111  // one to where we currently are. That ensures that we rotate
112  // the board around the smallest distance getting there.
113  if( m_rotate_aux_t0.x > M_PI )
114  m_rotate_aux_t1.x = static_cast<float>( 2.0f * M_PI );
115 
116  if( m_rotate_aux_t0.y > M_PI )
117  m_rotate_aux_t1.y = static_cast<float>( 2.0f * M_PI );
118 
119  if( m_rotate_aux_t0.z > M_PI )
120  m_rotate_aux_t1.z = static_cast<float>( 2.0f * M_PI );
121 }
122 
123 
125 {
126  if( m_zoom < m_minZoom )
127  m_zoom = m_minZoom;
128 
129  if( m_zoom > m_maxZoom )
130  m_zoom = m_maxZoom;
131 
133 
136 }
137 
138 
140 {
141  m_viewMatrix = glm::translate( glm::mat4( 1.0f ), m_camera_pos ) *
143  glm::translate( glm::mat4( 1.0f ), -m_lookat_pos );
144 }
145 
146 
148 {
149  m_rotationMatrixAux = glm::rotate( glm::mat4( 1.0f ), m_rotate_aux.x,
150  SFVEC3F( 1.0f, 0.0f, 0.0f ) );
152 
154  SFVEC3F( 0.0f, 1.0f, 0.0f ) );
156 
158  SFVEC3F( 0.0f, 0.0f, 1.0f ) );
160 
161  m_parametersChanged = true;
162 
164  updateFrustum();
165 }
166 
167 
168 const glm::mat4 CAMERA::GetRotationMatrix() const
169 {
171 }
172 
173 
175 {
176  if( ( m_windowSize.x == 0 ) || ( m_windowSize.y == 0 ) )
177  return;
178 
179  m_frustum.ratio = (float) m_windowSize.x / (float)m_windowSize.y;
180  m_frustum.farD = glm::length( m_camera_pos_init ) * m_maxZoom * 2.0f;
181 
182  switch( m_projectionType )
183  {
184  default:
186 
187  m_frustum.nearD = 0.10f;
188 
189  // Ratio width / height of the window display
190  m_frustum.angle = 45.0f;
191 
192  m_projectionMatrix = glm::perspective( glm::radians( m_frustum.angle ), m_frustum.ratio,
194 
195  m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
196 
197  m_frustum.tang = glm::tan( glm::radians( m_frustum.angle ) * 0.5f );
198 
199  m_focalLen.x = ( (float)m_windowSize.y / (float)m_windowSize.x ) / m_frustum.tang;
200  m_focalLen.y = 1.0f / m_frustum.tang;
201 
206  break;
207 
209 
210  m_frustum.nearD = -m_frustum.farD; // Use a symmetrical clip plane for ortho projection
211 
212  // This formula was found by trial and error
213  const float orthoReductionFactor = glm::length( m_camera_pos_init ) *
214  m_zoom * m_zoom * 0.5f;
215 
216  // Initialize Projection Matrix for Orthographic View
217  m_projectionMatrix = glm::ortho( -m_frustum.ratio * orthoReductionFactor,
218  m_frustum.ratio * orthoReductionFactor,
219  -orthoReductionFactor,
220  orthoReductionFactor,
222 
223  m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
224 
225  m_frustum.nw = orthoReductionFactor * 2.0f * m_frustum.ratio;
226  m_frustum.nh = orthoReductionFactor * 2.0f;
229 
230  break;
231  }
232 
233  if( ( m_windowSize.x > 0 ) && ( m_windowSize.y > 0 ) )
234  {
235  m_scr_nX.resize( m_windowSize.x + 1 );
236  m_scr_nY.resize( m_windowSize.y + 1 );
237 
238  // Precalc X values for camera -> ray generation
239  for( unsigned int x = 0; x < (unsigned int)m_windowSize.x + 1; ++x )
240  {
241  // Converts 0.0 .. 1.0
242  const float xNormalizedDeviceCoordinates = ( ( (float)x + 0.5f ) /
243  (m_windowSize.x - 0.0f) );
244 
245  // Converts -1.0 .. 1.0
246  m_scr_nX[x] = 2.0f * xNormalizedDeviceCoordinates - 1.0f;
247  }
248 
249  // Precalc Y values for camera -> ray generation
250  for( unsigned int y = 0; y < (unsigned int)m_windowSize.y + 1 ; ++y )
251  {
252  // Converts 0.0 .. 1.0
253  const float yNormalizedDeviceCoordinates = ( ( (float)y + 0.5f ) /
254  (m_windowSize.y - 0.0f) );
255 
256  // Converts -1.0 .. 1.0
257  m_scr_nY[y] = 2.0f * yNormalizedDeviceCoordinates - 1.0f;
258  }
259 
260  updateFrustum();
261  }
262 }
263 
264 
266 {
267  // Update matrix and vectors
268  m_viewMatrixInverse = glm::inverse( m_viewMatrix );
269 
270  m_right = glm::normalize( SFVEC3F( m_viewMatrixInverse *
271  glm::vec4( SFVEC3F( 1.0, 0.0, 0.0 ), 0.0 ) ) );
272 
273  m_up = glm::normalize( SFVEC3F( m_viewMatrixInverse *
274  glm::vec4( SFVEC3F( 0.0, 1.0, 0.0 ), 0.0 ) ) );
275 
276  m_dir = glm::normalize( SFVEC3F( m_viewMatrixInverse *
277  glm::vec4( SFVEC3F( 0.0, 0.0, 1.0 ), 0.0 ) ) );
278 
279  m_pos = SFVEC3F( m_viewMatrixInverse * glm::vec4( SFVEC3F( 0.0, 0.0, 0.0 ), 1.0 ) );
280 
281  /*
282  * Frustum is a implementation based on a tutorial by
283  * http://www.lighthouse3d.com/tutorials/view-frustum-culling/
284  */
285 
286  // compute the centers of the near and far planes
289 
290  // compute the 4 corners of the frustum on the near plane
295 
296  // compute the 4 corners of the frustum on the far plane
301 
302  if( ( m_windowSize.x > 0 ) && ( m_windowSize.y > 0 ) )
303  {
304  // Reserve size for precalc values
305  m_right_nX.resize( m_windowSize.x + 1 );
306  m_up_nY.resize( m_windowSize.y + 1 );
307 
308  // Precalc X values for camera -> ray generation
309  const SFVEC3F right_nw = m_right * m_frustum.nw;
310 
311  for( unsigned int x = 0; x < ( (unsigned int) m_windowSize.x + 1 ); ++x )
312  m_right_nX[x] = right_nw * m_scr_nX[x];
313 
314  // Precalc Y values for camera -> ray generation
315  const SFVEC3F up_nh = m_up * m_frustum.nh;
316 
317  for( unsigned int y = 0; y < ( (unsigned int) m_windowSize.y + 1 ); ++y )
318  m_up_nY[y] = up_nh * m_scr_nY[y];
319  }
320 }
321 
322 
323 void CAMERA::MakeRay( const SFVEC2I& aWindowPos, SFVEC3F& aOutOrigin,
324  SFVEC3F& aOutDirection ) const
325 {
326  wxASSERT( aWindowPos.x < m_windowSize.x );
327  wxASSERT( aWindowPos.y < m_windowSize.y );
328 
329  const SFVEC3F up_plus_right = m_up_nY[aWindowPos.y] + m_right_nX[aWindowPos.x];
330 
331  switch( m_projectionType )
332  {
333  default:
335  aOutOrigin = up_plus_right + m_frustum.nc;
336  aOutDirection = glm::normalize( aOutOrigin - m_pos );
337  break;
338 
340  aOutOrigin = up_plus_right * 0.5f + m_frustum.nc;
341  aOutDirection = -m_dir + SFVEC3F( FLT_EPSILON );
342  break;
343  }
344 }
345 
346 
347 void CAMERA::MakeRay( const SFVEC2F& aWindowPos, SFVEC3F& aOutOrigin,
348  SFVEC3F& aOutDirection ) const
349 {
350  wxASSERT( aWindowPos.x < (float)m_windowSize.x );
351  wxASSERT( aWindowPos.y < (float)m_windowSize.y );
352 
353  const SFVEC2F floorWinPos_f = glm::floor( aWindowPos );
354  const SFVEC2I floorWinPos_i = (SFVEC2I)floorWinPos_f;
355  const SFVEC2F relativeWinPos = aWindowPos - floorWinPos_f;
356 
357  // Note: size of vectors m_up and m_right are m_windowSize + 1
358  const SFVEC3F up_plus_right = m_up_nY[floorWinPos_i.y] * (1.0f - relativeWinPos.y) +
359  m_up_nY[floorWinPos_i.y + 1] * relativeWinPos.y +
360  m_right_nX[floorWinPos_i.x] * (1.0f - relativeWinPos.x) +
361  m_right_nX[floorWinPos_i.x + 1] * relativeWinPos.x;
362 
363  switch( m_projectionType )
364  {
365  default:
367  aOutOrigin = up_plus_right + m_frustum.nc;
368  aOutDirection = glm::normalize( aOutOrigin - m_pos );
369  break;
370 
372  aOutOrigin = up_plus_right * 0.5f + m_frustum.nc;
373  aOutDirection = -m_dir + SFVEC3F( FLT_EPSILON );
374  break;
375  }
376 }
377 
378 
379 void CAMERA::MakeRayAtCurrrentMousePosition( SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const
380 {
381  const SFVEC2I windowPos = SFVEC2I( m_lastPosition.x, m_windowSize.y - m_lastPosition.y );
382 
383  if( ( 0 < windowPos.x ) && ( windowPos.x < m_windowSize.x ) &&
384  ( 0 < windowPos.y ) && ( windowPos.y < m_windowSize.y ) )
385  {
386  MakeRay( windowPos, aOutOrigin, aOutDirection );
387  }
388 }
389 
390 
391 const glm::mat4& CAMERA::GetProjectionMatrix() const
392 {
393  return m_projectionMatrix;
394 }
395 
396 
397 const glm::mat4& CAMERA::GetProjectionMatrixInv() const
398 {
399  return m_projectionMatrixInv;
400 }
401 
402 
404 {
405  return -m_camera_pos_init.z * m_frustum.tang;
406 }
407 
408 
410 {
411  m_parametersChanged = true;
412  m_camera_pos.x = 0.0f;
413  m_camera_pos.y = 0.0f;
414 
416  updateFrustum();
417 }
418 
419 
421 {
422  m_camera_pos_t1.x = 0.0f;
423  m_camera_pos_t1.y = 0.0f;
424 }
425 
426 
427 const glm::mat4& CAMERA::GetViewMatrix() const
428 {
429  return m_viewMatrix;
430 }
431 
432 
433 const glm::mat4& CAMERA::GetViewMatrix_Inv() const
434 {
435  return m_viewMatrixInverse;
436 }
437 
438 
439 void CAMERA::SetCurMousePosition( const wxPoint& aNewMousePosition )
440 {
441  m_lastPosition = aNewMousePosition;
442 }
443 
444 
446 {
449  else
451 
453 }
454 
455 
456 bool CAMERA::SetCurWindowSize( const wxSize& aSize )
457 {
458  const SFVEC2I newSize = SFVEC2I( aSize.x, aSize.y );
459 
460  if( m_windowSize != newSize )
461  {
462  m_windowSize = newSize;
464 
465  return true;
466  }
467 
468  return false;
469 }
470 
471 
473 {
474  m_zoom = 1.0f;
475 
477 
480 }
481 
482 bool CAMERA::Zoom( float aFactor )
483 {
484  if( ( m_zoom == m_minZoom && aFactor > 1 ) || ( m_zoom == m_maxZoom && aFactor < 1 )
485  || aFactor == 1 )
486  {
487  return false;
488  }
489 
490  m_zoom /= aFactor;
491 
492  zoomChanged();
493  return true;
494 }
495 
496 
497 bool CAMERA::Zoom_T1( float aFactor )
498 {
499  if( ( m_zoom == m_minZoom && aFactor > 1 ) || ( m_zoom == m_maxZoom && aFactor < 1 )
500  || aFactor == 1 )
501  {
502  return false;
503  }
504 
505  m_zoom_t1 = m_zoom / aFactor;
506 
507  if( m_zoom_t1 < m_minZoom )
509 
510  if( m_zoom_t1 > m_maxZoom )
512 
514 
515  return true;
516 }
517 
518 
519 void CAMERA::RotateX( float aAngleInRadians )
520 {
521  m_rotate_aux.x += aAngleInRadians;
523 }
524 
525 
526 void CAMERA::RotateY( float aAngleInRadians )
527 {
528  m_rotate_aux.y += aAngleInRadians;
530 }
531 
532 
533 void CAMERA::RotateZ( float aAngleInRadians )
534 {
535  m_rotate_aux.z += aAngleInRadians;
537 }
538 
539 
540 void CAMERA::RotateX_T1( float aAngleInRadians )
541 {
542  m_rotate_aux_t1.x += aAngleInRadians;
543 }
544 
545 
546 void CAMERA::RotateY_T1( float aAngleInRadians )
547 {
548  m_rotate_aux_t1.y += aAngleInRadians;
549 }
550 
551 
552 void CAMERA::RotateZ_T1( float aAngleInRadians )
553 {
554  m_rotate_aux_t1.z += aAngleInRadians;
555 }
556 
557 
559 {
563  m_zoom_t0 = m_zoom;
564 
568  m_zoom_t1 = m_zoom;
569 }
570 
571 
572 void CAMERA::Interpolate( float t )
573 {
574  wxASSERT( t >= 0.0f );
575 
576  const float t0 = 1.0f - t;
577 
581  m_zoom = m_zoom_t0 * t0 + m_zoom_t1 * t;
582 
583  m_parametersChanged = true;
584 
587 }
588 
589 
591 {
592  const bool parametersChanged = m_parametersChanged;
593 
594  m_parametersChanged = false;
595 
596  return parametersChanged;
597 }
SFVEC3F fbl
Far Bottom Left.
Definition: camera.h:57
void RotateX(float aAngleInRadians)
Definition: camera.cpp:519
#define DEFAULT_MIN_ZOOM
Definition: camera.cpp:50
SFVEC3F fc
Definition: camera.h:50
#define DEFAULT_MAX_ZOOM
Definition: camera.cpp:51
glm::mat4 m_viewMatrixInverse
Definition: camera.h:289
SFVEC3F m_rotate_aux
Stores the rotation angle auxiliary.
Definition: camera.h:313
void RotateZ_T1(float aAngleInRadians)
Definition: camera.cpp:552
float m_zoom
3D zoom value – Z-distance is scaled by it
Definition: camera.h:266
SFVEC3F ftl
Far Top Left.
Definition: camera.h:55
bool Zoom_T1(float aFactor)
Definition: camera.cpp:497
const glm::mat4 & GetViewMatrix_Inv() const
Definition: camera.cpp:433
bool m_parametersChanged
Set to true if any of the parameters in the camera was changed.
Definition: camera.h:335
CAMERA(float aInitialDistance)
Initialize a camera.
Definition: camera.cpp:54
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:391
Define an abstract camera.
SFVEC3F m_lookat_pos_t0
Definition: camera.h:309
SFVEC3F m_dir
Definition: camera.h:298
SFVEC3F m_lookat_pos_t1
Definition: camera.h:310
void RotateY_T1(float aAngleInRadians)
Definition: camera.cpp:546
virtual void Reset()
Reset the camera to initial state.
Definition: camera.cpp:71
float m_zoom_t1
Definition: camera.h:268
SFVEC3F nbr
Near Bottom Right.
Definition: camera.h:54
SFVEC3F m_rotate_aux_t1
Definition: camera.h:315
float ratio
Definition: camera.h:59
const glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:168
glm::ivec2 SFVEC2I
Definition: xv3d_types.h:39
SFVEC2F m_focalLen
Definition: camera.h:301
std::vector< SFVEC3F > m_right_nX
Precalc values array used to calc ray for each pixel, for X and Y axis of each new camera position.
Definition: camera.h:329
glm::mat4 m_viewMatrix
Definition: camera.h:288
float nw
Definition: camera.h:60
SFVEC3F m_up
Definition: camera.h:297
float fw
Definition: camera.h:60
float tang
Definition: camera.h:59
SFVEC3F fbr
Far Bottom Right.
Definition: camera.h:58
float fh
Definition: camera.h:60
SFVEC3F m_camera_pos_init
Definition: camera.h:303
SFVEC3F m_pos
Definition: camera.h:299
float GetCameraMinDimension() const
Definition: camera.cpp:403
void rebuildProjection()
Definition: camera.cpp:174
PROJECTION_TYPE m_projectionType
Definition: camera.h:292
SFVEC3F nbl
Near Bottom Left.
Definition: camera.h:53
wxPoint m_lastPosition
The last mouse position in the screen.
Definition: camera.h:284
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
SFVEC3F m_camera_pos_t0
Definition: camera.h:305
void updateRotationMatrix()
Definition: camera.cpp:147
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:427
void RotateY(float aAngleInRadians)
Definition: camera.cpp:526
void normalise2PI(float &aAngle)
Definition: camera.cpp:34
void MakeRay(const SFVEC2I &aWindowPos, SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on a windows screen position.
Definition: camera.cpp:323
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:439
float farD
Definition: camera.h:59
void updateViewMatrix()
Definition: camera.cpp:139
SFVEC3F m_right
Definition: camera.h:296
virtual void Reset_T1()
Definition: camera.cpp:103
void zoomChanged()
Definition: camera.cpp:124
const glm::mat4 & GetProjectionMatrixInv() const
Definition: camera.cpp:397
float m_zoom_t0
Definition: camera.h:267
glm::mat4 m_rotationMatrix
Definition: camera.h:286
void MakeRayAtCurrrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on the latest mouse position.
Definition: camera.cpp:379
float m_maxZoom
Definition: camera.h:274
SFVEC2I m_windowSize
The window size that this camera is working.
Definition: camera.h:279
SFVEC3F m_camera_pos_t1
Definition: camera.h:306
bool Zoom(float aFactor)
Definition: camera.cpp:482
SFVEC3F m_lookat_pos
Definition: camera.h:308
glm::mat4 m_projectionMatrix
Definition: camera.h:290
CAMERA_INTERPOLATION m_interpolation_mode
Definition: camera.h:317
SFVEC3F m_camera_pos
Definition: camera.h:304
SFVEC3F m_board_lookat_pos_init
Default boardlookat position (the board center).
Definition: camera.h:311
float nearD
Definition: camera.h:59
void updateFrustum()
Definition: camera.cpp:265
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:456
float m_minZoom
Possible 3D zoom range.
Definition: camera.h:273
std::vector< SFVEC3F > m_up_nY
Definition: camera.h:330
virtual void SetT0_and_T1_current_T()
This will set T0 and T1 with the current values.
Definition: camera.cpp:558
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
Definition: camera.h:344
void ResetXYpos()
Definition: camera.cpp:409
CAMERA_FRUSTUM m_frustum
Definition: camera.h:294
virtual void Interpolate(float t)
It will update the matrix to interpolate between T0 and T1 values.
Definition: camera.cpp:572
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
SFVEC3F ntl
Near Top Left.
Definition: camera.h:51
void RotateZ(float aAngleInRadians)
Definition: camera.cpp:533
SFVEC3F ntr
Near Top Right.
Definition: camera.h:52
void ToggleProjection()
Definition: camera.cpp:445
glm::mat4 m_rotationMatrixAux
Definition: camera.h:287
bool ParametersChanged()
Definition: camera.cpp:590
std::vector< float > m_scr_nY
Definition: camera.h:323
void ResetXYpos_T1()
Definition: camera.cpp:420
void ZoomReset()
Definition: camera.cpp:472
std::vector< float > m_scr_nX
Precalc values array used to calc ray for each pixel (constant for the same window size).
Definition: camera.h:322
SFVEC3F nc
Definition: camera.h:49
glm::mat4 m_projectionMatrixInv
Definition: camera.h:291
float nh
Definition: camera.h:60
SFVEC3F m_rotate_aux_t0
Definition: camera.h:314
float angle
Definition: camera.h:59
SFVEC3F ftr
Far Top Right.
Definition: camera.h:56
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:540