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.

661 lines
17 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: 3d_canvas.cpp
  3. /////////////////////////////////////////////////////////////////////////////
  4. #include "fctsys.h"
  5. #include "trigo.h"
  6. #include "wx/image.h"
  7. #if !wxUSE_GLCANVAS
  8. #error Please set wxUSE_GLCANVAS to 1 in setup.h.
  9. #endif
  10. #include "wx/dataobj.h"
  11. #include "wx/clipbrd.h"
  12. #include <wx/wupdlock.h>
  13. #include "gestfich.h"
  14. #include "3d_viewer.h"
  15. #include "trackball.h"
  16. /* Tool and button Bitmaps */
  17. #include "bitmaps.h"
  18. /*
  19. * Pcb3D_GLCanvas implementation
  20. */
  21. BEGIN_EVENT_TABLE( Pcb3D_GLCanvas, wxGLCanvas )
  22. EVT_PAINT( Pcb3D_GLCanvas::OnPaint )
  23. // key event:
  24. EVT_CHAR( Pcb3D_GLCanvas::OnChar )
  25. // mouse events
  26. EVT_RIGHT_DOWN( Pcb3D_GLCanvas::OnRightClick )
  27. EVT_MOUSEWHEEL( Pcb3D_GLCanvas::OnMouseWheel )
  28. EVT_MOTION( Pcb3D_GLCanvas::OnMouseMove )
  29. // other events
  30. EVT_ERASE_BACKGROUND( Pcb3D_GLCanvas::OnEraseBackground )
  31. EVT_MENU_RANGE( ID_POPUP_3D_VIEW_START, ID_POPUP_3D_VIEW_END,
  32. Pcb3D_GLCanvas::OnPopUpMenu )
  33. END_EVENT_TABLE()
  34. Pcb3D_GLCanvas::Pcb3D_GLCanvas( WinEDA3D_DrawFrame* parent ) :
  35. #if wxCHECK_VERSION( 2, 9, 0 )
  36. wxGLCanvas( parent, -1, NULL, wxDefaultPosition, wxDefaultSize,
  37. wxFULL_REPAINT_ON_RESIZE )
  38. #else
  39. wxGLCanvas( parent, -1, wxDefaultPosition, wxDefaultSize,
  40. wxFULL_REPAINT_ON_RESIZE )
  41. #endif
  42. {
  43. m_init = FALSE;
  44. m_gllist = 0;
  45. m_Parent = parent;
  46. #if wxCHECK_VERSION( 2, 9, 0 )
  47. // Explicitly create a new rendering context instance for this canvas.
  48. m_glRC = new wxGLContext( this );
  49. #endif
  50. DisplayStatus();
  51. }
  52. Pcb3D_GLCanvas::~Pcb3D_GLCanvas()
  53. {
  54. ClearLists();
  55. m_init = FALSE;
  56. #if wxCHECK_VERSION( 2, 9, 0 )
  57. delete m_glRC;
  58. #endif
  59. }
  60. void Pcb3D_GLCanvas::ClearLists()
  61. {
  62. if( m_gllist > 0 )
  63. glDeleteLists( m_gllist, 1 );
  64. m_gllist = 0;
  65. }
  66. void Pcb3D_GLCanvas::OnChar( wxKeyEvent& event )
  67. {
  68. SetView3D( event.GetKeyCode() );
  69. event.Skip();
  70. }
  71. void Pcb3D_GLCanvas::SetView3D( int keycode )
  72. {
  73. int ii;
  74. double delta_move = 0.7 * g_Parm_3D_Visu.m_Zoom;
  75. switch( keycode )
  76. {
  77. case WXK_LEFT:
  78. g_Draw3d_dx -= delta_move;
  79. break;
  80. case WXK_RIGHT:
  81. g_Draw3d_dx += delta_move;
  82. break;
  83. case WXK_UP:
  84. g_Draw3d_dy += delta_move;
  85. break;
  86. case WXK_DOWN:
  87. g_Draw3d_dy -= delta_move;
  88. break;
  89. case WXK_HOME:
  90. g_Parm_3D_Visu.m_Zoom = 1.0;
  91. g_Draw3d_dx = g_Draw3d_dy = 0;
  92. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  93. break;
  94. case WXK_END:
  95. break;
  96. case WXK_F1:
  97. g_Parm_3D_Visu.m_Zoom /= 1.4;
  98. if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
  99. g_Parm_3D_Visu.m_Zoom = 0.01;
  100. break;
  101. case WXK_F2:
  102. g_Parm_3D_Visu.m_Zoom *= 1.4;
  103. break;
  104. case '+':
  105. break;
  106. case '-':
  107. break;
  108. case 'r':
  109. case 'R':
  110. g_Draw3d_dx = g_Draw3d_dy = 0;
  111. for( ii = 0; ii < 4; ii++ )
  112. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  113. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  114. break;
  115. case 'x':
  116. for( ii = 0; ii < 4; ii++ )
  117. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  118. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  119. g_Parm_3D_Visu.m_ROTZ = -90;
  120. g_Parm_3D_Visu.m_ROTX = -90;
  121. break;
  122. case 'X':
  123. for( ii = 0; ii < 4; ii++ )
  124. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  125. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  126. g_Parm_3D_Visu.m_ROTZ = 90;
  127. g_Parm_3D_Visu.m_ROTX = -90;
  128. break;
  129. case 'y':
  130. for( ii = 0; ii < 4; ii++ )
  131. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  132. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  133. g_Parm_3D_Visu.m_ROTX = -90;
  134. break;
  135. case 'Y':
  136. for( ii = 0; ii < 4; ii++ )
  137. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  138. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  139. g_Parm_3D_Visu.m_ROTX = -90;
  140. g_Parm_3D_Visu.m_ROTZ = -180;
  141. break;
  142. case 'z':
  143. for( ii = 0; ii < 4; ii++ )
  144. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  145. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  146. break;
  147. case 'Z':
  148. for( ii = 0; ii < 4; ii++ )
  149. g_Parm_3D_Visu.m_Rot[ii] = 0.0;
  150. trackball( g_Parm_3D_Visu.m_Quat, 0.0, 0.0, 0.0, 0.0 );
  151. g_Parm_3D_Visu.m_ROTX = -180;
  152. break;
  153. default:
  154. return;
  155. }
  156. DisplayStatus();
  157. Refresh( FALSE );
  158. }
  159. void Pcb3D_GLCanvas::OnMouseWheel( wxMouseEvent& event )
  160. {
  161. wxSize size( GetClientSize() );
  162. if( event.ShiftDown() )
  163. {
  164. if( event.GetWheelRotation() < 0 )
  165. {
  166. /* up */
  167. SetView3D( WXK_UP );
  168. }
  169. else
  170. {
  171. /* down */
  172. SetView3D( WXK_DOWN );
  173. }
  174. }
  175. else if( event.ControlDown() )
  176. {
  177. if( event.GetWheelRotation() > 0 )
  178. {
  179. /* right */
  180. SetView3D( WXK_RIGHT );
  181. }
  182. else
  183. {
  184. /* left */
  185. SetView3D( WXK_LEFT );
  186. }
  187. }
  188. else
  189. {
  190. if( event.GetWheelRotation() > 0 )
  191. {
  192. g_Parm_3D_Visu.m_Zoom /= 1.4;
  193. if( g_Parm_3D_Visu.m_Zoom <= 0.01 )
  194. g_Parm_3D_Visu.m_Zoom = 0.01;
  195. }
  196. else
  197. g_Parm_3D_Visu.m_Zoom *= 1.4;
  198. DisplayStatus();
  199. Refresh( FALSE );
  200. }
  201. g_Parm_3D_Visu.m_Beginx = event.GetX();
  202. g_Parm_3D_Visu.m_Beginy = event.GetY();
  203. }
  204. void Pcb3D_GLCanvas::OnMouseMove( wxMouseEvent& event )
  205. {
  206. wxSize size( GetClientSize() );
  207. double spin_quat[4];
  208. if( event.Dragging() )
  209. {
  210. if( event.LeftIsDown() )
  211. {
  212. /* drag in progress, simulate trackball */
  213. trackball( spin_quat,
  214. (2.0 * g_Parm_3D_Visu.m_Beginx - size.x) / size.x,
  215. (size.y - 2.0 * g_Parm_3D_Visu.m_Beginy) / size.y,
  216. ( 2.0 * event.GetX() - size.x) / size.x,
  217. ( size.y - 2.0 * event.GetY() ) / size.y );
  218. add_quats( spin_quat, g_Parm_3D_Visu.m_Quat, g_Parm_3D_Visu.m_Quat );
  219. }
  220. else if( event.MiddleIsDown() )
  221. {
  222. /* middle button drag -> pan */
  223. /* Current zoom and an additional factor are taken into account
  224. * for the amount of panning. */
  225. const double PAN_FACTOR = 8.0 * g_Parm_3D_Visu.m_Zoom;
  226. g_Draw3d_dx -= PAN_FACTOR *
  227. ( g_Parm_3D_Visu.m_Beginx - event.GetX() ) / size.x;
  228. g_Draw3d_dy -= PAN_FACTOR *
  229. (event.GetY() - g_Parm_3D_Visu.m_Beginy) / size.y;
  230. }
  231. /* orientation has changed, redraw mesh */
  232. DisplayStatus();
  233. Refresh( FALSE );
  234. }
  235. g_Parm_3D_Visu.m_Beginx = event.GetX();
  236. g_Parm_3D_Visu.m_Beginy = event.GetY();
  237. }
  238. /* Construct and display a popup menu when the right button is clicked.
  239. */
  240. void Pcb3D_GLCanvas::OnRightClick( wxMouseEvent& event )
  241. {
  242. wxPoint pos;
  243. wxMenu PopUpMenu;
  244. pos.x = event.GetX(); pos.y = event.GetY();
  245. wxMenuItem* item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMIN,
  246. _( "Zoom +" ) );
  247. item->SetBitmap( zoom_in_xpm );
  248. PopUpMenu.Append( item );
  249. item = new wxMenuItem( &PopUpMenu, ID_POPUP_ZOOMOUT,
  250. _( "Zoom -" ) );
  251. item->SetBitmap( zoom_out_xpm );
  252. PopUpMenu.Append( item );
  253. PopUpMenu.AppendSeparator();
  254. item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZPOS,
  255. _( "Top View" ) );
  256. item->SetBitmap( axis3d_top_xpm );
  257. PopUpMenu.Append( item );
  258. item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_ZNEG,
  259. _( "Bottom View" ) );
  260. item->SetBitmap( axis3d_bottom_xpm );
  261. PopUpMenu.Append( item );
  262. PopUpMenu.AppendSeparator();
  263. item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XPOS,
  264. _( "Right View" ) );
  265. item->SetBitmap( axis3d_right_xpm );
  266. PopUpMenu.Append( item );
  267. item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_XNEG,
  268. _( "Left View" ) );
  269. item->SetBitmap( axis3d_left_xpm );
  270. PopUpMenu.Append( item );
  271. PopUpMenu.AppendSeparator();
  272. item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YPOS,
  273. _( "Front View" ) );
  274. item->SetBitmap( axis3d_front_xpm );
  275. PopUpMenu.Append( item );
  276. item = new wxMenuItem( &PopUpMenu, ID_POPUP_VIEW_YNEG,
  277. _( "Back View" ) );
  278. item->SetBitmap( axis3d_back_xpm );
  279. PopUpMenu.Append( item );
  280. PopUpMenu.AppendSeparator();
  281. item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_LEFT,
  282. _( "Move left <-" ) );
  283. item->SetBitmap( left_xpm );
  284. PopUpMenu.Append( item );
  285. item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_RIGHT,
  286. _( "Move right ->" ) );
  287. item->SetBitmap( right_xpm );
  288. PopUpMenu.Append( item );
  289. item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_UP,
  290. _( "Move Up ^" ) );
  291. item->SetBitmap( up_xpm );
  292. PopUpMenu.Append( item );
  293. item = new wxMenuItem( &PopUpMenu, ID_POPUP_MOVE3D_DOWN,
  294. _( "Move Down" ) );
  295. item->SetBitmap( down_xpm );
  296. PopUpMenu.Append( item );
  297. PopupMenu( &PopUpMenu, pos );
  298. }
  299. void Pcb3D_GLCanvas::OnPopUpMenu( wxCommandEvent& event )
  300. {
  301. int key = 0;
  302. switch( event.GetId() )
  303. {
  304. case ID_POPUP_ZOOMIN:
  305. key = WXK_F1;
  306. break;
  307. case ID_POPUP_ZOOMOUT:
  308. key = WXK_F2;
  309. break;
  310. case ID_POPUP_VIEW_XPOS:
  311. key = 'x';
  312. break;
  313. case ID_POPUP_VIEW_XNEG:
  314. key = 'X';
  315. break;
  316. case ID_POPUP_VIEW_YPOS:
  317. key = 'y';
  318. break;
  319. case ID_POPUP_VIEW_YNEG:
  320. key = 'Y';
  321. break;
  322. case ID_POPUP_VIEW_ZPOS:
  323. key = 'z';
  324. break;
  325. case ID_POPUP_VIEW_ZNEG:
  326. key = 'Z';
  327. break;
  328. case ID_POPUP_MOVE3D_LEFT:
  329. key = WXK_LEFT;
  330. break;
  331. case ID_POPUP_MOVE3D_RIGHT:
  332. key = WXK_RIGHT;
  333. break;
  334. case ID_POPUP_MOVE3D_UP:
  335. key = WXK_UP;
  336. break;
  337. case ID_POPUP_MOVE3D_DOWN:
  338. key = WXK_DOWN;
  339. break;
  340. default:
  341. return;
  342. }
  343. SetView3D( key );
  344. }
  345. void Pcb3D_GLCanvas::DisplayStatus()
  346. {
  347. wxString msg;
  348. msg.Printf( wxT( "dx %3.2f" ), g_Draw3d_dx );
  349. m_Parent->SetStatusText( msg, 1 );
  350. msg.Printf( wxT( "dy %3.2f" ), g_Draw3d_dy );
  351. m_Parent->SetStatusText( msg, 2 );
  352. msg.Printf( wxT( "View: %3.1f" ), 45 * g_Parm_3D_Visu.m_Zoom );
  353. m_Parent->SetStatusText( msg, 3 );
  354. }
  355. void Pcb3D_GLCanvas::OnPaint( wxPaintEvent& event )
  356. {
  357. wxPaintDC dc( this );
  358. Redraw();
  359. event.Skip();
  360. }
  361. void Pcb3D_GLCanvas::OnEraseBackground( wxEraseEvent& event )
  362. {
  363. // Do nothing, to avoid flashing.
  364. }
  365. /* Initialize broad parameters for OpenGL */
  366. void Pcb3D_GLCanvas::InitGL()
  367. {
  368. wxSize size = GetClientSize();
  369. if( !m_init )
  370. {
  371. m_init = TRUE;
  372. g_Parm_3D_Visu.m_Zoom = 1.0;
  373. ZBottom = 1.0; ZTop = 10.0;
  374. glDisable( GL_CULL_FACE ); // show back faces
  375. glEnable( GL_DEPTH_TEST ); // Enable z-buferring
  376. glEnable( GL_LINE_SMOOTH );
  377. glEnable( GL_COLOR_MATERIAL );
  378. glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
  379. /* speedups */
  380. glEnable( GL_DITHER );
  381. glShadeModel( GL_SMOOTH );
  382. glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST );
  383. glHint( GL_POLYGON_SMOOTH_HINT, GL_FASTEST );
  384. /* blend */
  385. glEnable( GL_BLEND );
  386. glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
  387. }
  388. /* set viewing projection */
  389. // Ratio width / height of the window display
  390. double ratio_HV = (double) size.x / size.y;
  391. glMatrixMode( GL_PROJECTION );
  392. glLoadIdentity();
  393. #define MAX_VIEW_ANGLE 160.0 / 45.0
  394. if( g_Parm_3D_Visu.m_Zoom > MAX_VIEW_ANGLE )
  395. g_Parm_3D_Visu.m_Zoom = MAX_VIEW_ANGLE;
  396. gluPerspective( 45.0 * g_Parm_3D_Visu.m_Zoom, ratio_HV, 1, 10 );
  397. // glFrustum(-1., 1.1F, -1.1F, 1.1F, ZBottom, ZTop);
  398. /* position viewer */
  399. glMatrixMode( GL_MODELVIEW );
  400. glLoadIdentity();
  401. glTranslatef( 0.0F, 0.0F, -( ZBottom + ZTop) / 2 );
  402. /* clear color and depth buffers */
  403. glClearColor( g_Parm_3D_Visu.m_BgColor.m_Red,
  404. g_Parm_3D_Visu.m_BgColor.m_Green,
  405. g_Parm_3D_Visu.m_BgColor.m_Blue, 1 );
  406. glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  407. /* Setup light souces: */
  408. SetLights();
  409. }
  410. /* Initialize OpenGL light sources. */
  411. void Pcb3D_GLCanvas::SetLights()
  412. {
  413. double light;
  414. GLfloat light_color[4];
  415. /* set viewing projection */
  416. light_color[3] = 1.0;
  417. GLfloat Z_axis_pos[4] = { 0.0, 0.0, 3.0, 0.0 };
  418. GLfloat lowZ_axis_pos[4] = { 0.0, 0.0, -3.0, 0.5 };
  419. /* activate light */
  420. light = 1.0;
  421. light_color[0] = light_color[1] = light_color[2] = light;
  422. glLightfv( GL_LIGHT0, GL_POSITION, Z_axis_pos );
  423. glLightfv( GL_LIGHT0, GL_DIFFUSE, light_color );
  424. light = 0.3;
  425. light_color[0] = light_color[1] = light_color[2] = light;
  426. glLightfv( GL_LIGHT1, GL_POSITION, lowZ_axis_pos );
  427. glLightfv( GL_LIGHT1, GL_DIFFUSE, light_color );
  428. glEnable( GL_LIGHT0 ); // White spot on Z axis
  429. glEnable( GL_LIGHT1 ); // White spot on Z axis ( bottom)
  430. glEnable( GL_LIGHTING );
  431. }
  432. /* Create a Screenshot of the current 3D view.
  433. * Output file format is png or jpeg, or image is copied to the clipboard
  434. */
  435. void Pcb3D_GLCanvas::TakeScreenshot( wxCommandEvent& event )
  436. {
  437. wxFileName fn( m_Parent->m_Parent->GetScreen()->m_FileName );
  438. wxString FullFileName;
  439. wxString file_ext, mask;
  440. bool fmt_is_jpeg = FALSE;
  441. if( event.GetId() == ID_MENU_SCREENCOPY_JPEG )
  442. fmt_is_jpeg = TRUE;
  443. if( event.GetId() != ID_TOOL_SCREENCOPY_TOCLIBBOARD )
  444. {
  445. file_ext = fmt_is_jpeg ? wxT( "jpg" ) : wxT( "png" );
  446. mask = wxT( "*." ) + file_ext;
  447. FullFileName = m_Parent->m_Parent->GetScreen()->m_FileName;
  448. fn.SetExt( file_ext );
  449. FullFileName =
  450. EDA_FileSelector( _( "3D Image filename:" ), wxEmptyString,
  451. fn.GetFullName(), file_ext, mask, this,
  452. wxFD_SAVE, TRUE );
  453. if( FullFileName.IsEmpty() )
  454. return;
  455. }
  456. Redraw( true );
  457. wxSize image_size = GetClientSize();
  458. #ifndef __WXMAC__
  459. wxClientDC dc( this );
  460. wxBitmap bitmap( image_size.x, image_size.y );
  461. wxMemoryDC memdc;
  462. memdc.SelectObject( bitmap );
  463. memdc.Blit( 0, 0, image_size.x, image_size.y, &dc, 0, 0 );
  464. memdc.SelectObject( wxNullBitmap );
  465. #else
  466. struct vieport_params
  467. {
  468. GLint originx;
  469. GLint originy;
  470. GLint x;
  471. GLint y;
  472. } viewport;
  473. wxWindowUpdateLocker noUpdates( this );
  474. glGetIntegerv( GL_VIEWPORT, (GLint*) &viewport );
  475. unsigned char* pixelbuffer = (unsigned char*) malloc( viewport.x * viewport.y * 3 );
  476. unsigned char* alphabuffer = (unsigned char*) malloc( viewport.x * viewport.y );
  477. wxImage image( viewport.x, viewport.y );
  478. glPixelStorei( GL_PACK_ALIGNMENT, 1 );
  479. glReadBuffer( GL_BACK_LEFT );
  480. glReadPixels( viewport.originx,
  481. viewport.originy,
  482. viewport.x,
  483. viewport.y,
  484. GL_RGB,
  485. GL_UNSIGNED_BYTE,
  486. pixelbuffer );
  487. glReadPixels( viewport.originx,
  488. viewport.originy,
  489. viewport.x,
  490. viewport.y,
  491. GL_ALPHA,
  492. GL_UNSIGNED_BYTE,
  493. alphabuffer );
  494. image.SetData( pixelbuffer );
  495. image.SetAlpha( alphabuffer );
  496. image = image.Mirror( false );
  497. wxBitmap bitmap( image );
  498. #endif
  499. if( event.GetId() == ID_TOOL_SCREENCOPY_TOCLIBBOARD )
  500. {
  501. wxBitmapDataObject* dobjBmp = new wxBitmapDataObject;
  502. dobjBmp->SetBitmap( bitmap );
  503. if( wxTheClipboard->Open() )
  504. {
  505. if( !wxTheClipboard->SetData( dobjBmp ) )
  506. wxMessageBox( _( "Failed to copy image to clipboard" ) );
  507. wxTheClipboard->Flush(); /* the data in clipboard will stay
  508. * available after the
  509. * application exits */
  510. wxTheClipboard->Close();
  511. }
  512. }
  513. else
  514. {
  515. wxImage image = bitmap.ConvertToImage();
  516. if( !image.SaveFile( FullFileName,
  517. fmt_is_jpeg ? wxBITMAP_TYPE_JPEG :
  518. wxBITMAP_TYPE_PNG ) )
  519. wxMessageBox( _( "Can't save file" ) );
  520. image.Destroy();
  521. }
  522. }