You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

574 lines
16 KiB

  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) 1992-2017 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. /**
  25. * @file ccamera.cpp
  26. * @brief
  27. */
  28. #include "ccamera.h"
  29. #include <wx/log.h>
  30. /**
  31. * Trace mask used to enable or disable the trace output of this class.
  32. * The debug output can be turned on by setting the WXTRACE environment variable to
  33. * "KI_TRACE_CCAMERA". See the wxWidgets documentation on wxLogTrace for
  34. * more information.
  35. */
  36. const wxChar *CCAMERA::m_logTrace = wxT( "KI_TRACE_CCAMERA" );
  37. #define MIN_ZOOM 0.10f
  38. #define MAX_ZOOM 1.25f
  39. CCAMERA::CCAMERA( float aRangeScale )
  40. {
  41. wxLogTrace( m_logTrace, wxT( "CCAMERA::CCAMERA" ) );
  42. m_range_scale = aRangeScale;
  43. m_camera_pos_init = SFVEC3F( 0.0f, 0.0f, -(aRangeScale * 2.0f ) );
  44. m_board_lookat_pos_init = SFVEC3F( 0.0f );
  45. m_windowSize = SFVEC2I( 0, 0 );
  46. m_projectionType = PROJECTION_PERSPECTIVE;
  47. m_interpolation_mode = INTERPOLATION_BEZIER;
  48. Reset();
  49. }
  50. void CCAMERA::Reset()
  51. {
  52. m_parametersChanged = true;
  53. m_projectionMatrix = glm::mat4( 1.0f );
  54. m_projectionMatrixInv = glm::mat4( 1.0f );
  55. m_rotationMatrix = glm::mat4( 1.0f );
  56. m_rotationMatrixAux = glm::mat4( 1.0f );
  57. m_lastPosition = wxPoint( 0, 0 );
  58. m_zoom = 1.0f;
  59. m_zoom_t0 = 1.0f;
  60. m_zoom_t1 = 1.0f;
  61. m_camera_pos = m_camera_pos_init;
  62. m_camera_pos_t0 = m_camera_pos_init;
  63. m_camera_pos_t1 = m_camera_pos_init;
  64. m_lookat_pos = m_board_lookat_pos_init;
  65. m_lookat_pos_t0 = m_board_lookat_pos_init;
  66. m_lookat_pos_t1 = m_board_lookat_pos_init;
  67. m_rotate_aux = SFVEC3F( 0.0f );
  68. m_rotate_aux_t0 = SFVEC3F( 0.0f );
  69. m_rotate_aux_t1 = SFVEC3F( 0.0f );
  70. updateRotationMatrix();
  71. updateViewMatrix();
  72. m_viewMatrixInverse = glm::inverse( m_viewMatrix );
  73. m_scr_nX.clear();
  74. m_scr_nY.clear();
  75. rebuildProjection();
  76. }
  77. void CCAMERA::Reset_T1()
  78. {
  79. m_camera_pos_t1 = m_camera_pos_init;
  80. m_zoom_t1 = 1.0f;
  81. m_rotate_aux_t1 = SFVEC3F( 0.0f );
  82. m_lookat_pos_t1 = m_board_lookat_pos_init;
  83. }
  84. void CCAMERA::updateViewMatrix()
  85. {
  86. m_viewMatrix = glm::translate( glm::mat4( 1.0f ), m_camera_pos ) *
  87. m_rotationMatrix * m_rotationMatrixAux *
  88. glm::translate( glm::mat4( 1.0f ), -m_lookat_pos );
  89. }
  90. void CCAMERA::updateRotationMatrix()
  91. {
  92. m_rotationMatrixAux = glm::rotate( glm::mat4( 1.0f ),
  93. m_rotate_aux.x,
  94. SFVEC3F( 1.0f, 0.0f, 0.0f ) );
  95. m_rotationMatrixAux = glm::rotate( m_rotationMatrixAux,
  96. m_rotate_aux.y,
  97. SFVEC3F( 0.0f, 1.0f, 0.0f ) );
  98. m_rotationMatrixAux = glm::rotate( m_rotationMatrixAux,
  99. m_rotate_aux.z,
  100. SFVEC3F( 0.0f, 0.0f, 1.0f ) );
  101. m_parametersChanged = true;
  102. updateViewMatrix();
  103. updateFrustum();
  104. }
  105. const glm::mat4 CCAMERA::GetRotationMatrix() const
  106. {
  107. return m_rotationMatrix * m_rotationMatrixAux;
  108. }
  109. void CCAMERA::rebuildProjection()
  110. {
  111. if( (m_windowSize.x == 0) ||
  112. (m_windowSize.y == 0) )
  113. return;
  114. m_frustum.ratio = (float) m_windowSize.x / (float)m_windowSize.y;
  115. // Consider that we can render double the length multiplied by the 2/sqrt(2)
  116. //
  117. m_frustum.farD = glm::length( m_camera_pos_init ) * 2.0f * ( 2.0f * sqrtf( 2.0f ) );
  118. switch( m_projectionType )
  119. {
  120. default:
  121. case PROJECTION_PERSPECTIVE:
  122. m_frustum.nearD = 0.10f;
  123. // Ratio width / height of the window display
  124. m_frustum.angle = 45.0f * m_zoom;
  125. m_projectionMatrix = glm::perspective( glm::radians( m_frustum.angle ),
  126. m_frustum.ratio,
  127. m_frustum.nearD,
  128. m_frustum.farD );
  129. m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
  130. m_frustum.tang = glm::tan( glm::radians( m_frustum.angle ) * 0.5f ) ;
  131. m_focalLen.x = ( (float)m_windowSize.y / (float)m_windowSize.x ) / m_frustum.tang;
  132. m_focalLen.y = 1.0f / m_frustum.tang;
  133. m_frustum.nh = m_frustum.nearD * m_frustum.tang;
  134. m_frustum.nw = m_frustum.nh * m_frustum.ratio;
  135. m_frustum.fh = m_frustum.farD * m_frustum.tang;
  136. m_frustum.fw = m_frustum.fh * m_frustum.ratio;
  137. break;
  138. case PROJECTION_ORTHO:
  139. m_frustum.nearD = -m_frustum.farD; // Use a symmetrical clip plane for ortho projection
  140. const float orthoReductionFactor = m_zoom / 75.0f;
  141. // Initialize Projection Matrix for Ortographic View
  142. m_projectionMatrix = glm::ortho( -m_windowSize.x * orthoReductionFactor,
  143. m_windowSize.x * orthoReductionFactor,
  144. -m_windowSize.y * orthoReductionFactor,
  145. m_windowSize.y * orthoReductionFactor,
  146. m_frustum.nearD, m_frustum.farD );
  147. m_projectionMatrixInv = glm::inverse( m_projectionMatrix );
  148. m_frustum.nw = m_windowSize.x * orthoReductionFactor * 2.0f;
  149. m_frustum.nh = m_windowSize.y * orthoReductionFactor * 2.0f;
  150. m_frustum.fw = m_frustum.nw;
  151. m_frustum.fh = m_frustum.nh;
  152. break;
  153. }
  154. if ( (m_windowSize.x > 0) &&
  155. (m_windowSize.y > 0) )
  156. {
  157. m_scr_nX.resize( m_windowSize.x + 1 );
  158. m_scr_nY.resize( m_windowSize.y + 1 );
  159. // Precalc X values for camera -> ray generation
  160. for( unsigned int x = 0; x < (unsigned int)m_windowSize.x + 1; ++x )
  161. {
  162. // Converts 0.0 .. 1.0
  163. const float xNormalizedDeviceCoordinates = ( ( (float)x + 0.5f ) /
  164. (m_windowSize.x - 0.0f) );
  165. // Converts -1.0 .. 1.0
  166. m_scr_nX[x] = 2.0f * xNormalizedDeviceCoordinates - 1.0f;
  167. }
  168. // Precalc Y values for camera -> ray generation
  169. for( unsigned int y = 0; y < (unsigned int)m_windowSize.y + 1 ; ++y )
  170. {
  171. // Converts 0.0 .. 1.0
  172. const float yNormalizedDeviceCoordinates = ( ( (float)y + 0.5f ) /
  173. (m_windowSize.y - 0.0f) );
  174. // Converts -1.0 .. 1.0
  175. m_scr_nY[y] = 2.0f * yNormalizedDeviceCoordinates - 1.0f;
  176. }
  177. updateFrustum();
  178. }
  179. }
  180. void CCAMERA::updateFrustum()
  181. {
  182. // Update matrix and vectors
  183. m_viewMatrixInverse = glm::inverse( m_viewMatrix );
  184. m_right = glm::normalize( SFVEC3F( m_viewMatrixInverse *
  185. glm::vec4( SFVEC3F( 1.0, 0.0, 0.0 ), 0.0 ) ) );
  186. m_up = glm::normalize( SFVEC3F( m_viewMatrixInverse *
  187. glm::vec4( SFVEC3F( 0.0, 1.0, 0.0 ), 0.0 ) ) );
  188. m_dir = glm::normalize( SFVEC3F( m_viewMatrixInverse *
  189. glm::vec4( SFVEC3F( 0.0, 0.0, 1.0 ), 0.0 ) ) );
  190. m_pos = SFVEC3F( m_viewMatrixInverse * glm::vec4( SFVEC3F( 0.0, 0.0, 0.0 ), 1.0 ) );
  191. /*
  192. * Frustum is a implementation based on a tutorial by
  193. * http://www.lighthouse3d.com/tutorials/view-frustum-culling/
  194. */
  195. // compute the centers of the near and far planes
  196. m_frustum.nc = m_pos - m_dir * m_frustum.nearD;
  197. m_frustum.fc = m_pos - m_dir * m_frustum.farD;
  198. // compute the 4 corners of the frustum on the near plane
  199. m_frustum.ntl = m_frustum.nc + m_up * m_frustum.nh - m_right * m_frustum.nw;
  200. m_frustum.ntr = m_frustum.nc + m_up * m_frustum.nh + m_right * m_frustum.nw;
  201. m_frustum.nbl = m_frustum.nc - m_up * m_frustum.nh - m_right * m_frustum.nw;
  202. m_frustum.nbr = m_frustum.nc - m_up * m_frustum.nh + m_right * m_frustum.nw;
  203. // compute the 4 corners of the frustum on the far plane
  204. m_frustum.ftl = m_frustum.fc + m_up * m_frustum.fh - m_right * m_frustum.fw;
  205. m_frustum.ftr = m_frustum.fc + m_up * m_frustum.fh + m_right * m_frustum.fw;
  206. m_frustum.fbl = m_frustum.fc - m_up * m_frustum.fh - m_right * m_frustum.fw;
  207. m_frustum.fbr = m_frustum.fc - m_up * m_frustum.fh + m_right * m_frustum.fw;
  208. if ( (m_windowSize.x > 0) &&
  209. (m_windowSize.y > 0) )
  210. {
  211. // Reserve size for precalc values
  212. m_right_nX.resize( m_windowSize.x + 1 );
  213. m_up_nY.resize( m_windowSize.y + 1 );
  214. // Precalc X values for camera -> ray generation
  215. const SFVEC3F right_nw = m_right * m_frustum.nw;
  216. for( unsigned int x = 0; x < ((unsigned int)m_windowSize.x + 1); ++x )
  217. m_right_nX[x] = right_nw * m_scr_nX[x];
  218. // Precalc Y values for camera -> ray generation
  219. const SFVEC3F up_nh = m_up * m_frustum.nh;
  220. for( unsigned int y = 0; y < ((unsigned int)m_windowSize.y + 1); ++y )
  221. m_up_nY[y] = up_nh * m_scr_nY[y];
  222. }
  223. }
  224. void CCAMERA::MakeRay( const SFVEC2I &aWindowPos,
  225. SFVEC3F &aOutOrigin,
  226. SFVEC3F &aOutDirection ) const
  227. {
  228. wxASSERT( aWindowPos.x < m_windowSize.x );
  229. wxASSERT( aWindowPos.y < m_windowSize.y );
  230. const SFVEC3F up_plus_right = m_up_nY[aWindowPos.y] +
  231. m_right_nX[aWindowPos.x];
  232. switch( m_projectionType )
  233. {
  234. default:
  235. case PROJECTION_PERSPECTIVE:
  236. aOutOrigin = up_plus_right + m_frustum.nc;
  237. aOutDirection = glm::normalize( aOutOrigin - m_pos );
  238. break;
  239. case PROJECTION_ORTHO:
  240. aOutOrigin = up_plus_right * 0.5f + m_frustum.nc;
  241. aOutDirection = -m_dir;
  242. break;
  243. }
  244. }
  245. void CCAMERA::MakeRay( const SFVEC2F &aWindowPos, SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection ) const
  246. {
  247. wxASSERT( aWindowPos.x < (float)m_windowSize.x );
  248. wxASSERT( aWindowPos.y < (float)m_windowSize.y );
  249. const SFVEC2F floorWinPos_f = glm::floor( aWindowPos );
  250. const SFVEC2I floorWinPos_i = (SFVEC2I)floorWinPos_f;
  251. const SFVEC2F relativeWinPos = aWindowPos - floorWinPos_f;
  252. // Note: size of vectors m_up and m_right are m_windowSize + 1
  253. const SFVEC3F up_plus_right = m_up_nY[floorWinPos_i.y] * (1.0f - relativeWinPos.y) +
  254. m_up_nY[floorWinPos_i.y + 1] * relativeWinPos.y +
  255. m_right_nX[floorWinPos_i.x] * (1.0f - relativeWinPos.x) +
  256. m_right_nX[floorWinPos_i.x + 1] * relativeWinPos.x;
  257. switch( m_projectionType )
  258. {
  259. default:
  260. case PROJECTION_PERSPECTIVE:
  261. aOutOrigin = up_plus_right + m_frustum.nc;
  262. aOutDirection = glm::normalize( aOutOrigin - m_pos );
  263. break;
  264. case PROJECTION_ORTHO:
  265. aOutOrigin = up_plus_right * 0.5f + m_frustum.nc;
  266. aOutDirection = -m_dir;
  267. break;
  268. }
  269. }
  270. void CCAMERA::MakeRayAtCurrrentMousePosition( SFVEC3F &aOutOrigin,
  271. SFVEC3F &aOutDirection ) const
  272. {
  273. MakeRay( SFVEC2I( m_lastPosition.x,
  274. m_windowSize.y - m_lastPosition.y ),
  275. aOutOrigin, aOutDirection );
  276. }
  277. const glm::mat4 &CCAMERA::GetProjectionMatrix() const
  278. {
  279. return m_projectionMatrix;
  280. }
  281. const glm::mat4 &CCAMERA::GetProjectionMatrixInv() const
  282. {
  283. return m_projectionMatrixInv;
  284. }
  285. void CCAMERA::ResetXYpos()
  286. {
  287. m_parametersChanged = true;
  288. m_camera_pos.x = 0.0f;
  289. m_camera_pos.y = 0.0f;
  290. updateViewMatrix();
  291. updateFrustum();
  292. }
  293. void CCAMERA::ResetXYpos_T1()
  294. {
  295. m_camera_pos_t1.x = 0.0f;
  296. m_camera_pos_t1.y = 0.0f;
  297. }
  298. const glm::mat4 &CCAMERA::GetViewMatrix() const
  299. {
  300. return m_viewMatrix;
  301. }
  302. const glm::mat4 &CCAMERA::GetViewMatrix_Inv() const
  303. {
  304. return m_viewMatrixInverse;
  305. }
  306. void CCAMERA::SetCurMousePosition( const wxPoint &aNewMousePosition )
  307. {
  308. m_lastPosition = aNewMousePosition;
  309. }
  310. void CCAMERA::SetProjection( PROJECTION_TYPE aProjectionType )
  311. {
  312. if( m_projectionType != aProjectionType )
  313. {
  314. m_projectionType = aProjectionType;
  315. rebuildProjection();
  316. }
  317. }
  318. void CCAMERA::ToggleProjection()
  319. {
  320. if( m_projectionType == PROJECTION_ORTHO )
  321. m_projectionType = PROJECTION_PERSPECTIVE;
  322. else
  323. m_projectionType = PROJECTION_ORTHO;
  324. rebuildProjection();
  325. }
  326. bool CCAMERA::SetCurWindowSize( const wxSize &aSize )
  327. {
  328. const SFVEC2I newSize = SFVEC2I( aSize.x, aSize.y );
  329. if( m_windowSize != newSize )
  330. {
  331. m_windowSize = newSize;
  332. rebuildProjection();
  333. return true;
  334. }
  335. return false;
  336. }
  337. void CCAMERA::ZoomReset()
  338. {
  339. m_zoom = 1.0f;
  340. m_camera_pos.z = m_camera_pos_init.z;
  341. updateViewMatrix();
  342. rebuildProjection();
  343. }
  344. bool CCAMERA::Zoom( float aFactor )
  345. {
  346. if ( ( m_zoom == MIN_ZOOM && aFactor > 1 ) || ( m_zoom == MAX_ZOOM && aFactor < 1 ) || aFactor == 1 )
  347. return false;
  348. m_zoom /= aFactor;
  349. if( m_zoom <= MIN_ZOOM )
  350. m_zoom = MIN_ZOOM;
  351. if( m_zoom >= MAX_ZOOM )
  352. m_zoom = MAX_ZOOM;
  353. m_camera_pos.z = m_camera_pos_init.z * m_zoom;
  354. updateViewMatrix();
  355. rebuildProjection();
  356. return true;
  357. }
  358. bool CCAMERA::Zoom_T1( float aFactor )
  359. {
  360. if( ( m_zoom == MIN_ZOOM && aFactor > 1 ) || ( m_zoom == MAX_ZOOM && aFactor < 1 ) || aFactor == 1 )
  361. return false;
  362. m_zoom_t1 = m_zoom / aFactor;
  363. if (m_zoom_t1 < MIN_ZOOM )
  364. m_zoom_t1 = MIN_ZOOM;
  365. if (m_zoom_t1 > MAX_ZOOM )
  366. m_zoom_t1 = MAX_ZOOM;
  367. m_camera_pos_t1.z = m_camera_pos_init.z * m_zoom_t1;
  368. return true;
  369. }
  370. float CCAMERA::ZoomGet() const
  371. {
  372. return m_zoom;
  373. }
  374. void CCAMERA::RotateX( float aAngleInRadians )
  375. {
  376. m_rotate_aux.x += aAngleInRadians;
  377. updateRotationMatrix();
  378. }
  379. void CCAMERA::RotateY( float aAngleInRadians )
  380. {
  381. m_rotate_aux.y += aAngleInRadians;
  382. updateRotationMatrix();
  383. }
  384. void CCAMERA::RotateZ( float aAngleInRadians )
  385. {
  386. m_rotate_aux.z += aAngleInRadians;
  387. updateRotationMatrix();
  388. }
  389. void CCAMERA::RotateX_T1( float aAngleInRadians )
  390. {
  391. m_rotate_aux_t1.x += aAngleInRadians;
  392. }
  393. void CCAMERA::RotateY_T1( float aAngleInRadians )
  394. {
  395. m_rotate_aux_t1.y += aAngleInRadians;
  396. }
  397. void CCAMERA::RotateZ_T1( float aAngleInRadians )
  398. {
  399. m_rotate_aux_t1.z += aAngleInRadians;
  400. }
  401. void CCAMERA::SetT0_and_T1_current_T()
  402. {
  403. m_camera_pos_t0 = m_camera_pos;
  404. m_lookat_pos_t0 = m_lookat_pos;
  405. m_rotate_aux_t0 = m_rotate_aux;
  406. m_zoom_t0 = m_zoom;
  407. m_camera_pos_t1 = m_camera_pos;
  408. m_lookat_pos_t1 = m_lookat_pos;
  409. m_rotate_aux_t1 = m_rotate_aux;
  410. m_zoom_t1 = m_zoom;
  411. }
  412. void CCAMERA::Interpolate( float t )
  413. {
  414. wxASSERT( t >= 0.0f );
  415. const float t0 = 1.0f - t;
  416. m_camera_pos = m_camera_pos_t0 * t0 + m_camera_pos_t1 * t;
  417. m_lookat_pos = m_lookat_pos_t0 * t0 + m_lookat_pos_t1 * t;
  418. m_rotate_aux = m_rotate_aux_t0 * t0 + m_rotate_aux_t1 * t;
  419. m_zoom = m_zoom_t0 * t0 + m_zoom_t1 * t;
  420. m_parametersChanged = true;
  421. updateRotationMatrix();
  422. rebuildProjection();
  423. }
  424. bool CCAMERA::ParametersChanged()
  425. {
  426. const bool parametersChanged = m_parametersChanged;
  427. m_parametersChanged = false;
  428. return parametersChanged;
  429. }