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.

471 lines
14 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 c3d_model_viewer.cpp
  26. * @brief Implements a model viewer canvas. The propose of model viewer is to
  27. * render 3d models that come in the original data from the files without any
  28. * transformations.
  29. */
  30. #include <iostream>
  31. #include "3d_rendering/3d_render_ogl_legacy/c_ogl_3dmodel.h"
  32. #include "c3d_model_viewer.h"
  33. #include "../3d_rendering/3d_render_ogl_legacy/ogl_legacy_utils.h"
  34. #include "../3d_cache/3d_cache.h"
  35. #include "common_ogl/ogl_utils.h"
  36. #include <wx/dcclient.h>
  37. #include <base_units.h>
  38. #include <gl_context_mgr.h>
  39. /**
  40. * Scale convertion from 3d model units to pcb units
  41. */
  42. #define UNITS3D_TO_UNITSPCB (IU_PER_MM)
  43. /**
  44. * Trace mask used to enable or disable the trace output of this class.
  45. * The debug output can be turned on by setting the WXTRACE environment variable to
  46. * "KI_TRACE_EDA_3D_MODEL_VIEWER". See the wxWidgets documentation on wxLogTrace for
  47. * more information.
  48. */
  49. const wxChar * C3D_MODEL_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_MODEL_VIEWER" );
  50. BEGIN_EVENT_TABLE( C3D_MODEL_VIEWER, wxGLCanvas )
  51. EVT_PAINT( C3D_MODEL_VIEWER::OnPaint )
  52. // mouse events
  53. EVT_LEFT_DOWN( C3D_MODEL_VIEWER::OnLeftDown )
  54. EVT_LEFT_UP( C3D_MODEL_VIEWER::OnLeftUp )
  55. EVT_MIDDLE_UP( C3D_MODEL_VIEWER::OnMiddleUp )
  56. EVT_MIDDLE_DOWN(C3D_MODEL_VIEWER::OnMiddleDown)
  57. EVT_RIGHT_DOWN( C3D_MODEL_VIEWER::OnRightClick )
  58. EVT_MOUSEWHEEL( C3D_MODEL_VIEWER::OnMouseWheel )
  59. EVT_MOTION( C3D_MODEL_VIEWER::OnMouseMove )
  60. #ifdef USE_OSX_MAGNIFY_EVENT
  61. EVT_MAGNIFY( C3D_MODEL_VIEWER::OnMagnify )
  62. #endif
  63. // other events
  64. EVT_ERASE_BACKGROUND( C3D_MODEL_VIEWER::OnEraseBackground )
  65. END_EVENT_TABLE()
  66. // This defines the range that all coord will have to be rendered.
  67. // It will use this value to convert to a normalized value between
  68. // -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
  69. #define RANGE_SCALE_3D 8.0f
  70. C3D_MODEL_VIEWER::C3D_MODEL_VIEWER(wxWindow *aParent,
  71. const int *aAttribList , S3D_CACHE *aCacheManager) :
  72. HIDPI_GL_CANVAS( aParent,
  73. wxID_ANY,
  74. aAttribList,
  75. wxDefaultPosition,
  76. wxDefaultSize,
  77. wxFULL_REPAINT_ON_RESIZE ),
  78. m_trackBallCamera( RANGE_SCALE_3D * 2.0f ),
  79. m_cacheManager(aCacheManager)
  80. {
  81. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::C3D_MODEL_VIEWER" ) );
  82. m_ogl_initialized = false;
  83. m_reload_is_needed = false;
  84. m_ogl_3dmodel = NULL;
  85. m_3d_model = NULL;
  86. m_BiuTo3Dunits = 1.0;
  87. m_glRC = NULL;
  88. }
  89. C3D_MODEL_VIEWER::~C3D_MODEL_VIEWER()
  90. {
  91. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::~C3D_MODEL_VIEWER" ) );
  92. if( m_glRC )
  93. {
  94. GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this );
  95. delete m_ogl_3dmodel;
  96. m_ogl_3dmodel = NULL;
  97. GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
  98. GL_CONTEXT_MANAGER::Get().DestroyCtx( m_glRC );
  99. }
  100. }
  101. void C3D_MODEL_VIEWER::Set3DModel( const S3DMODEL &a3DModel )
  102. {
  103. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
  104. // Validate a3DModel pointers
  105. wxASSERT( a3DModel.m_Materials != NULL );
  106. wxASSERT( a3DModel.m_Meshes != NULL );
  107. wxASSERT( a3DModel.m_MaterialsSize > 0 );
  108. wxASSERT( a3DModel.m_MeshesSize > 0 );
  109. // Delete the old model
  110. delete m_ogl_3dmodel;
  111. m_ogl_3dmodel = NULL;
  112. m_3d_model = NULL;
  113. if( (a3DModel.m_Materials != NULL) && (a3DModel.m_Meshes != NULL) &&
  114. (a3DModel.m_MaterialsSize > 0) && (a3DModel.m_MeshesSize > 0) )
  115. {
  116. m_3d_model = &a3DModel;
  117. m_reload_is_needed = true;
  118. }
  119. Refresh();
  120. }
  121. void C3D_MODEL_VIEWER::Set3DModel(const wxString &aModelPathName)
  122. {
  123. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
  124. if( m_cacheManager )
  125. {
  126. const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName );
  127. if( model )
  128. Set3DModel( (const S3DMODEL &)*model );
  129. else
  130. Clear3DModel();
  131. }
  132. }
  133. void C3D_MODEL_VIEWER::Clear3DModel()
  134. {
  135. // Delete the old model
  136. m_reload_is_needed = false;
  137. delete m_ogl_3dmodel;
  138. m_ogl_3dmodel = NULL;
  139. m_3d_model = NULL;
  140. Refresh();
  141. }
  142. void C3D_MODEL_VIEWER::ogl_initialize()
  143. {
  144. glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
  145. glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
  146. glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
  147. glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
  148. glEnable( GL_DEPTH_TEST );
  149. //glDepthFunc( GL_LEQUAL );
  150. glEnable( GL_CULL_FACE );
  151. glShadeModel( GL_SMOOTH );
  152. glEnable( GL_LINE_SMOOTH );
  153. glEnable( GL_NORMALIZE );
  154. // Setup light
  155. // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
  156. // /////////////////////////////////////////////////////////////////////////
  157. const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
  158. const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  159. const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  160. // defines a directional light that points along the negative z-axis
  161. const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
  162. const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
  163. glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
  164. glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
  165. glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
  166. glLightfv( GL_LIGHT0, GL_POSITION, position );
  167. glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
  168. }
  169. void C3D_MODEL_VIEWER::ogl_set_arrow_material()
  170. {
  171. glEnable( GL_COLOR_MATERIAL );
  172. glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
  173. const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
  174. glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
  175. glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
  176. }
  177. void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent &event )
  178. {
  179. wxPaintDC( this );
  180. event.Skip( false );
  181. // SwapBuffer requires the window to be shown before calling
  182. if( !IsShownOnScreen() )
  183. {
  184. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnPaint !IsShown" ) );
  185. return;
  186. }
  187. // "Makes the OpenGL state that is represented by the OpenGL rendering
  188. // context context current, i.e. it will be used by all subsequent OpenGL calls.
  189. // This function may only be called when the window is shown on screen"
  190. if( m_glRC == NULL )
  191. m_glRC = GL_CONTEXT_MANAGER::Get().CreateCtx( this );
  192. GL_CONTEXT_MANAGER::Get().LockCtx( m_glRC, this );
  193. // Set the OpenGL viewport according to the client size of this canvas.
  194. // This is done here rather than in a wxSizeEvent handler because our
  195. // OpenGL rendering context (and thus viewport setting) is used with
  196. // multiple canvases: If we updated the viewport in the wxSizeEvent
  197. // handler, changing the size of one canvas causes a viewport setting that
  198. // is wrong when next another canvas is repainted.
  199. wxSize clientSize = GetClientSize();
  200. if( !m_ogl_initialized )
  201. {
  202. m_ogl_initialized = true;
  203. ogl_initialize();
  204. }
  205. if( m_reload_is_needed )
  206. {
  207. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
  208. m_reload_is_needed = false;
  209. m_ogl_3dmodel = new C_OGL_3DMODEL( *m_3d_model, MATERIAL_MODE_NORMAL );
  210. // It convert a model as it was a board, so get the max size dimension of the board
  211. // and compute the conversion scale
  212. m_BiuTo3Dunits = (double)RANGE_SCALE_3D /
  213. ( (double)m_ogl_3dmodel->GetBBox().GetMaxDimension() *
  214. UNITS3D_TO_UNITSPCB );
  215. }
  216. glViewport( 0, 0, clientSize.x, clientSize.y );
  217. m_trackBallCamera.SetCurWindowSize( clientSize );
  218. // clear color and depth buffers
  219. // /////////////////////////////////////////////////////////////////////////
  220. glEnable( GL_DEPTH_TEST );
  221. glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
  222. glClearDepth( 1.0f );
  223. glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  224. // Set projection and modelview matrixes
  225. // /////////////////////////////////////////////////////////////////////////
  226. glMatrixMode( GL_PROJECTION );
  227. glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
  228. glMatrixMode( GL_MODELVIEW );
  229. glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
  230. glEnable( GL_LIGHTING );
  231. glEnable( GL_LIGHT0 );
  232. // Render Model
  233. if( m_ogl_3dmodel )
  234. {
  235. glPushMatrix();
  236. double modelunit_to_3d_units_factor = m_BiuTo3Dunits * UNITS3D_TO_UNITSPCB;
  237. glScaled( modelunit_to_3d_units_factor,
  238. modelunit_to_3d_units_factor,
  239. modelunit_to_3d_units_factor );
  240. // Center model in the render viewport
  241. const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
  242. glTranslatef( -model_center.x, -model_center.y, -model_center.z );
  243. m_ogl_3dmodel->Draw_opaque();
  244. m_ogl_3dmodel->Draw_transparent();
  245. glPopMatrix();
  246. }
  247. // YxY squared view port
  248. glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
  249. glClear( GL_DEPTH_BUFFER_BIT );
  250. glMatrixMode( GL_PROJECTION );
  251. glLoadIdentity();
  252. gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
  253. glMatrixMode( GL_MODELVIEW );
  254. glLoadIdentity();
  255. const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
  256. SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
  257. const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
  258. glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
  259. ogl_set_arrow_material();
  260. glColor3f( 0.9f, 0.0f, 0.0f );
  261. OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ),
  262. SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
  263. 0.275f );
  264. glColor3f( 0.0f, 0.9f, 0.0f );
  265. OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ),
  266. SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
  267. 0.275f );
  268. glColor3f( 0.0f, 0.0f, 0.9f );
  269. OGL_draw_arrow( SFVEC3F( 0.0f, 0.0f, 0.0f ),
  270. SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
  271. 0.275f );
  272. // "Swaps the double-buffer of this window, making the back-buffer the
  273. // front-buffer and vice versa, so that the output of the previous OpenGL
  274. // commands is displayed on the window."
  275. SwapBuffers();
  276. GL_CONTEXT_MANAGER::Get().UnlockCtx( m_glRC );
  277. }
  278. void C3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent &event )
  279. {
  280. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnEraseBackground" ) );
  281. // Do nothing, to avoid flashing.
  282. }
  283. void C3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent &event )
  284. {
  285. wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnMouseWheel" ) );
  286. if( event.ShiftDown() )
  287. {
  288. //if( event.GetWheelRotation() < 0 )
  289. //SetView3D( WXK_UP ); // move up
  290. //else
  291. //SetView3D( WXK_DOWN ); // move down
  292. }
  293. else if( event.ControlDown() )
  294. {
  295. //if( event.GetWheelRotation() > 0 )
  296. //SetView3D( WXK_RIGHT ); // move right
  297. //else
  298. //SetView3D( WXK_LEFT ); // move left
  299. }
  300. else
  301. {
  302. m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
  303. //DisplayStatus();
  304. Refresh( false );
  305. }
  306. m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
  307. }
  308. #ifdef USE_OSX_MAGNIFY_EVENT
  309. void C3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
  310. {
  311. /*
  312. double magnification = ( event.GetMagnification() + 1.0f );
  313. GetPrm3DVisu().m_Zoom /= magnification;
  314. if( GetPrm3DVisu().m_Zoom <= 0.01 )
  315. GetPrm3DVisu().m_Zoom = 0.01;
  316. DisplayStatus();
  317. Refresh( false );
  318. */
  319. }
  320. #endif
  321. void C3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent &event )
  322. {
  323. m_trackBallCamera.SetCurWindowSize( GetClientSize() );
  324. if( event.Dragging() )
  325. {
  326. if( event.LeftIsDown() ) // Drag
  327. m_trackBallCamera.Drag( event.GetPosition() );
  328. //else if( event.MiddleIsDown() ) // Pan
  329. // m_trackBallCamera.Pan( event.GetPosition() );
  330. // orientation has changed, redraw mesh
  331. Refresh( false );
  332. }
  333. m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
  334. }
  335. void C3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent &event )
  336. {
  337. //m_is_moving_mouse = true;
  338. event.Skip();
  339. }
  340. void C3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent &event )
  341. {
  342. //m_is_moving_mouse = false;
  343. //Refresh( false );
  344. event.Skip();
  345. }
  346. void C3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent &event )
  347. {
  348. //m_is_moving_mouse = true;
  349. event.Skip();
  350. }
  351. void C3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent &event )
  352. {
  353. //m_is_moving_mouse = false;
  354. //Refresh( false );
  355. event.Skip();
  356. }
  357. void C3D_MODEL_VIEWER::OnRightClick( wxMouseEvent &event )
  358. {
  359. event.Skip();
  360. }