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 <mrluzeiro@ua.pt>
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 MIN_ZOOM 0.10f
51 #define MAX_ZOOM 1.25f
52 
53 
54 CAMERA::CAMERA( float aRangeScale )
55 {
56  wxLogTrace( m_logTrace, wxT( "CAMERA::CAMERA" ) );
57 
58  m_range_scale = aRangeScale;
59  m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -(aRangeScale * 2.0f ) );
61  m_windowSize = SFVEC2I( 0, 0 );
64 
65  Reset();
66 }
67 
68 
70 {
71  m_parametersChanged = true;
72  m_projectionMatrix = glm::mat4( 1.0f );
73  m_projectionMatrixInv = glm::mat4( 1.0f );
74  m_rotationMatrix = glm::mat4( 1.0f );
75  m_rotationMatrixAux = glm::mat4( 1.0f );
76  m_lastPosition = wxPoint( 0, 0 );
77 
78  m_zoom = 1.0f;
79  m_zoom_t0 = 1.0f;
80  m_zoom_t1 = 1.0f;
87 
88  m_rotate_aux = SFVEC3F( 0.0f );
89  m_rotate_aux_t0 = SFVEC3F( 0.0f );
90  m_rotate_aux_t1 = SFVEC3F( 0.0f );
91 
94  m_viewMatrixInverse = glm::inverse( m_viewMatrix );
95  m_scr_nX.clear();
96  m_scr_nY.clear();
98 }
99 
100 
102 {
104  m_zoom_t1 = 1.0f;
105  m_rotate_aux_t1 = SFVEC3F( 0.0f );
107 
108  // Since 0 = 2pi, we want to reset the angle to be the closest
109  // one to where we currently are. That ensures that we rotate
110  // the board around the smallest distance getting there.
111  if( m_rotate_aux_t0.x > M_PI )
112  m_rotate_aux_t1.x = static_cast<float>( 2.0f * M_PI );
113 
114  if( m_rotate_aux_t0.y > M_PI )
115  m_rotate_aux_t1.y = static_cast<float>( 2.0f * M_PI );
116 
117  if( m_rotate_aux_t0.z > M_PI )
118  m_rotate_aux_t1.z = static_cast<float>( 2.0f * M_PI );
119 }
120 
121 
123 {
124  m_viewMatrix = glm::translate( glm::mat4( 1.0f ), m_camera_pos ) *
126  glm::translate( glm::mat4( 1.0f ), -m_lookat_pos );
127 }
128 
129 
131 {
132  m_rotationMatrixAux = glm::rotate( glm::mat4( 1.0f ), m_rotate_aux.x,
133  SFVEC3F( 1.0f, 0.0f, 0.0f ) );
135 
137  SFVEC3F( 0.0f, 1.0f, 0.0f ) );
139 
141  SFVEC3F( 0.0f, 0.0f, 1.0f ) );
143 
144  m_parametersChanged = true;
145 
147  updateFrustum();
148 }
149 
150 
151 const glm::mat4 CAMERA::GetRotationMatrix() const
152 {
154 }
155 
156 
158 {
159  if( ( m_windowSize.x == 0 ) || ( m_windowSize.y == 0 ) )
160  return;
161 
162  m_frustum.ratio = (float) m_windowSize.x / (float)m_windowSize.y;
163 
164  // Consider that we can render double the length multiplied by the 2/sqrt(2)
165  m_frustum.farD = glm::length( m_camera_pos_init ) * 2.0f * ( 2.0f * sqrtf( 2.0f ) );
166 
167  switch( m_projectionType )
168  {
169  default:
171 
172  m_frustum.nearD = 0.10f;
173 
174  // Ratio width / height of the window display
175  m_frustum.angle = 45.0f * m_zoom;
176 
177  m_projectionMatrix = glm::perspective( glm::radians( m_frustum.angle ), m_frustum.ratio,
179 
180  m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
181 
182  m_frustum.tang = glm::tan( glm::radians( m_frustum.angle ) * 0.5f );
183 
184  m_focalLen.x = ( (float)m_windowSize.y / (float)m_windowSize.x ) / m_frustum.tang;
185  m_focalLen.y = 1.0f / m_frustum.tang;
186 
191  break;
192 
194 
195  m_frustum.nearD = -m_frustum.farD; // Use a symmetrical clip plane for ortho projection
196 
197  // This formula was found by trial and error
198  const float orthoReductionFactor = glm::length( m_camera_pos_init ) *
199  m_zoom * m_zoom * 0.5f;
200 
201  // Initialize Projection Matrix for Orthographic View
202  m_projectionMatrix = glm::ortho( -m_frustum.ratio * orthoReductionFactor,
203  m_frustum.ratio * orthoReductionFactor,
204  -orthoReductionFactor,
205  orthoReductionFactor,
207 
208  m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
209 
210  m_frustum.nw = orthoReductionFactor * 2.0f * m_frustum.ratio;
211  m_frustum.nh = orthoReductionFactor * 2.0f;
214 
215  break;
216  }
217 
218  if( ( m_windowSize.x > 0 ) && ( m_windowSize.y > 0 ) )
219  {
220  m_scr_nX.resize( m_windowSize.x + 1 );
221  m_scr_nY.resize( m_windowSize.y + 1 );
222 
223  // Precalc X values for camera -> ray generation
224  for( unsigned int x = 0; x < (unsigned int)m_windowSize.x + 1; ++x )
225  {
226  // Converts 0.0 .. 1.0
227  const float xNormalizedDeviceCoordinates = ( ( (float)x + 0.5f ) /
228  (m_windowSize.x - 0.0f) );
229 
230  // Converts -1.0 .. 1.0
231  m_scr_nX[x] = 2.0f * xNormalizedDeviceCoordinates - 1.0f;
232  }
233 
234  // Precalc Y values for camera -> ray generation
235  for( unsigned int y = 0; y < (unsigned int)m_windowSize.y + 1 ; ++y )
236  {
237  // Converts 0.0 .. 1.0
238  const float yNormalizedDeviceCoordinates = ( ( (float)y + 0.5f ) /
239  (m_windowSize.y - 0.0f) );
240 
241  // Converts -1.0 .. 1.0
242  m_scr_nY[y] = 2.0f * yNormalizedDeviceCoordinates - 1.0f;
243  }
244 
245  updateFrustum();
246  }
247 }
248 
249 
251 {
252  // Update matrix and vectors
253  m_viewMatrixInverse = glm::inverse( m_viewMatrix );
254 
255  m_right = glm::normalize( SFVEC3F( m_viewMatrixInverse *
256  glm::vec4( SFVEC3F( 1.0, 0.0, 0.0 ), 0.0 ) ) );
257 
258  m_up = glm::normalize( SFVEC3F( m_viewMatrixInverse *
259  glm::vec4( SFVEC3F( 0.0, 1.0, 0.0 ), 0.0 ) ) );
260 
261  m_dir = glm::normalize( SFVEC3F( m_viewMatrixInverse *
262  glm::vec4( SFVEC3F( 0.0, 0.0, 1.0 ), 0.0 ) ) );
263 
264  m_pos = SFVEC3F( m_viewMatrixInverse * glm::vec4( SFVEC3F( 0.0, 0.0, 0.0 ), 1.0 ) );
265 
266  /*
267  * Frustum is a implementation based on a tutorial by
268  * http://www.lighthouse3d.com/tutorials/view-frustum-culling/
269  */
270 
271  // compute the centers of the near and far planes
274 
275  // compute the 4 corners of the frustum on the near plane
280 
281  // compute the 4 corners of the frustum on the far plane
286 
287  if( ( m_windowSize.x > 0 ) && ( m_windowSize.y > 0 ) )
288  {
289  // Reserve size for precalc values
290  m_right_nX.resize( m_windowSize.x + 1 );
291  m_up_nY.resize( m_windowSize.y + 1 );
292 
293  // Precalc X values for camera -> ray generation
294  const SFVEC3F right_nw = m_right * m_frustum.nw;
295 
296  for( unsigned int x = 0; x < ( (unsigned int) m_windowSize.x + 1 ); ++x )
297  m_right_nX[x] = right_nw * m_scr_nX[x];
298 
299  // Precalc Y values for camera -> ray generation
300  const SFVEC3F up_nh = m_up * m_frustum.nh;
301 
302  for( unsigned int y = 0; y < ( (unsigned int) m_windowSize.y + 1 ); ++y )
303  m_up_nY[y] = up_nh * m_scr_nY[y];
304  }
305 }
306 
307 
308 void CAMERA::MakeRay( const SFVEC2I& aWindowPos, SFVEC3F& aOutOrigin,
309  SFVEC3F& aOutDirection ) const
310 {
311  wxASSERT( aWindowPos.x < m_windowSize.x );
312  wxASSERT( aWindowPos.y < m_windowSize.y );
313 
314  const SFVEC3F up_plus_right = m_up_nY[aWindowPos.y] + m_right_nX[aWindowPos.x];
315 
316  switch( m_projectionType )
317  {
318  default:
320  aOutOrigin = up_plus_right + m_frustum.nc;
321  aOutDirection = glm::normalize( aOutOrigin - m_pos );
322  break;
323 
325  aOutOrigin = up_plus_right * 0.5f + m_frustum.nc;
326  aOutDirection = -m_dir + SFVEC3F( FLT_EPSILON );
327  break;
328  }
329 }
330 
331 
332 void CAMERA::MakeRay( const SFVEC2F& aWindowPos, SFVEC3F& aOutOrigin,
333  SFVEC3F& aOutDirection ) const
334 {
335  wxASSERT( aWindowPos.x < (float)m_windowSize.x );
336  wxASSERT( aWindowPos.y < (float)m_windowSize.y );
337 
338  const SFVEC2F floorWinPos_f = glm::floor( aWindowPos );
339  const SFVEC2I floorWinPos_i = (SFVEC2I)floorWinPos_f;
340  const SFVEC2F relativeWinPos = aWindowPos - floorWinPos_f;
341 
342  // Note: size of vectors m_up and m_right are m_windowSize + 1
343  const SFVEC3F up_plus_right = m_up_nY[floorWinPos_i.y] * (1.0f - relativeWinPos.y) +
344  m_up_nY[floorWinPos_i.y + 1] * relativeWinPos.y +
345  m_right_nX[floorWinPos_i.x] * (1.0f - relativeWinPos.x) +
346  m_right_nX[floorWinPos_i.x + 1] * relativeWinPos.x;
347 
348  switch( m_projectionType )
349  {
350  default:
352  aOutOrigin = up_plus_right + m_frustum.nc;
353  aOutDirection = glm::normalize( aOutOrigin - m_pos );
354  break;
355 
357  aOutOrigin = up_plus_right * 0.5f + m_frustum.nc;
358  aOutDirection = -m_dir + SFVEC3F( FLT_EPSILON );
359  break;
360  }
361 }
362 
363 
364 void CAMERA::MakeRayAtCurrrentMousePosition( SFVEC3F& aOutOrigin, SFVEC3F& aOutDirection ) const
365 {
366  const SFVEC2I windowPos = SFVEC2I( m_lastPosition.x, m_windowSize.y - m_lastPosition.y );
367 
368  if( ( 0 < windowPos.x ) && ( windowPos.x < m_windowSize.x ) &&
369  ( 0 < windowPos.y ) && ( windowPos.y < m_windowSize.y ) )
370  {
371  MakeRay( windowPos, aOutOrigin, aOutDirection );
372  }
373 }
374 
375 
376 const glm::mat4& CAMERA::GetProjectionMatrix() const
377 {
378  return m_projectionMatrix;
379 }
380 
381 
382 const glm::mat4& CAMERA::GetProjectionMatrixInv() const
383 {
384  return m_projectionMatrixInv;
385 }
386 
387 
389 {
390  m_parametersChanged = true;
391  m_camera_pos.x = 0.0f;
392  m_camera_pos.y = 0.0f;
393 
395  updateFrustum();
396 }
397 
398 
400 {
401  m_camera_pos_t1.x = 0.0f;
402  m_camera_pos_t1.y = 0.0f;
403 }
404 
405 
406 const glm::mat4& CAMERA::GetViewMatrix() const
407 {
408  return m_viewMatrix;
409 }
410 
411 
412 const glm::mat4& CAMERA::GetViewMatrix_Inv() const
413 {
414  return m_viewMatrixInverse;
415 }
416 
417 
418 void CAMERA::SetCurMousePosition( const wxPoint& aNewMousePosition )
419 {
420  m_lastPosition = aNewMousePosition;
421 }
422 
423 
425 {
428  else
430 
432 }
433 
434 
435 bool CAMERA::SetCurWindowSize( const wxSize& aSize )
436 {
437  const SFVEC2I newSize = SFVEC2I( aSize.x, aSize.y );
438 
439  if( m_windowSize != newSize )
440  {
441  m_windowSize = newSize;
443 
444  return true;
445  }
446 
447  return false;
448 }
449 
450 
452 {
453  m_zoom = 1.0f;
454 
456 
459 }
460 
461 bool CAMERA::Zoom( float aFactor )
462 {
463  if( ( m_zoom == MIN_ZOOM && aFactor > 1 ) || ( m_zoom == MAX_ZOOM && aFactor < 1 )
464  || aFactor == 1 )
465  return false;
466 
467  m_zoom /= aFactor;
468 
469  if( m_zoom <= MIN_ZOOM )
470  m_zoom = MIN_ZOOM;
471 
472  if( m_zoom >= MAX_ZOOM )
473  m_zoom = MAX_ZOOM;
474 
476 
479 
480  return true;
481 }
482 
483 
484 bool CAMERA::Zoom_T1( float aFactor )
485 {
486  if( ( m_zoom == MIN_ZOOM && aFactor > 1 ) || ( m_zoom == MAX_ZOOM && aFactor < 1 )
487  || aFactor == 1 )
488  return false;
489 
490  m_zoom_t1 = m_zoom / aFactor;
491 
492  if( m_zoom_t1 < MIN_ZOOM )
494 
495  if( m_zoom_t1 > MAX_ZOOM )
497 
499 
500  return true;
501 }
502 
503 
504 float CAMERA::ZoomGet() const
505 {
506  return m_zoom;
507 }
508 
509 
510 void CAMERA::RotateX( float aAngleInRadians )
511 {
512  m_rotate_aux.x += aAngleInRadians;
514 }
515 
516 
517 void CAMERA::RotateY( float aAngleInRadians )
518 {
519  m_rotate_aux.y += aAngleInRadians;
521 }
522 
523 
524 void CAMERA::RotateZ( float aAngleInRadians )
525 {
526  m_rotate_aux.z += aAngleInRadians;
528 }
529 
530 
531 void CAMERA::RotateX_T1( float aAngleInRadians )
532 {
533  m_rotate_aux_t1.x += aAngleInRadians;
534 }
535 
536 
537 void CAMERA::RotateY_T1( float aAngleInRadians )
538 {
539  m_rotate_aux_t1.y += aAngleInRadians;
540 }
541 
542 
543 void CAMERA::RotateZ_T1( float aAngleInRadians )
544 {
545  m_rotate_aux_t1.z += aAngleInRadians;
546 }
547 
548 
550 {
554  m_zoom_t0 = m_zoom;
555 
559  m_zoom_t1 = m_zoom;
560 }
561 
562 
563 void CAMERA::Interpolate( float t )
564 {
565  wxASSERT( t >= 0.0f );
566 
567  const float t0 = 1.0f - t;
568 
572  m_zoom = m_zoom_t0 * t0 + m_zoom_t1 * t;
573 
574  m_parametersChanged = true;
575 
578 }
579 
580 
582 {
583  const bool parametersChanged = m_parametersChanged;
584 
585  m_parametersChanged = false;
586 
587  return parametersChanged;
588 }
SFVEC3F fbl
Far Bottom Left.
Definition: camera.h:57
void RotateX(float aAngleInRadians)
Definition: camera.cpp:510
#define MIN_ZOOM
Definition: camera.cpp:50
SFVEC3F fc
Definition: camera.h:50
glm::mat4 m_viewMatrixInverse
Definition: camera.h:273
SFVEC3F m_rotate_aux
Stores the rotation angle auxiliary.
Definition: camera.h:297
void RotateZ_T1(float aAngleInRadians)
Definition: camera.cpp:543
float m_zoom
3D zoom value (Min 0.0 ...
Definition: camera.h:256
SFVEC3F ftl
Far Top Left.
Definition: camera.h:55
bool Zoom_T1(float aFactor)
Definition: camera.cpp:484
const glm::mat4 & GetViewMatrix_Inv() const
Definition: camera.cpp:412
bool m_parametersChanged
Set to true if any of the parameters in the camera was changed.
Definition: camera.h:319
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:376
Define an abstract camera.
SFVEC3F m_lookat_pos_t0
Definition: camera.h:293
SFVEC3F m_dir
Definition: camera.h:282
SFVEC3F m_lookat_pos_t1
Definition: camera.h:294
void RotateY_T1(float aAngleInRadians)
Definition: camera.cpp:537
#define MAX_ZOOM
Definition: camera.cpp:51
virtual void Reset()
Reset the camera to initial state.
Definition: camera.cpp:69
float m_zoom_t1
Definition: camera.h:258
SFVEC3F nbr
Near Bottom Right.
Definition: camera.h:54
SFVEC3F m_rotate_aux_t1
Definition: camera.h:299
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:151
glm::ivec2 SFVEC2I
Definition: xv3d_types.h:39
SFVEC2F m_focalLen
Definition: camera.h:285
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:313
glm::mat4 m_viewMatrix
Definition: camera.h:272
float nw
Definition: camera.h:60
SFVEC3F m_up
Definition: camera.h:281
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:287
SFVEC3F m_pos
Definition: camera.h:283
void rebuildProjection()
Definition: camera.cpp:157
PROJECTION_TYPE m_projectionType
Definition: camera.h:276
SFVEC3F nbl
Near Bottom Left.
Definition: camera.h:53
wxPoint m_lastPosition
The last mouse position in the screen.
Definition: camera.h:268
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
SFVEC3F m_camera_pos_t0
Definition: camera.h:289
void updateRotationMatrix()
Definition: camera.cpp:130
CAMERA(float aRangeScale)
Initialize a camera.
Definition: camera.cpp:54
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:406
void RotateY(float aAngleInRadians)
Definition: camera.cpp:517
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:308
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:418
float farD
Definition: camera.h:59
void updateViewMatrix()
Definition: camera.cpp:122
SFVEC3F m_right
Definition: camera.h:280
virtual void Reset_T1()
Definition: camera.cpp:101
const glm::mat4 & GetProjectionMatrixInv() const
Definition: camera.cpp:382
float m_zoom_t0
Definition: camera.h:257
glm::mat4 m_rotationMatrix
Definition: camera.h:270
void MakeRayAtCurrrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on the latest mouse position.
Definition: camera.cpp:364
SFVEC2I m_windowSize
The window size that this camera is working.
Definition: camera.h:263
SFVEC3F m_camera_pos_t1
Definition: camera.h:290
float ZoomGet() const
Definition: camera.cpp:504
bool Zoom(float aFactor)
Definition: camera.cpp:461
SFVEC3F m_lookat_pos
Definition: camera.h:292
glm::mat4 m_projectionMatrix
Definition: camera.h:274
CAMERA_INTERPOLATION m_interpolation_mode
Definition: camera.h:301
SFVEC3F m_camera_pos
Definition: camera.h:288
SFVEC3F m_board_lookat_pos_init
Default boardlookat position (the board center).
Definition: camera.h:295
float nearD
Definition: camera.h:59
void updateFrustum()
Definition: camera.cpp:250
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:435
std::vector< SFVEC3F > m_up_nY
Definition: camera.h:314
virtual void SetT0_and_T1_current_T()
This will set T0 and T1 with the current values.
Definition: camera.cpp:549
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
Definition: camera.h:328
void ResetXYpos()
Definition: camera.cpp:388
CAMERA_FRUSTUM m_frustum
Definition: camera.h:278
virtual void Interpolate(float t)
It will update the matrix to interpolate between T0 and T1 values.
Definition: camera.cpp:563
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
SFVEC3F ntl
Near Top Left.
Definition: camera.h:51
float m_range_scale
The nominal range expected to be used in the camera.
Definition: camera.h:251
void RotateZ(float aAngleInRadians)
Definition: camera.cpp:524
SFVEC3F ntr
Near Top Right.
Definition: camera.h:52
void ToggleProjection()
Definition: camera.cpp:424
glm::mat4 m_rotationMatrixAux
Definition: camera.h:271
bool ParametersChanged()
Definition: camera.cpp:581
std::vector< float > m_scr_nY
Definition: camera.h:307
void ResetXYpos_T1()
Definition: camera.cpp:399
void ZoomReset()
Definition: camera.cpp:451
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:306
SFVEC3F nc
Definition: camera.h:49
glm::mat4 m_projectionMatrixInv
Definition: camera.h:275
float nh
Definition: camera.h:60
SFVEC3F m_rotate_aux_t0
Definition: camera.h:298
float angle
Definition: camera.h:59
SFVEC3F ftr
Far Top Right.
Definition: camera.h:56
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:531