KiCad PCB EDA Suite
eda_3d_canvas.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) 1992-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 
30 #include <GL/glew.h> // Must be included first
31 #include <gl_utils.h>
32 #include <wx/tokenzr.h>
33 
34 #include "../common_ogl/ogl_utils.h"
35 #include "eda_3d_canvas.h"
36 #include <eda_3d_viewer.h>
39 #include <3d_viewer_id.h>
40 #include <class_board.h>
41 #include <reporter.h>
42 #include <gl_context_mgr.h>
43 #include <profile.h> // To use GetRunningMicroSecs or another profiling utility
44 #include <bitmaps.h>
45 #include <menus_helpers.h>
46 #include <pgm_base.h>
48 #include <tool/tool_dispatcher.h>
49 
51 
52 
60 const wxChar * EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
61 
63 
64 // A custom event, used to call DoRePaint during an idle time
65 wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent);
66 
67 
68 BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
69  EVT_PAINT( EDA_3D_CANVAS::OnPaint )
70 
71  // mouse events
72  EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
73  EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
74  EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
76  EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
77  EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
78 
79 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
80  EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
81 #endif
82 
83  // other events
84  EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
85  //EVT_REFRESH_CUSTOM_COMMAND( ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
86  EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
87 
88  EVT_CLOSE( EDA_3D_CANVAS::OnCloseWindow )
89  EVT_SIZE( EDA_3D_CANVAS::OnResize )
90 END_EVENT_TABLE()
91 
92 
93 EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList, BOARD* aBoard,
94  BOARD_ADAPTER& aBoardAdapter, CCAMERA& aCamera,
95  S3D_CACHE* a3DCachePointer )
96  : HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
97  wxFULL_REPAINT_ON_RESIZE ),
98  m_eventDispatcher( nullptr ),
99  m_parentStatusBar( nullptr ),
100  m_parentInfoBar( nullptr ),
101  m_glRC( nullptr ),
102  m_is_opengl_initialized( false ),
103  m_mouse_is_moving( false ),
104  m_mouse_was_moved( false ),
105  m_camera_is_moving( false ),
106  m_render_pivot( false ),
107  m_camera_moving_speed( 1.0f ),
108  m_strtime_camera_movement( 0 ),
109  m_animation_enabled( true ),
110  m_moving_speed_multiplier( 3 ),
111  m_boardAdapter( aBoardAdapter ),
112  m_camera( aCamera ),
113  m_3d_render( nullptr ),
114  m_opengl_supports_raytracing( false ),
115  m_render_raytracing_was_requested( false )
116 {
117  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::EDA_3D_CANVAS" );
118 
119  m_editing_timeout_timer.SetOwner( this );
120  Connect( m_editing_timeout_timer.GetId(),
121  wxEVT_TIMER,
122  wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ),
123  NULL,
124  this );
125 
126  m_redraw_trigger_timer.SetOwner( this );
127  Connect( m_redraw_trigger_timer.GetId(),
128  wxEVT_TIMER,
129  wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ),
130  NULL,
131  this );
132 
133  m_3d_render_raytracing = new C3D_RENDER_RAYTRACING( m_boardAdapter, m_camera );
134  m_3d_render_ogl_legacy = new C3D_RENDER_OGL_LEGACY( m_boardAdapter, m_camera );
135 
136  wxASSERT( m_3d_render_raytracing != NULL );
137  wxASSERT( m_3d_render_ogl_legacy != NULL );
138 
139  auto busy_indicator_factory = []() { return std::make_unique<WX_BUSY_INDICATOR>(); };
140 
141  m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
142  m_3d_render_ogl_legacy->SetBusyIndicatorFactory( busy_indicator_factory );
143 
144  RenderEngineChanged();
145 
146  wxASSERT( aBoard != NULL );
147  m_boardAdapter.SetBoard( aBoard );
148 
149  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
150 
151  wxASSERT( a3DCachePointer != NULL );
152  m_boardAdapter.Set3DCacheManager( a3DCachePointer );
153 
154  const wxEventType events[] =
155  {
156  // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
157  // especially special key like arrow keys, are handled by the GAL event dispatcher,
158  // and not sent to GUI without filtering, because they have a default action (scroll)
159  // that must not be called.
160  wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
161  wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
162  wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
163  wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
164 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
165  wxEVT_MAGNIFY,
166 #endif
167  wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
168  };
169 
170  for( wxEventType eventType : events )
171  Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), NULL, m_eventDispatcher );
172 }
173 
174 
176 {
177  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::~EDA_3D_CANVAS" );
178 
179  releaseOpenGL();
180 }
181 
182 
184 {
185  if( m_glRC )
186  {
188 
189  delete m_3d_render_raytracing;
191 
192  delete m_3d_render_ogl_legacy;
194 
195  // This is just a copy of a pointer, can safely be set to NULL
196  m_3d_render = NULL;
197 
200  m_glRC = NULL;
201  }
202 }
203 
204 
205 void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent &event )
206 {
207  releaseOpenGL();
208 
209  event.Skip();
210 }
211 
212 
213 void EDA_3D_CANVAS::OnResize( wxSizeEvent &event )
214 {
215  this->Request_refresh();
216 }
217 
218 
220 {
221  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL" );
222 
223  const GLenum err = glewInit();
224 
225  if( GLEW_OK != err )
226  {
227  const wxString msgError = (const char*) glewGetErrorString( err );
228 
229  wxLogMessage( msgError );
230 
231  return false;
232  }
233  else
234  {
235  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s",
236  FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
237  }
238 
239  wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
240 
241  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL version string %s.",
242  __WXFUNCTION__, version );
243 
244  // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
245  // getting the OpenGL major and minor version as integers didn't exist.
246  wxString tmp;
247 
248  wxStringTokenizer tokenizer( version );
249 
251 
252  if( tokenizer.HasMoreTokens() )
253  {
254  long major = 0;
255  long minor = 0;
256 
257  tmp = tokenizer.GetNextToken();
258 
259  tokenizer.SetString( tmp, wxString( "." ) );
260 
261  if( tokenizer.HasMoreTokens() )
262  tokenizer.GetNextToken().ToLong( &major );
263 
264  if( tokenizer.HasMoreTokens() )
265  tokenizer.GetNextToken().ToLong( &minor );
266 
267  if( major < 2 || ( (major == 2 ) && (minor < 1) ) )
268  {
269  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL ray tracing not supported.",
270  __WXFUNCTION__ );
271 
272  if( GetParent() )
273  {
274  wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
275  GetParent()->ProcessWindowEvent( evt );
276  }
277 
279  }
280  }
281 
284 
285  return true;
286 }
287 
288 
289 void EDA_3D_CANVAS::GetScreenshot( wxImage &aDstImage )
290 {
291  OGL_GetScreenshot( aDstImage );
292 }
293 
294 
295 void EDA_3D_CANVAS::ReloadRequest( BOARD *aBoard , S3D_CACHE *aCachePointer )
296 {
297  if( aCachePointer != NULL )
298  m_boardAdapter.Set3DCacheManager( aCachePointer );
299 
300  if( aBoard != NULL )
301  m_boardAdapter.SetBoard( aBoard );
302 
303  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
304 
305  if( m_3d_render )
307 }
308 
309 
311 {
313 
314  if( m_3d_render )
316 
318  //m_mouse_was_moved = true;
319 
320  Request_refresh();
321 }
322 
323 
325 {
326  if( m_parentStatusBar )
327  {
328  wxString msg;
329 
330  msg.Printf( "dx %3.2f", m_camera.GetCameraPos().x );
331  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
332 
333  msg.Printf( "dy %3.2f", m_camera.GetCameraPos().y );
334  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
335  }
336 }
337 
338 
339 void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
340 {
341  // Please have a look at:
342  // https://lists.launchpad.net/kicad-developers/msg25149.html
343  // wxPaintDC( this );
344  // aEvent.Skip( false );
345  DoRePaint();
346 }
347 
348 
350 {
351  // SwapBuffer requires the window to be shown before calling
352  if( !IsShownOnScreen() )
353  {
354  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::DoRePaint !IsShown" );
355  return;
356  }
357 
358  // Because the board to draw is handled by the parent viewer frame,
359  // ensure this parent is still alive. When it is closed before the viewer
360  // frame, a paint event can be generated after the parent is closed,
361  // therefore with invalid board.
362  // This is dependent of the platform.
363  // Especially on OSX, but also on Windows, it frequently happens
364  if( !GetParent()->GetParent()->IsShown() )
365  return; // The parent board editor frame is no more alive
366 
367  wxString err_messages;
368 
369  // !TODO: implement error reporter
370  //WX_STRING_REPORTER errorReporter( &err_messages );
371  INFOBAR_REPORTER warningReporter( m_parentInfoBar );
372  STATUSBAR_REPORTER activityReporter(
374 
375  unsigned strtime = GetRunningMicroSecs();
376 
377  // "Makes the OpenGL state that is represented by the OpenGL rendering
378  // context context current, i.e. it will be used by all subsequent OpenGL calls.
379  // This function may only be called when the window is shown on screen"
380 
381  // Explicitly create a new rendering context instance for this canvas.
382  if( m_glRC == NULL )
384 
386 
387  // Set the OpenGL viewport according to the client size of this canvas.
388  // This is done here rather than in a wxSizeEvent handler because our
389  // OpenGL rendering context (and thus viewport setting) is used with
390  // multiple canvases: If we updated the viewport in the wxSizeEvent
391  // handler, changing the size of one canvas causes a viewport setting that
392  // is wrong when next another canvas is repainted.
393  wxSize clientSize = GetNativePixelSize();
394 
395  const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
396 
397  // Initialize openGL if need
398  // /////////////////////////////////////////////////////////////////////////
400  {
401  if( !initializeOpenGL() )
402  {
404 
405  return;
406  }
407  }
408 
409  // Don't attend to ray trace if OpenGL doesn't support it.
411  {
415  }
416 
417  // Check if a raytacing was requested and need to switch to raytracing mode
419  {
420  const bool was_camera_changed = m_camera.ParametersChanged();
421 
422  // It reverts back to OpenGL mode if it was requested a raytracing
423  // render of the current scene. AND the mouse / camera is moving
424  if( ( m_mouse_is_moving ||
426  was_camera_changed ||
427  windows_size_changed ) &&
429  {
432  }
433  }
434 
435  float curtime_delta_s = 0.0f;
436 
437  if( m_camera_is_moving )
438  {
439  const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
440  curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
441  m_camera.Interpolate( curtime_delta_s );
442 
443  if( curtime_delta_s > 1.0f )
444  {
445  m_render_pivot = false;
446  m_camera_is_moving = false;
447  m_mouse_was_moved = true;
448 
450  DisplayStatus();
451  }
452  else
453  {
454  Request_refresh();
455  }
456  }
457 
458  // It will return true if the render request a new redraw
459  bool requested_redraw = false;
460 
461  if( m_3d_render )
462  {
463  m_3d_render->SetCurWindowSize( clientSize );
464 
465  requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
466  &activityReporter, &warningReporter );
467  }
468 
469  if( m_render_pivot )
470  {
471  const float scale = glm::min( m_camera.ZoomGet(), 1.0f );
472  render_pivot( curtime_delta_s, scale * scale );
473  }
474 
475  // "Swaps the double-buffer of this window, making the back-buffer the
476  // front-buffer and vice versa, so that the output of the previous OpenGL
477  // commands is displayed on the window."
478  SwapBuffers();
479 
481 
482  if( !activityReporter.HasMessage() )
483  {
485  {
486  // Calculation time in milliseconds
487  const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
488 
489  activityReporter.Report( wxString::Format( _( "Render time %.0f ms ( %.1f fps)" ),
490  calculation_time, 1000.0 / calculation_time ) );
491  }
492  }
493 
494  // This will reset the flag of camera parameters changed
496 
497  warningReporter.Finalize();
498 
499  if( !err_messages.IsEmpty() )
500  wxLogMessage( err_messages );
501 
502  if( (!m_camera_is_moving) && requested_redraw )
503  {
504  m_mouse_was_moved = false;
505  Request_refresh( false );
506  }
507 }
508 
509 
511 {
512  m_eventDispatcher = aEventDispatcher;
513 
514  if( m_eventDispatcher )
515  {
516  m_parent->Connect( wxEVT_TOOL,
517  wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ),
519  }
520  else
521  {
522  // While loop is used to be sure that all event handlers are removed.
523  while( m_parent->Disconnect( wxEVT_TOOL,
524  wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ),
525  NULL, m_eventDispatcher ) );
526  }
527 }
528 
529 
530 void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
531 {
532  if( !m_eventDispatcher )
533  aEvent.Skip();
534  else
536 
537  Refresh();
538 }
539 
540 
541 void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent &event )
542 {
543  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnEraseBackground" );
544  // Do nothing, to avoid flashing.
545 }
546 
547 
548 void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent &event )
549 {
550  bool mouseActivity = false;
551 
552  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnMouseWheel" );
553 
554  if( m_camera_is_moving )
555  return;
556 
557  float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
558 
560  delta_move *= (0.01f * event.GetWheelRotation());
561  else
562  if( event.GetWheelRotation() < 0 )
563  delta_move = -delta_move;
564 
565  // mousewheel_panning enabled:
566  // wheel -> pan;
567  // wheel + shift -> horizontal scrolling;
568  // wheel + ctrl -> zooming;
569  // mousewheel_panning disabled:
570  // wheel + shift -> vertical scrolling;
571  // wheel + ctrl -> horizontal scrolling;
572  // wheel -> zooming.
573 
574  if( m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) && !event.ControlDown() )
575  {
576  if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
577  m_camera.Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
578  else
579  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
580 
581  mouseActivity = true;
582  }
583  else if( event.ShiftDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
584  {
585  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
586  mouseActivity = true;
587  }
588  else if( event.ControlDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
589  {
590  m_camera.Pan( SFVEC3F( delta_move, 0.0f, 0.0f ) );
591  mouseActivity = true;
592  }
593  else
594  {
595  mouseActivity = m_camera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
596  }
597 
598  // If it results on a camera movement
599  if( mouseActivity )
600  {
601  DisplayStatus();
602  Request_refresh();
603 
604  m_mouse_is_moving = true;
605  m_mouse_was_moved = true;
606 
608  }
609 
610  // Update the cursor current mouse position on the camera
611  m_camera.SetCurMousePosition( event.GetPosition() );
612 }
613 
614 
615 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
616 void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
617 {
618  SetFocus();
619 
620  if( m_camera_is_moving )
621  return;
622 
623  //m_is_moving_mouse = true;
625 
626  float magnification = ( event.GetMagnification() + 1.0f );
627 
628  m_camera.Zoom( magnification );
629 
630  DisplayStatus();
631  Request_refresh();
632 }
633 #endif
634 
635 
636 void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent &event )
637 {
638  //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) );
639 
640  if( m_camera_is_moving )
641  return;
642 
644 
645  if( event.Dragging() )
646  {
647  if( event.LeftIsDown() ) // Drag
648  m_camera.Drag( event.GetPosition() );
649  else if( event.MiddleIsDown() ) // Pan
650  m_camera.Pan( event.GetPosition() );
651 
652  m_mouse_is_moving = true;
653  m_mouse_was_moved = true;
654 
655  // orientation has changed, redraw mesh
656  DisplayStatus();
657  Request_refresh();
658  }
659 
660  const wxPoint eventPosition = event.GetPosition();
661  m_camera.SetCurMousePosition( eventPosition );
662 }
663 
664 
665 void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent &event )
666 {
667  SetFocus();
669 }
670 
671 
672 void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent &event )
673 {
674  if( m_camera_is_moving )
675  return;
676 
677  if( m_mouse_is_moving )
678  {
679  m_mouse_is_moving = false;
681  }
682 }
683 
684 
685 void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent &event )
686 {
687  SetFocus();
689 }
690 
691 
692 void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent &event )
693 {
694  if( m_camera_is_moving )
695  return;
696 
697  if( m_mouse_is_moving )
698  {
699  m_mouse_is_moving = false;
701  }
702  else
703  {
705  }
706 }
707 
708 
709 void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent &event )
710 {
711  (void)event;
712 
713  m_mouse_is_moving = false;
714  m_mouse_was_moved = false;
715 
716  Request_refresh();
717 }
718 
719 
721 {
723 }
724 
725 
727 {
728  m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut() , wxTIMER_ONE_SHOT );
729 }
730 
731 
732 void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent &event )
733 {
734  Request_refresh( true );
735 }
736 
737 
738 void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
739 {
740  DoRePaint();
741 }
742 
743 
744 void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
745 {
746  if( aRedrawImmediately )
747  {
748  // Just calling Refresh() does not work always
749  // Using an event to call DoRepaint ensure the repaint code will be executed,
750  // and PostEvent will take priority to other events like mouse movements, keys, etc.
751  // and is executed during the next idle time
752  wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
753  wxPostEvent( this, redrawEvent );
754  }
755  else
756  {
757  // Schedule a timed redraw
758  m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
759  }
760 }
761 
762 
763 void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
764 {
765  wxASSERT( aMovingSpeed > FLT_EPSILON );
766 
767  // Fast forward the animation if the animation is disabled
768  if( !m_animation_enabled )
769  {
770  m_camera.Interpolate( 1.0f );
771  DisplayStatus();
772  Request_refresh();
773  return;
774  }
775 
776  // Map speed multipler option to actual multiplier value
777  // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
778  aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
779 
780  m_render_pivot = aRenderPivot;
781  m_camera_moving_speed = aMovingSpeed;
782 
784 
785  DisplayStatus();
786  Request_refresh();
787 
788  m_camera_is_moving = true;
789 
791 }
792 
793 
795 {
796  SFVEC3F rayOrigin;
797  SFVEC3F rayDir;
798 
799  // Generate a ray origin and direction based on current mouser position and camera
800  m_camera.MakeRayAtCurrrentMousePosition( rayOrigin, rayDir );
801 
802  RAY mouseRay;
803  mouseRay.Init( rayOrigin, rayDir );
804 
805  float hit_t;
806 
807  // Test it with the board bounding box
808  if( m_boardAdapter.GetBBox3DU().Intersect( mouseRay, &hit_t ) )
809  {
812  m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
814 
816  }
817 }
818 
819 
820 bool EDA_3D_CANVAS::SetView3D( int aKeycode )
821 {
822  if( m_camera_is_moving )
823  return false;
824 
825  const float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
826  const float arrow_moving_time_speed = 8.0f;
827  bool handled = false;
828 
829  switch( aKeycode )
830  {
831  case WXK_SPACE:
833  return true;
834 
835  case WXK_LEFT:
838  m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
839  request_start_moving_camera( arrow_moving_time_speed, false );
840  return true;
841 
842  case WXK_RIGHT:
845  m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
846  request_start_moving_camera( arrow_moving_time_speed, false );
847  return true;
848 
849  case WXK_UP:
852  m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
853  request_start_moving_camera( arrow_moving_time_speed, false );
854  return true;
855 
856  case WXK_DOWN:
859  m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
860  request_start_moving_camera( arrow_moving_time_speed, false );
861  return true;
862 
863  case WXK_HOME:
866  m_camera.Reset_T1();
867  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 1/1.26f ), 1.26f ) );
868  return true;
869 
870  case WXK_END:
871  break;
872 
873  case WXK_TAB:
876  m_camera.RotateZ_T1( glm::radians( 45.0f ) );
878  handled = true;
879  break;
880 
881  case WXK_F1:
884 
885  if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
887 
888  return true;
889 
890  case WXK_F2:
893 
894  if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
896 
897  return true;
898 
899  case ID_VIEW3D_RESET:
902  m_camera.Reset_T1();
903  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
904  return true;
905 
906  case ID_VIEW3D_RIGHT:
909  m_camera.Reset_T1();
910  m_camera.RotateZ_T1( glm::radians( -90.0f ) );
911  m_camera.RotateX_T1( glm::radians( -90.0f ) );
913  return true;
914 
915  case ID_VIEW3D_LEFT:
918  m_camera.Reset_T1();
919  m_camera.RotateZ_T1( glm::radians( 90.0f ) );
920  m_camera.RotateX_T1( glm::radians( -90.0f ) );
922  return true;
923 
924  case ID_VIEW3D_FRONT:
927  m_camera.Reset_T1();
928  m_camera.RotateX_T1( glm::radians( -90.0f ) );
930  return true;
931 
932  case ID_VIEW3D_BACK:
935  m_camera.Reset_T1();
936  m_camera.RotateX_T1( glm::radians( -90.0f ) );
937  // The rotation angle should be 180.
938  // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
939  // using 180 deg if the previous rotated position was already 180 deg
940  m_camera.RotateZ_T1( glm::radians( 179.999f ) );
942  return true;
943 
944  case ID_VIEW3D_TOP:
947  m_camera.Reset_T1();
948  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
949  return true;
950 
951  case ID_VIEW3D_BOTTOM:
954  m_camera.Reset_T1();
955  m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
956  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
957  return true;
958 
959  case ID_VIEW3D_FLIP:
962  m_camera.RotateY_T1( glm::radians( 179.999f ) );
964  return true;
965 
966  default:
967  return false;
968  }
969 
970  m_mouse_was_moved = true;
971 
973 
974  DisplayStatus();
975  Request_refresh();
976 
977  return handled;
978 }
979 
980 
982 {
983  switch( m_boardAdapter.RenderEngineGet() )
984  {
987  default: m_3d_render = NULL; break;
988  }
989 
990  if( m_3d_render )
992 
993  m_mouse_was_moved = false;
994 
995  Request_refresh();
996 }
C3D_RENDER_RAYTRACING * m_3d_render_raytracing
STATUSBAR_REPORTER is a wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:264
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition: ccamera.h:128
virtual wxSize GetNativePixelSize() const
void OGL_GetScreenshot(wxImage &aDstImage)
OGL_GetScreenshot - got the pixel data of current OpenGL image.
Definition: ogl_utils.cpp:36
bool GetFlag(DISPLAY3D_FLG aFlag) const
GetFlag - get a configuration status of a flag.
void OnMouseMove(wxMouseEvent &event)
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:104
void OnResize(wxSizeEvent &event)
void request_start_moving_camera(float aMovingSpeed=2.0f, bool aRenderPivot=true)
request_start_moving_camera - start a camera movement
void releaseOpenGL()
releaseOpenGL - free created targets and openGL context
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:114
virtual void SetT0_and_T1_current_T()
SetT0_and_T1_current_T - This will set T0 and T1 with the current values.
Definition: ccamera.cpp:555
bool m_mouse_is_moving
void OnRefreshRequest(wxEvent &aEvent)
void ResetXYpos_T1()
Definition: ccamera.cpp:412
unsigned m_strtime_camera_movement
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
WX_INFOBAR * m_parentInfoBar
void stop_editingTimeOut_Timer()
stop_editingTimeOut_Timer - stop the editing time, so it will not timeout
void Init(const SFVEC3F &o, const SFVEC3F &d)
Definition: ray.cpp:41
wxStatusBar * m_parentStatusBar
virtual bool Redraw(bool aIsMoving, REPORTER *aStatusReporter=NULL, REPORTER *aWarningReporter=NULL)=0
Redraw - Ask to redraw the view.
Class CCAMERA is a virtual class used to derive CCAMERA objects from.
Definition: ccamera.h:79
wxGLContext * m_glRC
const CBBOX & GetBBox3DU() const noexcept
GetBBox3DU - Get the bbox of the pcb board.
void OnCloseWindow(wxCloseEvent &event)
wxGLCanvas wrapper for HiDPI/Retina support.
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:47
wxTimer m_editing_timeout_timer
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
Definition: ray.h:67
void DisplayStatus()
DisplayStatus - Update the status bar with the position information.
C3D_RENDER_OGL_LEGACY * m_3d_render_ogl_legacy
S3D_CACHE.
Definition: 3d_cache.h:54
void Request_refresh(bool aRedrawImmediately=true)
Request_refresh - Schedule a refresh update of the canvas.
void ReloadRequest(BOARD *aBoard=NULL, S3D_CACHE *aCachePointer=NULL)
virtual int GetWaitForEditingTimeOut()=0
GetWaitForEditingTimeOut - Give the interface the time (in ms) that it should wait for editing or mov...
void UnlockCtx(wxGLContext *aContext)
Function UnlockCtx allows other canvases to bind an OpenGL context.
void RenderEngineSet(RENDER_ENGINE aRenderEngine) noexcept
RenderEngineSet.
SFVEC3F at(float t) const
Definition: ray.h:89
void DoRePaint()
The actual function to repaint the canvas.
void SetCurMousePosition(const wxPoint &aPosition)
It updates the current mouse position without make any new recalculations on camera.
Definition: ccamera.cpp:431
void SetBoard(BOARD *aBoard) noexcept
SetBoard - Set current board to be rendered.
BOARD_ADAPTER & m_boardAdapter
bool Zoom(float aFactor)
Definition: ccamera.cpp:474
void OnLeftDown(wxMouseEvent &event)
float m_camera_moving_speed
static GL_CONTEXT_MANAGER & Get()
Function Get returns the GL_CONTEXT_MANAGER instance (singleton).
void OnLeftUp(wxMouseEvent &event)
void restart_editingTimeOut_Timer()
restart_editingTimeOut_Timer - reset the editing timer
void RenderRaytracingRequest()
RenderRaytracingRequest - Request to render the current view in Raytracing mode.
void SetEventDispatcher(TOOL_DISPATCHER *aEventDispatcher)
Function SetEventDispatcher() Sets a dispatcher that processes events and forwards them to tools.
float ZoomGet() const
Definition: ccamera.cpp:510
#define NULL
void OnMiddleDown(wxMouseEvent &event)
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
CCAMERA & m_camera
bool m_mouse_was_moved
void OnPaint(wxPaintEvent &aEvent)
Called by a wxPaintEvent event.
void OnEraseBackground(wxEraseEvent &event)
void OnMouseWheel(wxMouseEvent &event)
void RenderEngineChanged()
RenderEngineChanged - Notify that the render engine was changed.
virtual void DispatchWxEvent(wxEvent &aEvent)
Processes wxEvents (mostly UI events), translates them to TOOL_EVENTs, and makes tools handle those.
bool m_camera_is_moving
virtual void Interpolate(float t)
Interpolate - It will update the matrix to interpolate between T0 and T1 values.
Definition: ccamera.cpp:569
virtual void Drag(const wxPoint &aNewMousePosition)=0
Calculate a new mouse drag position.
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1681
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Function Report is a pure virtual function to override in the derived object.
Definition: reporter.cpp:146
RENDER_ENGINE RenderEngineGet() const noexcept
RenderEngineGet.
void Finalize()
Update the infobar with the reported text.
Definition: reporter.cpp:179
bool SetCurWindowSize(const wxSize &aSize)
SetCurWindowSize - update the windows size of the camera.
Definition: ccamera.cpp:448
void OnTimerTimeout_Redraw(wxTimerEvent &event)
bool Zoom_T1(float aFactor)
Definition: ccamera.cpp:493
virtual void Pan_T1(const SFVEC3F &aDeltaOffsetInc)=0
void RotateX_T1(float aAngleInRadians)
Definition: ccamera.cpp:537
SETTINGS_MANAGER * GetSettingsManager()
virtual void Reset_T1()
Definition: ccamera.cpp:103
bool m_animation_enabled
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Function LockCtx sets a context as current and prevents other canvases from switching it.
void Set3DCacheManager(S3D_CACHE *aCachePointer) noexcept
Set3DCacheManager - Update the Cache manager pointer.
Definition: board_adapter.h:80
bool m_is_opengl_initialized
C3D_RENDER_BASE * m_3d_render
void OnMiddleUp(wxMouseEvent &event)
The C3D_RENDER_OGL_LEGACY class render the board using openGL legacy mode.
TOOL_DISPATCHER * m_eventDispatcher
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
int m_moving_speed_multiplier
const int scale
see class PGM_BASE
bool initializeOpenGL()
initializeOpenGL
void OnEvent(wxEvent &aEvent)
Used to forward events to the canvas from popups, etc.
bool m_opengl_supports_raytracing
Declaration of the eda_3d_viewer class.
void RotateY_T1(float aAngleInRadians)
Definition: ccamera.cpp:543
void MakeRayAtCurrrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
MakeRayAtCurrrentMousePosition - Make a ray based on the latest mouse position.
Definition: ccamera.cpp:380
void move_pivot_based_on_cur_mouse_position()
move_pivot_based_on_cur_mouse_position - This function hits a ray to the board and start a moviment
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
Information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:176
virtual void DispatchWxCommand(wxCommandEvent &aEvent)
Processes wxCommands (mostly menu related events) and runs appropriate actions (eg.
#define _(s)
Definition: 3d_actions.cpp:33
bool Intersect(const RAY &aRay, float *t) const
Function Intersect.
Definition: cbbox_ray.cpp:46
unsigned GetRunningMicroSecs()
Function GetRunningMicroSecs An alternate way to calculate an elapset time (in microsecondes) to clas...
virtual void Pan(const wxPoint &aNewMousePosition)=0
glm::vec3 SFVEC3F
Definition: xv3d_types.h:47
bool SetView3D(int aKeycode)
SetView3D - Helper function to call view commands.
void OnTimerTimeout_Editing(wxTimerEvent &event)
const SFVEC3F & GetCameraPos() const
Definition: ccamera.h:134
void DestroyCtx(wxGLContext *aContext)
Function DestroyCtx destroys a managed OpenGL context.
void SetColorSettings(COLOR_SETTINGS *aSettings) noexcept
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=NULL)
Function CreateCtx creates a managed OpenGL context.
virtual void SetCurWindowSize(const wxSize &aSize)=0
SetCurWindowSize - Before each render, the canvas will tell the render what is the size of its window...
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:41
void ReloadRequest()
ReloadRequest - !TODO: this must be reviewed to add flags to improve specific render.
void RotateZ_T1(float aAngleInRadians)
Definition: ccamera.cpp:549
wxTimer m_redraw_trigger_timer
Class BOARD_ADAPTER Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:68
static const float m_delta_move_step_factor
void render_pivot(float t, float aScale)
render_pivot - render the pivot cursor
INFOBAR_REPORTER is a wrapper for reporting to a WX_INFOBAR UI element.
Definition: reporter.h:292
void SetInterpolateMode(CAMERA_INTERPOLATION aInterpolateMode)
Definition: ccamera.h:200
bool HasMessage() const override
Function HasMessage Returns true if the reporter client is non-empty.
Definition: reporter.cpp:154
bool ParametersChanged()
Function ParametersChanged.
Definition: ccamera.cpp:587
bool m_render_raytracing_was_requested