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